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: upsertRegistrationIdentifier gateway endpoint #8034

Merged
merged 23 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d05ff89
feat: Created an upsertRegistrationIdentifier endpoint to gateway
Hyper3x Nov 27, 2024
57c383c
mod : allowed any state records
Hyper3x Nov 29, 2024
30c0181
mod : removed upsert state-transition
Hyper3x Nov 29, 2024
fb845d1
DRAFT upsertRegistrationIdentifier endpoint to gateway #7908
Hyper3x Nov 21, 2024
db3f93b
feat: Created an upsertRegistrationIdentifier endpoint to gateway
Hyper3x Nov 27, 2024
a8fd674
mod : allowed any state records
Hyper3x Nov 29, 2024
82a4c78
mod : removed upsert state-transition
Hyper3x Nov 29, 2024
3c1f31a
issues fixed
Hyper3x Dec 2, 2024
dede05b
Merge branch 'develop' into ocrvs-7908
tahmidrahman-dsi Dec 4, 2024
9bd25f0
mod : required changes done
Hyper3x Dec 5, 2024
4736229
Merge branch 'develop' into ocrvs-7908
tahmidrahman-dsi Dec 5, 2024
bf5478f
fix: remove upsert stuff from states
tahmidrahman-dsi Dec 6, 2024
13ae4a2
fix: cleanup upsert handler
tahmidrahman-dsi Dec 9, 2024
45fd2e7
fix: small amends
tahmidrahman-dsi Dec 9, 2024
1a0b60e
fix: update wrong transformations
tahmidrahman-dsi Dec 9, 2024
49e4c11
Merge branch 'develop' into ocrvs-7908
naftis Dec 9, 2024
83af2a4
fix: remove unused schema
naftis Dec 9, 2024
497ccf0
Merge branch 'ocrvs-7908' of https://github.com/opencrvs/opencrvs-cor…
naftis Dec 9, 2024
cd96efd
refactor: wording
naftis Dec 9, 2024
f9902f3
fix: remove task history comment
naftis Dec 9, 2024
8575ec0
refactor: for clarity, allow only updating one identifier at time
naftis Dec 9, 2024
7fd261f
fix: remove unnecessary if as 'get' methods should already throw
naftis Dec 9, 2024
071efd3
Merge branch 'develop' into ocrvs-7908
tahmidrahman-dsi Dec 10, 2024
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- Reoder the sytem user add/edit field for surname to be first, also change labels from `Last name` to `User's surname` and lastly remove the NID question from the form [#6830](https://github.com/opencrvs/opencrvs-core/issues/6830)
- Corrected the total amount displayed for _certification_ and _correction_ fees on the Performance Page, ensuring accurate fee tracking across certification and correction sequences. [#7793](https://github.com/opencrvs/opencrvs-core/issues/7793)
- Auth now allows registrar's token to be exchanged for a new token that strictly allows confirming or rejecting a specific record. Core now passes this token to country configuration instead of the registrar's token [#7728](https://github.com/opencrvs/opencrvs-core/issues/7728) [#7849](https://github.com/opencrvs/opencrvs-core/issues/7849)
- A new GraphQL mutation `upsertRegistrationIdentifier` is added to allow updating the patient identifiers of a registration record such as NID [#8034](https://github.com/opencrvs/opencrvs-core/pull/8034)

### Improvements

Expand Down Expand Up @@ -45,7 +46,6 @@

- Add an optional configurable field in section `canContinue` which takes an expression. Falsy value of this expression will disable the continue button in forms. This can be used to work with fetch field which has a loading state and prevent the user to get past the section while the request is still in progress.


## [1.6.0](https://github.com/opencrvs/opencrvs-core/compare/v1.5.1...v1.6.0)

### Breaking changes
Expand Down Expand Up @@ -77,6 +77,7 @@
]
}
```

- `hasChildLocation` query has been removed from gateway. We have created the query `isLeafLevelLocation` instead which is more descriptive on its intended use.

### New features
Expand Down Expand Up @@ -302,6 +303,7 @@ In the next OpenCRVS release v1.5.0, there will be two significant changes both
- **New handlebars serving the location ids of the admin level locations**

Apart from the new handlebars, a couple more improvements were introduced:

- stricter type for locations in client
- **"location"** handlebar helper can now resolve offices & facilities
- restrict the properties exposed through **"location"** handlebar helper
Expand Down
23 changes: 21 additions & 2 deletions packages/gateway/src/features/registration/root-resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ import {
verifyRegistration,
markNotADuplicate,
rejectRegistration,
confirmRegistration
confirmRegistration,
upsertRegistrationIdentifier
} from '@gateway/workflow/index'
import { getRecordById } from '@gateway/records'
import { UnassignError, UserInputError } from '@gateway/utils/graphql-errors'
Expand Down Expand Up @@ -612,7 +613,6 @@ export const resolvers: GQLResolver = {

try {
const taskEntry = await confirmRegistration(id, authHeader, {
error: details.error,
registrationNumber: details.registrationNumber,
identifiers: details.identifiers
})
Expand Down Expand Up @@ -641,6 +641,25 @@ export const resolvers: GQLResolver = {
} catch (error) {
throw new Error(`Error in rejectRegistration: ${error.message}`)
}
},
async upsertRegistrationIdentifier(
_,
{ id, identifierType, identifierValue },
{ headers: authHeader }
) {
if (!hasRecordAccess(authHeader, id)) {
throw new Error('User does not have access to the record')
}

try {
const taskEntry = await upsertRegistrationIdentifier(id, authHeader, {
identifiers: [{ type: identifierType, value: identifierValue }]
})

return taskEntry.resource.id
} catch (error) {
throw new Error(`Failed to confirm registration: ${error.message}`)
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion packages/gateway/src/features/registration/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,6 @@ input IdentifierInput {

input ConfirmRegistrationInput {
registrationNumber: String!
error: String
identifiers: [IdentifierInput!]
}

Expand Down Expand Up @@ -657,4 +656,9 @@ type Mutation {
): ID!
confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID!
rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID!
upsertRegistrationIdentifier(
id: ID!
identifierType: String!
identifierValue: String!
): ID!
}
20 changes: 19 additions & 1 deletion packages/gateway/src/graphql/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface GQLMutation {
markEventAsDuplicate: string
confirmRegistration: string
rejectRegistration: string
upsertRegistrationIdentifier: string
createOrUpdateUser: GQLUser
activateUser?: string
changePassword?: string
Expand Down Expand Up @@ -601,7 +602,6 @@ export interface GQLReinstated {

export interface GQLConfirmRegistrationInput {
registrationNumber: string
error?: string
identifiers?: Array<GQLIdentifierInput>
}

Expand Down Expand Up @@ -2632,6 +2632,7 @@ export interface GQLMutationTypeResolver<TParent = any> {
markEventAsDuplicate?: MutationToMarkEventAsDuplicateResolver<TParent>
confirmRegistration?: MutationToConfirmRegistrationResolver<TParent>
rejectRegistration?: MutationToRejectRegistrationResolver<TParent>
upsertRegistrationIdentifier?: MutationToUpsertRegistrationIdentifierResolver<TParent>
createOrUpdateUser?: MutationToCreateOrUpdateUserResolver<TParent>
activateUser?: MutationToActivateUserResolver<TParent>
changePassword?: MutationToChangePasswordResolver<TParent>
Expand Down Expand Up @@ -3208,6 +3209,23 @@ export interface MutationToRejectRegistrationResolver<
): TResult
}

export interface MutationToUpsertRegistrationIdentifierArgs {
id: string
identifierType: string
identifierValue: string
}
export interface MutationToUpsertRegistrationIdentifierResolver<
TParent = any,
TResult = any
> {
(
parent: TParent,
args: MutationToUpsertRegistrationIdentifierArgs,
context: Context,
info: GraphQLResolveInfo
): TResult
}

export interface MutationToCreateOrUpdateUserArgs {
user: GQLUserInput
}
Expand Down
6 changes: 5 additions & 1 deletion packages/gateway/src/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ type Mutation {
): ID!
confirmRegistration(id: ID!, details: ConfirmRegistrationInput!): ID!
rejectRegistration(id: ID!, details: RejectRegistrationInput!): ID!
upsertRegistrationIdentifier(
id: ID!
identifierType: String!
identifierValue: String!
): ID!
createOrUpdateUser(user: UserInput!): User!
activateUser(
userId: String!
Expand Down Expand Up @@ -734,7 +739,6 @@ type Reinstated {

input ConfirmRegistrationInput {
registrationNumber: String!
error: String
identifiers: [IdentifierInput!]
}

Expand Down
24 changes: 23 additions & 1 deletion packages/gateway/src/workflow/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ export async function confirmRegistration(
id: string,
authHeader: IAuthHeader,
details: {
error: string | undefined
registrationNumber: string
identifiers?: IdentifierInput[]
}
Expand Down Expand Up @@ -281,6 +280,29 @@ export async function rejectRegistration(
return taskEntry
}

export async function upsertRegistrationIdentifier(
id: string,
authHeader: IAuthHeader,
details: {
registrationNumber?: string
identifiers?: IdentifierInput[]
}
) {
const res: ReadyForReviewRecord = await createRequest(
'POST',
`/records/${id}/upsert-identifiers`,
authHeader,
details
)

const taskEntry = res.entry.find((e) => e.resource.resourceType === 'Task')
if (!taskEntry) {
throw new Error('No task entry found in the confirmation response')
}

return taskEntry
}

export async function archiveRegistration(
id: string,
authHeader: IAuthHeader,
Expand Down
11 changes: 11 additions & 0 deletions packages/workflow/src/config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { approveCorrectionRoute } from '@workflow/records/handler/correction/app
import { requestCorrectionRoute } from '@workflow/records/handler/correction/request'
import { makeCorrectionRoute } from '@workflow/records/handler/correction/make-correction'
import { eventNotificationHandler } from '@workflow/records/handler/eventNotificationHandler'
import { upsertRegistrationHandler } from '@workflow/records/handler/upsert-identifiers'

export const getRoutes = () => {
const routes = [
Expand Down Expand Up @@ -69,6 +70,16 @@ export const getRoutes = () => {
'Register event based on tracking id and registration number.'
}
},
{
method: 'POST',
path: '/records/{id}/upsert-identifiers',
handler: upsertRegistrationHandler,
config: {
tags: ['api'],
description:
'Upsert Register event based on tracking id and registration number.'
}
},
{
method: 'POST',
path: '/create-record',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
findExtension,
getResourceFromBundleById,
resourceIdentifierToUUID,
SupportedPatientIdentifierCode
SupportedPatientIdentifierCode,
ValidRecord
} from '@opencrvs/commons/types'
import { COUNTRY_CONFIG_URL } from '@workflow/constants'
import {
Expand All @@ -37,6 +38,7 @@ import {
} from '@workflow/features/registration/fhir/fhir-utils'
import { getPractitionerRef } from '@workflow/features/user/utils'
import { ITokenPayload } from '@workflow/utils/auth-utils'
import { isEqual } from 'lodash'
import fetch from 'node-fetch'

export async function invokeRegistrationValidation(
Expand Down Expand Up @@ -204,3 +206,47 @@ export function updatePatientIdentifierWithRN(
return patient
})
}

export function upsertPatientIdentifiers(
record: ValidRecord,
composition: Composition,
sectionCodes: string[],
identifiers: {
type: SupportedPatientIdentifierCode
value: string
}[]
): Saved<Patient>[] {
return sectionCodes.map((sectionCode) => {
const sectionEntry = getSectionEntryBySectionCode(composition, sectionCode)
const patientId = resourceIdentifierToUUID(
sectionEntry.reference as ResourceIdentifier
)
const patient = getResourceFromBundleById<Patient>(record, patientId)

if (!patient.identifier) {
patient.identifier = []
}
identifiers.forEach((id) => {
const existingIdentifier = patient.identifier!.find((existingId) =>
isEqual(existingId.type, id.type)
)
if (existingIdentifier) {
existingIdentifier.value = id.value
} else {
patient.identifier!.push({
type: {
coding: [
{
system: `${OPENCRVS_SPECIFICATION_URL}identifier-type`,
code: id.type
}
]
},
value: id.value
})
}
})

return patient
})
}
60 changes: 60 additions & 0 deletions packages/workflow/src/records/handler/upsert-identifiers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* OpenCRVS is also distributed under the terms of the Civil Registration
* & Healthcare Disclaimer located at http://opencrvs.org/license.
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import { getToken } from '@workflow/utils/auth-utils'
import * as Hapi from '@hapi/hapi'
import { getRecordById } from '@workflow/records/index'
import { indexBundle } from '@workflow/records/search'
import { toIdentifierUpserted } from '@workflow/records/state-transitions'
import { SupportedPatientIdentifierCode } from '@opencrvs/commons/types'
import { sendBundleToHearth } from '@workflow/records/fhir'

interface IdentifierInput {
type: SupportedPatientIdentifierCode
value: string
}

interface EventRegistrationPayload {
identifiers: IdentifierInput[]
}

export async function upsertRegistrationHandler(
request: Hapi.Request,
h: Hapi.ResponseToolkit
) {
const token = getToken(request)
const compositionId = request.params.id
const { identifiers } = request.payload as EventRegistrationPayload

const savedRecord = await getRecordById(
compositionId,
request.headers.authorization,
[
'ARCHIVED',
'CERTIFIED',
'CORRECTION_REQUESTED',
'IN_PROGRESS',
'READY_FOR_REVIEW',
'ISSUED',
'REGISTERED',
'REJECTED',
'VALIDATED',
'WAITING_VALIDATION'
],
true
)

const upsertedRecord = toIdentifierUpserted(savedRecord, identifiers)

await sendBundleToHearth(upsertedRecord)
await indexBundle(upsertedRecord, token)

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