Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Created confirmRegistration, rejectRegistration GraphQL-endpoints #7925

Merged
merged 14 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ services:
- NODE_ENV=development
- FHIR_URL=http://hearth:3447/fhir
- AUTH_URL=http://auth:4040
- CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration
- CHECK_INVALID_TOKEN=true
ports:
- '3040:3040'
Expand Down
3 changes: 2 additions & 1 deletion packages/commons/src/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export const userScopes = {
bypassRateLimit: 'bypassratelimit',
teams: 'teams',
config: 'config',
confirmRegistration: 'record.confirm-registration'
confirmRegistration: 'record.confirm-registration',
rejectRegistration: 'record.reject-registration'
} as const

export const userRoleScopes = {
Expand Down
44 changes: 36 additions & 8 deletions packages/gateway/src/features/registration/root-resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ import {
duplicateRegistration,
viewDeclaration,
verifyRegistration,
markNotADuplicate
markNotADuplicate,
rejectRegistration,
confirmRegistration
} from '@gateway/workflow/index'
import { getRecordById } from '@gateway/records'

Expand Down Expand Up @@ -636,20 +638,46 @@ export const resolvers: GQLResolver = {

return taskEntry.resource.id
},
async confirmRegistration(_, { id }, { headers: authHeader }) {
async confirmRegistration(_, { id, details }, { headers: authHeader }) {
if (!inScope(authHeader, ['record.confirm-registration'])) {
throw new Error(
'User does not have a "record.confirm-registration" scope'
)
throw new Error('User does not have a Confirm Registration scope')
}

if (!hasRecordAccess(authHeader, id)) {
throw new Error('User does not have access to the record')
}

try {
const taskEntry = await confirmRegistration(id, authHeader, {
error: details.error,
registrationNumber: details.registrationNumber,
identifiers: details.identifiers
})

return taskEntry.resource.id
} catch (error) {
throw new Error(`Failed to confirm registration: ${error.message}`)
}
},
async rejectRegistration(_, { id, details }, { headers: authHeader }) {
if (!inScope(authHeader, ['record.reject-registration'])) {
throw new Error('User does not have a Reject Registration" scope')
}

if (!hasRecordAccess(authHeader, id)) {
throw new Error('User does not have access to the record')
}

// @TODO this is a no-op, only to test the token exchange actually works
// An upcoming pull request will implement this and a `rejectRegistration` mutations
return id
try {
const taskEntry = await rejectRegistration(id, authHeader, {
comment: details.comment || 'No comment provided',
reason: details.reason
})

return taskEntry.resource.id
} catch (error) {
throw new Error(`Error in rejectRegistration: ${error.message}`)
}
}
}
}
Expand Down
19 changes: 18 additions & 1 deletion packages/gateway/src/features/registration/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,22 @@ input CorrectionRejectionInput {
timeLoggedMS: Int!
}

input IdentifierInput {
type: String!
value: String!
}

input ConfirmRegistrationInput {
registrationNumber: String!
error: String
identifiers: [IdentifierInput!]
}

input RejectRegistrationInput {
reason: String!
comment: String
}

