diff --git a/infrastructure/postgres/setup-analytics.sh b/infrastructure/postgres/setup-analytics.sh index 0d09144c3d..97bcb0f484 100644 --- a/infrastructure/postgres/setup-analytics.sh +++ b/infrastructure/postgres/setup-analytics.sh @@ -85,6 +85,8 @@ CREATE TABLE IF NOT EXISTS analytics.event_actions ( UNIQUE (id, event_id) ); +ALTER TABLE analytics.event_actions ADD COLUMN IF NOT EXISTS custom_action_type TEXT; + CREATE TABLE IF NOT EXISTS analytics.location_levels ( id text PRIMARY KEY, level int NOT NULL, diff --git a/package.json b/package.json index 22450a22fc..5649c6b75c 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@hapi/boom": "^9.1.1", "@hapi/hapi": "^20.0.1", "@hapi/inert": "^6.0.3", - "@opencrvs/toolkit": "1.9.0-rc.d3d43c3", + "@opencrvs/toolkit": "1.9.0-rc.d748ac1", "@types/chalk": "^2.2.0", "@types/csv2json": "^1.4.0", "@types/fhir": "^0.0.30", diff --git a/src/api/custom-event/handler.ts b/src/api/custom-event/handler.ts index e575d75d1e..df488f2118 100644 --- a/src/api/custom-event/handler.ts +++ b/src/api/custom-event/handler.ts @@ -24,6 +24,13 @@ export function getCustomEventsHandler( .code(200) } +export async function onCustomActionHandler( + request: ActionConfirmationRequest, + h: Hapi.ResponseToolkit +) { + return h.response().code(200) +} + export async function onAnyActionHandler( request: ActionConfirmationRequest, h: Hapi.ResponseToolkit @@ -33,6 +40,7 @@ export async function onAnyActionHandler( const token = request.auth.artifacts.token as string const event = request.payload + await sendInformantNotification({ event, token }) return h.response().code(200) diff --git a/src/api/notification/informantNotification.ts b/src/api/notification/informantNotification.ts index 3cbe401def..e08c51df1f 100644 --- a/src/api/notification/informantNotification.ts +++ b/src/api/notification/informantNotification.ts @@ -26,8 +26,6 @@ import { InformantType as BirthInformantType } from '@countryconfig/form/v2/birt import { InformantTemplateType } from './sms-service' import { generateFailureLog, NotificationParams, notify } from './handler' import { InformantType as DeathInformantType } from '@countryconfig/form/v2/death/forms/pages/informant' -import { birthEvent } from '@countryconfig/form/v2/birth' -import { deathEvent } from '@countryconfig/form/v2/death' const resolveName = (name: FieldUpdateValue) => { const nameObj = { @@ -77,18 +75,6 @@ function getInformant(eventType: string, declaration: Record) { throw new Error('Invalid event type') } -function getEventConfig(eventType: string) { - if (eventType === Event.Birth) { - return birthEvent - } - - if (eventType === Event.Death) { - return deathEvent - } - - throw new Error('Invalid event type') -} - async function getNotificationParams( event: EventDocument, token: string, diff --git a/src/data-seeding/roles/roles.ts b/src/data-seeding/roles/roles.ts index 68e0189520..ec9ae77975 100644 --- a/src/data-seeding/roles/roles.ts +++ b/src/data-seeding/roles/roles.ts @@ -221,7 +221,8 @@ export const roles: Role[] = [ 'record.register[event=birth|death|tennis-club-membership]', 'record.registered.print-certified-copies[event=birth|death|tennis-club-membership]', 'record.registered.correct[event=birth|death|tennis-club-membership]', - 'record.unassign-others[event=birth|death|tennis-club-membership]' + 'record.unassign-others[event=birth|death|tennis-club-membership]', + 'record.custom-action[event=birth,customActionType=Approve]' ] }, { diff --git a/src/form/v2/birth/index.ts b/src/form/v2/birth/index.ts index 3897d74db6..8c04d1339b 100644 --- a/src/form/v2/birth/index.ts +++ b/src/form/v2/birth/index.ts @@ -219,6 +219,32 @@ export const birthEvent = defineConfig({ } ] }, + { + type: ActionType.CUSTOM, + customActionType: 'Approve', + label: { + defaultMessage: 'Approve', + description: + 'This is shown as the action name anywhere the user can trigger the action from', + id: 'event.birth.action.approve.label' + }, + supportingCopy: { + defaultMessage: + 'This birth has been registered late. You are now approving it for further validation and registration.', + description: 'This is the confirmation text for the approve action', + id: 'event.birth.action.approve.confirmationText' + }, + form: [], + flags: [ + { id: 'approval-required-for-late-registration', operation: 'remove' } + ], + auditHistoryLabel: { + defaultMessage: 'Approved', + description: + 'The label to show in audit history for the approve action', + id: 'event.birth.action.approve.audit-history-label' + } + }, { type: ActionType.VALIDATE, label: { diff --git a/src/index.ts b/src/index.ts index 3a128999f7..acc411894e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -58,9 +58,9 @@ import { fontsHandler } from './api/fonts/handler' import { recordNotificationHandler } from './api/record-notification/handler' import { getCustomEventsHandler, - onAnyActionHandler + onAnyActionHandler, + onCustomActionHandler } from '@countryconfig/api/custom-event/handler' -import { readFileSync } from 'fs' import { ActionDocument, ActionStatus, @@ -92,7 +92,7 @@ export interface ITokenPayload { export default function getPlugins() { const plugins: any[] = [inert, JWT] - if (process.env.NODE_ENV === 'production') { + if (process.env.NODE_ENV !== 'test') { plugins.push({ plugin: Pino, options: { @@ -641,6 +641,16 @@ export async function createServer() { } }) + server.route({ + method: 'POST', + path: `/trigger/events/{event}/actions/${ActionType.CUSTOM}`, + handler: onCustomActionHandler, + options: { + tags: ['api', 'events'], + description: 'Receives notifications on event custom action' + } + }) + server.route({ method: 'POST', path: '/trigger/events/{event}/actions/{action}', @@ -748,9 +758,9 @@ export async function createServer() { await syncLocationLevels() await syncLocationStatistics() - const logMsg = `Server successfully started on ${COUNTRY_CONFIG_HOST}:${COUNTRY_CONFIG_PORT}` - logger.info(logMsg) - server.log('info', logMsg) + logger.info( + `Server successfully started on ${COUNTRY_CONFIG_HOST}:${COUNTRY_CONFIG_PORT}` + ) } return { server, start, stop } diff --git a/src/logger.ts b/src/logger.ts index 55a694bce0..5526689551 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -19,6 +19,11 @@ export const logger = ] }) : pino({ + serializers: { + req: () => undefined, + res: () => undefined, + responseTime: () => undefined + }, transport: { target: 'pino-pretty', options: { diff --git a/src/translations/client.csv b/src/translations/client.csv index 4cba2527a3..cd0699f6e6 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -862,6 +862,7 @@ event.birth.action.mark-as-duplicate.label,Label for review potential duplicate event.birth.action.register.label,Label for review record button in dropdown menu,Register,Enregistrer event.birth.action.request-correction.label,This is shown as the action name anywhere the user can trigger the action from,Correct record,Enregistrement correct event.birth.action.validate.label,This is shown as the action name anywhere the user can trigger the action from,Validate,Valider +event.birth.action.approve.confirmationText,This is the confirmation text for the approve action,This birth has been registered late. You are now approving it for further validation and registration.,Cette naissance a été enregistrée en retard. Vous l'approuvez maintenant pour une validation et un enregistrement supplémentaires. event.birth.label,This is what this event is referred as in the system,Birth,Naissance event.birth.summary.child.dob.empty,Label for date of birth not available,No date of birth,Pas de date de naissance event.birth.summary.child.placeOfBirth.empty,Label for place of birth not available,No place of birth,Pas de lieu de naissance @@ -1113,7 +1114,7 @@ event.tennis-club-membership.summary.field.recommender.id.empty,Shown when the r event.tennis-club-membership.summary.field.recommender.id.label,Label for the recommender's ID field,Recommender's ID,Recommender's ID events.overview.tabs.record,Title of the Record tab,Record,Enregistrement events.overview.tabs.audit,Title of the Audit tab,Audit,Audit -events.history.status,Events status history,"{status, select, Requested {Waiting for external validation} other {{action, select, CREATE {Declaration started} NOTIFY {Sent incomplete} VALIDATE {Sent for approval} DRAFT {Draft} DECLARE {Sent for review} REGISTER {Registered} PRINT_CERTIFICATE {Certified} REJECT {Rejected} ARCHIVE {Archived} DUPLICATE_DETECTED {Flagged as potential duplicate} MARKED_AS_DUPLICATE {Marked as a duplicate} MARK_AS_NOT_DUPLICATE {Marked not a duplicate} CORRECTED {Record corrected} REQUEST_CORRECTION {Correction requested} APPROVE_CORRECTION {Correction approved} REJECT_CORRECTION {Correction rejected} READ {Viewed} ASSIGN {Assigned} UNASSIGN {Unassigned} UPDATE {Updated} other {Unknown}}}}","{status, select, Requested {En attente de validation externe} other {{action, select, CREATE {Déclaration commencée} NOTIFY {Envoyé incomplet} VALIDATE {Envoyé pour approbation} DRAFT {Brouillon} DECLARE {Envoyé pour révision} REGISTER {Enregistré} PRINT_CERTIFICATE {Certifié} REJECT {Rejeté} ARCHIVE {Archivé} DUPLICATE_DETECTED {Marqué comme doublon} MARKED_AS_DUPLICATE {Marqué comme doublon} MARK_AS_NOT_DUPLICATE {Marqué comme non en doublon} CORRECTED {Enregistrement corrigé} REQUEST_CORRECTION {Correction demandée} APPROVE_CORRECTION {Correction approuvée} REJECT_CORRECTION {Correction refusée} READ {Consulté} ASSIGN {Attribué} UNASSIGN {Désattribué} UPDATE {Mis à jour} other {Inconnu}}}}" +events.history.status,Events status history,"{status, select, Requested {Waiting for external validation} other {{action, select, CREATE {Declaration started} NOTIFY {Notified} VALIDATE {Sent for approval} DRAFT {Draft} DECLARE {Sent for review} REGISTER {Registered} PRINT_CERTIFICATE {Certified} REJECT {Rejected} ARCHIVE {Archived} DUPLICATE_DETECTED {Flagged as potential duplicate} MARKED_AS_DUPLICATE {Marked as a duplicate} MARK_AS_NOT_DUPLICATE {Marked not a duplicate} CORRECTED {Record corrected} REQUEST_CORRECTION {Correction requested} APPROVE_CORRECTION {Correction approved} REJECT_CORRECTION {Correction rejected} READ {Viewed} ASSIGN {Assigned} UNASSIGN {Unassigned} UPDATE {Updated} other {Unknown}}}}","{status, select, Requested {En attente de validation externe} other {{action, select, CREATE {Déclaration commencée} NOTIFY {Envoyé incomplet} VALIDATE {Envoyé pour approbation} DRAFT {Brouillon} DECLARE {Envoyé pour révision} REGISTER {Enregistré} PRINT_CERTIFICATE {Certifié} REJECT {Rejeté} ARCHIVE {Archivé} DUPLICATE_DETECTED {Marqué comme doublon} MARKED_AS_DUPLICATE {Marqué comme doublon} MARK_AS_NOT_DUPLICATE {Marqué comme non en doublon} CORRECTED {Enregistrement corrigé} REQUEST_CORRECTION {Correction demandée} APPROVE_CORRECTION {Correction approuvée} REJECT_CORRECTION {Correction refusée} READ {Consulté} ASSIGN {Attribué} UNASSIGN {Désattribué} UPDATE {Mis à jour} other {Inconnu}}}}" events.outbox.processingAction,Message in outbox when processing action,"{action, select, DECLARE {Sending} REGISTER {Registering} VALIDATE {Sending for approval} NOTIFY {Sending} REJECT {Sending for updates} ARCHIVE {Archiving} PRINT_CERTIFICATE {Certifying} REQUEST_CORRECTION {Requesting correction} APPROVE_CORRECTION {Approving correction} REJECT_CORRECTION {Rejecting correction} ASSIGN {Assigning} UNASSIGN {Unassigning} other {processing action}}","{action, select, DECLARE {Envoi} REGISTER {Enregistrement} VALIDATE {Envoi pour approbation} NOTIFY {Envoi} REJECT {Envoi pour mise à jour} ARCHIVE {Archivage} PRINT_CERTIFICATE {Certification} REQUEST_CORRECTION {Demande de correction} APPROVE_CORRECTION {Approbation de la correction} REJECT_CORRECTION {Rejet de la correction} ASSIGN {Attribution} UNASSIGN {Désattribution} other {action en cours}}" events.outbox.waitingForAction,Message in outbox when waiting for action,"Waiting to {action, select, DECLARE {send} CREATE {send} REGISTER {register} VALIDATE {send for approval} NOTIFY {send} REJECT {send for updates} ARCHIVE {archive} PRINT_CERTIFICATE {certify} REQUEST_CORRECTION {request correction} APPROVE_CORRECTION {approve correction} REJECT_CORRECTION {reject correction} ASSIGN {assign} UNASSIGN {unassign} other {action}}","En attente de {action, select, DECLARE {l'envoi} CREATE {l'envoi} REGISTER {l'enregistrement} VALIDATE {l'envoi pour approbation} NOTIFY {l'envoi} REJECT {l'envoi pour mise à jour} ARCHIVE {l'archivage} PRINT_CERTIFICATE {la certification} REQUEST_CORRECTION {la demande de correction} APPROVE_CORRECTION {l'approbation de la correction} REJECT_CORRECTION {le rejet de la correction} ASSIGN {l'attribution} UNASSIGN {la désattribution} other {l'action}}" events.status,Dynamic event status,"{status, select, OUTBOX {Syncing..} CREATED {Draft} VALIDATED {Validated} DRAFT {Draft} DECLARED {Declared} REGISTERED {Registered} CERTIFIED {Certified} REJECTED {Rejected} ARCHIVED {Archived} MARKED_AS_DUPLICATE {Marked as a duplicate} MARK_AS_NOT_DUPLICATE {Marked not a duplicate} NOTIFIED {In progress} other {Unknown}}","{status, select, OUTBOX {Synchronisation..} CREATED {Brouillon} VALIDATED {Validé} DRAFT {Brouillon} DECLARED {Déclaré} REGISTERED {Enregistré} CERTIFIED {Certifié} REJECTED {Nécessite une mise à jour} ARCHIVED {Archivé} MARKED_AS_DUPLICATE {Marqué comme doublon} MARK_AS_NOT_DUPLICATE {Marqué comme non en doublon} NOTIFIED {Notifié} other {Inconnu}}" diff --git a/yarn.lock b/yarn.lock index 4007936275..d9594638ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -834,10 +834,10 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@opencrvs/toolkit@1.9.0-rc.d3d43c3": - version "1.9.0-rc.d3d43c3" - resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-1.9.0-rc.d3d43c3.tgz#f78a25338ec6305216009472b2ac06f150717a0b" - integrity sha512-wqVrQLevWZ7RzrZKtZHFAeWZQDszV5cPH3AIMs8V2Yx6kdFS/xTXzwUVbHZWvi+hX/8uw4uwHPadgqT0nsqHyA== +"@opencrvs/toolkit@1.9.0-rc.d748ac1": + version "1.9.0-rc.d748ac1" + resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-1.9.0-rc.d748ac1.tgz#7f082546ebcf136aff204e50f2fcfbc86087c1c8" + integrity sha512-jrOHw/G3Ikm9SckvgC4J0bceoyxiIVflzeyiX8BJEqrj+rzzzMx+UojpAzr5uJglbxXTVOkZ4UFvLf63MovEGw== dependencies: "@trpc/client" "11.4.3" "@trpc/server" "11.4.3"