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

fix: correct user role not showing if role is updated #7562

Merged
merged 8 commits into from
Sep 4, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
- On slow connections or in rare corner cases, it was possible that the same record got saved to the database twice. This was caused by a bug in how the unique technical identifier we generate were stored as FHIR. The backend now ensures every record is submitted only once. [#7477](https://github.com/opencrvs/opencrvs-core/issues/7477)
- Fixed an issue where address line fields (e.g., address line 1, address line 2, etc.) were not being updated correctly when a user attempted to update a record's event location, such as place of birth or place of death. [#7531](https://github.com/opencrvs/opencrvs-core/issues/7531)
- When a declaration(birth/death) is created the event location information was not being parsed to ElasticSearch which caused the Advanced search feature to not work when searching for records by event location.[7494](https://github.com/opencrvs/opencrvs-core/issues/7494)
- When any user's role was updated, incorrect role was shown for that user's actions in the history section of a declaration's record audit page. [#7495](https://github.com/opencrvs/opencrvs-core/issues/7495)

## 1.5.0 (TBD)

Expand Down
37 changes: 37 additions & 0 deletions packages/commons/src/fhir/practitioner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,40 @@ export function getPractitionerContactDetails(practitioner: Practitioner) {

throw new Error('No contact details found for practitioner')
}

export const getUserRoleFromHistory = (
practitionerRoleHistory: PractitionerRoleHistory[],
lastModified: string
) => {
const practitionerRoleHistorySorted = practitionerRoleHistory.sort((a, b) => {
if (a.meta?.lastUpdated === b.meta?.lastUpdated) {
return 0
}
if (a.meta?.lastUpdated === undefined) {
return 1
}
if (b.meta?.lastUpdated === undefined) {
return -1
}
return (
new Date(b.meta?.lastUpdated).valueOf() -
new Date(a.meta?.lastUpdated).valueOf()
)
})

const result = practitionerRoleHistorySorted.find(
(it) =>
it?.meta?.lastUpdated &&
lastModified &&
it?.meta?.lastUpdated <= lastModified!
)

const targetCode = result?.code?.find((element) => {
return element.coding?.[0].system === 'http://opencrvs.org/specs/types'
})

const role = targetCode?.coding?.[0].code
const systemRole = result?.code?.[0].coding?.[0].code

return { role, systemRole }
}
165 changes: 165 additions & 0 deletions packages/gateway/src/features/registration/type-resolvers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import {
} from '@opencrvs/commons/fixtures'
import { Context } from '@gateway/graphql/context'
import { getAuthHeader } from '@opencrvs/commons/http'
import {
getUserRoleFromHistory,
PractitionerRoleHistory
} from '@opencrvs/commons/types'

const MOCK_TOKEN = jwt.sign(
{ scope: ['validate'] },
Expand Down Expand Up @@ -378,3 +382,164 @@ test('running a full aggregated marriage FHIR bundle through resolvers produces
)
expect(response.data).toMatchSnapshot()
})

test('getting role at a specific time from roleHistory', async () => {
const practitionerRoleHistory = [
{
resourceType: 'PractitionerRole',
practitioner: {
reference: 'Practitioner/f28e6b7e-30ee-460a-8851-7e71b46c97cd'
},
code: [
{
coding: [
{
system: 'http://opencrvs.org/specs/roles',
code: 'NATIONAL_REGISTRAR'
}
]
},
{
coding: [
{
system: 'http://opencrvs.org/specs/types',
code: '[{"lang":"en","label":"National Registrar"},{"lang":"fr","label":"Registraire national"}]'
}
]
}
],
location: [
{ reference: 'Location/1e73d8a7-964d-4ea1-be77-0de1d2ced0a9' }
],
id: 'ef518d5e-28a5-4c6f-9152-da2c28815e9b',
meta: {
lastUpdated: '2024-09-02T08:13:50.173+00:00',
versionId: '4dfc703b-a5f2-4f56-a11e-5827b3160e0f'
},
_transforms: { meta: { lastUpdated: '2024-09-02T08:13:50.173Z' } },
_request: { method: 'PUT' }
},
{
resourceType: 'PractitionerRoleHistory',
practitioner: {
reference: 'Practitioner/f28e6b7e-30ee-460a-8851-7e71b46c97cd'
},
code: [
{
coding: [
{
system: 'http://opencrvs.org/specs/roles',
code: 'LOCAL_REGISTRAR'
}
]
},
{
coding: [
{
system: 'http://opencrvs.org/specs/types',
code: '[{"lang":"en","label":"Local Registrar"},{"lang":"fr","label":"Registraire local"}]'
}
]
}
],
location: [
{ reference: 'Location/1e73d8a7-964d-4ea1-be77-0de1d2ced0a9' }
],
meta: {
lastUpdated: '2024-08-30T13:10:48.262+00:00',
versionId: '95d3b84c-a77c-4634-8033-7802ba4bf5f1'
},
_transforms: { meta: { lastUpdated: '2024-08-30T13:10:48.262Z' } },
_request: { method: 'POST' },
id: 'ef518d5e-28a5-4c6f-9152-da2c28815e9b'
},
{
resourceType: 'PractitionerRoleHistory',
practitioner: {
reference: 'Practitioner/f28e6b7e-30ee-460a-8851-7e71b46c97cd'
},
code: [
{
coding: [
{
system: 'http://opencrvs.org/specs/roles',
code: 'NATIONAL_REGISTRAR'
}
]
},
{
coding: [
{
system: 'http://opencrvs.org/specs/types',
code: '[{"lang":"en","label":"National Registrar"},{"lang":"fr","label":"Registraire national"}]'
}
]
}
],
location: [
{ reference: 'Location/1e73d8a7-964d-4ea1-be77-0de1d2ced0a9' }
],
id: 'ef518d5e-28a5-4c6f-9152-da2c28815e9b',
meta: {
lastUpdated: '2024-08-30T13:14:16.965+00:00',
versionId: '05e67e48-9774-43c4-9136-53653d8577b4'
},
_transforms: { meta: { lastUpdated: '2024-08-30T13:14:16.965Z' } },
_request: { method: 'PUT' }
},
{
resourceType: 'PractitionerRoleHistory',
practitioner: {
reference: 'Practitioner/f28e6b7e-30ee-460a-8851-7e71b46c97cd'
},
code: [
{
coding: [
{
system: 'http://opencrvs.org/specs/roles',
code: 'LOCAL_SYSTEM_ADMIN'
}
]
},
{
coding: [
{
system: 'http://opencrvs.org/specs/types',
code: '[{"lang":"en","label":"Local System Admin"},{"lang":"fr","label":"Administrateur système local"}]'
}
]
}
],
location: [
{ reference: 'Location/1e73d8a7-964d-4ea1-be77-0de1d2ced0a9' }
],
id: 'ef518d5e-28a5-4c6f-9152-da2c28815e9b',
meta: {
lastUpdated: '2024-09-02T08:12:52.860+00:00',
versionId: '70690251-e1cf-4196-ae9a-9b579edd6e10'
},
_transforms: { meta: { lastUpdated: '2024-09-02T08:12:52.860Z' } },
_request: { method: 'PUT' }
}
] as unknown as PractitionerRoleHistory[]

/*
2024-09-02T08:13:50.173+00:00 NATIONAL_REGISTRAR
2024-08-30T13:10:48.262+00:00 LOCAL_REGISTRAR
2024-08-30T13:14:16.965+00:00 NATIONAL_REGISTRAR *
2024-09-02T08:12:52.860+00:00 LOCAL_SYSTEM_ADMIN
*/

const lastModified = '2024-08-30T13:14:33.704Z'
const expectedRole =
'[{"lang":"en","label":"National Registrar"},{"lang":"fr","label":"Registraire national"}]'
const expectedSystemRole = 'NATIONAL_REGISTRAR'

const { role, systemRole } = getUserRoleFromHistory(
practitionerRoleHistory,
lastModified
)

expect(role).toEqual(expectedRole)
expect(systemRole).toEqual(expectedSystemRole)
})
31 changes: 16 additions & 15 deletions packages/gateway/src/features/registration/type-resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ import {
findLastOfficeFromSavedBundle,
findLastOfficeLocationFromSavedBundle,
notCorrectedHistory,
findResourceFromBundleById
findResourceFromBundleById,
getUserRoleFromHistory
} from '@opencrvs/commons/types'

import { GQLQuestionnaireQuestion, GQLResolver } from '@gateway/graphql/schema'
Expand Down Expand Up @@ -1496,25 +1497,25 @@ export const typeResolvers: GQLResolver = {

const practitionerRoleHistory =
await dataSources.fhirAPI.getPractionerRoleHistory(practitionerRoleId)
const result = practitionerRoleHistory.find(
(it) =>
it?.meta?.lastUpdated &&
task.lastModified &&
it?.meta?.lastUpdated <= task.lastModified!
)

const targetCode = result?.code?.find((element) => {
return element.coding?.[0].system === 'http://opencrvs.org/specs/types'
})

const role = targetCode?.coding?.[0].code

const userResponse = await dataSources.usersAPI.getUserByPractitionerId(
resourceIdentifierToUUID(user.valueReference.reference)
)

if (role) {
userResponse.role.labels = JSON.parse(role)
const { role, systemRole } = getUserRoleFromHistory(
practitionerRoleHistory,
task.lastModified
)

if (role && systemRole) {
return {
...userResponse,
role: {
...userResponse.role,
labels: JSON.parse(role)
},
systemRole
}
}

return userResponse
Expand Down
Loading