From d05ff89b49914a0630bedcd59e96e2e9130768db Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Wed, 27 Nov 2024 13:21:48 +0530 Subject: [PATCH 01/18] feat: Created an upsertRegistrationIdentifier endpoint to gateway --- .../features/registration/root-resolvers.ts | 24 ++++- .../src/features/registration/schema.graphql | 10 ++- packages/gateway/src/graphql/schema.d.ts | 24 ++++- packages/gateway/src/graphql/schema.graphql | 10 ++- packages/gateway/src/workflow/index.ts | 24 ++++- packages/workflow/src/config/routes.ts | 11 +++ .../registration/fhir/fhir-bundle-modifier.ts | 43 ++++++++- .../src/features/registration/upsert.ts | 85 ++++++++++++++++++ .../workflow/src/records/state-transitions.ts | 88 ++++++++++++++++++- 9 files changed, 311 insertions(+), 8 deletions(-) create mode 100644 packages/workflow/src/features/registration/upsert.ts diff --git a/packages/gateway/src/features/registration/root-resolvers.ts b/packages/gateway/src/features/registration/root-resolvers.ts index 5c7b37692b2..9847b8cf0b4 100644 --- a/packages/gateway/src/features/registration/root-resolvers.ts +++ b/packages/gateway/src/features/registration/root-resolvers.ts @@ -60,7 +60,8 @@ import { verifyRegistration, markNotADuplicate, rejectRegistration, - confirmRegistration + confirmRegistration, + upsertRegistrationIdentifier } from '@gateway/workflow/index' import { getRecordById } from '@gateway/records' import { UnassignError, UserInputError } from '@gateway/utils/graphql-errors' @@ -612,7 +613,6 @@ export const resolvers: GQLResolver = { try { const taskEntry = await confirmRegistration(id, authHeader, { - error: details.error, registrationNumber: details.registrationNumber, identifiers: details.identifiers }) @@ -641,6 +641,26 @@ export const resolvers: GQLResolver = { } catch (error) { throw new Error(`Error in rejectRegistration: ${error.message}`) } + }, + async upsertRegistrationIdentifier( + _, + { id, details }, + { headers: authHeader } + ) { + if (!hasRecordAccess(authHeader, id)) { + throw new Error('User does not have access to the record') + } + + try { + const taskEntry = await upsertRegistrationIdentifier(id, authHeader, { + registrationNumber: details.registrationNumber, + identifiers: details.identifiers + }) + + return taskEntry.resource.id + } catch (error) { + throw new Error(`Failed to confirm registration: ${error.message}`) + } } } } diff --git a/packages/gateway/src/features/registration/schema.graphql b/packages/gateway/src/features/registration/schema.graphql index cc30d8580f1..37b8640efbe 100644 --- a/packages/gateway/src/features/registration/schema.graphql +++ b/packages/gateway/src/features/registration/schema.graphql @@ -570,7 +570,6 @@ input IdentifierInput { input ConfirmRegistrationInput { registrationNumber: String! - error: String identifiers: [IdentifierInput!] } @@ -579,6 +578,11 @@ input RejectRegistrationInput { comment: String } +input UpsertRegistrationIdentifierInput { + registrationNumber: String! + identifiers: [IdentifierInput!] +} + 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 @@ -657,4 +661,8 @@ type Mutation { ): ID! confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID! rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID! + upsertRegistrationIdentifier( + id: ID! + details: UpsertRegistrationIdentifierInput! + ): ID! } diff --git a/packages/gateway/src/graphql/schema.d.ts b/packages/gateway/src/graphql/schema.d.ts index b136865a761..393eea3d921 100644 --- a/packages/gateway/src/graphql/schema.d.ts +++ b/packages/gateway/src/graphql/schema.d.ts @@ -88,6 +88,7 @@ export interface GQLMutation { markEventAsDuplicate: string confirmRegistration: string rejectRegistration: string + upsertRegistrationIdentifier: string createOrUpdateUser: GQLUser activateUser?: string changePassword?: string @@ -600,7 +601,6 @@ export interface GQLReinstated { export interface GQLConfirmRegistrationInput { registrationNumber: string - error?: string identifiers?: Array } @@ -609,6 +609,11 @@ export interface GQLRejectRegistrationInput { comment?: string } +export interface GQLUpsertRegistrationIdentifierInput { + registrationNumber: string + identifiers?: Array +} + export interface GQLUserInput { id?: string name: Array @@ -2630,6 +2635,7 @@ export interface GQLMutationTypeResolver { markEventAsDuplicate?: MutationToMarkEventAsDuplicateResolver confirmRegistration?: MutationToConfirmRegistrationResolver rejectRegistration?: MutationToRejectRegistrationResolver + upsertRegistrationIdentifier?: MutationToUpsertRegistrationIdentifierResolver createOrUpdateUser?: MutationToCreateOrUpdateUserResolver activateUser?: MutationToActivateUserResolver changePassword?: MutationToChangePasswordResolver @@ -3206,6 +3212,22 @@ export interface MutationToRejectRegistrationResolver< ): TResult } +export interface MutationToUpsertRegistrationIdentifierArgs { + id: string + details: GQLUpsertRegistrationIdentifierInput +} +export interface MutationToUpsertRegistrationIdentifierResolver< + TParent = any, + TResult = any +> { + ( + parent: TParent, + args: MutationToUpsertRegistrationIdentifierArgs, + context: Context, + info: GraphQLResolveInfo + ): TResult +} + export interface MutationToCreateOrUpdateUserArgs { user: GQLUserInput } diff --git a/packages/gateway/src/graphql/schema.graphql b/packages/gateway/src/graphql/schema.graphql index e10ab1a0738..445889defd2 100644 --- a/packages/gateway/src/graphql/schema.graphql +++ b/packages/gateway/src/graphql/schema.graphql @@ -219,6 +219,10 @@ type Mutation { ): ID! confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID! rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID! + upsertRegistrationIdentifier( + id: ID! + details: UpsertRegistrationIdentifierInput! + ): ID! createOrUpdateUser(user: UserInput!): User! activateUser( userId: String! @@ -733,7 +737,6 @@ type Reinstated { input ConfirmRegistrationInput { registrationNumber: String! - error: String identifiers: [IdentifierInput!] } @@ -742,6 +745,11 @@ input RejectRegistrationInput { comment: String } +input UpsertRegistrationIdentifierInput { + registrationNumber: String! + identifiers: [IdentifierInput!] +} + input UserInput { id: ID name: [HumanNameInput!]! diff --git a/packages/gateway/src/workflow/index.ts b/packages/gateway/src/workflow/index.ts index fe44e60e818..49e69c866c7 100644 --- a/packages/gateway/src/workflow/index.ts +++ b/packages/gateway/src/workflow/index.ts @@ -244,7 +244,6 @@ export async function confirmRegistration( id: string, authHeader: IAuthHeader, details: { - error: string | undefined registrationNumber: string identifiers?: IdentifierInput[] } @@ -281,6 +280,29 @@ export async function rejectRegistration( return taskEntry } +export async function upsertRegistrationIdentifier( + id: string, + authHeader: IAuthHeader, + details: { + registrationNumber: string + identifiers?: IdentifierInput[] + } +) { + const res: ReadyForReviewRecord = await createRequest( + 'POST', + `/records/${id}/upsert`, + 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 archiveRegistration( id: string, authHeader: IAuthHeader, diff --git a/packages/workflow/src/config/routes.ts b/packages/workflow/src/config/routes.ts index f7a649ded9e..96d63b6de6b 100644 --- a/packages/workflow/src/config/routes.ts +++ b/packages/workflow/src/config/routes.ts @@ -29,6 +29,7 @@ import { approveCorrectionRoute } from '@workflow/records/handler/correction/app import { requestCorrectionRoute } from '@workflow/records/handler/correction/request' import { makeCorrectionRoute } from '@workflow/records/handler/correction/make-correction' import { eventNotificationHandler } from '@workflow/records/handler/eventNotificationHandler' +import { upsertRegistrationHandler } from '@workflow/features/registration/upsert' export const getRoutes = () => { const routes = [ @@ -69,6 +70,16 @@ export const getRoutes = () => { 'Register event based on tracking id and registration number.' } }, + { + method: 'POST', + path: '/records/{id}/upsert', + handler: upsertRegistrationHandler, + config: { + tags: ['api'], + description: + 'Upsert Register event based on tracking id and registration number.' + } + }, { method: 'POST', path: '/create-record', diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index d7eb7ce7401..7ed262cf6bd 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -23,7 +23,8 @@ import { findExtension, getResourceFromBundleById, resourceIdentifierToUUID, - SupportedPatientIdentifierCode + SupportedPatientIdentifierCode, + InProgressRecord } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -204,3 +205,43 @@ export function updatePatientIdentifierWithRN( return patient }) } + +export function upsertPatientIdentifierWithRN( + record: InProgressRecord, + composition: Composition, + sectionCodes: string[], + identifierType: SupportedPatientIdentifierCode, + registrationNumber: string +): Saved[] { + return sectionCodes.map((sectionCode) => { + const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode) + const patientId = resourceIdentifierToUUID( + sectionEntry.reference as ResourceIdentifier + ) + const patient = getResourceFromBundleById(record, patientId) + + if (!patient.identifier) { + patient.identifier = [] + } + const rnIdentifier = patient.identifier.find( + (identifier: fhir3.Identifier) => + identifier.type?.coding?.[0].code === identifierType + ) + if (rnIdentifier) { + rnIdentifier.value = registrationNumber + } else { + patient.identifier.push({ + type: { + coding: [ + { + system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, + code: identifierType + } + ] + }, + value: registrationNumber + }) + } + return patient + }) +} diff --git a/packages/workflow/src/features/registration/upsert.ts b/packages/workflow/src/features/registration/upsert.ts new file mode 100644 index 00000000000..8d21be3046b --- /dev/null +++ b/packages/workflow/src/features/registration/upsert.ts @@ -0,0 +1,85 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { getToken } from '@workflow/utils/auth-utils' +import * as Hapi from '@hapi/hapi' +import { getRecordById } from '@workflow/records/index' +import { toUpsertRegistrationIdentifier } from '@workflow/records/state-transitions' +import { getEventType } from './utils' +import { indexBundle } from '@workflow/records/search' +import { auditEvent } from '@workflow/records/audit' +import { + isNotificationEnabled, + sendNotification +} from '@workflow/records/notification' +import { invokeWebhooks } from '@workflow/records/webhooks' +import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' + +export interface UpsertRegistrationPayload { + trackingId: string + registrationNumber: string + error?: string + identifiers?: { + type: SupportedPatientIdentifierCode + value: string + }[] +} + +export async function upsertRegistrationHandler( + request: Hapi.Request, + h: Hapi.ResponseToolkit +) { + const token = getToken(request) + const compositionId = request.params.id + const { registrationNumber, error, identifiers } = + request.payload as UpsertRegistrationPayload + + if (error) { + throw new Error(`Upsert operation failed with error: ${error}`) + } + + const savedRecord = await getRecordById( + compositionId, + request.headers.authorization, + ['IN_PROGRESS'], + true + ) + if (!savedRecord) { + throw new Error('Could not find record in elastic search!') + } + + const bundle = await toUpsertRegistrationIdentifier( + request, + savedRecord, + registrationNumber, + token, + identifiers + ) + + const event = getEventType(bundle) + + // Index the updated bundle + await indexBundle(bundle, token) + + // Audit the event + await auditEvent('registered', bundle, token) + + // Send notifications if enabled + if (await isNotificationEnabled('registered', event, token)) { + await sendNotification('registered', bundle, token) + } + + // Invoke webhooks for the updated bundle + await invokeWebhooks({ bundle, token, event }) + + // Return the updated bundle + return h.response(bundle).code(200) +} diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 099dbafe6d6..08587f8282d 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -60,7 +60,8 @@ import { invokeRegistrationValidation, setupLastRegOffice, setupLastRegUser, - updatePatientIdentifierWithRN + updatePatientIdentifierWithRN, + upsertPatientIdentifierWithRN } from '@workflow/features/registration/fhir/fhir-bundle-modifier' import { EventRegistrationPayload } from '@workflow/features/registration/handler' import { ASSIGNED_EXTENSION_URL } from '@workflow/features/task/fhir/constants' @@ -552,6 +553,91 @@ export async function toRegistered( ) } +export async function toUpsertRegistrationIdentifier( + request: Hapi.Request, + record: InProgressRecord, + registrationNumber: EventRegistrationPayload['registrationNumber'], + token: string, + identifiers?: EventRegistrationPayload['identifiers'] +): Promise { + const previousTask = getTaskFromSavedBundle(record) + const registeredTaskWithoutPractitionerExtensions = + createRegisterTask(previousTask) + + // Add practitioner details (same logic as before) + const [registeredTask, practitionerResourcesBundle] = + await withPractitionerDetails( + registeredTaskWithoutPractitionerExtensions, + token + ) + + const event = getTaskEventType(registeredTask) as EVENT_TYPE + const composition = getComposition(record) + + // Update patient identifiers (upsert logic here) + const patientsWithRegNumber = upsertPatientIdentifierWithRN( + record, + composition, + SECTION_CODE[event], + REG_NUMBER_SYSTEM[event], + registrationNumber + ) + + /* Upsert registration number here */ + const system = `http://opencrvs.org/specs/id/${ + event.toLowerCase() as Lowercase + }-registration-number` as const + + registeredTask.identifier.push({ + system, + value: registrationNumber as RegistrationNumber + }) + + if (event === EVENT_TYPE.BIRTH && identifiers) { + // Handle identifiers for children in birth events + identifiers.forEach((childIdentifier) => { + const previousIdentifier = patientsWithRegNumber[0].identifier!.find( + ({ type }) => type?.coding?.[0].code === childIdentifier.type + ) + if (!previousIdentifier) { + patientsWithRegNumber[0].identifier!.push({ + type: { + coding: [ + { + system: 'http://opencrvs.org/specs/identifier-type', + code: childIdentifier.type + } + ] + }, + value: childIdentifier.value + }) + } else { + previousIdentifier.value = childIdentifier.value + } + }) + } + + const patientIds = patientsWithRegNumber.map((p) => p.id) + const patientsEntriesWithRN = record.entry.filter((e) => + patientIds.includes(e.resource.id) + ) + + // Bundle the updated resources + const unsavedChangedResources: Bundle = { + type: 'document', + resourceType: 'Bundle', + entry: [...patientsEntriesWithRN, { resource: registeredTask }] + } + + const upsertedRecord = (await mergeChangedResourcesIntoRecord( + record, + unsavedChangedResources, + practitionerResourcesBundle + )) as RegisteredRecord + + return upsertedRecord +} + export async function toArchived( record: RegisteredRecord | ReadyForReviewRecord | ValidatedRecord, token: string, From 57c383cbc5d917c11b272494c9f9037ec0e624f3 Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Fri, 29 Nov 2024 11:39:39 +0530 Subject: [PATCH 02/18] mod : allowed any state records --- packages/workflow/src/config/routes.ts | 2 +- .../registration/fhir/fhir-bundle-modifier.ts | 4 +- .../handler}/upsert.ts | 29 +++--- .../workflow/src/records/handler/utils.ts | 88 +++++++++++++++++++ .../workflow/src/records/state-transitions.ts | 2 +- packages/workflow/src/states.ts | 7 +- 6 files changed, 114 insertions(+), 18 deletions(-) rename packages/workflow/src/{features/registration => records/handler}/upsert.ts (84%) create mode 100644 packages/workflow/src/records/handler/utils.ts diff --git a/packages/workflow/src/config/routes.ts b/packages/workflow/src/config/routes.ts index 96d63b6de6b..eb52f37419f 100644 --- a/packages/workflow/src/config/routes.ts +++ b/packages/workflow/src/config/routes.ts @@ -29,7 +29,7 @@ import { approveCorrectionRoute } from '@workflow/records/handler/correction/app import { requestCorrectionRoute } from '@workflow/records/handler/correction/request' import { makeCorrectionRoute } from '@workflow/records/handler/correction/make-correction' import { eventNotificationHandler } from '@workflow/records/handler/eventNotificationHandler' -import { upsertRegistrationHandler } from '@workflow/features/registration/upsert' +import { upsertRegistrationHandler } from '@workflow/records/handler/upsert' export const getRoutes = () => { const routes = [ diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index 7ed262cf6bd..d4b089a4625 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -24,7 +24,7 @@ import { getResourceFromBundleById, resourceIdentifierToUUID, SupportedPatientIdentifierCode, - InProgressRecord + ValidRecord } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -207,7 +207,7 @@ export function updatePatientIdentifierWithRN( } export function upsertPatientIdentifierWithRN( - record: InProgressRecord, + record: ValidRecord, composition: Composition, sectionCodes: string[], identifierType: SupportedPatientIdentifierCode, diff --git a/packages/workflow/src/features/registration/upsert.ts b/packages/workflow/src/records/handler/upsert.ts similarity index 84% rename from packages/workflow/src/features/registration/upsert.ts rename to packages/workflow/src/records/handler/upsert.ts index 8d21be3046b..ac4574b2e84 100644 --- a/packages/workflow/src/features/registration/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -8,7 +8,6 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ - import { getToken } from '@workflow/utils/auth-utils' import * as Hapi from '@hapi/hapi' import { getRecordById } from '@workflow/records/index' @@ -23,10 +22,10 @@ import { import { invokeWebhooks } from '@workflow/records/webhooks' import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' -export interface UpsertRegistrationPayload { +export interface EventRegistrationPayload { trackingId: string registrationNumber: string - error?: string + error: string identifiers?: { type: SupportedPatientIdentifierCode value: string @@ -40,16 +39,27 @@ export async function upsertRegistrationHandler( const token = getToken(request) const compositionId = request.params.id const { registrationNumber, error, identifiers } = - request.payload as UpsertRegistrationPayload + request.payload as EventRegistrationPayload if (error) { - throw new Error(`Upsert operation failed with error: ${error}`) + throw new Error(`Callback triggered with an error: ${error}`) } const savedRecord = await getRecordById( compositionId, request.headers.authorization, - ['IN_PROGRESS'], + [ + 'ARCHIVED', + 'CERTIFIED', + 'CORRECTION_REQUESTED', + 'IN_PROGRESS', + 'READY_FOR_REVIEW', + 'ISSUED', + 'REGISTERED', + 'REJECTED', + 'VALIDATED', + 'WAITING_VALIDATION' + ], true ) if (!savedRecord) { @@ -63,23 +73,16 @@ export async function upsertRegistrationHandler( token, identifiers ) - const event = getEventType(bundle) - // Index the updated bundle await indexBundle(bundle, token) - - // Audit the event await auditEvent('registered', bundle, token) - // Send notifications if enabled if (await isNotificationEnabled('registered', event, token)) { await sendNotification('registered', bundle, token) } - // Invoke webhooks for the updated bundle await invokeWebhooks({ bundle, token, event }) - // Return the updated bundle return h.response(bundle).code(200) } diff --git a/packages/workflow/src/records/handler/utils.ts b/packages/workflow/src/records/handler/utils.ts new file mode 100644 index 00000000000..5db366cf346 --- /dev/null +++ b/packages/workflow/src/records/handler/utils.ts @@ -0,0 +1,88 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { Bundle, Composition, Task, TrackingID } from '@opencrvs/commons/types' +import { COUNTRY_CONFIG_URL } from '@workflow/constants' +import { EVENT_TYPE } from '@workflow/features/registration/fhir/constants' +import { getTaskEventType } from '@workflow/features/task/fhir/utils' +import fetch from 'node-fetch' +import * as ShortUIDGen from 'short-uid' + +export async function generateTrackingIdForEvents( + eventType: EVENT_TYPE, + bundle: Bundle, + token: string +) { + const trackingIdFromCountryConfig = await getTrackingIdFromCountryConfig( + bundle, + token + ) + if (trackingIdFromCountryConfig) { + return trackingIdFromCountryConfig as TrackingID + } else { + // using first letter of eventType for prefix + // TODO: for divorce, need to think about prefix as Death & Divorce prefix is same 'D' + return generateTrackingId(eventType.charAt(0)) as TrackingID + } +} + +async function getTrackingIdFromCountryConfig( + bundle: Bundle, + token: string +): Promise { + return fetch(new URL('/tracking-id', COUNTRY_CONFIG_URL).toString(), { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-type': 'application/json' + }, + body: JSON.stringify(bundle) + }).then((res) => { + if (res.ok) return res.text() + else if (res.status === 404) return null + else throw new Error(res.statusText) + }) +} + +function generateTrackingId(prefix: string): string { + return prefix.concat(new ShortUIDGen().randomUUID()).toUpperCase() +} + +export function convertStringToASCII(str: string): string { + return [...str] + .map((char) => char.charCodeAt(0).toString()) + .reduce((acc, v) => acc.concat(v)) +} + +const DETECT_EVENT: Record = { + 'birth-notification': EVENT_TYPE.BIRTH, + 'birth-declaration': EVENT_TYPE.BIRTH, + 'death-notification': EVENT_TYPE.DEATH, + 'death-declaration': EVENT_TYPE.DEATH, + 'marriage-notification': EVENT_TYPE.MARRIAGE, + 'marriage-declaration': EVENT_TYPE.MARRIAGE +} + +function getCompositionEventType(compoition: Composition) { + const eventType = compoition?.type?.coding?.[0].code + return eventType && DETECT_EVENT[eventType] +} + +export function getEventType(fhirBundle: Bundle) { + if (fhirBundle.entry && fhirBundle.entry[0] && fhirBundle.entry[0].resource) { + const firstEntry = fhirBundle.entry[0].resource + if (firstEntry.resourceType === 'Composition') { + return getCompositionEventType(firstEntry as Composition) as EVENT_TYPE + } else { + return getTaskEventType(firstEntry as Task) as EVENT_TYPE + } + } + throw new Error('Invalid FHIR bundle found') +} diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 08587f8282d..606a0c5aaf7 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -555,7 +555,7 @@ export async function toRegistered( export async function toUpsertRegistrationIdentifier( request: Hapi.Request, - record: InProgressRecord, + record: ValidRecord, registrationNumber: EventRegistrationPayload['registrationNumber'], token: string, identifiers?: EventRegistrationPayload['identifiers'] diff --git a/packages/workflow/src/states.ts b/packages/workflow/src/states.ts index 21053a5784f..6c3cd66ec44 100644 --- a/packages/workflow/src/states.ts +++ b/packages/workflow/src/states.ts @@ -21,7 +21,8 @@ import { ReadyForReviewRecord, WaitingForValidationRecord, RejectedRecord, - ArchivedRecord + ArchivedRecord, + ValidRecord } from '@opencrvs/commons/types' import { getRecordById } from './records' @@ -38,6 +39,7 @@ type WaitForExternalValidation = Nominal<{}, 'WaitForExternalValidation'> type Archive = Nominal<{}, 'Archive'> type Reject = Nominal<{}, 'Reject'> type Reinstate = Nominal<{}, 'Reinstate'> +type Upsert = Nominal<{}, 'Upsert'> type ActionIdentifiers = { REQUEST_CORRECTION: RequestCorrection @@ -53,6 +55,7 @@ type ActionIdentifiers = { WAITING_VALIDATION: WaitForExternalValidation ARCHIVE: Archive REINSTATE: Reinstate + UPSERT: Upsert } /* @@ -108,6 +111,8 @@ type StateTree = Reject, RejectedRecord > + // Upsert declaration + | Transition /* * Internals From 30c01818b39fe3458d50b974ae85c51c4ea15a85 Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Fri, 29 Nov 2024 12:09:44 +0530 Subject: [PATCH 03/18] mod : removed upsert state-transition --- .../registration/fhir/fhir-bundle-modifier.ts | 43 +-------- .../workflow/src/records/handler/upsert.ts | 17 ++-- .../workflow/src/records/state-transitions.ts | 88 +------------------ 3 files changed, 8 insertions(+), 140 deletions(-) diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index d4b089a4625..d7eb7ce7401 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -23,8 +23,7 @@ import { findExtension, getResourceFromBundleById, resourceIdentifierToUUID, - SupportedPatientIdentifierCode, - ValidRecord + SupportedPatientIdentifierCode } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -205,43 +204,3 @@ export function updatePatientIdentifierWithRN( return patient }) } - -export function upsertPatientIdentifierWithRN( - record: ValidRecord, - composition: Composition, - sectionCodes: string[], - identifierType: SupportedPatientIdentifierCode, - registrationNumber: string -): Saved[] { - return sectionCodes.map((sectionCode) => { - const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode) - const patientId = resourceIdentifierToUUID( - sectionEntry.reference as ResourceIdentifier - ) - const patient = getResourceFromBundleById(record, patientId) - - if (!patient.identifier) { - patient.identifier = [] - } - const rnIdentifier = patient.identifier.find( - (identifier: fhir3.Identifier) => - identifier.type?.coding?.[0].code === identifierType - ) - if (rnIdentifier) { - rnIdentifier.value = registrationNumber - } else { - patient.identifier.push({ - type: { - coding: [ - { - system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, - code: identifierType - } - ] - }, - value: registrationNumber - }) - } - return patient - }) -} diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert.ts index ac4574b2e84..0f0f9fda043 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -11,7 +11,6 @@ import { getToken } from '@workflow/utils/auth-utils' import * as Hapi from '@hapi/hapi' import { getRecordById } from '@workflow/records/index' -import { toUpsertRegistrationIdentifier } from '@workflow/records/state-transitions' import { getEventType } from './utils' import { indexBundle } from '@workflow/records/search' import { auditEvent } from '@workflow/records/audit' @@ -20,7 +19,10 @@ import { sendNotification } from '@workflow/records/notification' import { invokeWebhooks } from '@workflow/records/webhooks' -import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' +import { + SupportedPatientIdentifierCode, + RegisteredRecord +} from '@opencrvs/commons/types' export interface EventRegistrationPayload { trackingId: string @@ -38,8 +40,7 @@ export async function upsertRegistrationHandler( ) { const token = getToken(request) const compositionId = request.params.id - const { registrationNumber, error, identifiers } = - request.payload as EventRegistrationPayload + const { error } = request.payload as EventRegistrationPayload if (error) { throw new Error(`Callback triggered with an error: ${error}`) @@ -66,13 +67,7 @@ export async function upsertRegistrationHandler( throw new Error('Could not find record in elastic search!') } - const bundle = await toUpsertRegistrationIdentifier( - request, - savedRecord, - registrationNumber, - token, - identifiers - ) + const bundle = savedRecord as RegisteredRecord const event = getEventType(bundle) await indexBundle(bundle, token) diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 606a0c5aaf7..099dbafe6d6 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -60,8 +60,7 @@ import { invokeRegistrationValidation, setupLastRegOffice, setupLastRegUser, - updatePatientIdentifierWithRN, - upsertPatientIdentifierWithRN + updatePatientIdentifierWithRN } from '@workflow/features/registration/fhir/fhir-bundle-modifier' import { EventRegistrationPayload } from '@workflow/features/registration/handler' import { ASSIGNED_EXTENSION_URL } from '@workflow/features/task/fhir/constants' @@ -553,91 +552,6 @@ export async function toRegistered( ) } -export async function toUpsertRegistrationIdentifier( - request: Hapi.Request, - record: ValidRecord, - registrationNumber: EventRegistrationPayload['registrationNumber'], - token: string, - identifiers?: EventRegistrationPayload['identifiers'] -): Promise { - const previousTask = getTaskFromSavedBundle(record) - const registeredTaskWithoutPractitionerExtensions = - createRegisterTask(previousTask) - - // Add practitioner details (same logic as before) - const [registeredTask, practitionerResourcesBundle] = - await withPractitionerDetails( - registeredTaskWithoutPractitionerExtensions, - token - ) - - const event = getTaskEventType(registeredTask) as EVENT_TYPE - const composition = getComposition(record) - - // Update patient identifiers (upsert logic here) - const patientsWithRegNumber = upsertPatientIdentifierWithRN( - record, - composition, - SECTION_CODE[event], - REG_NUMBER_SYSTEM[event], - registrationNumber - ) - - /* Upsert registration number here */ - const system = `http://opencrvs.org/specs/id/${ - event.toLowerCase() as Lowercase - }-registration-number` as const - - registeredTask.identifier.push({ - system, - value: registrationNumber as RegistrationNumber - }) - - if (event === EVENT_TYPE.BIRTH && identifiers) { - // Handle identifiers for children in birth events - identifiers.forEach((childIdentifier) => { - const previousIdentifier = patientsWithRegNumber[0].identifier!.find( - ({ type }) => type?.coding?.[0].code === childIdentifier.type - ) - if (!previousIdentifier) { - patientsWithRegNumber[0].identifier!.push({ - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/identifier-type', - code: childIdentifier.type - } - ] - }, - value: childIdentifier.value - }) - } else { - previousIdentifier.value = childIdentifier.value - } - }) - } - - const patientIds = patientsWithRegNumber.map((p) => p.id) - const patientsEntriesWithRN = record.entry.filter((e) => - patientIds.includes(e.resource.id) - ) - - // Bundle the updated resources - const unsavedChangedResources: Bundle = { - type: 'document', - resourceType: 'Bundle', - entry: [...patientsEntriesWithRN, { resource: registeredTask }] - } - - const upsertedRecord = (await mergeChangedResourcesIntoRecord( - record, - unsavedChangedResources, - practitionerResourcesBundle - )) as RegisteredRecord - - return upsertedRecord -} - export async function toArchived( record: RegisteredRecord | ReadyForReviewRecord | ValidatedRecord, token: string, From fb845d18eda778bbd50b0a17c75573e67347ba6a Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Thu, 21 Nov 2024 10:18:45 +0530 Subject: [PATCH 04/18] DRAFT upsertRegistrationIdentifier endpoint to gateway #7908 --- .../workflow/src/records/state-transitions.ts | 183 ++++++++++++++++++ packages/workflow/src/records/validations.ts | 7 + 2 files changed, 190 insertions(+) diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 099dbafe6d6..dd249eac0ba 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -552,6 +552,189 @@ export async function toRegistered( ) } +export async function toUpsertRegistrationIdentifier( + request: Hapi.Request, + record: WaitingForValidationRecord, + registrationNumber: EventRegistrationPayload['registrationNumber'], + token: string, + identifiers?: EventRegistrationPayload['identifiers'] +): Promise { + const previousTask = getTaskFromSavedBundle(record) + const registeredTaskWithoutPractitionerExtensions = + createRegisterTask(previousTask) + + // Add practitioner details (same logic as before) + const [registeredTask, practitionerResourcesBundle] = + await withPractitionerDetails( + registeredTaskWithoutPractitionerExtensions, + token + ) + + const event = getTaskEventType(registeredTask) as EVENT_TYPE + const composition = getComposition(record) + + // Update patient identifiers (upsert logic here) + const patientsWithRegNumber = updatePatientIdentifierWithRN( + record, + composition, + SECTION_CODE[event], + REG_NUMBER_SYSTEM[event], + registrationNumber + ) + + /* Upsert registration number here */ + const system = `http://opencrvs.org/specs/id/${ + event.toLowerCase() as Lowercase + }-registration-number` as const + + // Find or create registration number identifier + let registrationIdentifier = registeredTask.identifier.find( + (identifier) => identifier.system === system + ) + + if (!registrationIdentifier) { + // If identifier doesn't exist, create it + registeredTask.identifier.push({ + system, + value: registrationNumber as RegistrationNumber + }) + } else { + // If identifier exists, update it + registrationIdentifier.value = registrationNumber as RegistrationNumber + } + + if (event === EVENT_TYPE.BIRTH && identifiers) { + // Handle identifiers for children in birth events + identifiers.forEach((childIdentifier) => { + const previousIdentifier = patientsWithRegNumber[0].identifier!.find( + ({ type }) => type?.coding?.[0].code === childIdentifier.type + ) + if (!previousIdentifier) { + patientsWithRegNumber[0].identifier!.push({ + type: { + coding: [ + { + system: 'http://opencrvs.org/specs/identifier-type', + code: childIdentifier.type + } + ] + }, + value: childIdentifier.value + }) + } else { + previousIdentifier.value = childIdentifier.value + } + }) + } + + const patientIds = patientsWithRegNumber.map((p) => p.id) + const patientsEntriesWithRN = record.entry.filter((e) => + patientIds.includes(e.resource.id) + ) + + // Bundle the updated resources + const unsavedChangedResources: Bundle = { + type: 'document', + resourceType: 'Bundle', + entry: [...patientsEntriesWithRN, { resource: registeredTask }] + } + + // Change the state to REGISTERED + return changeState( + await mergeChangedResourcesIntoRecord( + record, + unsavedChangedResources, + practitionerResourcesBundle + ), + 'REGISTERED' + ) +} + +// export async function toUpsertRegistrationIdentifier( +// request: Hapi.Request, +// record: WaitingForValidationRecord, +// registrationNumber: EventRegistrationPayload['registrationNumber'], +// token: string, +// identifiers?: EventRegistrationPayload['identifiers'] +// ): Promise { +// const previousTask = getTaskFromSavedBundle(record) +// const registeredTaskWithoutPractitionerExtensions = +// createRegisterTask(previousTask) + +// const [registeredTask, practitionerResourcesBundle] = +// await withPractitionerDetails( +// registeredTaskWithoutPractitionerExtensions, +// token +// ) + +// const event = getTaskEventType(registeredTask) as EVENT_TYPE +// const composition = getComposition(record) + +// // for patient entries of child, deceased, bride, groom +// const patientsWithRegNumber = updatePatientIdentifierWithRN( +// record, +// composition, +// SECTION_CODE[event], +// REG_NUMBER_SYSTEM[event], +// registrationNumber +// ) + +// /* Setting registration number here */ +// const system = `http://opencrvs.org/specs/id/${ +// event.toLowerCase() as Lowercase +// }-registration-number` as const + +// registeredTask.identifier.push({ +// system, +// value: registrationNumber as RegistrationNumber +// }) + +// if (event === EVENT_TYPE.BIRTH && identifiers) { +// // For birth event patients[0] is child and it should +// // already be initialized with the RN identifier +// identifiers.forEach((childIdentifier) => { +// const previousIdentifier = patientsWithRegNumber[0].identifier!.find( +// ({ type }) => type?.coding?.[0].code === childIdentifier.type +// ) +// if (!previousIdentifier) { +// patientsWithRegNumber[0].identifier!.push({ +// type: { +// coding: [ +// { +// system: 'http://opencrvs.org/specs/identifier-type', +// code: childIdentifier.type +// } +// ] +// }, +// value: childIdentifier.value +// }) +// } else { +// previousIdentifier.value = childIdentifier.value +// } +// }) +// } + +// const patientIds = patientsWithRegNumber.map((p) => p.id) +// const patientsEntriesWithRN = record.entry.filter((e) => +// patientIds.includes(e.resource.id) +// ) + +// const unsavedChangedResources: Bundle = { +// type: 'document', +// resourceType: 'Bundle', +// entry: [...patientsEntriesWithRN, { resource: registeredTask }] +// } + +// return changeState( +// await mergeChangedResourcesIntoRecord( +// record, +// unsavedChangedResources, +// practitionerResourcesBundle +// ), +// 'REGISTERED' +// ) +// } + export async function toArchived( record: RegisteredRecord | ReadyForReviewRecord | ValidatedRecord, token: string, diff --git a/packages/workflow/src/records/validations.ts b/packages/workflow/src/records/validations.ts index fb81ff2b635..0cd062ba7be 100644 --- a/packages/workflow/src/records/validations.ts +++ b/packages/workflow/src/records/validations.ts @@ -53,6 +53,13 @@ export const CertifyRequestSchema = z.object({ }) }) +export const upsertIdentifierSchema = z.object({ + identifier: z.object({ + type: z.string(), + value: z.string() + }) +}) + const PaymentSchema = z.object({ type: z.enum(['MANUAL']), amount: z.number(), From db3f93b421a8624b8b05d09315d8af50f8db3b9f Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Wed, 27 Nov 2024 13:21:48 +0530 Subject: [PATCH 05/18] feat: Created an upsertRegistrationIdentifier endpoint to gateway --- packages/workflow/src/config/routes.ts | 12 +- .../registration/fhir/fhir-bundle-modifier.ts | 43 +++++- .../src/features/registration/upsert.ts | 85 ++++++++++++ .../workflow/src/records/state-transitions.ts | 127 +++--------------- 4 files changed, 153 insertions(+), 114 deletions(-) create mode 100644 packages/workflow/src/features/registration/upsert.ts diff --git a/packages/workflow/src/config/routes.ts b/packages/workflow/src/config/routes.ts index eb52f37419f..190b8fac88b 100644 --- a/packages/workflow/src/config/routes.ts +++ b/packages/workflow/src/config/routes.ts @@ -29,7 +29,7 @@ import { approveCorrectionRoute } from '@workflow/records/handler/correction/app import { requestCorrectionRoute } from '@workflow/records/handler/correction/request' import { makeCorrectionRoute } from '@workflow/records/handler/correction/make-correction' import { eventNotificationHandler } from '@workflow/records/handler/eventNotificationHandler' -import { upsertRegistrationHandler } from '@workflow/records/handler/upsert' +import { upsertRegistrationHandler } from '@workflow/features/registration/upsert' export const getRoutes = () => { const routes = [ @@ -80,6 +80,16 @@ export const getRoutes = () => { 'Upsert Register event based on tracking id and registration number.' } }, + { + method: 'POST', + path: '/records/{id}/upsert', + handler: upsertRegistrationHandler, + config: { + tags: ['api'], + description: + 'Upsert Register event based on tracking id and registration number.' + } + }, { method: 'POST', path: '/create-record', diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index d7eb7ce7401..7ed262cf6bd 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -23,7 +23,8 @@ import { findExtension, getResourceFromBundleById, resourceIdentifierToUUID, - SupportedPatientIdentifierCode + SupportedPatientIdentifierCode, + InProgressRecord } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -204,3 +205,43 @@ export function updatePatientIdentifierWithRN( return patient }) } + +export function upsertPatientIdentifierWithRN( + record: InProgressRecord, + composition: Composition, + sectionCodes: string[], + identifierType: SupportedPatientIdentifierCode, + registrationNumber: string +): Saved[] { + return sectionCodes.map((sectionCode) => { + const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode) + const patientId = resourceIdentifierToUUID( + sectionEntry.reference as ResourceIdentifier + ) + const patient = getResourceFromBundleById(record, patientId) + + if (!patient.identifier) { + patient.identifier = [] + } + const rnIdentifier = patient.identifier.find( + (identifier: fhir3.Identifier) => + identifier.type?.coding?.[0].code === identifierType + ) + if (rnIdentifier) { + rnIdentifier.value = registrationNumber + } else { + patient.identifier.push({ + type: { + coding: [ + { + system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, + code: identifierType + } + ] + }, + value: registrationNumber + }) + } + return patient + }) +} diff --git a/packages/workflow/src/features/registration/upsert.ts b/packages/workflow/src/features/registration/upsert.ts new file mode 100644 index 00000000000..8d21be3046b --- /dev/null +++ b/packages/workflow/src/features/registration/upsert.ts @@ -0,0 +1,85 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { getToken } from '@workflow/utils/auth-utils' +import * as Hapi from '@hapi/hapi' +import { getRecordById } from '@workflow/records/index' +import { toUpsertRegistrationIdentifier } from '@workflow/records/state-transitions' +import { getEventType } from './utils' +import { indexBundle } from '@workflow/records/search' +import { auditEvent } from '@workflow/records/audit' +import { + isNotificationEnabled, + sendNotification +} from '@workflow/records/notification' +import { invokeWebhooks } from '@workflow/records/webhooks' +import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' + +export interface UpsertRegistrationPayload { + trackingId: string + registrationNumber: string + error?: string + identifiers?: { + type: SupportedPatientIdentifierCode + value: string + }[] +} + +export async function upsertRegistrationHandler( + request: Hapi.Request, + h: Hapi.ResponseToolkit +) { + const token = getToken(request) + const compositionId = request.params.id + const { registrationNumber, error, identifiers } = + request.payload as UpsertRegistrationPayload + + if (error) { + throw new Error(`Upsert operation failed with error: ${error}`) + } + + const savedRecord = await getRecordById( + compositionId, + request.headers.authorization, + ['IN_PROGRESS'], + true + ) + if (!savedRecord) { + throw new Error('Could not find record in elastic search!') + } + + const bundle = await toUpsertRegistrationIdentifier( + request, + savedRecord, + registrationNumber, + token, + identifiers + ) + + const event = getEventType(bundle) + + // Index the updated bundle + await indexBundle(bundle, token) + + // Audit the event + await auditEvent('registered', bundle, token) + + // Send notifications if enabled + if (await isNotificationEnabled('registered', event, token)) { + await sendNotification('registered', bundle, token) + } + + // Invoke webhooks for the updated bundle + await invokeWebhooks({ bundle, token, event }) + + // Return the updated bundle + return h.response(bundle).code(200) +} diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index dd249eac0ba..08587f8282d 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -60,7 +60,8 @@ import { invokeRegistrationValidation, setupLastRegOffice, setupLastRegUser, - updatePatientIdentifierWithRN + updatePatientIdentifierWithRN, + upsertPatientIdentifierWithRN } from '@workflow/features/registration/fhir/fhir-bundle-modifier' import { EventRegistrationPayload } from '@workflow/features/registration/handler' import { ASSIGNED_EXTENSION_URL } from '@workflow/features/task/fhir/constants' @@ -554,7 +555,7 @@ export async function toRegistered( export async function toUpsertRegistrationIdentifier( request: Hapi.Request, - record: WaitingForValidationRecord, + record: InProgressRecord, registrationNumber: EventRegistrationPayload['registrationNumber'], token: string, identifiers?: EventRegistrationPayload['identifiers'] @@ -574,7 +575,7 @@ export async function toUpsertRegistrationIdentifier( const composition = getComposition(record) // Update patient identifiers (upsert logic here) - const patientsWithRegNumber = updatePatientIdentifierWithRN( + const patientsWithRegNumber = upsertPatientIdentifierWithRN( record, composition, SECTION_CODE[event], @@ -587,21 +588,10 @@ export async function toUpsertRegistrationIdentifier( event.toLowerCase() as Lowercase }-registration-number` as const - // Find or create registration number identifier - let registrationIdentifier = registeredTask.identifier.find( - (identifier) => identifier.system === system - ) - - if (!registrationIdentifier) { - // If identifier doesn't exist, create it - registeredTask.identifier.push({ - system, - value: registrationNumber as RegistrationNumber - }) - } else { - // If identifier exists, update it - registrationIdentifier.value = registrationNumber as RegistrationNumber - } + registeredTask.identifier.push({ + system, + value: registrationNumber as RegistrationNumber + }) if (event === EVENT_TYPE.BIRTH && identifiers) { // Handle identifiers for children in birth events @@ -639,101 +629,14 @@ export async function toUpsertRegistrationIdentifier( entry: [...patientsEntriesWithRN, { resource: registeredTask }] } - // Change the state to REGISTERED - return changeState( - await mergeChangedResourcesIntoRecord( - record, - unsavedChangedResources, - practitionerResourcesBundle - ), - 'REGISTERED' - ) -} + const upsertedRecord = (await mergeChangedResourcesIntoRecord( + record, + unsavedChangedResources, + practitionerResourcesBundle + )) as RegisteredRecord -// export async function toUpsertRegistrationIdentifier( -// request: Hapi.Request, -// record: WaitingForValidationRecord, -// registrationNumber: EventRegistrationPayload['registrationNumber'], -// token: string, -// identifiers?: EventRegistrationPayload['identifiers'] -// ): Promise { -// const previousTask = getTaskFromSavedBundle(record) -// const registeredTaskWithoutPractitionerExtensions = -// createRegisterTask(previousTask) - -// const [registeredTask, practitionerResourcesBundle] = -// await withPractitionerDetails( -// registeredTaskWithoutPractitionerExtensions, -// token -// ) - -// const event = getTaskEventType(registeredTask) as EVENT_TYPE -// const composition = getComposition(record) - -// // for patient entries of child, deceased, bride, groom -// const patientsWithRegNumber = updatePatientIdentifierWithRN( -// record, -// composition, -// SECTION_CODE[event], -// REG_NUMBER_SYSTEM[event], -// registrationNumber -// ) - -// /* Setting registration number here */ -// const system = `http://opencrvs.org/specs/id/${ -// event.toLowerCase() as Lowercase -// }-registration-number` as const - -// registeredTask.identifier.push({ -// system, -// value: registrationNumber as RegistrationNumber -// }) - -// if (event === EVENT_TYPE.BIRTH && identifiers) { -// // For birth event patients[0] is child and it should -// // already be initialized with the RN identifier -// identifiers.forEach((childIdentifier) => { -// const previousIdentifier = patientsWithRegNumber[0].identifier!.find( -// ({ type }) => type?.coding?.[0].code === childIdentifier.type -// ) -// if (!previousIdentifier) { -// patientsWithRegNumber[0].identifier!.push({ -// type: { -// coding: [ -// { -// system: 'http://opencrvs.org/specs/identifier-type', -// code: childIdentifier.type -// } -// ] -// }, -// value: childIdentifier.value -// }) -// } else { -// previousIdentifier.value = childIdentifier.value -// } -// }) -// } - -// const patientIds = patientsWithRegNumber.map((p) => p.id) -// const patientsEntriesWithRN = record.entry.filter((e) => -// patientIds.includes(e.resource.id) -// ) - -// const unsavedChangedResources: Bundle = { -// type: 'document', -// resourceType: 'Bundle', -// entry: [...patientsEntriesWithRN, { resource: registeredTask }] -// } - -// return changeState( -// await mergeChangedResourcesIntoRecord( -// record, -// unsavedChangedResources, -// practitionerResourcesBundle -// ), -// 'REGISTERED' -// ) -// } + return upsertedRecord +} export async function toArchived( record: RegisteredRecord | ReadyForReviewRecord | ValidatedRecord, From a8fd674f6ffff2a311646b799818d2290749f722 Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Fri, 29 Nov 2024 11:39:39 +0530 Subject: [PATCH 06/18] mod : allowed any state records --- .../registration/fhir/fhir-bundle-modifier.ts | 4 ++-- packages/workflow/src/records/handler/upsert.ts | 11 +++++++++-- packages/workflow/src/records/state-transitions.ts | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index 7ed262cf6bd..d4b089a4625 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -24,7 +24,7 @@ import { getResourceFromBundleById, resourceIdentifierToUUID, SupportedPatientIdentifierCode, - InProgressRecord + ValidRecord } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -207,7 +207,7 @@ export function updatePatientIdentifierWithRN( } export function upsertPatientIdentifierWithRN( - record: InProgressRecord, + record: ValidRecord, composition: Composition, sectionCodes: string[], identifierType: SupportedPatientIdentifierCode, diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert.ts index 0f0f9fda043..82e0e978ab2 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -40,7 +40,8 @@ export async function upsertRegistrationHandler( ) { const token = getToken(request) const compositionId = request.params.id - const { error } = request.payload as EventRegistrationPayload + const { registrationNumber, error, identifiers } = + request.payload as EventRegistrationPayload if (error) { throw new Error(`Callback triggered with an error: ${error}`) @@ -67,7 +68,13 @@ export async function upsertRegistrationHandler( throw new Error('Could not find record in elastic search!') } - const bundle = savedRecord as RegisteredRecord + const bundle = await toUpsertRegistrationIdentifier( + request, + savedRecord, + registrationNumber, + token, + identifiers + ) const event = getEventType(bundle) await indexBundle(bundle, token) diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 08587f8282d..606a0c5aaf7 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -555,7 +555,7 @@ export async function toRegistered( export async function toUpsertRegistrationIdentifier( request: Hapi.Request, - record: InProgressRecord, + record: ValidRecord, registrationNumber: EventRegistrationPayload['registrationNumber'], token: string, identifiers?: EventRegistrationPayload['identifiers'] From 82a4c7899605cc2b39a0c5885e8665cac0160aad Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Fri, 29 Nov 2024 12:09:44 +0530 Subject: [PATCH 07/18] mod : removed upsert state-transition --- .../registration/fhir/fhir-bundle-modifier.ts | 43 +--------- .../workflow/src/records/handler/upsert.ts | 11 +-- .../workflow/src/records/state-transitions.ts | 85 ------------------- 3 files changed, 3 insertions(+), 136 deletions(-) diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index d4b089a4625..d7eb7ce7401 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -23,8 +23,7 @@ import { findExtension, getResourceFromBundleById, resourceIdentifierToUUID, - SupportedPatientIdentifierCode, - ValidRecord + SupportedPatientIdentifierCode } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -205,43 +204,3 @@ export function updatePatientIdentifierWithRN( return patient }) } - -export function upsertPatientIdentifierWithRN( - record: ValidRecord, - composition: Composition, - sectionCodes: string[], - identifierType: SupportedPatientIdentifierCode, - registrationNumber: string -): Saved[] { - return sectionCodes.map((sectionCode) => { - const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode) - const patientId = resourceIdentifierToUUID( - sectionEntry.reference as ResourceIdentifier - ) - const patient = getResourceFromBundleById(record, patientId) - - if (!patient.identifier) { - patient.identifier = [] - } - const rnIdentifier = patient.identifier.find( - (identifier: fhir3.Identifier) => - identifier.type?.coding?.[0].code === identifierType - ) - if (rnIdentifier) { - rnIdentifier.value = registrationNumber - } else { - patient.identifier.push({ - type: { - coding: [ - { - system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, - code: identifierType - } - ] - }, - value: registrationNumber - }) - } - return patient - }) -} diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert.ts index 82e0e978ab2..0f0f9fda043 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -40,8 +40,7 @@ export async function upsertRegistrationHandler( ) { const token = getToken(request) const compositionId = request.params.id - const { registrationNumber, error, identifiers } = - request.payload as EventRegistrationPayload + const { error } = request.payload as EventRegistrationPayload if (error) { throw new Error(`Callback triggered with an error: ${error}`) @@ -68,13 +67,7 @@ export async function upsertRegistrationHandler( throw new Error('Could not find record in elastic search!') } - const bundle = await toUpsertRegistrationIdentifier( - request, - savedRecord, - registrationNumber, - token, - identifiers - ) + const bundle = savedRecord as RegisteredRecord const event = getEventType(bundle) await indexBundle(bundle, token) diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 606a0c5aaf7..751ccc44088 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -553,91 +553,6 @@ export async function toRegistered( ) } -export async function toUpsertRegistrationIdentifier( - request: Hapi.Request, - record: ValidRecord, - registrationNumber: EventRegistrationPayload['registrationNumber'], - token: string, - identifiers?: EventRegistrationPayload['identifiers'] -): Promise { - const previousTask = getTaskFromSavedBundle(record) - const registeredTaskWithoutPractitionerExtensions = - createRegisterTask(previousTask) - - // Add practitioner details (same logic as before) - const [registeredTask, practitionerResourcesBundle] = - await withPractitionerDetails( - registeredTaskWithoutPractitionerExtensions, - token - ) - - const event = getTaskEventType(registeredTask) as EVENT_TYPE - const composition = getComposition(record) - - // Update patient identifiers (upsert logic here) - const patientsWithRegNumber = upsertPatientIdentifierWithRN( - record, - composition, - SECTION_CODE[event], - REG_NUMBER_SYSTEM[event], - registrationNumber - ) - - /* Upsert registration number here */ - const system = `http://opencrvs.org/specs/id/${ - event.toLowerCase() as Lowercase - }-registration-number` as const - - registeredTask.identifier.push({ - system, - value: registrationNumber as RegistrationNumber - }) - - if (event === EVENT_TYPE.BIRTH && identifiers) { - // Handle identifiers for children in birth events - identifiers.forEach((childIdentifier) => { - const previousIdentifier = patientsWithRegNumber[0].identifier!.find( - ({ type }) => type?.coding?.[0].code === childIdentifier.type - ) - if (!previousIdentifier) { - patientsWithRegNumber[0].identifier!.push({ - type: { - coding: [ - { - system: 'http://opencrvs.org/specs/identifier-type', - code: childIdentifier.type - } - ] - }, - value: childIdentifier.value - }) - } else { - previousIdentifier.value = childIdentifier.value - } - }) - } - - const patientIds = patientsWithRegNumber.map((p) => p.id) - const patientsEntriesWithRN = record.entry.filter((e) => - patientIds.includes(e.resource.id) - ) - - // Bundle the updated resources - const unsavedChangedResources: Bundle = { - type: 'document', - resourceType: 'Bundle', - entry: [...patientsEntriesWithRN, { resource: registeredTask }] - } - - const upsertedRecord = (await mergeChangedResourcesIntoRecord( - record, - unsavedChangedResources, - practitionerResourcesBundle - )) as RegisteredRecord - - return upsertedRecord -} - export async function toArchived( record: RegisteredRecord | ReadyForReviewRecord | ValidatedRecord, token: string, From 3c1f31a7aa72cff4b94120cd7e9ead1dc1d8bab1 Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Mon, 2 Dec 2024 11:23:42 +0530 Subject: [PATCH 08/18] issues fixed --- packages/workflow/src/config/routes.ts | 2 +- .../src/features/registration/upsert.ts | 85 ------------------- .../workflow/src/records/state-transitions.ts | 3 +- 3 files changed, 2 insertions(+), 88 deletions(-) delete mode 100644 packages/workflow/src/features/registration/upsert.ts diff --git a/packages/workflow/src/config/routes.ts b/packages/workflow/src/config/routes.ts index 190b8fac88b..814d4d58d97 100644 --- a/packages/workflow/src/config/routes.ts +++ b/packages/workflow/src/config/routes.ts @@ -29,7 +29,7 @@ import { approveCorrectionRoute } from '@workflow/records/handler/correction/app import { requestCorrectionRoute } from '@workflow/records/handler/correction/request' import { makeCorrectionRoute } from '@workflow/records/handler/correction/make-correction' import { eventNotificationHandler } from '@workflow/records/handler/eventNotificationHandler' -import { upsertRegistrationHandler } from '@workflow/features/registration/upsert' +import { upsertRegistrationHandler } from '@workflow/records/handler/upsert' export const getRoutes = () => { const routes = [ diff --git a/packages/workflow/src/features/registration/upsert.ts b/packages/workflow/src/features/registration/upsert.ts deleted file mode 100644 index 8d21be3046b..00000000000 --- a/packages/workflow/src/features/registration/upsert.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ - -import { getToken } from '@workflow/utils/auth-utils' -import * as Hapi from '@hapi/hapi' -import { getRecordById } from '@workflow/records/index' -import { toUpsertRegistrationIdentifier } from '@workflow/records/state-transitions' -import { getEventType } from './utils' -import { indexBundle } from '@workflow/records/search' -import { auditEvent } from '@workflow/records/audit' -import { - isNotificationEnabled, - sendNotification -} from '@workflow/records/notification' -import { invokeWebhooks } from '@workflow/records/webhooks' -import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' - -export interface UpsertRegistrationPayload { - trackingId: string - registrationNumber: string - error?: string - identifiers?: { - type: SupportedPatientIdentifierCode - value: string - }[] -} - -export async function upsertRegistrationHandler( - request: Hapi.Request, - h: Hapi.ResponseToolkit -) { - const token = getToken(request) - const compositionId = request.params.id - const { registrationNumber, error, identifiers } = - request.payload as UpsertRegistrationPayload - - if (error) { - throw new Error(`Upsert operation failed with error: ${error}`) - } - - const savedRecord = await getRecordById( - compositionId, - request.headers.authorization, - ['IN_PROGRESS'], - true - ) - if (!savedRecord) { - throw new Error('Could not find record in elastic search!') - } - - const bundle = await toUpsertRegistrationIdentifier( - request, - savedRecord, - registrationNumber, - token, - identifiers - ) - - const event = getEventType(bundle) - - // Index the updated bundle - await indexBundle(bundle, token) - - // Audit the event - await auditEvent('registered', bundle, token) - - // Send notifications if enabled - if (await isNotificationEnabled('registered', event, token)) { - await sendNotification('registered', bundle, token) - } - - // Invoke webhooks for the updated bundle - await invokeWebhooks({ bundle, token, event }) - - // Return the updated bundle - return h.response(bundle).code(200) -} diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 751ccc44088..099dbafe6d6 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -60,8 +60,7 @@ import { invokeRegistrationValidation, setupLastRegOffice, setupLastRegUser, - updatePatientIdentifierWithRN, - upsertPatientIdentifierWithRN + updatePatientIdentifierWithRN } from '@workflow/features/registration/fhir/fhir-bundle-modifier' import { EventRegistrationPayload } from '@workflow/features/registration/handler' import { ASSIGNED_EXTENSION_URL } from '@workflow/features/task/fhir/constants' From 9bd25f0977ab8fd28ccc3f4873179d0aed710688 Mon Sep 17 00:00:00 2001 From: Hyper3x Date: Thu, 5 Dec 2024 15:30:18 +0530 Subject: [PATCH 09/18] mod : required changes done --- packages/workflow/src/config/routes.ts | 10 --- .../registration/fhir/fhir-bundle-modifier.ts | 43 ++++++++- .../workflow/src/records/handler/upsert.ts | 2 +- .../workflow/src/records/handler/utils.ts | 88 ------------------- 4 files changed, 43 insertions(+), 100 deletions(-) delete mode 100644 packages/workflow/src/records/handler/utils.ts diff --git a/packages/workflow/src/config/routes.ts b/packages/workflow/src/config/routes.ts index 814d4d58d97..eb52f37419f 100644 --- a/packages/workflow/src/config/routes.ts +++ b/packages/workflow/src/config/routes.ts @@ -80,16 +80,6 @@ export const getRoutes = () => { 'Upsert Register event based on tracking id and registration number.' } }, - { - method: 'POST', - path: '/records/{id}/upsert', - handler: upsertRegistrationHandler, - config: { - tags: ['api'], - description: - 'Upsert Register event based on tracking id and registration number.' - } - }, { method: 'POST', path: '/create-record', diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index d7eb7ce7401..d4b089a4625 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -23,7 +23,8 @@ import { findExtension, getResourceFromBundleById, resourceIdentifierToUUID, - SupportedPatientIdentifierCode + SupportedPatientIdentifierCode, + ValidRecord } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -204,3 +205,43 @@ export function updatePatientIdentifierWithRN( return patient }) } + +export function upsertPatientIdentifierWithRN( + record: ValidRecord, + composition: Composition, + sectionCodes: string[], + identifierType: SupportedPatientIdentifierCode, + registrationNumber: string +): Saved[] { + return sectionCodes.map((sectionCode) => { + const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode) + const patientId = resourceIdentifierToUUID( + sectionEntry.reference as ResourceIdentifier + ) + const patient = getResourceFromBundleById(record, patientId) + + if (!patient.identifier) { + patient.identifier = [] + } + const rnIdentifier = patient.identifier.find( + (identifier: fhir3.Identifier) => + identifier.type?.coding?.[0].code === identifierType + ) + if (rnIdentifier) { + rnIdentifier.value = registrationNumber + } else { + patient.identifier.push({ + type: { + coding: [ + { + system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, + code: identifierType + } + ] + }, + value: registrationNumber + }) + } + return patient + }) +} diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert.ts index 0f0f9fda043..c6ad67f4c01 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -11,7 +11,7 @@ import { getToken } from '@workflow/utils/auth-utils' import * as Hapi from '@hapi/hapi' import { getRecordById } from '@workflow/records/index' -import { getEventType } from './utils' +import { getEventType } from '@workflow/features/registration/utils' import { indexBundle } from '@workflow/records/search' import { auditEvent } from '@workflow/records/audit' import { diff --git a/packages/workflow/src/records/handler/utils.ts b/packages/workflow/src/records/handler/utils.ts deleted file mode 100644 index 5db366cf346..00000000000 --- a/packages/workflow/src/records/handler/utils.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * OpenCRVS is also distributed under the terms of the Civil Registration - * & Healthcare Disclaimer located at http://opencrvs.org/license. - * - * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. - */ -import { Bundle, Composition, Task, TrackingID } from '@opencrvs/commons/types' -import { COUNTRY_CONFIG_URL } from '@workflow/constants' -import { EVENT_TYPE } from '@workflow/features/registration/fhir/constants' -import { getTaskEventType } from '@workflow/features/task/fhir/utils' -import fetch from 'node-fetch' -import * as ShortUIDGen from 'short-uid' - -export async function generateTrackingIdForEvents( - eventType: EVENT_TYPE, - bundle: Bundle, - token: string -) { - const trackingIdFromCountryConfig = await getTrackingIdFromCountryConfig( - bundle, - token - ) - if (trackingIdFromCountryConfig) { - return trackingIdFromCountryConfig as TrackingID - } else { - // using first letter of eventType for prefix - // TODO: for divorce, need to think about prefix as Death & Divorce prefix is same 'D' - return generateTrackingId(eventType.charAt(0)) as TrackingID - } -} - -async function getTrackingIdFromCountryConfig( - bundle: Bundle, - token: string -): Promise { - return fetch(new URL('/tracking-id', COUNTRY_CONFIG_URL).toString(), { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - 'Content-type': 'application/json' - }, - body: JSON.stringify(bundle) - }).then((res) => { - if (res.ok) return res.text() - else if (res.status === 404) return null - else throw new Error(res.statusText) - }) -} - -function generateTrackingId(prefix: string): string { - return prefix.concat(new ShortUIDGen().randomUUID()).toUpperCase() -} - -export function convertStringToASCII(str: string): string { - return [...str] - .map((char) => char.charCodeAt(0).toString()) - .reduce((acc, v) => acc.concat(v)) -} - -const DETECT_EVENT: Record = { - 'birth-notification': EVENT_TYPE.BIRTH, - 'birth-declaration': EVENT_TYPE.BIRTH, - 'death-notification': EVENT_TYPE.DEATH, - 'death-declaration': EVENT_TYPE.DEATH, - 'marriage-notification': EVENT_TYPE.MARRIAGE, - 'marriage-declaration': EVENT_TYPE.MARRIAGE -} - -function getCompositionEventType(compoition: Composition) { - const eventType = compoition?.type?.coding?.[0].code - return eventType && DETECT_EVENT[eventType] -} - -export function getEventType(fhirBundle: Bundle) { - if (fhirBundle.entry && fhirBundle.entry[0] && fhirBundle.entry[0].resource) { - const firstEntry = fhirBundle.entry[0].resource - if (firstEntry.resourceType === 'Composition') { - return getCompositionEventType(firstEntry as Composition) as EVENT_TYPE - } else { - return getTaskEventType(firstEntry as Task) as EVENT_TYPE - } - } - throw new Error('Invalid FHIR bundle found') -} From bf5478fd8bf7fd00f9ae8e43e0c9109ca5578699 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Fri, 6 Dec 2024 14:55:52 +0600 Subject: [PATCH 10/18] fix: remove upsert stuff from states --- packages/workflow/src/states.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/workflow/src/states.ts b/packages/workflow/src/states.ts index 6c3cd66ec44..21053a5784f 100644 --- a/packages/workflow/src/states.ts +++ b/packages/workflow/src/states.ts @@ -21,8 +21,7 @@ import { ReadyForReviewRecord, WaitingForValidationRecord, RejectedRecord, - ArchivedRecord, - ValidRecord + ArchivedRecord } from '@opencrvs/commons/types' import { getRecordById } from './records' @@ -39,7 +38,6 @@ type WaitForExternalValidation = Nominal<{}, 'WaitForExternalValidation'> type Archive = Nominal<{}, 'Archive'> type Reject = Nominal<{}, 'Reject'> type Reinstate = Nominal<{}, 'Reinstate'> -type Upsert = Nominal<{}, 'Upsert'> type ActionIdentifiers = { REQUEST_CORRECTION: RequestCorrection @@ -55,7 +53,6 @@ type ActionIdentifiers = { WAITING_VALIDATION: WaitForExternalValidation ARCHIVE: Archive REINSTATE: Reinstate - UPSERT: Upsert } /* @@ -111,8 +108,6 @@ type StateTree = Reject, RejectedRecord > - // Upsert declaration - | Transition /* * Internals From 13ae4a2dec76a7d32075c141a222400516049827 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 9 Dec 2024 13:03:20 +0600 Subject: [PATCH 11/18] fix: cleanup upsert handler --- .../registration/fhir/fhir-bundle-modifier.ts | 49 ++++++++++--------- .../workflow/src/records/handler/upsert.ts | 44 +++++------------ .../workflow/src/records/state-transitions.ts | 32 +++++++++++- 3 files changed, 67 insertions(+), 58 deletions(-) diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index d4b089a4625..aaf79628954 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -24,7 +24,8 @@ import { getResourceFromBundleById, resourceIdentifierToUUID, SupportedPatientIdentifierCode, - ValidRecord + ValidRecord, + PatientIdentifier } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -38,6 +39,7 @@ import { } from '@workflow/features/registration/fhir/fhir-utils' import { getPractitionerRef } from '@workflow/features/user/utils' import { ITokenPayload } from '@workflow/utils/auth-utils' +import { isEqual } from 'lodash' import fetch from 'node-fetch' export async function invokeRegistrationValidation( @@ -206,12 +208,11 @@ export function updatePatientIdentifierWithRN( }) } -export function upsertPatientIdentifierWithRN( +export function upsertPatientIdentifiers( record: ValidRecord, composition: Composition, sectionCodes: string[], - identifierType: SupportedPatientIdentifierCode, - registrationNumber: string + identifiers: PatientIdentifier[] ): Saved[] { return sectionCodes.map((sectionCode) => { const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode) @@ -223,25 +224,27 @@ export function upsertPatientIdentifierWithRN( if (!patient.identifier) { patient.identifier = [] } - const rnIdentifier = patient.identifier.find( - (identifier: fhir3.Identifier) => - identifier.type?.coding?.[0].code === identifierType - ) - if (rnIdentifier) { - rnIdentifier.value = registrationNumber - } else { - patient.identifier.push({ - type: { - coding: [ - { - system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, - code: identifierType - } - ] - }, - value: registrationNumber - }) - } + identifiers.forEach((id) => { + const existingIdentifier = patient.identifier!.find((existingId) => + isEqual(existingId.type, id.type) + ) + if (existingIdentifier) { + existingIdentifier.value = id.value + } else { + patient.identifier!.push({ + type: { + coding: [ + { + system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, + code: id.type.coding[0].code + } + ] + }, + value: id.value + }) + } + }) + return patient }) } diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert.ts index c6ad67f4c01..8d0f2429fa7 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -11,27 +11,14 @@ import { getToken } from '@workflow/utils/auth-utils' import * as Hapi from '@hapi/hapi' import { getRecordById } from '@workflow/records/index' -import { getEventType } from '@workflow/features/registration/utils' import { indexBundle } from '@workflow/records/search' -import { auditEvent } from '@workflow/records/audit' -import { - isNotificationEnabled, - sendNotification -} from '@workflow/records/notification' -import { invokeWebhooks } from '@workflow/records/webhooks' -import { - SupportedPatientIdentifierCode, - RegisteredRecord -} from '@opencrvs/commons/types' +import { PatientIdentifier } from '@opencrvs/commons/types' +import { toUpserted } from '@workflow/records/state-transitions' export interface EventRegistrationPayload { trackingId: string registrationNumber: string - error: string - identifiers?: { - type: SupportedPatientIdentifierCode - value: string - }[] + identifiers: PatientIdentifier[] } export async function upsertRegistrationHandler( @@ -40,11 +27,7 @@ export async function upsertRegistrationHandler( ) { const token = getToken(request) const compositionId = request.params.id - const { error } = request.payload as EventRegistrationPayload - - if (error) { - throw new Error(`Callback triggered with an error: ${error}`) - } + const { identifiers } = request.payload as EventRegistrationPayload const savedRecord = await getRecordById( compositionId, @@ -64,20 +47,15 @@ export async function upsertRegistrationHandler( true ) if (!savedRecord) { - throw new Error('Could not find record in elastic search!') + throw new Error( + 'Could not find record with composition id: ' + compositionId + ) } - const bundle = savedRecord as RegisteredRecord - const event = getEventType(bundle) - - await indexBundle(bundle, token) - await auditEvent('registered', bundle, token) - - if (await isNotificationEnabled('registered', event, token)) { - await sendNotification('registered', bundle, token) - } + const upsertedRecord = toUpserted(savedRecord, identifiers) - await invokeWebhooks({ bundle, token, event }) + await indexBundle(upsertedRecord, token) + // TBD: audit event - return h.response(bundle).code(200) + return h.response(upsertedRecord).code(200) } diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 099dbafe6d6..de65b47b112 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -49,7 +49,8 @@ import { resourceToBundleEntry, toHistoryResource, TaskHistory, - RejectedRecord + RejectedRecord, + PatientIdentifier } from '@opencrvs/commons/types' import { getUUID, logger, UUID } from '@opencrvs/commons' import { @@ -60,7 +61,8 @@ import { invokeRegistrationValidation, setupLastRegOffice, setupLastRegUser, - updatePatientIdentifierWithRN + updatePatientIdentifierWithRN, + upsertPatientIdentifiers } from '@workflow/features/registration/fhir/fhir-bundle-modifier' import { EventRegistrationPayload } from '@workflow/features/registration/handler' import { ASSIGNED_EXTENSION_URL } from '@workflow/features/task/fhir/constants' @@ -323,6 +325,32 @@ export async function toViewed( return viewedRecord } +export function toUpserted( + record: T, + identifiers: PatientIdentifier[] +): T { + const task = getTaskFromSavedBundle(record) + const event = getTaskEventType(task) + const composition = getComposition(record) + // TBD: task history entry + const patientWithUpsertedIdentifier = upsertPatientIdentifiers( + record, + composition, + SECTION_CODE[event], + identifiers + ) + const filteredEntry = record.entry.filter( + (e) => + !patientWithUpsertedIdentifier + .map((patient) => patient.id) + .includes(e.resource.id) + ) + return { + ...record, + entry: [...filteredEntry, ...patientWithUpsertedIdentifier] + } +} + export async function toDownloaded( record: ValidRecord, token: string, From 45fd2e745e32a8825b883fed587e99d9f2e0f1dc Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 9 Dec 2024 18:33:38 +0600 Subject: [PATCH 12/18] fix: small amends --- .../gateway/src/features/registration/schema.graphql | 2 +- packages/gateway/src/graphql/schema.d.ts | 2 +- packages/gateway/src/graphql/schema.graphql | 2 +- packages/gateway/src/workflow/index.ts | 2 +- .../features/registration/fhir/fhir-bundle-modifier.ts | 10 ++++++---- packages/workflow/src/records/handler/upsert.ts | 8 ++++++-- packages/workflow/src/records/state-transitions.ts | 7 +++++-- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/gateway/src/features/registration/schema.graphql b/packages/gateway/src/features/registration/schema.graphql index 37b8640efbe..edf92ef1c4c 100644 --- a/packages/gateway/src/features/registration/schema.graphql +++ b/packages/gateway/src/features/registration/schema.graphql @@ -579,7 +579,7 @@ input RejectRegistrationInput { } input UpsertRegistrationIdentifierInput { - registrationNumber: String! + registrationNumber: String identifiers: [IdentifierInput!] } diff --git a/packages/gateway/src/graphql/schema.d.ts b/packages/gateway/src/graphql/schema.d.ts index dc2544e59e2..d271e1b601f 100644 --- a/packages/gateway/src/graphql/schema.d.ts +++ b/packages/gateway/src/graphql/schema.d.ts @@ -611,7 +611,7 @@ export interface GQLRejectRegistrationInput { } export interface GQLUpsertRegistrationIdentifierInput { - registrationNumber: string + registrationNumber?: string identifiers?: Array } diff --git a/packages/gateway/src/graphql/schema.graphql b/packages/gateway/src/graphql/schema.graphql index e66bb529c2e..bebd3648f15 100644 --- a/packages/gateway/src/graphql/schema.graphql +++ b/packages/gateway/src/graphql/schema.graphql @@ -747,7 +747,7 @@ input RejectRegistrationInput { } input UpsertRegistrationIdentifierInput { - registrationNumber: String! + registrationNumber: String identifiers: [IdentifierInput!] } diff --git a/packages/gateway/src/workflow/index.ts b/packages/gateway/src/workflow/index.ts index 49e69c866c7..d9f4daf262d 100644 --- a/packages/gateway/src/workflow/index.ts +++ b/packages/gateway/src/workflow/index.ts @@ -284,7 +284,7 @@ export async function upsertRegistrationIdentifier( id: string, authHeader: IAuthHeader, details: { - registrationNumber: string + registrationNumber?: string identifiers?: IdentifierInput[] } ) { diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts index aaf79628954..08d6f5d5af3 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -24,8 +24,7 @@ import { getResourceFromBundleById, resourceIdentifierToUUID, SupportedPatientIdentifierCode, - ValidRecord, - PatientIdentifier + ValidRecord } from '@opencrvs/commons/types' import { COUNTRY_CONFIG_URL } from '@workflow/constants' import { @@ -212,7 +211,10 @@ export function upsertPatientIdentifiers( record: ValidRecord, composition: Composition, sectionCodes: string[], - identifiers: PatientIdentifier[] + identifiers: { + type: SupportedPatientIdentifierCode + value: string + }[] ): Saved[] { return sectionCodes.map((sectionCode) => { const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode) @@ -236,7 +238,7 @@ export function upsertPatientIdentifiers( coding: [ { system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`, - code: id.type.coding[0].code + code: id.type } ] }, diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert.ts index 8d0f2429fa7..eef69bd392d 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -12,13 +12,17 @@ import { getToken } from '@workflow/utils/auth-utils' import * as Hapi from '@hapi/hapi' import { getRecordById } from '@workflow/records/index' import { indexBundle } from '@workflow/records/search' -import { PatientIdentifier } from '@opencrvs/commons/types' import { toUpserted } from '@workflow/records/state-transitions' +import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' +interface IdentifierInput { + type: SupportedPatientIdentifierCode + value: string +} export interface EventRegistrationPayload { trackingId: string registrationNumber: string - identifiers: PatientIdentifier[] + identifiers: IdentifierInput[] } export async function upsertRegistrationHandler( diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index de65b47b112..78d43d1072e 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -50,7 +50,7 @@ import { toHistoryResource, TaskHistory, RejectedRecord, - PatientIdentifier + SupportedPatientIdentifierCode } from '@opencrvs/commons/types' import { getUUID, logger, UUID } from '@opencrvs/commons' import { @@ -327,7 +327,10 @@ export async function toViewed( export function toUpserted( record: T, - identifiers: PatientIdentifier[] + identifiers: { + type: SupportedPatientIdentifierCode + value: string + }[] ): T { const task = getTaskFromSavedBundle(record) const event = getTaskEventType(task) From 1a0b60ea0cc182ca2cff009650420e1c992b63bd Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 9 Dec 2024 18:49:45 +0600 Subject: [PATCH 13/18] fix: update wrong transformations --- packages/workflow/src/records/handler/upsert.ts | 2 ++ packages/workflow/src/records/state-transitions.ts | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert.ts index eef69bd392d..3fc31af1d58 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert.ts @@ -14,6 +14,7 @@ import { getRecordById } from '@workflow/records/index' import { indexBundle } from '@workflow/records/search' import { toUpserted } from '@workflow/records/state-transitions' import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' +import { sendBundleToHearth } from '@workflow/records/fhir' interface IdentifierInput { type: SupportedPatientIdentifierCode @@ -58,6 +59,7 @@ export async function upsertRegistrationHandler( const upsertedRecord = toUpserted(savedRecord, identifiers) + await sendBundleToHearth(upsertedRecord) await indexBundle(upsertedRecord, token) // TBD: audit event diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 78d43d1072e..6d88d6387ef 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -350,7 +350,13 @@ export function toUpserted( ) return { ...record, - entry: [...filteredEntry, ...patientWithUpsertedIdentifier] + entry: [ + ...filteredEntry, + ...patientWithUpsertedIdentifier.map((resource) => ({ + ...record.entry.find((e) => e.resource.id === resource.id), + resource + })) + ] } } From 83af2a42fe9dd2bd947c1ebf24f8f4926fde53f4 Mon Sep 17 00:00:00 2001 From: naftis Date: Mon, 9 Dec 2024 17:26:01 +0200 Subject: [PATCH 14/18] fix: remove unused schema --- packages/workflow/src/records/validations.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/workflow/src/records/validations.ts b/packages/workflow/src/records/validations.ts index 0cd062ba7be..fb81ff2b635 100644 --- a/packages/workflow/src/records/validations.ts +++ b/packages/workflow/src/records/validations.ts @@ -53,13 +53,6 @@ export const CertifyRequestSchema = z.object({ }) }) -export const upsertIdentifierSchema = z.object({ - identifier: z.object({ - type: z.string(), - value: z.string() - }) -}) - const PaymentSchema = z.object({ type: z.enum(['MANUAL']), amount: z.number(), From cd96efd3197789dafc1ac39ce235242f8f4773d9 Mon Sep 17 00:00:00 2001 From: naftis Date: Mon, 9 Dec 2024 17:30:27 +0200 Subject: [PATCH 15/18] refactor: wording --- packages/gateway/src/workflow/index.ts | 2 +- packages/workflow/src/config/routes.ts | 4 ++-- .../handler/{upsert.ts => upsert-identifiers.ts} | 10 ++++------ packages/workflow/src/records/state-transitions.ts | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) rename packages/workflow/src/records/handler/{upsert.ts => upsert-identifiers.ts} (87%) diff --git a/packages/gateway/src/workflow/index.ts b/packages/gateway/src/workflow/index.ts index d9f4daf262d..317fb86fc48 100644 --- a/packages/gateway/src/workflow/index.ts +++ b/packages/gateway/src/workflow/index.ts @@ -290,7 +290,7 @@ export async function upsertRegistrationIdentifier( ) { const res: ReadyForReviewRecord = await createRequest( 'POST', - `/records/${id}/upsert`, + `/records/${id}/upsert-identifiers`, authHeader, details ) diff --git a/packages/workflow/src/config/routes.ts b/packages/workflow/src/config/routes.ts index eb52f37419f..124a85828d1 100644 --- a/packages/workflow/src/config/routes.ts +++ b/packages/workflow/src/config/routes.ts @@ -29,7 +29,7 @@ import { approveCorrectionRoute } from '@workflow/records/handler/correction/app import { requestCorrectionRoute } from '@workflow/records/handler/correction/request' import { makeCorrectionRoute } from '@workflow/records/handler/correction/make-correction' import { eventNotificationHandler } from '@workflow/records/handler/eventNotificationHandler' -import { upsertRegistrationHandler } from '@workflow/records/handler/upsert' +import { upsertRegistrationHandler } from '@workflow/records/handler/upsert-identifiers' export const getRoutes = () => { const routes = [ @@ -72,7 +72,7 @@ export const getRoutes = () => { }, { method: 'POST', - path: '/records/{id}/upsert', + path: '/records/{id}/upsert-identifiers', handler: upsertRegistrationHandler, config: { tags: ['api'], diff --git a/packages/workflow/src/records/handler/upsert.ts b/packages/workflow/src/records/handler/upsert-identifiers.ts similarity index 87% rename from packages/workflow/src/records/handler/upsert.ts rename to packages/workflow/src/records/handler/upsert-identifiers.ts index 3fc31af1d58..6ee2209c7c6 100644 --- a/packages/workflow/src/records/handler/upsert.ts +++ b/packages/workflow/src/records/handler/upsert-identifiers.ts @@ -12,7 +12,7 @@ import { getToken } from '@workflow/utils/auth-utils' import * as Hapi from '@hapi/hapi' import { getRecordById } from '@workflow/records/index' import { indexBundle } from '@workflow/records/search' -import { toUpserted } from '@workflow/records/state-transitions' +import { toIdentifierUpserted } from '@workflow/records/state-transitions' import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types' import { sendBundleToHearth } from '@workflow/records/fhir' @@ -20,9 +20,8 @@ interface IdentifierInput { type: SupportedPatientIdentifierCode value: string } -export interface EventRegistrationPayload { - trackingId: string - registrationNumber: string + +interface EventRegistrationPayload { identifiers: IdentifierInput[] } @@ -57,11 +56,10 @@ export async function upsertRegistrationHandler( ) } - const upsertedRecord = toUpserted(savedRecord, identifiers) + const upsertedRecord = toIdentifierUpserted(savedRecord, identifiers) await sendBundleToHearth(upsertedRecord) await indexBundle(upsertedRecord, token) - // TBD: audit event return h.response(upsertedRecord).code(200) } diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index 6d88d6387ef..dd5a052c2ad 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -325,7 +325,7 @@ export async function toViewed( return viewedRecord } -export function toUpserted( +export function toIdentifierUpserted( record: T, identifiers: { type: SupportedPatientIdentifierCode From f9902f3e342c56b5acf327b670f712c9d63130fc Mon Sep 17 00:00:00 2001 From: naftis Date: Mon, 9 Dec 2024 17:33:53 +0200 Subject: [PATCH 16/18] fix: remove task history comment --- packages/workflow/src/records/state-transitions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/workflow/src/records/state-transitions.ts b/packages/workflow/src/records/state-transitions.ts index dd5a052c2ad..68f0dbb8a71 100644 --- a/packages/workflow/src/records/state-transitions.ts +++ b/packages/workflow/src/records/state-transitions.ts @@ -335,7 +335,6 @@ export function toIdentifierUpserted( const task = getTaskFromSavedBundle(record) const event = getTaskEventType(task) const composition = getComposition(record) - // TBD: task history entry const patientWithUpsertedIdentifier = upsertPatientIdentifiers( record, composition, From 8575ec00ec3db1fce0b94c027dd7da5cff20c99d Mon Sep 17 00:00:00 2001 From: naftis Date: Mon, 9 Dec 2024 17:40:33 +0200 Subject: [PATCH 17/18] refactor: for clarity, allow only updating one identifier at time --- CHANGELOG.md | 4 +++- .../gateway/src/features/registration/root-resolvers.ts | 5 ++--- packages/gateway/src/features/registration/schema.graphql | 8 ++------ packages/gateway/src/graphql/schema.d.ts | 8 ++------ packages/gateway/src/graphql/schema.graphql | 8 ++------ 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f291d4a175b..aa40b6386d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Reoder the sytem user add/edit field for surname to be first, also change labels from `Last name` to `User's surname` and lastly remove the NID question from the form [#6830](https://github.com/opencrvs/opencrvs-core/issues/6830) - Corrected the total amount displayed for _certification_ and _correction_ fees on the Performance Page, ensuring accurate fee tracking across certification and correction sequences. [#7793](https://github.com/opencrvs/opencrvs-core/issues/7793) - Auth now allows registrar's token to be exchanged for a new token that strictly allows confirming or rejecting a specific record. Core now passes this token to country configuration instead of the registrar's token [#7728](https://github.com/opencrvs/opencrvs-core/issues/7728) [#7849](https://github.com/opencrvs/opencrvs-core/issues/7849) +- A new GraphQL mutation `upsertRegistrationIdentifier` is added to allow updating the patient identifiers of a registration record such as NID [#8034](https://github.com/opencrvs/opencrvs-core/pull/8034) ### Improvements @@ -45,7 +46,6 @@ - Add an optional configurable field in section `canContinue` which takes an expression. Falsy value of this expression will disable the continue button in forms. This can be used to work with fetch field which has a loading state and prevent the user to get past the section while the request is still in progress. - ## [1.6.0](https://github.com/opencrvs/opencrvs-core/compare/v1.5.1...v1.6.0) ### Breaking changes @@ -77,6 +77,7 @@ ] } ``` + - `hasChildLocation` query has been removed from gateway. We have created the query `isLeafLevelLocation` instead which is more descriptive on its intended use. ### New features @@ -302,6 +303,7 @@ In the next OpenCRVS release v1.5.0, there will be two significant changes both - **New handlebars serving the location ids of the admin level locations** Apart from the new handlebars, a couple more improvements were introduced: + - stricter type for locations in client - **"location"** handlebar helper can now resolve offices & facilities - restrict the properties exposed through **"location"** handlebar helper diff --git a/packages/gateway/src/features/registration/root-resolvers.ts b/packages/gateway/src/features/registration/root-resolvers.ts index 9847b8cf0b4..2e801ceef67 100644 --- a/packages/gateway/src/features/registration/root-resolvers.ts +++ b/packages/gateway/src/features/registration/root-resolvers.ts @@ -644,7 +644,7 @@ export const resolvers: GQLResolver = { }, async upsertRegistrationIdentifier( _, - { id, details }, + { id, identifierType, identifierValue }, { headers: authHeader } ) { if (!hasRecordAccess(authHeader, id)) { @@ -653,8 +653,7 @@ export const resolvers: GQLResolver = { try { const taskEntry = await upsertRegistrationIdentifier(id, authHeader, { - registrationNumber: details.registrationNumber, - identifiers: details.identifiers + identifiers: [{ type: identifierType, value: identifierValue }] }) return taskEntry.resource.id diff --git a/packages/gateway/src/features/registration/schema.graphql b/packages/gateway/src/features/registration/schema.graphql index edf92ef1c4c..6a1d16902e9 100644 --- a/packages/gateway/src/features/registration/schema.graphql +++ b/packages/gateway/src/features/registration/schema.graphql @@ -578,11 +578,6 @@ input RejectRegistrationInput { comment: String } -input UpsertRegistrationIdentifierInput { - registrationNumber: String - identifiers: [IdentifierInput!] -} - 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 @@ -663,6 +658,7 @@ type Mutation { rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID! upsertRegistrationIdentifier( id: ID! - details: UpsertRegistrationIdentifierInput! + identifierType: String! + identifierValue: String! ): ID! } diff --git a/packages/gateway/src/graphql/schema.d.ts b/packages/gateway/src/graphql/schema.d.ts index d271e1b601f..ae46491ae10 100644 --- a/packages/gateway/src/graphql/schema.d.ts +++ b/packages/gateway/src/graphql/schema.d.ts @@ -610,11 +610,6 @@ export interface GQLRejectRegistrationInput { comment?: string } -export interface GQLUpsertRegistrationIdentifierInput { - registrationNumber?: string - identifiers?: Array -} - export interface GQLUserInput { id?: string name: Array @@ -3216,7 +3211,8 @@ export interface MutationToRejectRegistrationResolver< export interface MutationToUpsertRegistrationIdentifierArgs { id: string - details: GQLUpsertRegistrationIdentifierInput + identifierType: string + identifierValue: string } export interface MutationToUpsertRegistrationIdentifierResolver< TParent = any, diff --git a/packages/gateway/src/graphql/schema.graphql b/packages/gateway/src/graphql/schema.graphql index bebd3648f15..540e2e4b79e 100644 --- a/packages/gateway/src/graphql/schema.graphql +++ b/packages/gateway/src/graphql/schema.graphql @@ -221,7 +221,8 @@ type Mutation { rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID! upsertRegistrationIdentifier( id: ID! - details: UpsertRegistrationIdentifierInput! + identifierType: String! + identifierValue: String! ): ID! createOrUpdateUser(user: UserInput!): User! activateUser( @@ -746,11 +747,6 @@ input RejectRegistrationInput { comment: String } -input UpsertRegistrationIdentifierInput { - registrationNumber: String - identifiers: [IdentifierInput!] -} - input UserInput { id: ID name: [HumanNameInput!]! From 7fd261fddf476c765b15f6c7c903184602fcebe7 Mon Sep 17 00:00:00 2001 From: naftis Date: Mon, 9 Dec 2024 19:04:33 +0200 Subject: [PATCH 18/18] fix: remove unnecessary if as 'get' methods should already throw --- packages/workflow/src/records/handler/upsert-identifiers.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/workflow/src/records/handler/upsert-identifiers.ts b/packages/workflow/src/records/handler/upsert-identifiers.ts index 6ee2209c7c6..2d2ce4e3345 100644 --- a/packages/workflow/src/records/handler/upsert-identifiers.ts +++ b/packages/workflow/src/records/handler/upsert-identifiers.ts @@ -50,11 +50,6 @@ export async function upsertRegistrationHandler( ], true ) - if (!savedRecord) { - throw new Error( - 'Could not find record with composition id: ' + compositionId - ) - } const upsertedRecord = toIdentifierUpserted(savedRecord, identifiers)