diff --git a/docker-compose.yml b/docker-compose.yml index 681e1610276..95433f09401 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -71,7 +71,7 @@ services: - NODE_ENV=development - NOTIFICATION_SERVICE_URL=http://notification:2020/ - USER_MANAGEMENT_URL=http://user-mgnt:3030/ - - RESOURCE_SERVICE_URL=http://countryconfig:3040/ + - COUNTRY_CONFIG_URL=http://countryconfig:3040 - HEARTH_URL=http://hearth:3447/fhir - OPENHIM_URL=http://openhim-core:5001 - APPLICATION_CONFIG_URL=http://config:2021/ diff --git a/packages/workflow/src/constants.ts b/packages/workflow/src/constants.ts index 05eef704391..269625b64f4 100644 --- a/packages/workflow/src/constants.ts +++ b/packages/workflow/src/constants.ts @@ -20,8 +20,8 @@ export const NOTIFICATION_SERVICE_URL = export const MOSIP_TOKEN_SEEDER_URL = process.env.MOSIP_TOKEN_SEEDER_URL || 'http://localhost:8085' -export const RESOURCE_SERVICE_URL = - process.env.RESOURCE_SERVICE_URL || `http://localhost:3040/` +export const COUNTRY_CONFIG_URL = + process.env.COUNTRY_CONFIG_URL || `http://localhost:3040/` export const CERT_PUBLIC_KEY_PATH = (process.env.CERT_PUBLIC_KEY_PATH as string) || '../../.secrets/public-key.pem' diff --git a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.test.ts b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.test.ts index d3e69e9bf62..8bd951cf582 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.test.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.test.ts @@ -46,8 +46,9 @@ const fetch = fetchAny as any describe('Verify fhir bundle modifier functions', () => { describe('setTrackingId', () => { - it('Successfully modified the provided fhirBundle with birth trackingid', () => { - const fhirBundle = setTrackingId(testFhirBundle) + it('Successfully modified the provided fhirBundle with birth trackingid', async () => { + fetch.mockResponses(['B123456']) + const fhirBundle = await setTrackingId(testFhirBundle, '1234') if ( fhirBundle && fhirBundle.entry && @@ -75,8 +76,9 @@ describe('Verify fhir bundle modifier functions', () => { } }) - it('Successfully modified the provided fhirBundle with death trackingid', () => { - const fhirBundle = setTrackingId(testDeathFhirBundle) + it('Successfully modified the provided fhirBundle with death trackingid', async () => { + fetch.mockResponses(['D123456']) + const fhirBundle = await setTrackingId(testDeathFhirBundle, '1234') if ( fhirBundle && fhirBundle.entry && @@ -103,8 +105,9 @@ describe('Verify fhir bundle modifier functions', () => { } }) - it('Successfully modified the provided fhirBundle with marriage trackingid', () => { - const fhirBundle = setTrackingId(testMarriageFhirBundle) + it('Successfully modified the provided fhirBundle with marriage trackingid', async () => { + fetch.mockResponses(['M123456']) + const fhirBundle = await setTrackingId(testMarriageFhirBundle, '1234') if ( fhirBundle && fhirBundle.entry && @@ -131,31 +134,35 @@ describe('Verify fhir bundle modifier functions', () => { } }) - it('Throws error if invalid fhir bundle is provided', () => { + it('Throws error if invalid fhir bundle is provided', async () => { const invalidData = { ...testFhirBundle, entry: [] } - expect(() => setTrackingId(invalidData)).toThrowError( + await expect(setTrackingId(invalidData, '1234')).rejects.toThrowError( 'Invalid FHIR bundle found' ) }) - it('Will push the composite resource identifier if it is missing on fhirDoc', () => { - const fhirBundle = setTrackingId({ - ...testFhirBundle, - entry: [ - { - resource: { - code: { - coding: [ - { - system: 'http://opencrvs.org/specs/types', - code: 'BIRTH' - } - ] + it('Will push the composite resource identifier if it is missing on fhirDoc', async () => { + fetch.mockResponses(['B123456']) + const fhirBundle = await setTrackingId( + { + ...testFhirBundle, + entry: [ + { + resource: { + code: { + coding: [ + { + system: 'http://opencrvs.org/specs/types', + code: 'BIRTH' + } + ] + } } } - } - ] - }) + ] + }, + '1234' + ) if ( fhirBundle && 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 8b57b2f58d8..ce6a31cadef 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-bundle-modifier.ts @@ -45,10 +45,7 @@ import { } from '@workflow/features/user/utils' import { logger } from '@workflow/logger' import * as Hapi from '@hapi/hapi' -import { - APPLICATION_CONFIG_URL, - RESOURCE_SERVICE_URL -} from '@workflow/constants' +import { APPLICATION_CONFIG_URL, COUNTRY_CONFIG_URL } from '@workflow/constants' import { getToken, getTokenPayload, @@ -76,7 +73,7 @@ export async function modifyRegistrationBundle( throw new Error('Invalid FHIR bundle found for declaration') } /* setting unique trackingid here */ - fhirBundle = setTrackingId(fhirBundle) + fhirBundle = await setTrackingId(fhirBundle, token) const taskResource = selectOrCreateTaskRefResource(fhirBundle) as fhir.Task const eventType = getEventType(fhirBundle) @@ -165,7 +162,7 @@ export async function invokeRegistrationValidation( token: string ): Promise<{ bundle: fhir.Bundle; regValidationError?: boolean }> { try { - const res = await fetch(`${RESOURCE_SERVICE_URL}event-registration`, { + const res = await fetch(`${COUNTRY_CONFIG_URL}event-registration`, { method: 'POST', body: JSON.stringify(bundle), headers: { @@ -388,9 +385,16 @@ export async function touchBundle( return bundle } -export function setTrackingId(fhirBundle: fhir.Bundle): fhir.Bundle { +export async function setTrackingId( + fhirBundle: fhir.Bundle, + token: string +): Promise { const eventType = getEventType(fhirBundle) - const trackingId = generateTrackingIdForEvents(eventType) + const trackingId = await generateTrackingIdForEvents( + eventType, + fhirBundle, + token + ) const trackingIdFhirName = `${eventType.toLowerCase()}-tracking-id` if ( diff --git a/packages/workflow/src/features/registration/fhir/fhir-utils.test.ts b/packages/workflow/src/features/registration/fhir/fhir-utils.test.ts index c3385819681..2496d7da4fc 100644 --- a/packages/workflow/src/features/registration/fhir/fhir-utils.test.ts +++ b/packages/workflow/src/features/registration/fhir/fhir-utils.test.ts @@ -128,8 +128,9 @@ describe('Verify getCRVSOfficeName', () => { }) describe('Verify getTrackingId', () => { - it('Returned tracking id properly for birth', () => { - const trackingid = getTrackingId(setTrackingId(testFhirBundle)) + it('Returned tracking id properly for birth', async () => { + fetch.mockResponseOnce(null, { status: 404 }) + const trackingid = getTrackingId(await setTrackingId(testFhirBundle, '123')) if (trackingid) { expect(trackingid).toMatch(/^B/) expect(trackingid.length).toBe(7) @@ -138,8 +139,11 @@ describe('Verify getTrackingId', () => { } }) - it('Returned tracking id properly for death', () => { - const trackingid = getTrackingId(setTrackingId(testDeathFhirBundle)) + it('Returned tracking id properly for death', async () => { + fetch.mockResponseOnce(null, { status: 404 }) + const trackingid = getTrackingId( + await setTrackingId(testDeathFhirBundle, '123') + ) if (trackingid) { expect(trackingid).toMatch(/^D/) expect(trackingid.length).toBe(7) @@ -148,8 +152,11 @@ describe('Verify getTrackingId', () => { } }) - it('Returned tracking id properly for marriage', () => { - const trackingid = getTrackingId(setTrackingId(testMarriageFhirBundle)) + it('Returned tracking id properly for marriage', async () => { + fetch.mockResponseOnce(null, { status: 404 }) + const trackingid = getTrackingId( + await setTrackingId(testMarriageFhirBundle, '123') + ) if (trackingid) { expect(trackingid).toMatch(/^M/) expect(trackingid.length).toBe(7) diff --git a/packages/workflow/src/features/registration/handler.test.ts b/packages/workflow/src/features/registration/handler.test.ts index e08c4b8eba3..ba6d281811d 100644 --- a/packages/workflow/src/features/registration/handler.test.ts +++ b/packages/workflow/src/features/registration/handler.test.ts @@ -145,6 +145,7 @@ describe('Verify handler', () => { describe('createRegistrationHandler', () => { beforeEach(() => { fetch.mockResponses( + [null, { status: 404 }], [userMock, { status: 200 }], [fieldAgentPractitionerMock, { status: 200 }], [fieldAgentPractitionerRoleMock, { status: 200 }], @@ -547,43 +548,6 @@ describe('Verify handler', () => { expect(res.statusCode).toBe(500) }) - it('generates a new tracking id and repeats the request if a 409 is received from hearth', async () => { - fetch.mockResponses( - ['', { status: 409 }], - ['', { status: 409 }], - [ - JSON.stringify({ - resourceType: 'Bundle', - entry: [ - { - response: { location: 'Patient/12423/_history/1' } - } - ] - }) - ] - ) - - const token = jwt.sign( - { scope: ['declare'] }, - readFileSync('../auth/test/cert.key'), - { - algorithm: 'RS256', - issuer: 'opencrvs:auth-service', - audience: 'opencrvs:workflow-user' - } - ) - - const res = await server.server.inject({ - method: 'POST', - url: '/fhir', - payload: testFhirBundle, - headers: { - Authorization: `Bearer ${token}` - } - }) - expect(res.statusCode).toBe(200) - }) - it('fails after trying to generate a new trackingID and sending to Hearth 5 times', async () => { fetch.mockResponses( ['', { status: 409 }], diff --git a/packages/workflow/src/features/registration/handler.ts b/packages/workflow/src/features/registration/handler.ts index 87074b40c35..6f3b87c2916 100644 --- a/packages/workflow/src/features/registration/handler.ts +++ b/packages/workflow/src/features/registration/handler.ts @@ -14,7 +14,6 @@ import { markBundleAsValidated, markEventAsRegistered, modifyRegistrationBundle, - setTrackingId, markBundleAsWaitingValidation, updatePatientIdentifierWithRN, touchBundle, @@ -74,10 +73,7 @@ interface IEventRegistrationCallbackPayload { }[] } -async function sendBundleToHearth( - payload: fhir.Bundle, - count = 1 -): Promise { +async function sendBundleToHearth(payload: fhir.Bundle): Promise { const res = await fetch(HEARTH_URL, { method: 'POST', body: JSON.stringify(payload), @@ -86,11 +82,6 @@ async function sendBundleToHearth( } }) if (!res.ok) { - if (res.status === 409 && count < 5) { - setTrackingId(payload) - return await sendBundleToHearth(payload, count + 1) - } - throw new Error( `FHIR post to /fhir failed with [${res.status}] body: ${await res.text()}` ) diff --git a/packages/workflow/src/features/registration/utils.test.ts b/packages/workflow/src/features/registration/utils.test.ts index db1c01403bf..3a92a71f717 100644 --- a/packages/workflow/src/features/registration/utils.test.ts +++ b/packages/workflow/src/features/registration/utils.test.ts @@ -37,14 +37,24 @@ describe('Verify utility functions', () => { }) it('Generates proper birth tracking id successfully', async () => { - const trackingId = generateTrackingIdForEvents(EVENT_TYPE.BIRTH) + fetch.mockResponseOnce(null, { status: 404 }) + const trackingId = await generateTrackingIdForEvents( + EVENT_TYPE.BIRTH, + {} as fhir.Bundle, + '123' + ) expect(trackingId).toBeDefined() expect(trackingId.length).toBe(7) expect(trackingId).toMatch(/^B/) }) it('Generates proper death tracking id successfully', async () => { - const trackingId = generateTrackingIdForEvents(EVENT_TYPE.DEATH) + fetch.mockResponseOnce(null, { status: 404 }) + const trackingId = await generateTrackingIdForEvents( + EVENT_TYPE.DEATH, + {} as fhir.Bundle, + '123' + ) expect(trackingId).toBeDefined() expect(trackingId.length).toBe(7) @@ -52,7 +62,12 @@ describe('Verify utility functions', () => { }) it('Generates proper marriage tracking id successfully', async () => { - const trackingId = generateTrackingIdForEvents(EVENT_TYPE.MARRIAGE) + fetch.mockResponseOnce(null, { status: 404 }) + const trackingId = await generateTrackingIdForEvents( + EVENT_TYPE.MARRIAGE, + {} as fhir.Bundle, + '123' + ) expect(trackingId).toBeDefined() expect(trackingId.length).toBe(7) @@ -67,7 +82,8 @@ describe('Verify utility functions', () => { }) it('send in-progress birth declaration notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundle) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundle, '123') fetch.mockResponse(officeMock) expect( sendEventNotification( @@ -81,7 +97,8 @@ describe('Verify utility functions', () => { ).resolves.not.toThrow() }) it('send Birth declaration notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundle) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundle, '123') fetch.mockResponse(officeMock) expect( sendEventNotification( @@ -115,7 +132,8 @@ describe('Verify utility functions', () => { ) }) it('send mark birth registration notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundle) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundle, '123') fetch.mockResponse(officeMock) //@ts-ignore fhirBundle.entry[1].resource.identifier.push({ @@ -152,7 +170,8 @@ describe('Verify utility functions', () => { ) }) it('send Birth rejection notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundle) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundle, '123') fetch.mockResponse(officeMock) expect( sendEventNotification( @@ -166,7 +185,8 @@ describe('Verify utility functions', () => { ).toBeDefined() }) it('send in-progress death declaration notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundleWithIdsForDeath) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundleWithIdsForDeath, '123') fetch.mockResponse(officeMock) expect( sendEventNotification( @@ -180,7 +200,8 @@ describe('Verify utility functions', () => { ).toBeDefined() }) it('send Death declaration notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundleWithIdsForDeath) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundleWithIdsForDeath, '123') fetch.mockResponse(officeMock) expect( sendEventNotification( @@ -212,7 +233,8 @@ describe('Verify utility functions', () => { ) }) it('send mark death registration notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundleWithIdsForDeath) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundleWithIdsForDeath, '123') //@ts-ignore fhirBundle.entry[1].resource.identifier.push({ system: 'http://opencrvs.org/specs/id/death-registration-number', @@ -250,7 +272,8 @@ describe('Verify utility functions', () => { ) }) it('send Death rejection notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundleWithIdsForDeath) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundleWithIdsForDeath, '123') fetch.mockResponses([officeMock, { status: 200 }]) fetch.mockResponses([deathTaskMock, { status: 200 }]) expect( @@ -265,7 +288,8 @@ describe('Verify utility functions', () => { ).toBeDefined() }) it('send Death declaration notification successfully', async () => { - const fhirBundle = setTrackingId(testFhirBundleWithIdsForDeath) + fetch.mockResponseOnce(null, { status: 404 }) + const fhirBundle = await setTrackingId(testFhirBundleWithIdsForDeath, '123') fetch.mockResponses([officeMock, { status: 200 }]) fetch.mockResponses([deathTaskMock, { status: 200 }]) fetch.mockResponses([deathTaskMock, { status: 200 }]) @@ -283,6 +307,9 @@ describe('Verify utility functions', () => { }) describe('getMosipUINToken functions', () => { + beforeAll(() => { + fetch.mockClear() + }) it('Calls mosip token seeder function and returns success', async () => { fetch.mockResponse(mosipSuccessMock) const mosipResponse = await getMosipUINToken(mosipDeceasedPatientMock) diff --git a/packages/workflow/src/features/registration/utils.ts b/packages/workflow/src/features/registration/utils.ts index 41bc7514a4c..03431d74f67 100644 --- a/packages/workflow/src/features/registration/utils.ts +++ b/packages/workflow/src/features/registration/utils.ts @@ -12,7 +12,8 @@ import * as ShortUIDGen from 'short-uid' import { NOTIFICATION_SERVICE_URL, MOSIP_TOKEN_SEEDER_URL, - HEARTH_URL + HEARTH_URL, + COUNTRY_CONFIG_URL } from '@workflow/constants' import fetch from 'node-fetch' import { logger } from '@workflow/logger' @@ -64,10 +65,40 @@ export enum FHIR_RESOURCE_TYPE { PATIENT = 'Patient' } -export function generateTrackingIdForEvents(eventType: EVENT_TYPE): string { - // 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)) +export async function generateTrackingIdForEvents( + eventType: EVENT_TYPE, + bundle: fhir.Bundle, + token: string +): Promise { + const trackingIdFromCountryConfig = await getTrackingIdFromCountryConfig( + bundle, + token + ) + if (trackingIdFromCountryConfig) { + return trackingIdFromCountryConfig + } 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)) + } +} + +export async function getTrackingIdFromCountryConfig( + bundle: fhir.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 {