Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(api): Self Service Portal APIs #8286

Open
wants to merge 18 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
71a3725
Iterated the Record Search API to return all statuses to system clients.
Hyper3x Dec 31, 2024
b1b5dff
Merge branch 'ocrvs-2268' of https://github.com/opencrvs/opencrvs-cor…
tharakadadigama20 Jan 2, 2025
af8b82c
Issue 2268 webhook changes
tharakadadigama20 Jan 3, 2025
a91e3ff
code changes done according to comments
tharakadadigama20 Jan 6, 2025
75732ea
Webhook added to other statuses
tharakadadigama20 Jan 7, 2025
6d5b3c5
Merge branch 'develop' of https://github.com/opencrvs/opencrvs-core i…
tharakadadigama20 Jan 7, 2025
c35e683
approved webhook changed to validated
tharakadadigama20 Jan 8, 2025
f8bc249
Webhook changed to support event V2
tharakadadigama20 Jan 8, 2025
b60eebc
Merge branch 'develop' of https://github.com/opencrvs/opencrvs-core i…
tharakadadigama20 Jan 8, 2025
ce31f14
Additional triggers removed.
tharakadadigama20 Jan 8, 2025
fd0632a
Webhook removed inside corrections
tharakadadigama20 Jan 8, 2025
d57d8c1
Merge branch 'develop' into ocrvs-2268-webhook
euanmillar Jan 14, 2025
c473c3a
case insensitivity added to webhook condition
tharakadadigama20 Jan 16, 2025
397a6c5
Merge branch 'develop' of https://github.com/opencrvs/opencrvs-core i…
tharakadadigama20 Jan 21, 2025
bd0c563
Fix conflicts
euanmillar Jan 28, 2025
b99588a
Merge branch 'ocrvs-2268-webhook' of https://github.com/opencrvs/open…
euanmillar Jan 28, 2025
26e68ee
Merge branch 'develop' into ocrvs-2268-webhook
euanmillar Jan 28, 2025
0e8b6da
Merge branch 'develop' of https://github.com/opencrvs/opencrvs-core i…
tharakadadigama20 Jan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added knip_report.md
Empty file.
36 changes: 36 additions & 0 deletions packages/components/src/IDReader/IDReader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 React from 'react'

import { Divider } from '../Divider'
import { Text } from '../Text'
import { IDReaderProps } from './types'
import { Stack } from '../Stack'
import { MainContainer, ReadersContainer } from './components'

export const IDReader = (props: IDReaderProps) => {
const { dividerLabel, manualInputInstructionLabel, children } = props
return (
<MainContainer>
<Stack direction="column" alignItems="center" gap={0}>
<ReadersContainer>{children}</ReadersContainer>
<Divider>
<Text variant="reg18" element="p" align="center" color="grey500">
{dividerLabel}
</Text>
</Divider>
<Text variant="reg16" element="span" align="center">
{manualInputInstructionLabel}
</Text>
</Stack>
</MainContainer>
)
}
11 changes: 11 additions & 0 deletions packages/components/src/IDReader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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.
*/
export * from './IDReader'
7 changes: 5 additions & 2 deletions packages/search/src/features/search/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
SUPPORTED_PATIENT_IDENTIFIER_CODES,
CERTIFIED_STATUS,
REGISTERED_STATUS,
ISSUED_STATUS
ISSUED_STATUS,
DECLARED_STATUS,
REJECTED_STATUS,
VALIDATED_STATUS
} from '@opencrvs/commons/types'
import { IAdvancedSearchParam } from '@search/features/search/types'
import { transformDeprecatedParamsToSupported } from './deprecation-support'
Expand Down Expand Up @@ -114,7 +117,7 @@ export async function advancedQueryBuilder(
query_string: {
default_field: 'type',
query: isExternalSearch
? `(${REGISTERED_STATUS}) OR (${CERTIFIED_STATUS}) OR (${ISSUED_STATUS})`
? `(${REGISTERED_STATUS}) OR (${CERTIFIED_STATUS}) OR (${ISSUED_STATUS}) OR (${DECLARED_STATUS}) OR (${REJECTED_STATUS}) OR (${VALIDATED_STATUS})`
: `(${params.registrationStatuses!.join(') OR (')})`
}
})
Expand Down
10 changes: 10 additions & 0 deletions packages/webhooks/src/config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
deleteWebhookByClientIdHandler
} from '@webhooks/features/manage/handler'
import {
approveRejectHandler,
birthRegisteredHandler,
deathRegisteredHandler,
marriageRegisteredHandler
Expand Down Expand Up @@ -105,6 +106,15 @@ export const getRoutes = () => {
tags: ['api'],
description: 'Dispatches a webhook for the marriage registration event'
}
},
{
method: 'POST',
path: '/events/{eventType}/status/{statusType}',
handler: approveRejectHandler,
config: {
tags: ['api'],
description: 'Dispatches a webhook for the event'
}
}
]
return routes
Expand Down
67 changes: 66 additions & 1 deletion packages/webhooks/src/features/event/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Webhook, { IWebhookModel, TRIGGERS } from '@webhooks/model/webhook'
import { getQueue } from '@webhooks/queue'
import { Queue } from 'bullmq'
import fetch from 'node-fetch'
import * as ShortUIDGen from 'short-uid'
import ShortUIDGen from 'short-uid'
import { RegisteredRecord } from '@opencrvs/commons/types'

