diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index ec732943e5c..468ccd92f66 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -78,7 +78,6 @@ services: - NODE_ENV=development - FHIR_URL=http://hearth:3447/fhir - AUTH_URL=http://auth:4040 - - CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration - CHECK_INVALID_TOKEN=true ports: - '3040:3040' diff --git a/packages/client/graphql.schema.json b/packages/client/graphql.schema.json index ab9656a55c9..bc6cc734736 100644 --- a/packages/client/graphql.schema.json +++ b/packages/client/graphql.schema.json @@ -3978,6 +3978,61 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "ConfirmRegistrationInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "childIdentifiers", + "description": null, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "IdentifierInput", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "registrationNumber", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "trackingId", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "ContactPoint", @@ -5299,33 +5354,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "Dummy", - "description": null, - "fields": [ - { - "name": "dummy", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "OBJECT", "name": "DuplicatesInfo", @@ -6999,6 +7027,49 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "IdentifierInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "type", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "SupportedPatientIdentifierCode", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "value", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "IdentityInput", @@ -9299,6 +9370,55 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "confirmRegistration", + "description": null, + "args": [ + { + "name": "details", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ConfirmRegistrationInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "createBirthRegistration", "description": null, @@ -10667,6 +10787,55 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "rejectRegistration", + "description": null, + "args": [ + { + "name": "details", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "RejectRegistrationInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "rejectRegistrationCorrection", "description": null, @@ -16422,6 +16591,66 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "RejectRegistrationInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "comment", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "reason", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "RejectionReason", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "RejectionReason", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "OTHER", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "OBJECT", "name": "RelatedPerson", @@ -17803,6 +18032,113 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "ENUM", + "name": "SupportedPatientIdentifierCode", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "ALIEN_NUMBER", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "BIRTH_CONFIGURABLE_IDENTIFIER_1", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "BIRTH_CONFIGURABLE_IDENTIFIER_2", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "BIRTH_CONFIGURABLE_IDENTIFIER_3", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "BIRTH_PATIENT_ENTRY", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "BIRTH_REGISTRATION_NUMBER", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "DEATH_REGISTRATION_NUMBER", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "DECEASED_PATIENT_ENTRY", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "DRIVING_LICENSE", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "MARRIAGE_REGISTRATION_NUMBER", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "MOSIP_PSUT_TOKEN_ID", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "NATIONAL_ID", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "OTHER", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "PASSPORT", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "REFUGEE_NUMBER", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SOCIAL_SECURITY_NO", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "OBJECT", "name": "System", diff --git a/packages/client/src/declarations/createRecord.ts b/packages/client/src/declarations/createRecord.ts index 6a641d93b09..19762f27fca 100644 --- a/packages/client/src/declarations/createRecord.ts +++ b/packages/client/src/declarations/createRecord.ts @@ -41,6 +41,9 @@ const FETCH_RECORD_STATUS = gql` } ` +const waitWithIncreasingBackoff = (attemptNumber: number) => + new Promise((resolve) => setTimeout(resolve, 1000 + attemptNumber * 1000)) + export async function submitAndWaitUntilRecordInWorkqueue( mutation: DocumentNode, graphqlPayload: TransformedData, @@ -90,7 +93,8 @@ export async function submitAndWaitUntilRecordInWorkqueue( } } - await new Promise((resolve) => setTimeout(resolve, 1000 + nthTry * 1000)) + await waitWithIncreasingBackoff(nthTry) + nthTry++ } } diff --git a/packages/client/src/declarations/submissionMiddleware.ts b/packages/client/src/declarations/submissionMiddleware.ts index c8ac3d3bb0e..94ab2594067 100644 --- a/packages/client/src/declarations/submissionMiddleware.ts +++ b/packages/client/src/declarations/submissionMiddleware.ts @@ -43,12 +43,12 @@ import { NOT_A_DUPLICATE } from '@client/views/DataProvider/mutation' import { updateRegistrarWorkqueue } from '@client/workqueue' import { Action, Middleware, createAction } from '@reduxjs/toolkit' import { Dispatch } from 'redux' -// eslint-disable-next-line no-restricted-imports import { getReviewForm } from '@client/forms/register/review-selectors' import { IOfflineData } from '@client/offline/reducer' import { getOfflineData } from '@client/offline/selectors' import type { MutationToRequestRegistrationCorrectionArgs } from '@client/utils/gateway-deprecated-do-not-use' import { UserDetails } from '@client/utils/userUtils' +// eslint-disable-next-line no-restricted-imports import { captureException } from '@sentry/browser' import { submitAndWaitUntilRecordInWorkqueue } from './createRecord' @@ -362,7 +362,6 @@ export const submissionMiddleware: Middleware<{}, IStoreState> = captureException(error) return } - console.log(error) updateDeclaration(dispatch, { ...declaration, diff --git a/packages/client/src/tests/languages.json b/packages/client/src/tests/languages.json index e3329737a31..5aeb76b2a41 100644 --- a/packages/client/src/tests/languages.json +++ b/packages/client/src/tests/languages.json @@ -1020,8 +1020,6 @@ "review.rejection.form.commentLabel": "Comments or instructions for health worker to rectify declaration", "review.rejection.form.reasons": "Reason(s) for rejection:", "review.rejection.form.reasons.duplicate": "Duplicate declaration", - "review.rej.form.reasons.missSupDoc": "Missing supporting documents", - "review.rejection.form.reasons.misspelling": "Misspelling", "review.rejection.form.reasons.other": "Other", "review.rejection.form.submitButton": "Submit rejection", "review.rejection.form.title": "What update does the declaration require?", @@ -2169,8 +2167,6 @@ "review.rejection.form.commentLabel": "আবেদন সংশোধন করতে স্বাস্থ্য কর্মীর জন্য নির্দেশাবলী", "review.rejection.form.reasons": "প্রত্যাখ্যানের কারণসমূহ:", "review.rejection.form.reasons.duplicate": "নকল আবেদন", - "review.rej.form.reasons.missSupDoc": "প্রমাণক অনুপস্থিত", - "review.rejection.form.reasons.misspelling": "ভুল বানান", "review.rejection.form.reasons.other": "অন্যান্য", "review.rejection.form.submitButton": "প্রত্যাখ্যান জমা দিন", "review.rejection.form.title": "আবেদনটির কী হালনাগাদ দরকার?", diff --git a/packages/client/src/utils/gateway.ts b/packages/client/src/utils/gateway.ts index a80ee81bdc3..aabe02a8498 100644 --- a/packages/client/src/utils/gateway.ts +++ b/packages/client/src/utils/gateway.ts @@ -428,6 +428,12 @@ export type ComparisonInput = { nin?: InputMaybe> } +export type ConfirmRegistrationInput = { + childIdentifiers?: InputMaybe> + registrationNumber?: InputMaybe + trackingId?: InputMaybe +} + export type ContactPoint = { __typename?: 'ContactPoint' system?: Maybe @@ -558,11 +564,6 @@ export type DeclarationsStartedMetrics = { officeDeclarations: Scalars['Int'] } -export type Dummy = { - __typename?: 'Dummy' - dummy: Scalars['String'] -} - export type DuplicatesInfo = { __typename?: 'DuplicatesInfo' compositionId?: Maybe @@ -739,6 +740,11 @@ export type Identifier = { value?: Maybe } +export type IdentifierInput = { + type: SupportedPatientIdentifierCode + value: Scalars['String'] +} + export type IdentityInput = { fieldsModifiedByIdentity?: InputMaybe>> id?: InputMaybe @@ -927,6 +933,7 @@ export type Mutation = { changeEmail?: Maybe changePassword?: Maybe changePhone?: Maybe + confirmRegistration: Scalars['ID'] createBirthRegistration: Scalars['Void'] createBirthRegistrationCorrection: Scalars['ID'] createDeathRegistration: Scalars['Void'] @@ -959,6 +966,7 @@ export type Mutation = { reactivateSystem?: Maybe refreshSystemSecret?: Maybe registerSystem?: Maybe + rejectRegistration: Scalars['ID'] rejectRegistrationCorrection: Scalars['ID'] removeBookmarkedAdvancedSearch?: Maybe requestRegistrationCorrection: Scalars['ID'] @@ -1027,6 +1035,11 @@ export type MutationChangePhoneArgs = { verifyCode: Scalars['String'] } +export type MutationConfirmRegistrationArgs = { + details: ConfirmRegistrationInput + id: Scalars['ID'] +} + export type MutationCreateBirthRegistrationArgs = { details: BirthRegistrationInput } @@ -1180,6 +1193,11 @@ export type MutationRegisterSystemArgs = { system?: InputMaybe } +export type MutationRejectRegistrationArgs = { + details: RejectRegistrationInput + id: Scalars['ID'] +} + export type MutationRejectRegistrationCorrectionArgs = { details: CorrectionRejectionInput id: Scalars['ID'] @@ -1832,6 +1850,15 @@ export type Reinstated = { taskEntryResourceID: Scalars['ID'] } +export type RejectRegistrationInput = { + comment: Scalars['String'] + reason: RejectionReason +} + +export enum RejectionReason { + Other = 'OTHER' +} + export type RelatedPerson = { __typename?: 'RelatedPerson' _fhirID?: Maybe @@ -1982,6 +2009,25 @@ export type StatusWiseRegistrationCount = { status: Scalars['String'] } +export enum SupportedPatientIdentifierCode { + AlienNumber = 'ALIEN_NUMBER', + BirthConfigurableIdentifier_1 = 'BIRTH_CONFIGURABLE_IDENTIFIER_1', + BirthConfigurableIdentifier_2 = 'BIRTH_CONFIGURABLE_IDENTIFIER_2', + BirthConfigurableIdentifier_3 = 'BIRTH_CONFIGURABLE_IDENTIFIER_3', + BirthPatientEntry = 'BIRTH_PATIENT_ENTRY', + BirthRegistrationNumber = 'BIRTH_REGISTRATION_NUMBER', + DeathRegistrationNumber = 'DEATH_REGISTRATION_NUMBER', + DeceasedPatientEntry = 'DECEASED_PATIENT_ENTRY', + DrivingLicense = 'DRIVING_LICENSE', + MarriageRegistrationNumber = 'MARRIAGE_REGISTRATION_NUMBER', + MosipPsutTokenId = 'MOSIP_PSUT_TOKEN_ID', + NationalId = 'NATIONAL_ID', + Other = 'OTHER', + Passport = 'PASSPORT', + RefugeeNumber = 'REFUGEE_NUMBER', + SocialSecurityNo = 'SOCIAL_SECURITY_NO' +} + export type System = { __typename?: 'System' _id: Scalars['ID'] diff --git a/packages/client/src/views/SearchResult/SearchResult.test.tsx b/packages/client/src/views/SearchResult/SearchResult.test.tsx index 856e4b2ff76..bee821e38a8 100644 --- a/packages/client/src/views/SearchResult/SearchResult.test.tsx +++ b/packages/client/src/views/SearchResult/SearchResult.test.tsx @@ -141,8 +141,7 @@ describe('SearchResult tests', () => { duplicates: null, registeredLocationId: '308c35b4-04f8-4664-83f5-9790e790cde1', - reason: - 'duplicate,misspelling,missing_supporting_doc,other', + reason: 'duplicate', comment: 'Rejected' }, dateOfDeath: '2010-01-01', diff --git a/packages/client/src/views/SearchResult/SearchResult.tsx b/packages/client/src/views/SearchResult/SearchResult.tsx index a218871802f..0cc31fdd150 100644 --- a/packages/client/src/views/SearchResult/SearchResult.tsx +++ b/packages/client/src/views/SearchResult/SearchResult.tsx @@ -85,7 +85,7 @@ const ErrorText = styled.div` const ToolTipContainer = styled.span` text-align: center; ` - +// @TODO: Get rid of these magic strings - they are outdated and will get more outdated as time passes export function getRejectionReasonDisplayValue(reason: string) { switch (reason.toLowerCase()) { case 'duplicate': diff --git a/packages/commons/src/fhir/patient.ts b/packages/commons/src/fhir/patient.ts index bddb6294ce9..6d648679375 100644 --- a/packages/commons/src/fhir/patient.ts +++ b/packages/commons/src/fhir/patient.ts @@ -43,6 +43,10 @@ export type OpenCRVSPatientName = Omit & { use: string } +/** + * If you change this, also change packages/gateway/src/features/registration/schema.graphql SupportedPatientIdentifierCode + */ + export const SUPPORTED_PATIENT_IDENTIFIER_CODES = [ 'PASSPORT', 'NATIONAL_ID', diff --git a/packages/commons/src/message-queue/record.ts b/packages/commons/src/message-queue/record.ts index bde9a4313da..85325007a7f 100644 --- a/packages/commons/src/message-queue/record.ts +++ b/packages/commons/src/message-queue/record.ts @@ -8,7 +8,7 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import { Job, Processor, Queue, Worker } from 'bullmq' +import { ConnectionOptions, Job, Processor, Queue, Worker } from 'bullmq' import { PlainToken } from '../http' import { logger } from '../logger' import { @@ -52,12 +52,10 @@ type Payload = token: PlainToken } -export function useExternalValidationQueue(redisHost: string) { +export function useExternalValidationQueue(connection: ConnectionOptions) { const queue = new Queue( 'external-validations', - { - connection: { host: redisHost, port: 6379 } - } + { connection } ) async function sendForExternalValidation(payload: ExternalValidationPayload) { @@ -90,10 +88,8 @@ export function useExternalValidationQueue(redisHost: string) { sendForExternalValidation } } -export function useRecordQueue(redisHost: string) { - const queue = new Queue('records', { - connection: { host: redisHost, port: 6379 } - }) +export function useRecordQueue(connection: ConnectionOptions) { + const queue = new Queue('records', { connection }) async function createDeclaration(payload: Payload) { await queue.waitUntilReady() @@ -142,7 +138,7 @@ export function useRecordQueue(redisHost: string) { } export async function registerRecordWorker( - redisHost: string, + connection: ConnectionOptions, processJob: Processor ) { const worker = new Worker( @@ -151,7 +147,7 @@ export async function registerRecordWorker( return processJob(job) }, { - connection: { host: redisHost, port: 6379 } + connection } ) @@ -167,7 +163,7 @@ export async function registerRecordWorker( } export async function registerExternalValidationsWorker( - redisHost: string, + connection: ConnectionOptions, processJob: ( job: | Job @@ -184,7 +180,7 @@ export async function registerExternalValidationsWorker( return processJob(job) }, { - connection: { host: redisHost, port: 6379 } + connection } ) diff --git a/packages/gateway/src/constants.ts b/packages/gateway/src/constants.ts index 740cc44ad63..19f91a4b7d8 100644 --- a/packages/gateway/src/constants.ts +++ b/packages/gateway/src/constants.ts @@ -22,6 +22,7 @@ export const NATIVE_LANGUAGE = (() => { export const DEFAULT_TIMEOUT = 600000 export const REDIS_HOST = env.REDIS_HOST +export const REDIS_PORT = env.REDIS_PORT export const HOST = env.HOST export const PORT = env.PORT export const HOSTNAME = env.DOMAIN diff --git a/packages/gateway/src/environment.ts b/packages/gateway/src/environment.ts index 04f8d7cc5ad..8e2cb989900 100644 --- a/packages/gateway/src/environment.ts +++ b/packages/gateway/src/environment.ts @@ -12,6 +12,7 @@ import { cleanEnv, str, port, url, bool, num } from 'envalid' export const env = cleanEnv(process.env, { REDIS_HOST: str({ devDefault: 'localhost' }), + REDIS_PORT: port({ default: 6379 }), HOST: str({ devDefault: '0.0.0.0' }), PORT: port({ default: 7070 }), DOMAIN: str({ devDefault: '*' }), diff --git a/packages/gateway/src/features/registration/__snapshots__/type-resolvers.test.ts.snap b/packages/gateway/src/features/registration/__snapshots__/type-resolvers.test.ts.snap index 02451a6d6a3..1454dba80d5 100644 --- a/packages/gateway/src/features/registration/__snapshots__/type-resolvers.test.ts.snap +++ b/packages/gateway/src/features/registration/__snapshots__/type-resolvers.test.ts.snap @@ -170,6 +170,7 @@ Object { "foetalDeathsToMother": null, "history": Array [ Object { + "action": "ASSIGNED", "certificates": Array [ null, ], @@ -216,6 +217,7 @@ Object { "output": Array [], "payment": null, "reason": null, + "regStatus": "WAITING_VALIDATION", "requester": "", "requesterOther": "", "signature": null, @@ -480,10 +482,12 @@ Object { "reason": null, "timeLogged": null, "timestamp": "2023-10-02T13:51:55.410Z", + "type": "WAITING_VALIDATION", "user": null, }, ], "trackingId": "B3VUXES", + "type": "BIRTH", "witnessOneSignature": null, "witnessTwoSignature": null, }, @@ -655,6 +659,7 @@ Object { "femaleDependentsOfDeceased": 4, "history": Array [ Object { + "action": "VIEWED", "certificates": Array [], "comments": Array [], "date": "2023-09-22T11:52:48.611+00:00", @@ -699,6 +704,7 @@ Object { "output": Array [], "payment": null, "reason": null, + "regStatus": "REGISTERED", "requester": "", "requesterOther": "", "signature": null, @@ -914,10 +920,12 @@ Object { "reason": null, "timeLogged": null, "timestamp": "2023-09-22T11:52:48.439Z", + "type": "REGISTERED", "user": null, }, ], "trackingId": "DL1W8FV", + "type": "DEATH", "witnessOneSignature": null, "witnessTwoSignature": null, }, @@ -1203,6 +1211,7 @@ Object { }, "history": Array [ Object { + "action": "ASSIGNED", "certificates": Array [], "comments": Array [], "date": "2023-09-22T08:54:57.825+00:00", @@ -1247,6 +1256,7 @@ Object { "output": Array [], "payment": null, "reason": null, + "regStatus": "REGISTERED", "requester": "", "requesterOther": "", "signature": null, @@ -1426,10 +1436,12 @@ Object { "reason": null, "timeLogged": null, "timestamp": "2023-09-22T08:54:57.632Z", + "type": "REGISTERED", "user": null, }, ], "trackingId": "MTNJUSI", + "type": "MARRIAGE", "witnessOneSignature": "/mock-presigned-url", "witnessTwoSignature": "/mock-presigned-url", }, diff --git a/packages/gateway/src/features/registration/root-resolvers.ts b/packages/gateway/src/features/registration/root-resolvers.ts index 68c4994221c..1904b66e2db 100644 --- a/packages/gateway/src/features/registration/root-resolvers.ts +++ b/packages/gateway/src/features/registration/root-resolvers.ts @@ -31,6 +31,7 @@ import { import { archiveRegistration, certifyRegistration, + confirmRegistration, createRegistration, duplicateRegistration, fetchRegistrationForDownloading, @@ -634,6 +635,20 @@ export const resolvers: GQLResolver = { ) return taskEntry.resource.id + }, + async confirmRegistration(_, { id, details }, { headers: authHeader }) { + if (!inScope(authHeader, ['register', 'validate'])) { + throw new Error('User does not have a register or validate scope') + } + + return confirmRegistration(id, authHeader, details) + }, + async rejectRegistration(_, { id, details }, { headers: authHeader }) { + if (!inScope(authHeader, ['register', 'validate'])) { + throw new Error('User does not have a register or validate scope') + } + + return rejectDeclaration(id, authHeader, details.comment, details.reason) } } } diff --git a/packages/gateway/src/features/registration/schema.graphql b/packages/gateway/src/features/registration/schema.graphql index 75a3374d298..26e8fdf92b1 100644 --- a/packages/gateway/src/features/registration/schema.graphql +++ b/packages/gateway/src/features/registration/schema.graphql @@ -563,6 +563,45 @@ input CorrectionRejectionInput { timeLoggedMS: Int! } +# if you change this, also change packages/commons/src/fhir/patient.ts SUPPORTED_PATIENT_IDENTIFIER_CODES +enum SupportedPatientIdentifierCode { + PASSPORT + NATIONAL_ID + MOSIP_PSUT_TOKEN_ID + DECEASED_PATIENT_ENTRY + BIRTH_PATIENT_ENTRY + DRIVING_LICENSE + REFUGEE_NUMBER + ALIEN_NUMBER + OTHER + SOCIAL_SECURITY_NO + BIRTH_REGISTRATION_NUMBER + DEATH_REGISTRATION_NUMBER + MARRIAGE_REGISTRATION_NUMBER + BIRTH_CONFIGURABLE_IDENTIFIER_1 + BIRTH_CONFIGURABLE_IDENTIFIER_2 + BIRTH_CONFIGURABLE_IDENTIFIER_3 +} + +input IdentifierInput { + type: SupportedPatientIdentifierCode! + value: String! +} +input ConfirmRegistrationInput { + childIdentifiers: [IdentifierInput!] + registrationNumber: String + trackingId: String +} + +enum RejectionReason { + OTHER +} + +input RejectRegistrationInput { + reason: RejectionReason! # Rejection type + comment: String! # Free text comment for audit log +} + 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 @@ -639,4 +678,6 @@ type Mutation { comment: String duplicateTrackingId: String ): ID! + confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID! + rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID! } diff --git a/packages/gateway/src/features/search/type-resolvers.test.ts b/packages/gateway/src/features/search/type-resolvers.test.ts index 31266bf8fe2..dcfb1dee1f6 100644 --- a/packages/gateway/src/features/search/type-resolvers.test.ts +++ b/packages/gateway/src/features/search/type-resolvers.test.ts @@ -179,7 +179,7 @@ describe('Search type resolvers', () => { { operatedOn: '2019-12-12T15:24:53.586Z', operatorFirstNames: 'Mohammad', - rejectReason: 'missing_supporting_doc', + rejectReason: 'other', operatorFamilyName: 'Ashraful', rejectComment: 'No supporting documents provided.', operatorOfficeName: 'Alokbali Union Parishad', @@ -213,7 +213,7 @@ describe('Search type resolvers', () => { { operatedOn: '2019-12-12T15:24:53.586Z', operatorFirstNames: 'Mohammad', - rejectReason: 'missing_supporting_doc', + rejectReason: 'other', operatorFamilyName: 'Ashraful', rejectComment: 'No supporting documents provided.', operatorOfficeName: 'Alokbali Union Parishad', @@ -367,7 +367,7 @@ describe('Search type resolvers', () => { { operatedOn: '2019-12-12T15:24:53.586Z', operatorFirstNames: 'Mohammad', - rejectReason: 'missing_supporting_doc', + rejectReason: 'other', operatorFamilyName: 'Ashraful', rejectComment: 'No supporting documents provided.', operatorOfficeName: 'Alokbali Union Parishad', @@ -401,7 +401,7 @@ describe('Search type resolvers', () => { { operatedOn: '2019-12-12T15:24:53.586Z', operatorFirstNames: 'Mohammad', - rejectReason: 'missing_supporting_doc', + rejectReason: 'other', operatorFamilyName: 'Ashraful', rejectComment: 'No supporting documents provided.', operatorOfficeName: 'Alokbali Union Parishad', @@ -593,7 +593,7 @@ describe('Search type resolvers', () => { { operatedOn: '2019-12-12T15:24:53.586Z', operatorFirstNames: 'Mohammad', - rejectReason: 'missing_supporting_doc', + rejectReason: 'other', operatorFamilyName: 'Ashraful', rejectComment: 'No supporting documents provided.', operatorOfficeName: 'Alokbali Union Parishad', diff --git a/packages/gateway/src/graphql/index.graphql b/packages/gateway/src/graphql/index.graphql index ab8ca23a042..f03c833a683 100644 --- a/packages/gateway/src/graphql/index.graphql +++ b/packages/gateway/src/graphql/index.graphql @@ -21,10 +21,3 @@ # import Mutation from '../features/bookmarkAdvancedSearch/schema.graphql' # import Query.* from '../features/OIDPUserInfo/schema.graphql' # import * from 'common.graphql' - -# TODO -# https://github.com/prismagraphql/graphql-import/issues/180 -# https://github.com/prismagraphql/graphql-import/issues/45 -type Dummy { - dummy: String! -} diff --git a/packages/gateway/src/graphql/query-generator.ts b/packages/gateway/src/graphql/query-generator.ts index c7d74c1a34c..da2b20e0130 100644 --- a/packages/gateway/src/graphql/query-generator.ts +++ b/packages/gateway/src/graphql/query-generator.ts @@ -14,7 +14,8 @@ import { isListType, isNonNullType, isObjectType, - isScalarType + isScalarType, + isNamedType } from 'graphql' export function generateQueryForType( @@ -48,6 +49,8 @@ export function generateQueryForType( fieldStr += `${fieldName} { ${buildFields(fieldType)} } ` } else if (isScalarType(fieldType)) { fieldStr += `${fieldName} ` + } else if (isNamedType(fieldType)) { + fieldStr += `${fieldName} ` } } return fieldStr.trim() diff --git a/packages/gateway/src/graphql/schema.d.ts b/packages/gateway/src/graphql/schema.d.ts index dfa91b6f87f..6f14e9d66f9 100644 --- a/packages/gateway/src/graphql/schema.d.ts +++ b/packages/gateway/src/graphql/schema.d.ts @@ -87,6 +87,8 @@ export interface GQLMutation { markMarriageAsCertified: string markMarriageAsIssued: string markEventAsDuplicate: string + confirmRegistration: string + rejectRegistration: string createOrUpdateUser: GQLUser activateUser?: string changePassword?: string @@ -108,10 +110,6 @@ export interface GQLMutation { removeBookmarkedAdvancedSearch?: GQLBookMarkedSearches } -export interface GQLDummy { - dummy: string -} - export interface GQLNotificationResult { success: boolean } @@ -613,6 +611,17 @@ export interface GQLReinstated { registrationStatus?: GQLRegStatus } +export interface GQLConfirmRegistrationInput { + childIdentifiers?: Array + registrationNumber?: string + trackingId?: string +} + +export interface GQLRejectRegistrationInput { + reason: GQLRejectionReason + comment: string +} + export interface GQLUserInput { id?: string name: Array @@ -1221,6 +1230,15 @@ export const enum GQLRegStatus { ISSUED = 'ISSUED' } +export interface GQLIdentifierInput { + type: GQLSupportedPatientIdentifierCode + value: string +} + +export const enum GQLRejectionReason { + OTHER = 'OTHER' +} + export interface GQLHumanNameInput { use?: string firstNames?: string @@ -1639,6 +1657,25 @@ export interface GQLDeceasedInput { deathDate?: GQLPlainDate } +export const enum GQLSupportedPatientIdentifierCode { + PASSPORT = 'PASSPORT', + NATIONAL_ID = 'NATIONAL_ID', + MOSIP_PSUT_TOKEN_ID = 'MOSIP_PSUT_TOKEN_ID', + DECEASED_PATIENT_ENTRY = 'DECEASED_PATIENT_ENTRY', + BIRTH_PATIENT_ENTRY = 'BIRTH_PATIENT_ENTRY', + DRIVING_LICENSE = 'DRIVING_LICENSE', + REFUGEE_NUMBER = 'REFUGEE_NUMBER', + ALIEN_NUMBER = 'ALIEN_NUMBER', + OTHER = 'OTHER', + SOCIAL_SECURITY_NO = 'SOCIAL_SECURITY_NO', + BIRTH_REGISTRATION_NUMBER = 'BIRTH_REGISTRATION_NUMBER', + DEATH_REGISTRATION_NUMBER = 'DEATH_REGISTRATION_NUMBER', + MARRIAGE_REGISTRATION_NUMBER = 'MARRIAGE_REGISTRATION_NUMBER', + BIRTH_CONFIGURABLE_IDENTIFIER_1 = 'BIRTH_CONFIGURABLE_IDENTIFIER_1', + BIRTH_CONFIGURABLE_IDENTIFIER_2 = 'BIRTH_CONFIGURABLE_IDENTIFIER_2', + BIRTH_CONFIGURABLE_IDENTIFIER_3 = 'BIRTH_CONFIGURABLE_IDENTIFIER_3' +} + export interface GQLLabelInput { lang: string label: string @@ -1729,7 +1766,6 @@ export interface GQLPaymentInput { export interface GQLResolver { Query?: GQLQueryTypeResolver Mutation?: GQLMutationTypeResolver - Dummy?: GQLDummyTypeResolver NotificationResult?: GQLNotificationResultTypeResolver BirthRegistration?: GQLBirthRegistrationTypeResolver Date?: GraphQLScalarType @@ -2551,6 +2587,8 @@ export interface GQLMutationTypeResolver { markMarriageAsCertified?: MutationToMarkMarriageAsCertifiedResolver markMarriageAsIssued?: MutationToMarkMarriageAsIssuedResolver markEventAsDuplicate?: MutationToMarkEventAsDuplicateResolver + confirmRegistration?: MutationToConfirmRegistrationResolver + rejectRegistration?: MutationToRejectRegistrationResolver createOrUpdateUser?: MutationToCreateOrUpdateUserResolver activateUser?: MutationToActivateUserResolver changePassword?: MutationToChangePasswordResolver @@ -3083,6 +3121,38 @@ export interface MutationToMarkEventAsDuplicateResolver< ): TResult } +export interface MutationToConfirmRegistrationArgs { + id: string + details: GQLConfirmRegistrationInput +} +export interface MutationToConfirmRegistrationResolver< + TParent = any, + TResult = any +> { + ( + parent: TParent, + args: MutationToConfirmRegistrationArgs, + context: Context, + info: GraphQLResolveInfo + ): TResult +} + +export interface MutationToRejectRegistrationArgs { + id: string + details: GQLRejectRegistrationInput +} +export interface MutationToRejectRegistrationResolver< + TParent = any, + TResult = any +> { + ( + parent: TParent, + args: MutationToRejectRegistrationArgs, + context: Context, + info: GraphQLResolveInfo + ): TResult +} + export interface MutationToCreateOrUpdateUserArgs { user: GQLUserInput } @@ -3358,19 +3428,6 @@ export interface MutationToRemoveBookmarkedAdvancedSearchResolver< ): TResult } -export interface GQLDummyTypeResolver { - dummy?: DummyToDummyResolver -} - -export interface DummyToDummyResolver { - ( - parent: TParent, - args: {}, - context: Context, - info: GraphQLResolveInfo - ): TResult -} - export interface GQLNotificationResultTypeResolver { success?: NotificationResultToSuccessResolver } diff --git a/packages/gateway/src/graphql/schema.graphql b/packages/gateway/src/graphql/schema.graphql index e753e1e4f6d..bdccb8753a9 100644 --- a/packages/gateway/src/graphql/schema.graphql +++ b/packages/gateway/src/graphql/schema.graphql @@ -223,6 +223,8 @@ type Mutation { comment: String duplicateTrackingId: String ): ID! + confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID! + rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID! createOrUpdateUser(user: UserInput!): User! activateUser( userId: String! @@ -271,10 +273,6 @@ type Mutation { ): BookMarkedSearches } -type Dummy { - dummy: String! -} - type NotificationResult { success: Boolean! } @@ -728,6 +726,17 @@ type Reinstated { registrationStatus: RegStatus } +input ConfirmRegistrationInput { + childIdentifiers: [IdentifierInput!] + registrationNumber: String + trackingId: String +} + +input RejectRegistrationInput { + reason: RejectionReason! + comment: String! +} + input UserInput { id: ID name: [HumanNameInput!]! @@ -1312,6 +1321,15 @@ enum RegStatus { ISSUED } +input IdentifierInput { + type: SupportedPatientIdentifierCode! + value: String! +} + +enum RejectionReason { + OTHER +} + input HumanNameInput { use: String firstNames: String @@ -1729,6 +1747,25 @@ input DeceasedInput { deathDate: PlainDate } +enum SupportedPatientIdentifierCode { + PASSPORT + NATIONAL_ID + MOSIP_PSUT_TOKEN_ID + DECEASED_PATIENT_ENTRY + BIRTH_PATIENT_ENTRY + DRIVING_LICENSE + REFUGEE_NUMBER + ALIEN_NUMBER + OTHER + SOCIAL_SECURITY_NO + BIRTH_REGISTRATION_NUMBER + DEATH_REGISTRATION_NUMBER + MARRIAGE_REGISTRATION_NUMBER + BIRTH_CONFIGURABLE_IDENTIFIER_1 + BIRTH_CONFIGURABLE_IDENTIFIER_2 + BIRTH_CONFIGURABLE_IDENTIFIER_3 +} + input LabelInput { lang: String! label: String! diff --git a/packages/gateway/src/workflow/index.ts b/packages/gateway/src/workflow/index.ts index ba67e39d166..8b0baf386d9 100644 --- a/packages/gateway/src/workflow/index.ts +++ b/packages/gateway/src/workflow/index.ts @@ -8,7 +8,7 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import { REDIS_HOST, WORKFLOW_URL } from '@gateway/constants' +import { REDIS_HOST, REDIS_PORT, WORKFLOW_URL } from '@gateway/constants' import fetch from '@gateway/fetch' import { GQLBirthRegistrationInput, @@ -37,6 +37,7 @@ import { RejectedRecord, Resource, SavedBundle, + SupportedPatientIdentifierCode, ValidRecord } from '@opencrvs/commons/types' @@ -125,7 +126,10 @@ export function rejectRegistrationCorrection( ) } -const recordQueue = useRecordQueue(REDIS_HOST) +const recordQueue = useRecordQueue({ + host: REDIS_HOST, + port: REDIS_PORT +}) export async function createRegistration( record: @@ -393,3 +397,38 @@ export async function createHospitalNotification( bundle ) } + +type ConfirmRegistrationDetails = { + childIdentifiers?: { + type: SupportedPatientIdentifierCode + value: string + }[] +} +export async function confirmRegistration( + id: string, + authHeader: IAuthHeader, + details: ConfirmRegistrationDetails +) { + return createRequest( + 'POST', + `/records/${id}/confirm`, + authHeader, + details + ) +} + +type RejectRegistrationDetails = { + error: string +} +export async function rejectRegistration( + id: string, + authHeader: IAuthHeader, + details: RejectRegistrationDetails +) { + return createRequest( + 'POST', + `/records/${id}/reject`, + authHeader, + details + ) +} diff --git a/packages/metrics/src/features/registration/test-data/sent-for-updates-request.json b/packages/metrics/src/features/registration/test-data/sent-for-updates-request.json index bd739e256a4..8d34ec51938 100644 --- a/packages/metrics/src/features/registration/test-data/sent-for-updates-request.json +++ b/packages/metrics/src/features/registration/test-data/sent-for-updates-request.json @@ -75,7 +75,7 @@ "id": "77124886-8e45-4506-b38a-cc21f75b8dde", "note": [ { - "text": "reason=missing_supporting_doc&comment=fdfdfz", + "text": "reason=other&comment=fdfdfz", "time": "Mon, 11 May 2020 12:15:29 GMT", "authorString": "Practitioner/2da11e94-1a0d-4a77-8ab0-19f29f57a41a" } diff --git a/packages/metrics/src/features/registration/test-data/task-history.json b/packages/metrics/src/features/registration/test-data/task-history.json index 34e2ed602f9..b2ab01fe3c4 100644 --- a/packages/metrics/src/features/registration/test-data/task-history.json +++ b/packages/metrics/src/features/registration/test-data/task-history.json @@ -81,7 +81,7 @@ "id": "723621be-3f14-45fd-b045-2cf92a9643b6", "note": [ { - "text": "reason=misspelling&comment=blah", + "text": "reason=other&comment=blah", "time": "Tue, 12 May 2020 07:14:30 GMT", "authorString": "Practitioner/2da11e94-1a0d-4a77-8ab0-19f29f57a41a" } diff --git a/packages/search/src/test/utils.ts b/packages/search/src/test/utils.ts index ebaa9f328a2..7ebd90c71bd 100644 --- a/packages/search/src/test/utils.ts +++ b/packages/search/src/test/utils.ts @@ -3216,7 +3216,7 @@ export const mockDeathRejectionTaskBundle = { id: 'be13e81f-0cd7-4ff3-a2d3-a1bc7a7f543a', note: [ { - text: 'reason=missing_supporting_doc&comment=No documents found!', + text: 'reason=other&comment=No documents found!', time: 'Wed, 27 Mar 2019 11:44:41 GMT', authorString: 'Practitioner/220ad6b8-346f-4a1d-8a5c-086ce38067c9' } @@ -3287,7 +3287,7 @@ export const mockMarriageRejectionTaskBundle = { id: 'be13e81f-0cd7-4ff3-a2d3-a1bc7a7f543a', note: [ { - text: 'reason=missing_supporting_doc&comment=No documents found!', + text: 'reason=other&comment=No documents found!', time: 'Wed, 27 Mar 2019 11:44:41 GMT', authorString: 'Practitioner/220ad6b8-346f-4a1d-8a5c-086ce38067c9' } @@ -3355,7 +3355,7 @@ export const mockDeathRejectionTaskBundleWithoutCompositionReference = { id: 'be13e81f-0cd7-4ff3-a2d3-a1bc7a7f543a', note: [ { - text: 'reason=missing_supporting_doc&comment=No documents found!', + text: 'reason=other&comment=No documents found!', time: 'Wed, 27 Mar 2019 11:44:41 GMT', authorString: 'Practitioner/220ad6b8-346f-4a1d-8a5c-086ce38067c9' } diff --git a/packages/workflow/src/config/routes.ts b/packages/workflow/src/config/routes.ts index 3f2eb8c98a7..cbddd69d99f 100644 --- a/packages/workflow/src/config/routes.ts +++ b/packages/workflow/src/config/routes.ts @@ -8,9 +8,9 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import { markEventAsRegisteredCallbackHandler } from '@workflow/features/registration/handler' import { archiveRoute } from '@workflow/records/handler/archive' import { certifyRoute } from '@workflow/records/handler/certify' +import { confirmRegistrationHandler } from '@workflow/records/handler/confirm' import { approveCorrectionRoute } from '@workflow/records/handler/correction/approve' import { makeCorrectionRoute } from '@workflow/records/handler/correction/make-correction' import { rejectCorrectionRoute } from '@workflow/records/handler/correction/reject' @@ -60,12 +60,11 @@ export const getRoutes = () => { }, { method: 'POST', - path: '/confirm/registration', - handler: markEventAsRegisteredCallbackHandler, + path: '/records/{id}/confirm', + handler: confirmRegistrationHandler, config: { tags: ['api'], - description: - 'Register event based on tracking id and registration number.' + description: 'Confirm registration after external validation' } }, { diff --git a/packages/workflow/src/constants.ts b/packages/workflow/src/constants.ts index e167123cf58..8d2c6e7e46a 100644 --- a/packages/workflow/src/constants.ts +++ b/packages/workflow/src/constants.ts @@ -12,6 +12,7 @@ import { cleanEnv, str, num, url } from 'envalid' const env = cleanEnv(process.env, { REDIS_HOST: str({ devDefault: 'localhost' }), + REDIS_PORT: num({ default: 6379 }), HOST: str({ default: '0.0.0.0' }), PORT: num({ default: 5050 }), FHIR_URL: url({ devDefault: 'http://localhost:3447/fhir' }), @@ -32,6 +33,7 @@ const env = cleanEnv(process.env, { export const { REDIS_HOST, + REDIS_PORT, HOST, PORT, FHIR_URL, diff --git a/packages/workflow/src/features/registration/handler.ts b/packages/workflow/src/features/registration/handler.ts index df3bedefa5d..4a45c2c6a39 100644 --- a/packages/workflow/src/features/registration/handler.ts +++ b/packages/workflow/src/features/registration/handler.ts @@ -8,17 +8,9 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import * as Hapi from '@hapi/hapi' import { toTokenWithBearer } from '@opencrvs/commons' -import { - RecordValidatedPayload, - useExternalValidationQueue -} from '@opencrvs/commons/message-queue' -import { - isWaitingExternalValidation, - SupportedPatientIdentifierCode -} from '@opencrvs/commons/types' -import { REDIS_HOST } from '@workflow/constants' +import { RecordValidatedPayload } from '@opencrvs/commons/message-queue' +import { isWaitingExternalValidation } from '@opencrvs/commons/types' import { writeMetricsEvent } from '@workflow/records/audit' import { getRecordById } from '@workflow/records/index' import { @@ -28,7 +20,6 @@ import { import { indexBundleWithTransaction } from '@workflow/records/search' import { toRegistered } from '@workflow/records/state-transitions' import { invokeWebhooks } from '@workflow/records/webhooks' -import { getToken } from '@workflow/utils/auth-utils' import { getEventType } from './utils' export async function markEventAsRegistered({ @@ -73,31 +64,3 @@ export async function markEventAsRegistered({ return } - -const { recordValidated } = useExternalValidationQueue(REDIS_HOST) - -type RecordValidatedHTTPPayload = { - registrationNumber: string - childIdentifiers: { type: SupportedPatientIdentifierCode; value: string }[] - compositionId: string - trackingId: string -} - -export async function markEventAsRegisteredCallbackHandler( - request: Hapi.Request, - h: Hapi.ResponseToolkit -) { - const token = getToken(request) - const { registrationNumber, childIdentifiers, compositionId, trackingId } = - request.payload as RecordValidatedHTTPPayload - - await recordValidated({ - recordId: compositionId, - identifiers: childIdentifiers || [], - registrationNumber, - trackingId, - token - }) - - return h.response().code(200) -} diff --git a/packages/workflow/src/records/handler/confirm.ts b/packages/workflow/src/records/handler/confirm.ts new file mode 100644 index 00000000000..dc86dbea0a6 --- /dev/null +++ b/packages/workflow/src/records/handler/confirm.ts @@ -0,0 +1,56 @@ +/* + * 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 * as z from 'zod' +import { getToken } from '@workflow/utils/auth-utils' +import { validateRequest } from '@workflow/utils/index' +import { useExternalValidationQueue } from '@opencrvs/commons/message-queue' +import { REDIS_HOST, REDIS_PORT } from '@workflow/constants' +import { SUPPORTED_PATIENT_IDENTIFIER_CODES } from '@opencrvs/commons/types' +import * as Hapi from '@hapi/hapi' + +const requestSchema = z.object({ + registrationNumber: z.string(), + childIdentifiers: z + .array( + z.object({ + type: z.enum(SUPPORTED_PATIENT_IDENTIFIER_CODES), + value: z.string() + }) + ) + .optional(), + trackingId: z.string() +}) + +const { recordValidated } = useExternalValidationQueue({ + host: REDIS_HOST, + port: REDIS_PORT +}) + +export async function confirmRegistrationHandler( + request: Hapi.Request, + h: Hapi.ResponseToolkit +) { + const token = getToken(request) + const payload = validateRequest(requestSchema, request.payload) + const recordId = request.params.id + + const { registrationNumber, childIdentifiers, trackingId } = payload + + await recordValidated({ + recordId, + identifiers: childIdentifiers || [], + registrationNumber, + trackingId, + token + }) + + return h.response().code(202) +} diff --git a/packages/workflow/src/records/handler/create.ts b/packages/workflow/src/records/handler/create.ts index 9d6954bcf4c..f9953d87942 100644 --- a/packages/workflow/src/records/handler/create.ts +++ b/packages/workflow/src/records/handler/create.ts @@ -62,7 +62,7 @@ import { } from '@workflow/records/fhir' import { useExternalValidationQueue } from '@opencrvs/commons/message-queue' -import { REDIS_HOST } from '@workflow/constants' +import { REDIS_HOST, REDIS_PORT } from '@workflow/constants' import { getRecordById } from '@workflow/records' import { isNotificationEnabled, @@ -88,7 +88,10 @@ const requestSchema = z.object({ >() }) -const { sendForExternalValidation } = useExternalValidationQueue(REDIS_HOST) +const { sendForExternalValidation } = useExternalValidationQueue({ + host: REDIS_HOST, + port: REDIS_PORT +}) function findTask(bundle: Bundle) { const task = bundle.entry.map((e) => e.resource).find(isTask) diff --git a/packages/workflow/src/records/handler/register.ts b/packages/workflow/src/records/handler/register.ts index 33c9b077c4b..214e48e69ce 100644 --- a/packages/workflow/src/records/handler/register.ts +++ b/packages/workflow/src/records/handler/register.ts @@ -10,7 +10,7 @@ */ import { useExternalValidationQueue } from '@opencrvs/commons/message-queue' import { getComposition } from '@opencrvs/commons/types' -import { REDIS_HOST } from '@workflow/constants' +import { REDIS_HOST, REDIS_PORT } from '@workflow/constants' import { writeMetricsEvent } from '@workflow/records/audit' import { indexBundle } from '@workflow/records/search' import { toWaitingForExternalValidationState } from '@workflow/records/state-transitions' @@ -19,7 +19,10 @@ import { getToken } from '@workflow/utils/auth-utils' import { validateRequest } from '@workflow/utils/index' import * as z from 'zod' -const { sendForExternalValidation } = useExternalValidationQueue(REDIS_HOST) +const { sendForExternalValidation } = useExternalValidationQueue({ + host: REDIS_HOST, + port: REDIS_PORT +}) export const registerRoute = createRoute({ method: 'POST', diff --git a/packages/workflow/src/server.ts b/packages/workflow/src/server.ts index 8581c9671e2..d24a0e2b691 100644 --- a/packages/workflow/src/server.ts +++ b/packages/workflow/src/server.ts @@ -14,7 +14,9 @@ import { HOST, PORT, CERT_PUBLIC_KEY_PATH, - DEFAULT_TIMEOUT + DEFAULT_TIMEOUT, + REDIS_PORT, + REDIS_HOST } from '@workflow/constants' import getPlugins from '@workflow/config/plugins' import { getRoutes } from '@workflow/config/routes' @@ -62,7 +64,7 @@ export async function createServer() { async function start() { await server.start() - await register() + await register({ host: REDIS_HOST, port: REDIS_PORT }) server.log('info', `Workflow server started on ${HOST}:${PORT}`) } diff --git a/packages/workflow/src/workers.ts b/packages/workflow/src/workers.ts index e95b7bff4ed..193c70c9078 100644 --- a/packages/workflow/src/workers.ts +++ b/packages/workflow/src/workers.ts @@ -13,7 +13,6 @@ import { registerExternalValidationsWorker, registerRecordWorker } from '@opencrvs/commons/message-queue' -import { REDIS_HOST } from './constants' import { invokeRegistrationValidation } from './features/registration/fhir/fhir-bundle-modifier' import { markEventAsRegistered } from './features/registration/handler' import { @@ -22,8 +21,8 @@ import { validateRecordHandler } from './records/handler/create' -export async function register() { - await registerExternalValidationsWorker(REDIS_HOST, async (job) => { +export async function register(connection: { host: string; port: number }) { + await registerExternalValidationsWorker(connection, async (job) => { if (job.name === 'send-to-external-validation') { const { token, record } = job.data return invokeRegistrationValidation( @@ -36,7 +35,7 @@ export async function register() { return markEventAsRegistered(job.data) } }) - await registerRecordWorker(REDIS_HOST, async (job) => { + await registerRecordWorker(connection, async (job) => { if (job.name === 'create-registration') { return registerRecordHandler( job.data.payload, diff --git a/yarn.lock b/yarn.lock index d0a39eb7c35..7d31a63314f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8656,7 +8656,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@18.3.1", "@types/react@>=16": +"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@18.3.1", "@types/react@>=16", "@types/react@^16": version "18.3.1" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.1.tgz#fed43985caa834a2084d002e4771e15dfcbdbe8e" integrity sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw== @@ -8664,15 +8664,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^16": - version "16.14.61" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.61.tgz#ce498029a046d17908001e6e563f3febbaf41e7f" - integrity sha512-CK3zd17pDWAEMnN5TdzwQJQlto2dK/lb0WZsI74owWgQ8PR60WRk0sCeBxLWuSuuqqDZKqeUcxod/8yECqrP/g== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "^0.16" - csstype "^3.0.2" - "@types/readdir-glob@*": version "1.1.3" resolved "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.3.tgz" @@ -8741,11 +8732,6 @@ dependencies: htmlparser2 "^8.0.0" -"@types/scheduler@^0.16": - version "0.16.8" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - "@types/semver@^7.3.4": version "7.5.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" @@ -13969,7 +13955,7 @@ exponential-backoff@^3.1.1: resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -express@^4.17.1, express@^4.17.3: +express@^4.17.3: version "4.21.0" resolved "https://registry.yarnpkg.com/express/-/express-4.21.0.tgz#d57cb706d49623d4ac27833f1cbc466b668eb915" integrity sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng== @@ -14159,14 +14145,7 @@ fast-url-parser@1.1.3, fast-url-parser@^1.1.3: dependencies: punycode "^1.3.2" -fast-xml-parser@4.2.5: - version "4.2.5" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f" - integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== - dependencies: - strnum "^1.0.5" - -fast-xml-parser@^4.1.3, fast-xml-parser@^4.2.2: +fast-xml-parser@4.2.5, fast-xml-parser@4.4.1, fast-xml-parser@^4.1.3, fast-xml-parser@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==