type Mutation {
# Generic correction handlers for all event types
# Applying a correction request is made on a event level as payload is dependant on event type
Expand Down Expand Up @@ -643,5 +659,6 @@ type Mutation {
comment: String
duplicateTrackingId: String
): ID!
confirmRegistration(id: ID!): ID!
confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID!
rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID!
}
35 changes: 35 additions & 0 deletions packages/gateway/src/graphql/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface GQLMutation {
markMarriageAsIssued: string
markEventAsDuplicate: string
confirmRegistration: string
rejectRegistration: string
createOrUpdateUser: GQLUser
activateUser?: string
changePassword?: string
Expand Down Expand Up @@ -576,6 +577,17 @@ export interface GQLReinstated {
registrationStatus?: GQLRegStatus
}

export interface GQLConfirmRegistrationInput {
registrationNumber: string
error?: string
identifiers?: Array<GQLIdentifierInput>
}

export interface GQLRejectRegistrationInput {
reason: string
comment?: string
}

export interface GQLUserInput {
id?: string
name: Array<GQLHumanNameInput>
Expand Down Expand Up @@ -1169,6 +1181,11 @@ export const enum GQLRegStatus {
ISSUED = 'ISSUED'
}

export interface GQLIdentifierInput {
type: string
value: string
}

export interface GQLHumanNameInput {
use?: string
firstNames?: string
Expand Down Expand Up @@ -2452,6 +2469,7 @@ export interface GQLMutationTypeResolver<TParent = any> {
markMarriageAsIssued?: MutationToMarkMarriageAsIssuedResolver<TParent>
markEventAsDuplicate?: MutationToMarkEventAsDuplicateResolver<TParent>
confirmRegistration?: MutationToConfirmRegistrationResolver<TParent>
rejectRegistration?: MutationToRejectRegistrationResolver<TParent>
createOrUpdateUser?: MutationToCreateOrUpdateUserResolver<TParent>
activateUser?: MutationToActivateUserResolver<TParent>
changePassword?: MutationToChangePasswordResolver<TParent>
Expand Down Expand Up @@ -2986,6 +3004,7 @@ export interface MutationToMarkEventAsDuplicateResolver<

export interface MutationToConfirmRegistrationArgs {
id: string
details: GQLConfirmRegistrationInput
}
export interface MutationToConfirmRegistrationResolver<
TParent = any,
Expand All @@ -2999,6 +3018,22 @@ export interface MutationToConfirmRegistrationResolver<
): TResult
}

export interface MutationToRejectRegistrationArgs {
id: string
details: GQLRejectRegistrationInput
}
export interface MutationToRejectRegistrationResolver<
TParent = any,
TResult = any
> {
(
parent: TParent,
args: MutationToRejectRegistrationArgs,
context: Context,
info: GraphQLResolveInfo
): TResult
}

export interface MutationToCreateOrUpdateUserArgs {
user: GQLUserInput
}
Expand Down
19 changes: 18 additions & 1 deletion packages/gateway/src/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ type Mutation {
comment: String
duplicateTrackingId: String
): ID!
confirmRegistration(id: ID!): ID!
confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID!
rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID!
createOrUpdateUser(user: UserInput!): User!
activateUser(
userId: String!
Expand Down Expand Up @@ -697,6 +698,17 @@ type Reinstated {
registrationStatus: RegStatus
}

input ConfirmRegistrationInput {
registrationNumber: String!
error: String
identifiers: [IdentifierInput!]
}

input RejectRegistrationInput {
reason: String!
comment: String
}

input UserInput {
id: ID
name: [HumanNameInput!]!
Expand Down Expand Up @@ -1266,6 +1278,11 @@ enum RegStatus {
ISSUED
}

input IdentifierInput {
type: String!
value: String!
}

input HumanNameInput {
use: String
firstNames: String
Expand Down
46 changes: 46 additions & 0 deletions packages/gateway/src/workflow/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ import {
GQLPaymentInput
} from '@gateway/graphql/schema'

export type IdentifierInput = {
type: string
value: string
}

const createRequest = async <T = any>(
method: 'POST' | 'GET' | 'PUT' | 'DELETE',
path: string,
Expand Down Expand Up @@ -235,6 +240,47 @@ export async function markNotADuplicate(id: string, authHeader: IAuthHeader) {
return getComposition(response)
}

export async function confirmRegistration(
id: string,
authHeader: IAuthHeader,
details: {
error: string | undefined
registrationNumber: string
identifiers?: IdentifierInput[]
}
) {
const res: ReadyForReviewRecord = await createRequest(
'POST',
`/records/${id}/confirm`,
authHeader,
details
)

const taskEntry = res.entry.find((e) => e.resource.resourceType === 'Task')
if (!taskEntry) {
throw new Error('No task entry found in the confirmation response')
}

return taskEntry
}

export async function rejectRegistration(
recordId: string,
authHeader: IAuthHeader,
details: { comment: string; reason: string }
) {
const res: RejectedRecord = await createRequest(
'POST',
`/records/${recordId}/reject`,
authHeader,
details
)

const taskEntry = res.entry.find((e) => e.resource.resourceType === 'Task')!

return taskEntry
}

export async function archiveRegistration(
id: string,
authHeader: IAuthHeader,
Expand Down
2 changes: 1 addition & 1 deletion packages/workflow/src/config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const getRoutes = () => {
},
{
method: 'POST',
path: '/confirm/registration',
path: '/records/{id}/confirm',
handler: markEventAsRegisteredCallbackHandler,
config: {
tags: ['api'],
Expand Down
8 changes: 4 additions & 4 deletions packages/workflow/src/features/registration/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ export interface EventRegistrationPayload {
trackingId: string
registrationNumber: string
error: string
compositionId: string
childIdentifiers?: {
identifiers?: {
type: SupportedPatientIdentifierCode
value: string
}[]
Expand All @@ -38,7 +37,8 @@ export async function markEventAsRegisteredCallbackHandler(
h: Hapi.ResponseToolkit
) {
const token = getToken(request)
const { registrationNumber, error, childIdentifiers, compositionId } =
const compositionId = request.params.id
const { registrationNumber, error, identifiers } =
request.payload as EventRegistrationPayload

if (error) {
Expand All @@ -60,7 +60,7 @@ export async function markEventAsRegisteredCallbackHandler(
savedRecord,
registrationNumber,
token,
childIdentifiers
identifiers
)
const event = getEventType(bundle)

Expand Down
5 changes: 2 additions & 3 deletions packages/workflow/src/records/handler/register.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,9 @@ describe('Register record endpoint', () => {

const response = await server.server.inject({
method: 'POST',
url: '/confirm/registration',
url: '/records/7c3af302-08c9-41af-8701-92de9a71a3e4/confirm',
payload: {
registrationNumber: '1234',
compositionId: '7c3af302-08c9-41af-8701-92de9a71a3e4'
registrationNumber: '1234'
},
headers: {
Authorization: `Bearer ${token}`
Expand Down
6 changes: 3 additions & 3 deletions packages/workflow/src/records/state-transitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ export async function toRegistered(
record: WaitingForValidationRecord,
registrationNumber: EventRegistrationPayload['registrationNumber'],
token: string,
childIdentifiers?: EventRegistrationPayload['childIdentifiers']
identifiers?: EventRegistrationPayload['identifiers']
): Promise<RegisteredRecord> {
const previousTask = getTaskFromSavedBundle(record)
const registeredTaskWithoutPractitionerExtensions =
Expand Down Expand Up @@ -507,10 +507,10 @@ export async function toRegistered(
value: registrationNumber as RegistrationNumber
})

if (event === EVENT_TYPE.BIRTH && childIdentifiers) {
if (event === EVENT_TYPE.BIRTH && identifiers) {
// For birth event patients[0] is child and it should
// already be initialized with the RN identifier
childIdentifiers.forEach((childIdentifier) => {
identifiers.forEach((childIdentifier) => {
const previousIdentifier = patientsWithRegNumber[0].identifier!.find(
({ type }) => type?.coding?.[0].code === childIdentifier.type
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ sequenceDiagram
Workflow--)Metrics: POST bundle to /events/{event}/waiting-external-validation

Workflow--)Country-Config: POST record to /event-registration
Country-Config->>Workflow: POST /confirm/registration
Country-Config->>Workflow: POST /records/{id}/confirm
Workflow->>Search: Get record by id

Workflow->>User management: Fetch user/system information
Expand Down
Loading