diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89bcd9a95..736828e85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,14 +2,27 @@
## 1.7.0 Release candidate
+### Migration notes
+In order to make the upgrade easier, there are a couple of steps that need to be performed which will make the codebase ready for the upgrade:
+- Run this command from the root of the countryconfig repository ```curl https://raw.githubusercontent.com/opencrvs/opencrvs-countryconfig/configurable-roles/src/upgrade-to-1_7.ts | npx ts-node -T --cwd ./src```
+
+ It will remove `roles.csv` and generate a `roles.ts` file. It will also update the corresponding role column in `default-employees.csv` & `prod-employees.csv` while adding the corresponding translations in `client.csv`. The employee files are only used when seeding new environments, if you already have a v1.6.x of OpenCRVS deployed, the data in the environment will automatically get migrated after deploying the upgrade. The changes in these two files are made to keep the roles in sync with your previously deployed environments, if any.
+- After pulling in the v1.7.0 changes reject the changes incoming to `roles.ts`, `default-employees.csv` & `prod-employees.csv` files as we used the script above to auto-generate them.
+
+ The `roles.ts` file now defines all the roles available in the system. New roles can be added & existing roles can be customized by giving them different scopes.
+
+ *N.B. The default roles generated in the `roles.ts` file during migration should not be removed to maintain backwards compatibility*
+
### Breaking changes
- `INFORMANT_SIGNATURE` & `INFORMANT_SIGNATURE_REQUIRED` are now deprecated
- Existing implementations relying on database-stored SVGs need to be updated to use the new configuration-based approach. A migration needs to be run (defined in [migration](https://github.com/opencrvs/opencrvs-core/pull/7813/files#diff-e5472dec87399bb9f73f75ec379ceb6a32ca135bc01dd8d0eb8f7d7aaa0bc0b1)), and default certificate templates must be created for each event type, following the convention `${event}-certificate` as the certificate template ID.
+- **Roles** The previous `roles.csv` file has been deprecated. It will get removed once you run `yarn upgrade:code` command after pulling in the v1.7 changes. The command automatically generates a `roles.json` file which can be used as a baseline to configure the roles as per your requirements.
### New features
- Update the translations for System user add/edit form, `Last name` to `User's surname` and `First name` to `User's first name` to make them less confusing for system users [#6830](https://github.com/opencrvs/opencrvs-core/issues/6830)
+- **User scopes** Introduce granular scopes to grant specific permissions to a particular role. The specifics about the introduced scopes can be found here: *Link to scopes description file*
- **Refactored certificate handling:** SVGs are no longer stored in the database; streamlined configurations now include certificate details, and clients request SVGs directly via URLs.
- Add constant.humanName to allow countries to have custom ordering on their full name e.g. start with `lastName` or `firstName` [#6830](https://github.com/opencrvs/opencrvs-core/issues/6830)
diff --git a/e2e/testcases/birth/1-birth-event-declaration.spec.ts b/e2e/testcases/birth/1-birth-event-declaration.spec.ts
index 78426dfaf..69335fbcc 100644
--- a/e2e/testcases/birth/1-birth-event-declaration.spec.ts
+++ b/e2e/testcases/birth/1-birth-event-declaration.spec.ts
@@ -426,10 +426,10 @@ test.describe('1. Birth event declaration', () => {
/*
* Expected result: should
- * - be navigated to "in-progress" tab
+ * - be navigated to "my-drafts" tab
* - find the declared birth event record on this page list with saved data
*/
- await expect(page.locator('#content-name')).toHaveText('In progress')
+ await expect(page.locator('#content-name')).toHaveText('My drafts')
await expect(page.getByText(/seconds ago/)).toBeVisible()
})
})
@@ -483,13 +483,13 @@ test.describe('1. Birth event declaration', () => {
test('1.10.3 Click Confirm', async ({ page }) => {
await page.getByRole('button', { name: 'Confirm' }).click()
/*
- * Expected result: should be navigated to "in-progress" tab but no draft will be saved
+ * Expected result: should be navigated to "my-drafts" tab but no draft will be saved
*/
await page.waitForTimeout(500) // This page renders twice at first
await expect(
- page.locator('#content-name', { hasText: 'In progress' })
+ page.locator('#content-name', { hasText: 'My drafts' })
).toBeVisible()
await expect(page.getByText(/seconds ago/)).toBeHidden()
})
@@ -552,13 +552,13 @@ test.describe('1. Birth event declaration', () => {
await page.getByRole('button', { name: 'Confirm' }).click()
/*
- * Expected result: should be navigated to "in-progress" tab but no draft will be saved
+ * Expected result: should be navigated to "my-drafts" tab but no draft will be saved
*/
await page.waitForTimeout(500) // This page renders twice at first
await expect(
- page.locator('#content-name', { hasText: 'In progress' })
+ page.locator('#content-name', { hasText: 'My drafts' })
).toBeVisible()
await expect(page.getByText(/seconds ago/)).toBeHidden()
})
diff --git a/e2e/testcases/birth/queries.ts b/e2e/testcases/birth/queries.ts
index b4c30bc7d..b14dd2f87 100644
--- a/e2e/testcases/birth/queries.ts
+++ b/e2e/testcases/birth/queries.ts
@@ -150,6 +150,31 @@ export const GET_BIRTH_REGISTRATION_FOR_REVIEW = print(gql`
contactRelationship
contactPhoneNumber
contactEmail
+ assignment {
+ practitionerId
+ firstName
+ lastName
+ officeName
+ avatarURL
+ }
+ certificates {
+ hasShowedVerifiedDocument
+ certificateTemplateId
+ collector {
+ relationship
+ otherRelationship
+ name {
+ use
+ firstNames
+ familyName
+ }
+ telecom {
+ system
+ value
+ use
+ }
+ }
+ }
duplicates {
compositionId
trackingId
@@ -207,6 +232,7 @@ export const GET_BIRTH_REGISTRATION_FOR_REVIEW = print(gql`
requesterOther
noSupportingDocumentationRequired
hasShowedVerifiedDocument
+ certificateTemplateId
date
action
regStatus
@@ -250,13 +276,13 @@ export const GET_BIRTH_REGISTRATION_FOR_REVIEW = print(gql`
user {
id
role {
- _id
- labels {
- lang
- label
+ id
+ label {
+ id
+ defaultMessage
+ description
}
}
- systemRole
name {
firstNames
familyName
@@ -295,6 +321,7 @@ export const GET_BIRTH_REGISTRATION_FOR_REVIEW = print(gql`
}
certificates {
hasShowedVerifiedDocument
+ certificateTemplateId
collector {
relationship
otherRelationship
@@ -309,6 +336,21 @@ export const GET_BIRTH_REGISTRATION_FOR_REVIEW = print(gql`
use
}
}
+ certifier {
+ name {
+ use
+ firstNames
+ familyName
+ }
+ role {
+ id
+ label {
+ id
+ defaultMessage
+ description
+ }
+ }
+ }
}
duplicateOf
potentialDuplicates
diff --git a/e2e/testcases/death/1-death-event-declaration.spec.ts b/e2e/testcases/death/1-death-event-declaration.spec.ts
index 1f9d2e9c5..dfc7e8d54 100644
--- a/e2e/testcases/death/1-death-event-declaration.spec.ts
+++ b/e2e/testcases/death/1-death-event-declaration.spec.ts
@@ -406,10 +406,10 @@ test.describe('1. Death event declaration', () => {
/*
* Expected result: should
- * - be navigated to "in-progress" tab
+ * - be navigated to "my-drafts" tab
* - find the declared death event record on this page list with saved data
*/
- await expect(page.locator('#content-name')).toHaveText('In progress')
+ await expect(page.locator('#content-name')).toHaveText('My drafts')
await expect(page.getByText(/seconds ago/)).toBeVisible()
})
})
@@ -463,13 +463,13 @@ test.describe('1. Death event declaration', () => {
test('1.10.3 Click Confirm', async ({ page }) => {
await page.getByRole('button', { name: 'Confirm' }).click()
/*
- * Expected result: should be navigated to "in-progress" tab but no draft will be saved
+ * Expected result: should be navigated to "my-drafts" tab but no draft will be saved
*/
await page.waitForTimeout(500) // This page renders twice at first
await expect(
- page.locator('#content-name', { hasText: 'In progress' })
+ page.locator('#content-name', { hasText: 'My drafts' })
).toBeVisible()
await expect(page.getByText(/seconds ago/)).toBeHidden()
})
@@ -532,13 +532,13 @@ test.describe('1. Death event declaration', () => {
await page.getByRole('button', { name: 'Confirm' }).click()
/*
- * Expected result: should be navigated to "in-progress" tab but no draft will be saved
+ * Expected result: should be navigated to "my-drafts" tab but no draft will be saved
*/
await page.waitForTimeout(500) // This page renders twice at first
await expect(
- page.locator('#content-name', { hasText: 'In progress' })
+ page.locator('#content-name', { hasText: 'My drafts' })
).toBeVisible()
await expect(page.getByText(/seconds ago/)).toBeHidden()
})
diff --git a/e2e/testcases/death/queries.ts b/e2e/testcases/death/queries.ts
index 52eb3734b..d994b913d 100644
--- a/e2e/testcases/death/queries.ts
+++ b/e2e/testcases/death/queries.ts
@@ -214,6 +214,24 @@ export const GET_DEATH_REGISTRATION_FOR_REVIEW = print(gql`
contactRelationship
contactPhoneNumber
contactEmail
+ certificates {
+ hasShowedVerifiedDocument
+ certificateTemplateId
+ collector {
+ relationship
+ otherRelationship
+ name {
+ use
+ firstNames
+ familyName
+ }
+ telecom {
+ system
+ value
+ use
+ }
+ }
+ }
duplicates {
compositionId
trackingId
@@ -287,7 +305,9 @@ export const GET_DEATH_REGISTRATION_FOR_REVIEW = print(gql`
}
otherReason
requester
+ requesterOther
hasShowedVerifiedDocument
+ certificateTemplateId
noSupportingDocumentationRequired
date
action
@@ -318,13 +338,14 @@ export const GET_DEATH_REGISTRATION_FOR_REVIEW = print(gql`
user {
id
role {
- _id
- labels {
- lang
- label
+ id
+ label {
+ id
+ defaultMessage
+ description
}
}
- systemRole
+
name {
firstNames
familyName
@@ -363,6 +384,7 @@ export const GET_DEATH_REGISTRATION_FOR_REVIEW = print(gql`
}
certificates {
hasShowedVerifiedDocument
+ certificateTemplateId
collector {
relationship
otherRelationship
@@ -377,6 +399,21 @@ export const GET_DEATH_REGISTRATION_FOR_REVIEW = print(gql`
use
}
}
+ certifier {
+ name {
+ use
+ firstNames
+ familyName
+ }
+ role {
+ id
+ label {
+ id
+ defaultMessage
+ description
+ }
+ }
+ }
}
duplicateOf
potentialDuplicates
diff --git a/package.json b/package.json
index 87ba02848..b350a246d 100644
--- a/package.json
+++ b/package.json
@@ -56,8 +56,8 @@
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"eslint": "^8.43.0",
- "eslint-config-prettier": "^8.8.0",
- "eslint-plugin-prettier": "^4.2.1",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-prettier": "^5.2.1",
"husky": "1.0.0-rc.13",
"inquirer": "^9.2.12",
"js-yaml": "^4.1.0",
@@ -67,7 +67,7 @@
"node-ssh": "^13.2.0",
"nodemon": "^2.0.22",
"pino-pretty": "^11.0.0",
- "prettier": "^2.8.8",
+ "prettier": "^3.4.2",
"react-intl": "^6.4.3",
"vitest": "^2.1.2"
},
@@ -77,7 +77,7 @@
"@hapi/h2o2": "^9.1.0",
"@hapi/hapi": "^20.0.1",
"@hapi/inert": "^6.0.3",
- "@opencrvs/toolkit": "0.0.17-events",
+ "@opencrvs/toolkit": "0.0.22-scopes",
"@types/chalk": "^2.2.0",
"@types/code": "^4.0.3",
"@types/csv2json": "^1.4.0",
@@ -88,7 +88,7 @@
"@types/jest": "^24.0.13",
"@types/jwt-decode": "^2.2.1",
"@types/lodash": "^4.14.117",
- "@types/node": "^10.12.5",
+ "@types/node": "^18.19.1",
"@types/node-fetch": "^2.6.2",
"@types/nodemailer": "^6.4.14",
"@types/uuid": "^10.0.0",
diff --git a/src/api/certificates/handler.ts b/src/api/certificates/handler.ts
index a54b4b61a..e70593e9a 100644
--- a/src/api/certificates/handler.ts
+++ b/src/api/certificates/handler.ts
@@ -93,7 +93,7 @@ export async function certificateHandler(request: Request, h: ResponseToolkit) {
},
{
id: 'death-certificate',
- event: 'death' as Event.Death,
+ event: Event.Death,
label: {
id: 'certificates.death.certificate',
defaultMessage: 'Death Certificate',
@@ -117,7 +117,7 @@ export async function certificateHandler(request: Request, h: ResponseToolkit) {
},
{
id: 'death-certificate-certified-copy',
- event: 'death' as Event.Death,
+ event: Event.Death,
label: {
id: 'certificates.death.certificate.copy',
defaultMessage: 'Death Certificate certified copy',
@@ -142,7 +142,7 @@ export async function certificateHandler(request: Request, h: ResponseToolkit) {
},
{
id: 'marriage-certificate',
- event: 'marriage' as Event.Marriage,
+ event: Event.Marriage,
label: {
id: 'certificates.marriage.certificate',
defaultMessage: 'Marriage Certificate',
@@ -166,7 +166,7 @@ export async function certificateHandler(request: Request, h: ResponseToolkit) {
},
{
id: 'marriage-certificate-certified-copy',
- event: 'marriage' as Event.Marriage,
+ event: Event.Marriage,
label: {
id: 'certificates.marriage.certificate.copy',
defaultMessage: 'Marriage Certificate certified copy',
diff --git a/src/api/certificates/source/birth-certificate-certified-copy.svg b/src/api/certificates/source/birth-certificate-certified-copy.svg
index a279fbb87..f6ed4acb4 100644
--- a/src/api/certificates/source/birth-certificate-certified-copy.svg
+++ b/src/api/certificates/source/birth-certificate-certified-copy.svg
@@ -384,4 +384,4 @@
{{#ifCond printInAdvance '!==' true}}
{{/ifCond}}
-
\ No newline at end of file
+
diff --git a/src/api/certificates/source/birth-certificate.svg b/src/api/certificates/source/birth-certificate.svg
index 506461702..5a62836b3 100644
--- a/src/api/certificates/source/birth-certificate.svg
+++ b/src/api/certificates/source/birth-certificate.svg
@@ -291,4 +291,4 @@
{{#ifCond printInAdvance '!==' true}}
{{/ifCond}}
-
\ No newline at end of file
+
diff --git a/src/api/certificates/source/death-certificate-certified-copy.svg b/src/api/certificates/source/death-certificate-certified-copy.svg
index 804ee925e..efebe5d56 100644
--- a/src/api/certificates/source/death-certificate-certified-copy.svg
+++ b/src/api/certificates/source/death-certificate-certified-copy.svg
@@ -283,4 +283,4 @@
{{#ifCond printInAdvance '!==' true}}
{{/ifCond}}
-
\ No newline at end of file
+
diff --git a/src/api/certificates/source/death-certificate.svg b/src/api/certificates/source/death-certificate.svg
index c489f10db..50cc356c2 100644
--- a/src/api/certificates/source/death-certificate.svg
+++ b/src/api/certificates/source/death-certificate.svg
@@ -257,4 +257,4 @@
{{#ifCond printInAdvance '!==' true}}
{{/ifCond}}
-
\ No newline at end of file
+
diff --git a/src/api/certificates/source/marriage-certificate-certified-copy.svg b/src/api/certificates/source/marriage-certificate-certified-copy.svg
index 174336193..268392ea2 100644
--- a/src/api/certificates/source/marriage-certificate-certified-copy.svg
+++ b/src/api/certificates/source/marriage-certificate-certified-copy.svg
@@ -378,4 +378,4 @@
{{#ifCond printInAdvance '!==' true}}
{{/ifCond}}
-
\ No newline at end of file
+
diff --git a/src/api/certificates/source/marriage-certificate.svg b/src/api/certificates/source/marriage-certificate.svg
index cb9b3f87c..a66d82068 100644
--- a/src/api/certificates/source/marriage-certificate.svg
+++ b/src/api/certificates/source/marriage-certificate.svg
@@ -265,4 +265,4 @@
{{#ifCond printInAdvance '!==' true}}
{{/ifCond}}
-
\ No newline at end of file
+
diff --git a/src/data-seeding/employees/source/default-employees.csv b/src/data-seeding/employees/source/default-employees.csv
index 1888c3a03..f3cc79280 100644
--- a/src/data-seeding/employees/source/default-employees.csv
+++ b/src/data-seeding/employees/source/default-employees.csv
@@ -1,12 +1,12 @@
-primaryOfficeId,givenNames,familyName,systemRole,role,mobile,username,email,password
-CRVS_OFFICE_JWMRGwDBXK,Kalusha,Bwalya,FIELD_AGENT,Social Worker,+260911111111,k.bwalya,kalushabwalya17@gmail.com,test
-CRVS_OFFICE_JWMRGwDBXK,Felix,Katongo,REGISTRATION_AGENT,Registration Agent,+260922222222,f.katongo,kalushabwalya17+@gmail.com,test
-CRVS_OFFICE_JWMRGwDBXK,Kennedy,Mweene,LOCAL_REGISTRAR,Local Registrar,+260933333333,k.mweene,kalushabwalya1.7@gmail.com,test
-CRVS_OFFICE_JWMRGwDBXK,Emmanuel,Mayuka,LOCAL_SYSTEM_ADMIN,Local System Admin,+260921681112,e.mayuka,kalushabwalya.17@gmail.com,test
-CRVS_OFFICE_2OKicPQMNI,Jonathan,Campbell,NATIONAL_SYSTEM_ADMIN,National System Admin,+260921111111,j.campbell,kalushabwaly.a17@gmail.com,test
-CRVS_OFFICE_okQp4uKCz0,Patrick,Gondwe,FIELD_AGENT,Local Leader,+260912121212,p.gondwe,kalushabwal.ya17@gmail.com,test
-CRVS_OFFICE_okQp4uKCz0,Joshua,Mutale,REGISTRATION_AGENT,Registration Agent,+260923232323,j.mutale,kalushabwa.lya17@gmail.com,test
-CRVS_OFFICE_okQp4uKCz0,Derrick,Bulaya,LOCAL_REGISTRAR,Local Registrar,+260934343434,d.bulaya,kalushabw.alya17@gmail.com,test
-CRVS_OFFICE_okQp4uKCz0,Alex,Ngonga,LOCAL_SYSTEM_ADMIN,Local System Admin,+260978787878,a.ngonga,kalushab.walya17@gmail.com,test
-CRVS_OFFICE_2OKicPQMNI,Edgar,Kazembe,PERFORMANCE_MANAGEMENT,Performance Manager,+260977777777,e.kazembe,kalusha.bwalya17@gmail.com,test
-CRVS_OFFICE_2OKicPQMNI,Joseph,Musonda,NATIONAL_REGISTRAR,National Registrar,+260915151515,j.musonda,kalush.abwalya17@gmail.com,test
+primaryOfficeId,givenNames,familyName,role,mobile,username,email,password
+CRVS_OFFICE_JWMRGwDBXK,Kalusha,Bwalya,SOCIAL_WORKER,+260911111111,k.bwalya,kalushabwalya17@gmail.com,test
+CRVS_OFFICE_JWMRGwDBXK,Felix,Katongo,REGISTRATION_AGENT,+260922222222,f.katongo,kalushabwalya17+@gmail.com,test
+CRVS_OFFICE_JWMRGwDBXK,Kennedy,Mweene,LOCAL_REGISTRAR,+260933333333,k.mweene,kalushabwalya1.7@gmail.com,test
+CRVS_OFFICE_JWMRGwDBXK,Emmanuel,Mayuka,LOCAL_SYSTEM_ADMIN,+260921681112,e.mayuka,kalushabwalya.17@gmail.com,test
+CRVS_OFFICE_2OKicPQMNI,Jonathan,Campbell,NATIONAL_SYSTEM_ADMIN,+260921111111,j.campbell,kalushabwaly.a17@gmail.com,test
+CRVS_OFFICE_okQp4uKCz0,Patrick,Gondwe,LOCAL_LEADER,+260912121212,p.gondwe,kalushabwal.ya17@gmail.com,test
+CRVS_OFFICE_okQp4uKCz0,Joshua,Mutale,REGISTRATION_AGENT,+260923232323,j.mutale,kalushabwa.lya17@gmail.com,test
+CRVS_OFFICE_okQp4uKCz0,Derrick,Bulaya,LOCAL_REGISTRAR,+260934343434,d.bulaya,kalushabw.alya17@gmail.com,test
+CRVS_OFFICE_okQp4uKCz0,Alex,Ngonga,LOCAL_SYSTEM_ADMIN,+260978787878,a.ngonga,kalushab.walya17@gmail.com,test
+CRVS_OFFICE_2OKicPQMNI,Edgar,Kazembe,PERFORMANCE_MANAGER,+260977777777,e.kazembe,kalusha.bwalya17@gmail.com,test
+CRVS_OFFICE_2OKicPQMNI,Joseph,Musonda,NATIONAL_REGISTRAR,+260915151515,j.musonda,kalush.abwalya17@gmail.com,test
diff --git a/src/data-seeding/roles/handler.ts b/src/data-seeding/roles/handler.ts
index 4a611c563..b0de93462 100644
--- a/src/data-seeding/roles/handler.ts
+++ b/src/data-seeding/roles/handler.ts
@@ -8,34 +8,18 @@
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
-import { readCSVToJSON } from '@countryconfig/utils'
+import { PRODUCTION, QA_ENV } from '@countryconfig/constants'
+import { roles } from './roles'
import { Request, ResponseToolkit } from '@hapi/hapi'
-import { RoleSchema, Role } from './validator'
export async function rolesHandler(_: Request, h: ResponseToolkit) {
- const rawRoles: unknown[] = await readCSVToJSON(
- './src/data-seeding/roles/source/roles.csv'
- )
- const roles = RoleSchema.parse(rawRoles)
- .map(({ systemRole, ...rest }) => {
+ if (!PRODUCTION || QA_ENV) {
+ return roles.map((role) => {
return {
- systemRole,
- labels: Object.entries(rest).map(
- ([key, value]: [Exclude, string]) => ({
- lang: key.split('_')[1],
- label: value
- })
- )
+ ...role,
+ scopes: [...role.scopes, 'demo']
}
})
- .reduce<
- Record }>>
- >((acc, role) => {
- if (!acc[role.systemRole]) {
- acc[role.systemRole] = []
- }
- acc[role.systemRole].push({ labels: role.labels })
- return acc
- }, {})
+ }
return h.response(roles)
}
diff --git a/src/data-seeding/roles/roles.ts b/src/data-seeding/roles/roles.ts
new file mode 100644
index 000000000..ed7ee7f15
--- /dev/null
+++ b/src/data-seeding/roles/roles.ts
@@ -0,0 +1,244 @@
+import { SCOPES, Scope } from '@opencrvs/toolkit/scopes'
+import { MessageDescriptor } from 'react-intl'
+
+type Role = {
+ id: string
+ label: MessageDescriptor
+ scopes: Scope[]
+}
+
+export const roles: Role[] = [
+ {
+ id: 'FIELD_AGENT',
+ label: {
+ defaultMessage: 'Field Agent',
+ description: 'Name for user role Field Agent',
+ id: 'userRole.fieldAgent'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_SUBMIT_INCOMPLETE,
+ SCOPES.RECORD_SUBMIT_FOR_REVIEW,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ },
+ {
+ id: 'POLICE_OFFICER',
+ label: {
+ defaultMessage: 'Police Officer',
+ description: 'Name for user role Police Officer',
+ id: 'userRole.policeOfficer'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_SUBMIT_INCOMPLETE,
+ SCOPES.RECORD_SUBMIT_FOR_REVIEW,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ },
+ {
+ id: 'SOCIAL_WORKER',
+ label: {
+ defaultMessage: 'Social Worker',
+ description: 'Name for user role Social Worker',
+ id: 'userRole.socialWorker'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_SUBMIT_INCOMPLETE,
+ SCOPES.RECORD_SUBMIT_FOR_REVIEW,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ },
+ {
+ id: 'HEALTHCARE_WORKER',
+ label: {
+ defaultMessage: 'Healthcare Worker',
+ description: 'Name for user role Healthcare Worker',
+ id: 'userRole.healthcareWorker'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_SUBMIT_INCOMPLETE,
+ SCOPES.RECORD_SUBMIT_FOR_REVIEW,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ },
+ {
+ id: 'LOCAL_LEADER',
+ label: {
+ defaultMessage: 'Local Leader',
+ description: 'Name for user role Local Leader',
+ id: 'userRole.localLeader'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_SUBMIT_INCOMPLETE,
+ SCOPES.RECORD_SUBMIT_FOR_REVIEW,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ },
+ {
+ id: 'REGISTRATION_AGENT',
+ label: {
+ defaultMessage: 'Registration Agent',
+ description: 'Name for user role Registration Agent',
+ id: 'userRole.registrationAgent'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_DECLARATION_EDIT,
+ SCOPES.RECORD_SUBMIT_FOR_APPROVAL,
+ SCOPES.RECORD_SUBMIT_FOR_UPDATES,
+ SCOPES.RECORD_DECLARATION_ARCHIVE,
+ SCOPES.RECORD_DECLARATION_REINSTATE,
+ SCOPES.RECORD_REGISTRATION_REQUEST_CORRECTION,
+ SCOPES.RECORD_PRINT_RECORDS_SUPPORTING_DOCUMENTS,
+ SCOPES.RECORD_EXPORT_RECORDS,
+ SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ },
+ {
+ id: 'LOCAL_REGISTRAR',
+ label: {
+ defaultMessage: 'Local Registrar',
+ description: 'Name for user role Local Registrar',
+ id: 'userRole.localRegistrar'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_DECLARATION_EDIT,
+ SCOPES.RECORD_SUBMIT_FOR_UPDATES,
+ SCOPES.RECORD_REVIEW_DUPLICATES,
+ SCOPES.RECORD_DECLARATION_ARCHIVE,
+ SCOPES.RECORD_DECLARATION_REINSTATE,
+ SCOPES.REGISTER,
+ SCOPES.RECORD_REGISTRATION_CORRECT,
+ SCOPES.RECORD_PRINT_RECORDS_SUPPORTING_DOCUMENTS,
+ SCOPES.RECORD_EXPORT_RECORDS,
+ SCOPES.RECORD_UNASSIGN_OTHERS,
+ SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES,
+ SCOPES.RECORD_CONFIRM_REGISTRATION,
+ SCOPES.RECORD_REJECT_REGISTRATION,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PROFILE_ELECTRONIC_SIGNATURE,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ },
+ {
+ id: 'LOCAL_SYSTEM_ADMIN',
+ label: {
+ defaultMessage: 'Local System Admin',
+ description: 'Name for user role Local System Admin',
+ id: 'userRole.localSystemAdmin'
+ },
+ scopes: [
+ SCOPES.USER_READ_MY_OFFICE,
+ SCOPES.USER_CREATE_MY_JURISDICTION,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_JURISDICTION,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS
+ ]
+ },
+ {
+ id: 'NATIONAL_SYSTEM_ADMIN',
+ label: {
+ defaultMessage: 'National System Admin',
+ description: 'Name for user role National System Admin',
+ id: 'userRole.nationalSystemAdmin'
+ },
+ scopes: [
+ SCOPES.USER_CREATE,
+ SCOPES.USER_READ,
+ SCOPES.USER_UPDATE,
+ SCOPES.ORGANISATION_READ_LOCATIONS,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS,
+ SCOPES.CONFIG_UPDATE_ALL
+ ]
+ },
+ {
+ id: 'PERFORMANCE_MANAGER',
+ label: {
+ defaultMessage: 'Performance Manager',
+ description: 'Name for user role Performance Manager',
+ id: 'userRole.performanceManager'
+ },
+ scopes: [
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS
+ ]
+ },
+ {
+ id: 'NATIONAL_REGISTRAR',
+ label: {
+ defaultMessage: 'National Registrar',
+ description: 'Name for user role National Registrar',
+ id: 'userRole.nationalRegistrar'
+ },
+ scopes: [
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_DECLARATION_EDIT,
+ SCOPES.RECORD_SUBMIT_FOR_UPDATES,
+ SCOPES.RECORD_REVIEW_DUPLICATES,
+ SCOPES.RECORD_DECLARATION_ARCHIVE,
+ SCOPES.RECORD_DECLARATION_REINSTATE,
+ SCOPES.REGISTER,
+ SCOPES.RECORD_REGISTRATION_CORRECT,
+ SCOPES.RECORD_PRINT_RECORDS_SUPPORTING_DOCUMENTS,
+ SCOPES.RECORD_EXPORT_RECORDS,
+ SCOPES.RECORD_UNASSIGN_OTHERS,
+ SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES,
+ SCOPES.RECORD_CONFIRM_REGISTRATION,
+ SCOPES.RECORD_REJECT_REGISTRATION,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS,
+ SCOPES.PROFILE_ELECTRONIC_SIGNATURE,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE,
+ SCOPES.USER_READ_MY_OFFICE,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]
+ }
+]
diff --git a/src/data-seeding/roles/source/roles.csv b/src/data-seeding/roles/source/roles.csv
deleted file mode 100644
index 0606409b2..000000000
--- a/src/data-seeding/roles/source/roles.csv
+++ /dev/null
@@ -1,12 +0,0 @@
-systemRole,label_en,label_fr
-FIELD_AGENT,Field Agent,Agent de terrain
-FIELD_AGENT,Police Officer,Officier de police
-FIELD_AGENT,Social Worker,Travailleur social
-FIELD_AGENT,Healthcare Worker,Personnel de santé
-FIELD_AGENT,Local Leader,Leader local
-REGISTRATION_AGENT,Registration Agent,Agent d'enregistrement
-LOCAL_REGISTRAR,Local Registrar,Registraire local
-LOCAL_SYSTEM_ADMIN,Local System Admin,Administrateur système local
-NATIONAL_SYSTEM_ADMIN,National System Admin,Administrateur système national
-PERFORMANCE_MANAGEMENT,Performance Manager,Gestion des performances
-NATIONAL_REGISTRAR,National Registrar,Registraire national
diff --git a/src/data-seeding/roles/validator.ts b/src/data-seeding/roles/validator.ts
deleted file mode 100644
index 02c2d9c01..000000000
--- a/src/data-seeding/roles/validator.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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 { TypeOf, z } from 'zod'
-
-export type Role = TypeOf[number]
-
-export const RoleSchema = z.array(
- z.object({
- systemRole: z.enum([
- 'FIELD_AGENT',
- 'REGISTRATION_AGENT',
- 'LOCAL_REGISTRAR',
- 'LOCAL_SYSTEM_ADMIN',
- 'NATIONAL_SYSTEM_ADMIN',
- 'PERFORMANCE_MANAGEMENT',
- 'NATIONAL_REGISTRAR'
- ]),
- label_en: z.string(),
- label_fr: z.string()
- })
-)
diff --git a/src/form/tennis-club-membership.ts b/src/form/tennis-club-membership.ts
index ede50e298..67946642f 100644
--- a/src/form/tennis-club-membership.ts
+++ b/src/form/tennis-club-membership.ts
@@ -182,48 +182,70 @@ export const tennisClubMembershipEvent = defineConfig({
},
summary: {
title: {
- defaultMessage: '{applicant.firstname} {applicant.surname}',
- description: 'This is the title of the summary',
- id: 'event.tennis-club-membership.summary.title'
+ id: 'event.tennis-club-membership.summary.title',
+ label: {
+ defaultMessage: '{applicant.firstname} {applicant.surname}',
+ description: 'This is the title of the summary',
+ id: 'event.tennis-club-membership.summary.title'
+ },
+ emptyValueMessage: {
+ defaultMessage: 'Membership application',
+ description:
+ 'This is the message shown when the applicant name is missing',
+ id: 'event.tennis-club-membership.summary.title.empty'
+ }
},
fields: [
{
- id: 'applicant.firstname',
+ id: 'event.tennis-club-membership.summary.applicant.name',
+ label: {
+ defaultMessage: 'Applicant',
+ description: 'This is the label for the field',
+ id: 'event.tennis-club-membership.summary.field.name.label'
+ },
+ value: {
+ defaultMessage: '{applicant.firstname} {applicant.surname}',
+ id: 'event.tennis-club-membership.summary.field.name.label',
+ description: 'This is the value for the field'
+ },
emptyValueMessage: {
- defaultMessage: "Applicant's first name missing",
+ defaultMessage: "Applicant's name is missing",
description:
- "shown when the applicant's first name is missing in summary",
+ "shown when the applicant's names are missing in summary",
id: 'event.tennis-club-membership.summary.field.applicant.firstname.empty'
}
},
{
- id: 'applicant.surname',
- emptyValueMessage: {
- defaultMessage: "Applicant's surname missing",
- description: 'shown when the surname is missing in summary',
- id: 'event.tennis-club-membership.summary.field.applicant.surname.empty'
- }
- },
- {
- id: 'recommender.firstname',
- emptyValueMessage: {
- defaultMessage: "Recommender's first name missing",
- description:
- 'shown when the recommender first name is missing in summary',
- id: 'event.tennis-club-membership.summary.field.recommender.firstname.empty'
- }
- },
- {
- id: 'recommender.surname',
+ id: 'event.tennis-club-membership.summary.recommender.name',
+ label: {
+ defaultMessage: 'Recommender',
+ description: 'This is the label for the field',
+ id: 'event.tennis-club-membership.summary.field.recommender.name.label'
+ },
+ value: {
+ defaultMessage: '{recommender.firstname} {recommender.surname}',
+ id: 'event.tennis-club-membership.summary.field.name.label',
+ description: 'This is the value for the field'
+ },
emptyValueMessage: {
- defaultMessage: "Recommender's surname missing",
+ defaultMessage: "Recommender's name is missing",
description:
- 'shown when the recommender surname is missing in summary',
- id: 'event.tennis-club-membership.summary.field.recommender.surname.empty'
+ "shown when the recommender's names are missing in summary",
+ id: 'event.tennis-club-membership.summary.field.recommender.name.empty'
}
},
{
id: 'recommender.id',
+ label: {
+ defaultMessage: 'Membership ID',
+ description: 'This is the label for the field',
+ id: 'event.tennis-club-membership.summary.field.recommender.id.label'
+ },
+ value: {
+ defaultMessage: '{recommender.id}',
+ id: 'event.tennis-club-membership.summary.field.recommender.id',
+ description: 'This is the value for the field'
+ },
emptyValueMessage: {
defaultMessage: "Recommender's id missing",
description: 'shown when the recommender id is missing in summary',
diff --git a/src/form/v2/birth/index.ts b/src/form/v2/birth/index.ts
index ce0ff48e6..295b35c79 100644
--- a/src/form/v2/birth/index.ts
+++ b/src/form/v2/birth/index.ts
@@ -26,9 +26,12 @@ export const birthEvent = defineConfig({
},
summary: {
title: {
- defaultMessage: '{applicant.firstname} {applicant.surname}',
- description: 'This is the title of the summary',
- id: 'event.birth.summary.title'
+ id: 'event.birth.summary.title',
+ label: {
+ defaultMessage: '{applicant.firstname} {applicant.surname}',
+ description: 'This is the title of the summary',
+ id: 'event.birth.summary.title'
+ }
},
fields: []
},
diff --git a/src/index.ts b/src/index.ts
index 276cadff2..215d0c21b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -511,6 +511,7 @@ export async function createServer() {
path: '/roles',
handler: rolesHandler,
options: {
+ auth: false,
tags: ['api', 'user-roles'],
description: 'Returns user roles metadata'
}
diff --git a/src/translations/client.csv b/src/translations/client.csv
index 2e2747c2f..f9e441816 100644
--- a/src/translations/client.csv
+++ b/src/translations/client.csv
@@ -199,7 +199,6 @@ config.emailAllUsers.modal.title,Label for send email all users confirmation tit
config.emailAllUsers.subtitle,Subtitle for email all users,This email will be sent to all users you are active. Emails will be sent over the next 24 hours. Only one email can be sent per day,Cet e-mail sera envoyé à tous les utilisateurs que vous activez. Les courriels seront envoyés au cours des prochaines 24 heures. Un seul courriel peut être envoyé par jour
config.emailAllUsers.title,Title for email all users,Email all users,Envoyer un e-mail à tous les utilisateurs
config.userRoles.language,Language name,"{language, select, en {English} fr {French} other {{language}}}","{language, select, en {Anglais} fr {Français} other {{language}}}"
-config.userRoles.roleUpdateInstruction,Instruction for adding/updating role in role management modal,Add the roles to be assigned the system role of {systemRole},Ajoutez les rôles auxquels attribuer le rôle système de {systemRole}
conflicts.modal.assign.description,Description for modal when assign,Please note you will have sole access to this record. Please make any updates promptly otherwise unassign the record.,"Veuillez noter que vous aurez un accès exclusif à cet enregistrement. Veuillez effectuer rapidement les mises à jour éventuelles, sinon vous désassignez l'enregistrement."
conflicts.modal.assign.title,Title for modal when assign,Assign record?,Attribuer un enregistrement ?
conflicts.modal.assigned.description,Description for modal when record already assigned,{name} at {officeName} has sole editable access to this record,{name} à {officeName} a un accès unique et modifiable à cet enregistrement.
@@ -1386,6 +1385,7 @@ integrations.shaSecret,Label for SHA secret,SHA secret,SHA Secret
integrations.supportingDescription,,Supporting description to help user make a decision and navigate the content,Description complémentaire pour aider l'utilisateur à prendre une décision et à naviguer dans le contenu.
integrations.type.eventNotification,Label for event notification,Event notification,Notification d'événement
integrations.type.healthSystem,Label for health system type,Health integration,Intégration de la santé
+integrations.type.nationalId,Label for national id,National id,Identité nationale
integrations.type.recordSearch,Label for record search,Record search,Recherche d'enregistrements
integrations.type.webhook,Label for web hook,Webhook,Webhook
integrations.uniqueKeyDescription,Label for the unique key description,These unique keys will be required by the client integrating...,Ces clés uniques seront requises par l'interface du système
@@ -1686,6 +1686,7 @@ navigation.emailAllUsers,Email all users label in navigation,Email all users,Env
navigation.informantNotification,Informant notifications label in navigation,Informant notifications,Notifications des informateurs
navigation.integration,Integration forms label in navigation,Integrations,Intégrations
navigation.leaderboards,Leaderboards Dashboard Section,Leaderboards,Classements
+navigation.my-drafts,My drafts label in navigation,My drafts,Mes projets
navigation.organisation,Organisations label in navigation,Organisation,Organisation
navigation.outbox,Label for navigation item outbox,Outbox,Boîte d'envoi
navigation.performance,Performance label in navigation,Performance,Performance
@@ -2271,6 +2272,17 @@ user.profile.roleType,Title for roleType field,Role,Rôle/Type
user.profile.sectionTitle.audit,Title for audit section,History,Historique
user.profile.startDate,Title for startDate field,Start date,Date de début
user.profile.userName,Title for userName field,Username,Nom d'utilisateur
+userRole.fieldAgent,Name for user role Field Agent,Field Agent,Agent de terrain
+userRole.healthcareWorker,Name for user role Healthcare Worker,Healthcare Worker,Personnel de santé
+userRole.localLeader,Name for user role Local Leader,Local Leader,Leader local
+userRole.localRegistrar,Name for user role Local Registrar,Local Registrar,Registraire local
+userRole.localSystemAdmin,Name for user role Local System Admin,Local System Admin,Administrateur système local
+userRole.nationalRegistrar,Name for user role National Registrar,National Registrar,Registraire national
+userRole.nationalSystemAdmin,Name for user role National System Admin,National System Admin,Administrateur système national
+userRole.performanceManager,Name for user role Performance Manager,Performance Manager,Gestion des performances
+userRole.policeOfficer,Name for user role Police Officer,Police Officer,Officier de police
+userRole.registrationAgent,Name for user role Registration Agent,Registration Agent,Agent d'enregistrement
+userRole.socialWorker,Name for user role Social Worker,Social Worker,Travailleur social
userSetup.complete.instruction,Instruction for the setup complete,Login to your account with your username and newly created password,Connectez-vous maintenant à votre compte avec votre nom d'utilisateur et votre mot de passe nouvellement créé.
userSetup.complete.title,Title for the setup complete page,Account setup complete,Configuration du compte terminée
userSetup.instruction,User setup review page instruction,Check the details below to confirm your account details are correct,Vérifiez les détails ci-dessous pour confirmer que les détails de votre compte sont corrects. et faites les changements nécessaires pour confirmer que les détails de votre compte sont corrects.
diff --git a/src/upgrade-to-1_7.ts b/src/upgrade-to-1_7.ts
new file mode 100644
index 000000000..0e780488e
--- /dev/null
+++ b/src/upgrade-to-1_7.ts
@@ -0,0 +1,367 @@
+/*
+ * 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 { createReadStream, rmdirSync, writeFileSync } from 'fs'
+import { camelCase, snakeCase } from 'lodash'
+import { join } from 'path'
+import { logger } from './logger'
+import { stringify } from 'csv-stringify/sync'
+import fs from 'fs'
+import csv2json from 'csv2json'
+import { inspect } from 'util'
+import { format, resolveConfig } from 'prettier'
+
+/*
+ * inlining these two functions to not
+ * trigger envalid when running the script
+ */
+async function writeJSONToCSV(
+ filename: string,
+ data: Array>
+) {
+ const csv = stringify(data, {
+ header: true
+ })
+ return fs.promises.writeFile(filename, csv, 'utf8')
+}
+
+async function readCSVToJSON(filename: string) {
+ return new Promise((resolve, reject) => {
+ const chunks: string[] = []
+ createReadStream(filename)
+ .on('error', reject)
+ .pipe(
+ csv2json({
+ separator: ','
+ })
+ )
+ .on('data', (chunk) => chunks.push(chunk))
+ .on('error', reject)
+ .on('end', () => {
+ resolve(JSON.parse(chunks.join('')))
+ })
+ })
+}
+
+const DEFAULT_ROLE_SCOPES_PRE_1_7 = {
+ FIELD_AGENT: `[
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_SUBMIT_INCOMPLETE,
+ SCOPES.RECORD_SUBMIT_FOR_REVIEW,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]`,
+ REGISTRATION_AGENT: `[
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_DECLARATION_EDIT,
+ SCOPES.RECORD_SUBMIT_FOR_APPROVAL,
+ SCOPES.RECORD_SUBMIT_FOR_UPDATES,
+ SCOPES.RECORD_DECLARATION_ARCHIVE,
+ SCOPES.RECORD_DECLARATION_REINSTATE,
+ SCOPES.RECORD_REGISTRATION_REQUEST_CORRECTION,
+ SCOPES.RECORD_PRINT_RECORDS_SUPPORTING_DOCUMENTS,
+ SCOPES.RECORD_EXPORT_RECORDS,
+ SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]`,
+ LOCAL_REGISTRAR: `[
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_DECLARATION_EDIT,
+ SCOPES.RECORD_SUBMIT_FOR_UPDATES,
+ SCOPES.RECORD_REVIEW_DUPLICATES,
+ SCOPES.RECORD_DECLARATION_ARCHIVE,
+ SCOPES.RECORD_DECLARATION_REINSTATE,
+ SCOPES.REGISTER,
+ SCOPES.RECORD_REGISTRATION_CORRECT,
+ SCOPES.RECORD_PRINT_RECORDS_SUPPORTING_DOCUMENTS,
+ SCOPES.RECORD_EXPORT_RECORDS,
+ SCOPES.RECORD_UNASSIGN_OTHERS,
+ SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES,
+ SCOPES.RECORD_CONFIRM_REGISTRATION,
+ SCOPES.RECORD_REJECT_REGISTRATION,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PROFILE_ELECTRONIC_SIGNATURE,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]`,
+ LOCAL_SYSTEM_ADMIN: `[
+ SCOPES.USER_READ_MY_OFFICE,
+ SCOPES.USER_CREATE_MY_JURISDICTION,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_JURISDICTION,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS
+ ]`,
+ NATIONAL_SYSTEM_ADMIN: `[
+ SCOPES.USER_CREATE,
+ SCOPES.USER_READ,
+ SCOPES.USER_UPDATE,
+ SCOPES.ORGANISATION_READ_LOCATIONS,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS,
+ SCOPES.CONFIG_UPDATE_ALL
+ ]`,
+ PERFORMANCE_MANAGEMENT: `[
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS
+ ]`,
+ NATIONAL_REGISTRAR: `[
+ SCOPES.RECORD_DECLARE_BIRTH,
+ SCOPES.RECORD_DECLARE_DEATH,
+ SCOPES.RECORD_DECLARE_MARRIAGE,
+ SCOPES.RECORD_DECLARATION_EDIT,
+ SCOPES.RECORD_SUBMIT_FOR_UPDATES,
+ SCOPES.RECORD_REVIEW_DUPLICATES,
+ SCOPES.RECORD_DECLARATION_ARCHIVE,
+ SCOPES.RECORD_DECLARATION_REINSTATE,
+ SCOPES.REGISTER,
+ SCOPES.RECORD_REGISTRATION_CORRECT,
+ SCOPES.RECORD_PRINT_RECORDS_SUPPORTING_DOCUMENTS,
+ SCOPES.RECORD_EXPORT_RECORDS,
+ SCOPES.RECORD_UNASSIGN_OTHERS,
+ SCOPES.RECORD_PRINT_ISSUE_CERTIFIED_COPIES,
+ SCOPES.RECORD_CONFIRM_REGISTRATION,
+ SCOPES.RECORD_REJECT_REGISTRATION,
+ SCOPES.PERFORMANCE_READ,
+ SCOPES.PERFORMANCE_READ_DASHBOARDS,
+ SCOPES.PERFORMANCE_EXPORT_VITAL_STATISTICS,
+ SCOPES.PROFILE_ELECTRONIC_SIGNATURE,
+ SCOPES.ORGANISATION_READ_LOCATIONS_MY_OFFICE,
+ SCOPES.USER_READ_MY_OFFICE,
+ SCOPES.SEARCH_BIRTH,
+ SCOPES.SEARCH_DEATH,
+ SCOPES.SEARCH_MARRIAGE
+ ]`
+} as const
+
+const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom')
+
+class FormattedScopes {
+ scopes: string
+ constructor(scopes: string) {
+ this.scopes = scopes
+ }
+ [customInspectSymbol]() {
+ return this.scopes
+ }
+}
+
+async function main() {
+ await upgradeRolesDefinitions()
+}
+
+async function upgradeRolesDefinitions() {
+ const roles = await readCSVToJSON(
+ join(__dirname, './src/data-seeding/roles/source/roles.csv')
+ ).catch((err) => {
+ if (err.code === 'ENOENT') {
+ logger.warn(
+ 'data-seeding/roles/source/roles.csv does not exist in the codebase. Looks like this codebase has been upgraded already.'
+ )
+ }
+
+ process.exit(1)
+ })
+
+ const rolesWithGeneratedIds = roles.map((role) => ({
+ id: snakeCase(role.label_en).toUpperCase(),
+ label: {
+ defaultMessage: role.label_en,
+ description: `Name for user role ${role.label_en}`,
+ id: `userRole.${camelCase(role.label_en)}`
+ },
+ oldLabels: Object.fromEntries(
+ Object.keys(role)
+ .filter((key) => key.startsWith('label_'))
+ .map((key) => [key.split('_')[1], role[key]])
+ ),
+ systemRole: role.systemRole,
+ scopes:
+ DEFAULT_ROLE_SCOPES_PRE_1_7[
+ role.systemRole as keyof typeof DEFAULT_ROLE_SCOPES_PRE_1_7
+ ]
+ }))
+
+ /*
+ * Add language items to src/translations/client.csv
+ */
+ const copy = await readCSVToJSON(
+ join(__dirname, './src/translations/client.csv')
+ )
+
+ rolesWithGeneratedIds.forEach((role) => {
+ if (copy.find((item) => item.id === role.label.id)) {
+ logger.warn(
+ `Skipping role ${role.id} as it already exists in the client.csv`
+ )
+ return
+ }
+ const copyItem = {
+ id: role.label.id,
+ description: role.label.description,
+ ...role.oldLabels
+ }
+ logger.info(
+ `Adding language items for role ${role.id}: ${JSON.stringify(copyItem)}`
+ )
+ copy.push(copyItem)
+ })
+
+ /*
+ * Fix role references in default-employees.csv & prod-employees.csv from "National System Admin" to "NATIONAL_SYSTEM_ADMIN"
+ */
+
+ const defaultEmployees = await readCSVToJSON(
+ join(__dirname, './src/data-seeding/employees/source/default-employees.csv')
+ )
+
+ const defaultEmployeesWithRoles = defaultEmployees.map(
+ ({ systemRole, ...employee }) => {
+ if (!systemRole) {
+ logger.warn(
+ `Skipping employee "${employee.givenNames} ${employee.familyName}" as it already seems to have been migrated`
+ )
+ return employee
+ }
+ const role = rolesWithGeneratedIds.find(
+ (role) => role.oldLabels.en === employee.role
+ )
+ if (!role) {
+ logger.error(`Role with id ${employee.role} not found in roles.csv`)
+ process.exit(1)
+ }
+ return {
+ ...employee,
+ role: role.id
+ }
+ }
+ )
+
+ let prodEmployeesWithRoles = null
+
+ try {
+ const prodEmployees = await readCSVToJSON(
+ join(__dirname, './src/data-seeding/employees/source/prod-employees.csv')
+ )
+
+ prodEmployeesWithRoles = prodEmployees.map(
+ ({ systemRole, ...employee }) => {
+ if (!systemRole) {
+ logger.warn(
+ `Skipping employee "${employee.givenNames} ${employee.familyName}" as it already seems to have been migrated`
+ )
+ return employee
+ }
+ const role = rolesWithGeneratedIds.find(
+ (role) => role.oldLabels.en === employee.role
+ )
+ if (!role) {
+ logger.error(`Role with id ${employee.role} not found in roles.csv`)
+ process.exit(1)
+ }
+ return {
+ ...employee,
+ role: role.id
+ }
+ }
+ )
+ } catch (err) {
+ if (err.code === 'ENOENT') {
+ logger.warn(
+ './src/data-seeding/employees/source/prod-employees.csv does not exist in the codebase. Skipping'
+ )
+ }
+ }
+
+ /*
+ * Create the new "roles.ts" file with the updated roles and new format
+ */
+
+ const rolesWithoutDeprecatedFields = rolesWithGeneratedIds.map((role) => {
+ const { oldLabels, systemRole, ...rest } = role
+ return rest
+ })
+
+ const formattedRoles = rolesWithoutDeprecatedFields.map((role) => {
+ return {
+ ...role,
+ scopes: new FormattedScopes(role.scopes)
+ }
+ })
+
+ /*
+ * Persist changes
+ */
+ logger.info('Creating roles file')
+ writeFileSync(
+ join(__dirname, './src/data-seeding/roles/roles.ts'),
+ await format(
+ `
+ import { SCOPES, Scope } from '@opencrvs/toolkit/scopes'
+ import { MessageDescriptor } from 'react-intl'
+
+ type Role = {
+ id: string
+ label: MessageDescriptor
+ scopes: Scope[]
+ }
+
+ export const roles: Role[] = ${inspect(formattedRoles, { depth: null })}
+ `,
+ {
+ ...(await resolveConfig(__dirname)),
+ parser: 'babel-ts'
+ }
+ )
+ )
+
+ logger.info('Updating copy file')
+ await writeJSONToCSV(join(__dirname, './src/translations/client.csv'), copy)
+
+ logger.info('Updating default employees file')
+ await writeJSONToCSV(
+ join(
+ __dirname,
+ './src/data-seeding/employees/source/default-employees.csv'
+ ),
+ defaultEmployeesWithRoles
+ )
+ if (prodEmployeesWithRoles) {
+ logger.info('Updating prod employees file')
+ await writeJSONToCSV(
+ join(__dirname, './src/data-seeding/employees/source/prod-employees.csv'),
+ prodEmployeesWithRoles
+ )
+ }
+
+ logger.info('Removing old roles file')
+ rmdirSync(join(__dirname, './src/data-seeding/roles/source'), {
+ recursive: true
+ })
+}
+
+main()
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 609768ad4..84be60935 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -24,8 +24,7 @@ export const CHILD_CODE = 'child-details'
export const DECEASED_CODE = 'deceased-details'
export const OPENCRVS_SPECIFICATION_URL = 'http://opencrvs.org/specs/'
import { join } from 'path'
-import { promisify } from 'util'
-import { stringify, Options } from 'csv-stringify'
+import { stringify } from 'csv-stringify/sync'
export interface ILocation {
id?: string
@@ -181,12 +180,11 @@ export const convertToMSISDN = (phone: string, countryAlpha3: string) => {
)
}
-const csvStringify = promisify>, Options>(stringify)
export async function writeJSONToCSV(
filename: string,
data: Array>
) {
- const csv = await csvStringify(data, {
+ const csv = stringify(data, {
header: true
})
return fs.promises.writeFile(filename, csv, 'utf8')
@@ -252,9 +250,10 @@ export async function getStatistics(path?: string) {
if (!path) {
path = join(__dirname, '../data-seeding/locations/source/statistics.csv')
}
- const data = await readCSVToJSON<
- Array & { adminPcode: string }>
- >(path)
+ const data =
+ await readCSVToJSON & { adminPcode: string }>>(
+ path
+ )
return data.map((item) => {
const { adminPcode, name, ...yearKeys } = item
diff --git a/tsconfig.json b/tsconfig.json
index beba0ad94..35c676e53 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,6 +8,7 @@
"module": "Node16",
"outDir": "build/dist",
"sourceMap": true,
+ "resolveJsonModule": true,
"moduleResolution": "node16",
"rootDir": ".",
"lib": ["esnext.asynciterable", "es6", "es2017", "es2019", "es2022", "dom"],
diff --git a/yarn.lock b/yarn.lock
index 393ae4452..18ef96558 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1712,12 +1712,15 @@
dependencies:
"@octokit/openapi-types" "^18.0.0"
-"@opencrvs/toolkit@0.0.14-events":
- version "0.0.14-events"
- resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-0.0.14-events.tgz#f99b2dc14c30ec925f373e19eea5ea731abb79b9"
- integrity sha512-ZsSmmgB9anSVp2hxIquU/wfHVP8s//RydEVERmn7Dpr3S/6bVRsLS27P02lioJmCBMbEf8cNgstyMlgaE9XpAg==
+"@opencrvs/toolkit@0.0.22-scopes":
+ version "0.0.22-scopes"
+ resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-0.0.22-scopes.tgz#282e577cb6c12226472f113d8753c7fbe1b150e7"
+ integrity sha512-VLJSgOuoR10SJPVY9EumbeMqQyuRJRNhMoUEA7O4CafGnj3PsYbawNF44qb5VLe3eJ4uEwAPnZXVBU7BGWRz9w==
dependencies:
+ "@trpc/client" "^11.0.0-rc.648"
+ "@trpc/server" "^11.0.0-rc.532"
ajv "^8.17.1"
+ superjson "1.9.0-0"
"@parcel/watcher-android-arm64@2.5.0":
version "2.5.0"
@@ -1835,6 +1838,11 @@
tslib "^2.6.2"
webcrypto-core "^1.8.0"
+"@pkgr/core@^0.1.0":
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
+ integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
+
"@playwright/test@^1.48.0":
version "1.48.0"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.48.0.tgz#4b81434a3ca75e2a6f82a645287784223a45434c"
@@ -2012,6 +2020,16 @@
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
+"@trpc/client@^11.0.0-rc.648":
+ version "11.0.0-rc.711"
+ resolved "https://registry.yarnpkg.com/@trpc/client/-/client-11.0.0-rc.711.tgz#6d07b6fa8221fe0aa886495a507ca34575a32a25"
+ integrity sha512-m3uFfgEW+hbxA2IS4q238fTfT4hJFwz6Y4FQUfrgj3395W1afaOoUbF3S970jG9Gf/VTlIfTG8pQ1sTrZ7Uwug==
+
+"@trpc/server@^11.0.0-rc.532":
+ version "11.0.0-rc.711"
+ resolved "https://registry.yarnpkg.com/@trpc/server/-/server-11.0.0-rc.711.tgz#a9a0db9eea2bdc0fa705b1459f03325e54202add"
+ integrity sha512-dGaMXLFJaLg1XWGRQ3ZQp2dkq7Od6u+9DOrp+fn0s88sZW57eEhX2MOxWpFp5z9xLZNN0Hq5G6/4QtR0iMUVcw==
+
"@tsconfig/node10@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
@@ -2252,10 +2270,12 @@
dependencies:
undici-types "~5.26.4"
-"@types/node@^10.12.5":
- version "10.17.60"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
- integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
+"@types/node@^18.19.1":
+ version "18.19.68"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.68.tgz#f4f10d9927a7eaf3568c46a6d739cc0967ccb701"
+ integrity sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==
+ dependencies:
+ undici-types "~5.26.4"
"@types/node@^20.9.0":
version "20.11.5"
@@ -3508,7 +3528,7 @@ debounce@^1.2.0:
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
-debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+debug@4, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -3536,6 +3556,13 @@ debug@^4.1.0, debug@^4.3.6:
dependencies:
ms "^2.1.3"
+debug@^4.3.1:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
+ integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
+ dependencies:
+ ms "^2.1.3"
+
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -3827,17 +3854,18 @@ escape-string-regexp@^5.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
-eslint-config-prettier@^8.8.0:
- version "8.10.0"
- resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11"
- integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==
+eslint-config-prettier@^9.1.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f"
+ integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==
-eslint-plugin-prettier@^4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
- integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==
+eslint-plugin-prettier@^5.2.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz#d1c8f972d8f60e414c25465c163d16f209411f95"
+ integrity sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==
dependencies:
prettier-linter-helpers "^1.0.0"
+ synckit "^0.9.1"
eslint-scope@^5.1.1:
version "5.1.1"
@@ -5472,6 +5500,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash.clonedeep@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+ integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
@@ -6401,10 +6434,10 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
-prettier@^2.8.8:
- version "2.8.8"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
- integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
+prettier@^3.4.2:
+ version "3.4.2"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f"
+ integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==
pretty-format@^23.6.0:
version "23.6.0"
@@ -7347,6 +7380,14 @@ strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+superjson@1.9.0-0:
+ version "1.9.0-0"
+ resolved "https://registry.yarnpkg.com/superjson/-/superjson-1.9.0-0.tgz#4de316ddf80a4693ec7e112882221452cde5ff3f"
+ integrity sha512-JZHRiW+yFgzRSqf+iwKtycGqfpLnt9ct6Oa0D6/yqDt0rD+hQZMeMdk648M+oshlyv4VLpW82ZkiMdFlairdHA==
+ dependencies:
+ debug "^4.3.1"
+ lodash.clonedeep "^4.5.0"
+
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -7383,6 +7424,14 @@ symbol-observable@^1.1.0:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+synckit@^0.9.1:
+ version "0.9.2"
+ resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62"
+ integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==
+ dependencies:
+ "@pkgr/core" "^0.1.0"
+ tslib "^2.6.2"
+
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"