export interface IAuthHeader {
Expand Down Expand Up @@ -230,6 +230,71 @@ export async function marriageRegisteredHandler(
return h.response().code(200)
}

export async function approveRejectHandler(
request: Hapi.Request,
h: Hapi.ResponseToolkit
) {
const { eventType, statusType } = request.params
const bundle = request.payload as { trackingId: string }

const currentTrigger = `${eventType}/${statusType}`.toLowerCase()

const webhookQueue = getQueue()

const webhooks: IWebhookModel[] | null = await Webhook.find()
if (!webhooks) {
throw internal('Failed to find webhooks')
}
logger.info(`Subscribed webhooks: ${JSON.stringify(webhooks)}`)
for (const webhookToNotify of webhooks) {
logger.info(`Queueing webhook ${webhookToNotify.trigger} ${currentTrigger}`)
if (webhookToNotify.trigger.toLowerCase() === currentTrigger) {
const payload = {
timestamp: new Date().toISOString(),
id: webhookToNotify.webhookId,
event: {
hub: {
topic: currentTrigger
},
context: {
trackingId: bundle?.trackingId,
status: statusType
}
}
}
logger.info(
`Dispatching webhook: ${JSON.stringify({
timestamp: payload.timestamp,
id: payload.id,
event: { hub: { topic: payload.event.hub.topic } },
context: ['<<redacted>>']
})}`
)
const hmac = createRequestSignature(
'sha256',
webhookToNotify.sha_secret,
JSON.stringify(payload)
)
webhookQueue.add(
`${webhookToNotify.webhookId}_${currentTrigger}`,
{
payload,
url: webhookToNotify.address,
hmac
},
{
jobId: `WEBHOOK_${new ShortUIDGen().randomUUID().toUpperCase()}_${
webhookToNotify.webhookId
}`,
attempts: 3
}
)
}
}

return h.response().code(200)
}

const fetchSystemPermissions = async (
{ createdBy: { client_id, type } }: IWebhookModel,
authHeader: IAuthHeader,
Expand Down
26 changes: 13 additions & 13 deletions packages/webhooks/src/features/manage/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
generateChallenge
} from '@webhooks/features/manage/service'
import { internal } from '@hapi/boom'
import Webhook, { TRIGGERS } from '@webhooks/model/webhook'
import Webhook from '@webhooks/model/webhook'
import { logger } from '@opencrvs/commons'
import { v4 as uuid } from 'uuid'
import fetch from 'node-fetch'
Expand All @@ -40,17 +40,17 @@ export async function subscribeWebhooksHandler(
h: Hapi.ResponseToolkit
) {
const { hub } = request.payload as ISubscribePayload
if (!(hub.topic in TRIGGERS)) {
return h
.response({
hub: {
mode: 'denied',
topic: hub.topic,
reason: `Unsupported topic: ${hub.topic}`
}
})
.code(400)
}
// if (!(hub.topic in TRIGGERS)) {
// return h
// .response({
// hub: {
// mode: 'denied',
// topic: hub.topic,
// reason: `Unsupported topic: ${hub.topic}`
// }
// })
// .code(400)
// }
const token: ITokenPayload = getTokenPayload(
request.headers.authorization.split(' ')[1]
)
Expand Down Expand Up @@ -109,7 +109,7 @@ export async function subscribeWebhooksHandler(
createdBy,
address: hub.callback,
sha_secret: hub.secret,
trigger: hub.topic in TRIGGERS ? hub.topic : undefined
trigger: hub.topic
}
const challenge = generateChallenge()

Expand Down
11 changes: 11 additions & 0 deletions packages/workflow/src/records/handler/archive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { toArchived } from '@workflow/records/state-transitions'
import { indexBundle } from '@workflow/records/search'
import { createRoute } from '@workflow/states'
import { auditEvent } from '@workflow/records/audit'
import { invokeWebhooks } from '@workflow/records/webhooks'
import { getEventType } from '@workflow/features/registration/utils'
import { SCOPES } from '@opencrvs/commons/authentication'

const requestSchema = z.object({
Expand Down Expand Up @@ -46,6 +48,15 @@ export const archiveRoute = createRoute({

await indexBundle(archivedRecord, token)
await auditEvent('archived', archivedRecord, token)

await invokeWebhooks({
bundle: record,
token,
event: getEventType(record),
isNotRegistered: true,
statusType: 'archived'
})

return archivedRecord
}
})
10 changes: 10 additions & 0 deletions packages/workflow/src/records/handler/certify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { uploadCertificateAttachmentsToDocumentsStore } from '@workflow/document
import { getAuthHeader } from '@opencrvs/commons/http'
import { indexBundle } from '@workflow/records/search'
import { auditEvent } from '@workflow/records/audit'
import { invokeWebhooks } from '@workflow/records/webhooks'
import { getEventType } from '@workflow/features/registration/utils'
import { SCOPES } from '@opencrvs/commons/authentication'

export const certifyRoute = createRoute({
Expand Down Expand Up @@ -48,6 +50,14 @@ export const certifyRoute = createRoute({
await indexBundle(certifiedRecord, token)
await auditEvent('certified', certifiedRecord, token)

await invokeWebhooks({
bundle: record,
token,
event: getEventType(record),
isNotRegistered: true,
statusType: 'certified'
})

return certifiedRecord
}
})
10 changes: 10 additions & 0 deletions packages/workflow/src/records/handler/issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { uploadCertificateAttachmentsToDocumentsStore } from '@workflow/document
import { getAuthHeader } from '@opencrvs/commons/http'
import { indexBundle } from '@workflow/records/search'
import { auditEvent } from '@workflow/records/audit'
import { invokeWebhooks } from '@workflow/records/webhooks'
import { getEventType } from '@workflow/features/registration/utils'
import { SCOPES } from '@opencrvs/commons/authentication'

export const issueRoute = createRoute({
Expand Down Expand Up @@ -48,6 +50,14 @@ export const issueRoute = createRoute({
await indexBundle(issuedRecord, token)
await auditEvent('issued', issuedRecord, token)

await invokeWebhooks({
bundle: record,
token,
event: getEventType(record),
isNotRegistered: true,
statusType: 'issued'
})

return issuedRecord
}
})
10 changes: 10 additions & 0 deletions packages/workflow/src/records/handler/reject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { toRejected } from '@workflow/records/state-transitions'
import { indexBundle } from '@workflow/records/search'
import { auditEvent } from '@workflow/records/audit'
import { sendNotification } from '@workflow/records/notification'
import { getEventType } from '@workflow/features/registration/utils'
import { invokeWebhooks } from '@workflow/records/webhooks'
import { SCOPES } from '@opencrvs/commons/authentication'

const requestSchema = z.object({
Expand Down Expand Up @@ -50,6 +52,14 @@ export const rejectRoute = createRoute({
await auditEvent('sent-for-updates', rejectedRecord, token)
await sendNotification('sent-for-updates', rejectedRecord, token)

await invokeWebhooks({
bundle: record,
token,
event: getEventType(record),
isNotRegistered: true,
statusType: 'rejected'
})

return rejectedRecord
}
})
10 changes: 10 additions & 0 deletions packages/workflow/src/records/handler/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { toValidated } from '@workflow/records/state-transitions'
import { auditEvent } from '@workflow/records/audit'
import { validateRequest } from '@workflow/utils/index'
import * as z from 'zod'
import { invokeWebhooks } from '@workflow/records/webhooks'
import { getEventType } from '@workflow/features/registration/utils'
import { SCOPES } from '@opencrvs/commons/authentication'

export const validateRoute = createRoute({
Expand Down Expand Up @@ -44,6 +46,14 @@ export const validateRoute = createRoute({
await indexBundle(validatedRecord, token)
await auditEvent('sent-for-approval', validatedRecord, token)

await invokeWebhooks({
bundle: record,
token,
event: getEventType(record),
isNotRegistered: true,
statusType: 'validated'
})

return validatedRecord
}
})
Loading
Loading