From 2c73f5b5f95ded97a4c74c11a1c591824733bf55 Mon Sep 17 00:00:00 2001 From: euanmillar Date: Thu, 9 May 2024 16:50:02 +0100 Subject: [PATCH 001/146] Remove < Date: Mon, 13 May 2024 09:56:21 +0300 Subject: [PATCH 002/146] Code freeze 1.5.0 release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddf246efc..165b5bc89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [1.5.0] + ## [1.3.4](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.3.3...v1.3.4) ## Breaking changes From c3f14f510a7ed3b4a8831e3f9b2671c5b264a369 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Fri, 3 May 2024 17:41:37 +0600 Subject: [PATCH 003/146] feat: generate default address according to user's location (#978) * feat: add GATEWAY_URL env variable * feat: generate default address from user location * chore: remove initialValue from dynamicOptions --- infrastructure/docker-compose.deploy.yml | 1 + package.json | 4 +- src/constants.ts | 1 + src/form/addresses/address-fields.ts | 90 ++++++++++++++++------ src/form/index.ts | 21 +++-- src/form/types/types.ts | 1 - src/utils/address-utils.ts | 20 +++-- src/utils/users.ts | 97 ++++++++++++++++++++++++ yarn.lock | 7 +- 9 files changed, 207 insertions(+), 35 deletions(-) create mode 100644 src/utils/users.ts diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index aaf5ba3bb..b8fdc06ea 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -581,6 +581,7 @@ services: - APN_SERVICE_URL=http://apm-server:8200 - COUNTRY_CONFIG_URL=https://countryconfig.{{hostname}} - LOGIN_URL=https://login.{{hostname}} + - GATEWAY_URL=https://gateway.{{hostname}} - CLIENT_APP_URL=https://register.{{hostname}} - NOTIFICATION_TRANSPORT=${NOTIFICATION_TRANSPORT} - ALERT_EMAIL=${ALERT_EMAIL:-""} diff --git a/package.json b/package.json index 80c3e5063..363a7f8b4 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,8 @@ "dotenv": "^6.1.0", "esbuild": "^0.18.9", "google-libphonenumber": "^3.2.32", + "graphql": "^16.3.0", + "graphql-tag": "^2.12.6", "handlebars": "^4.7.7", "hapi-auth-jwt2": "10.4.0", "hapi-pino": "^6.3.0", @@ -124,4 +126,4 @@ "minimist": "^1.2.2", "acorn": "^6.4.1" } -} \ No newline at end of file +} diff --git a/src/constants.ts b/src/constants.ts index 0ff671d46..795d1cc3e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,6 +10,7 @@ */ export const TEST_SOURCE = `${process.cwd()}/src/tests/` export const DOMAIN = process.env.DOMAIN || '*' +export const GATEWAY_URL = process.env.GATEWAY_URL || 'http://localhost:7070' export const LOGIN_URL = process.env.LOGIN_URL || 'http://localhost:3020/' export const CLIENT_APP_URL = process.env.CLIENT_APP_URL || 'http://localhost:3000/' diff --git a/src/form/addresses/address-fields.ts b/src/form/addresses/address-fields.ts index 8db00a146..b53dff6b6 100644 --- a/src/form/addresses/address-fields.ts +++ b/src/form/addresses/address-fields.ts @@ -74,11 +74,13 @@ export function getAddressLocationSelect({ location, useCase, fhirLineArrayPosition, - isLowestAdministrativeLevel + isLowestAdministrativeLevel, + initialValue }: { section: string location: string useCase: string + initialValue: string /** Position where the location gets mapped into within a fhir.Address line-array */ fhirLineArrayPosition?: number /** If the structure the smallest possible level. Allows saving fhir.Address.partOf */ @@ -99,7 +101,7 @@ export function getAddressLocationSelect({ ? useCase : `${useCase}Address`, required: true, - initialValue: '', + initialValue, validator: [], placeholder: { defaultMessage: 'Select', @@ -108,8 +110,7 @@ export function getAddressLocationSelect({ }, dynamicOptions: { resource: 'locations', - dependency: getDependency(location, useCase, section), - initialValue: 'agentDefault' + dependency: getDependency(location, useCase, section) }, conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals( @@ -134,7 +135,8 @@ export function getAddressLocationSelect({ // We recommend that you do not edit this function function getAdminLevelSelects( section: string, - useCase: string + useCase: string, + addressHierarchy: string[] ): SerializedFormField[] { switch (ADMIN_LEVELS) { case 1: @@ -143,71 +145,114 @@ function getAdminLevelSelects( section, location: 'state', useCase, - isLowestAdministrativeLevel: true + isLowestAdministrativeLevel: true, + initialValue: addressHierarchy[0] }) ] case 2: return [ - getAddressLocationSelect({ section, location: 'state', useCase }), + getAddressLocationSelect({ + section, + location: 'state', + useCase, + initialValue: addressHierarchy[0] + }), getAddressLocationSelect({ section, location: 'district', useCase, - isLowestAdministrativeLevel: true + isLowestAdministrativeLevel: true, + initialValue: addressHierarchy[1] }) ] case 3: return [ - getAddressLocationSelect({ section, location: 'state', useCase }), - getAddressLocationSelect({ section, location: 'district', useCase }), + getAddressLocationSelect({ + section, + location: 'state', + useCase, + initialValue: addressHierarchy[0] + }), + getAddressLocationSelect({ + section, + location: 'district', + useCase, + initialValue: addressHierarchy[1] + }), getAddressLocationSelect({ section, location: 'locationLevel3', useCase, fhirLineArrayPosition: 10, - isLowestAdministrativeLevel: true + isLowestAdministrativeLevel: true, + initialValue: addressHierarchy[2] }) ] case 4: return [ - getAddressLocationSelect({ section, location: 'state', useCase }), - getAddressLocationSelect({ section, location: 'district', useCase }), + getAddressLocationSelect({ + section, + location: 'state', + useCase, + initialValue: addressHierarchy[0] + }), + getAddressLocationSelect({ + section, + location: 'district', + useCase, + initialValue: addressHierarchy[1] + }), getAddressLocationSelect({ section, location: 'locationLevel3', useCase, - fhirLineArrayPosition: 10 + fhirLineArrayPosition: 10, + initialValue: addressHierarchy[2] }), getAddressLocationSelect({ section, location: 'locationLevel4', useCase, fhirLineArrayPosition: 11, - isLowestAdministrativeLevel: true + isLowestAdministrativeLevel: true, + initialValue: addressHierarchy[3] }) ] case 5: return [ - getAddressLocationSelect({ section, location: 'state', useCase }), - getAddressLocationSelect({ section, location: 'district', useCase }), + getAddressLocationSelect({ + section, + location: 'state', + useCase, + initialValue: addressHierarchy[0] + }), + getAddressLocationSelect({ + section, + location: 'district', + useCase, + initialValue: addressHierarchy[1] + }), getAddressLocationSelect({ section, location: 'locationLevel3', useCase, - fhirLineArrayPosition: 10 + fhirLineArrayPosition: 10, + initialValue: addressHierarchy[2] }), getAddressLocationSelect({ section, location: 'locationLevel4', useCase, - fhirLineArrayPosition: 11 + fhirLineArrayPosition: 11, + initialValue: addressHierarchy[3] }), getAddressLocationSelect({ section, location: 'locationLevel5', useCase, fhirLineArrayPosition: 12, - isLowestAdministrativeLevel: true + isLowestAdministrativeLevel: true, + initialValue: addressHierarchy[4] }) ] } @@ -241,7 +286,8 @@ function getPlaceOfEventFields(useCase: EventLocationAddressCases) { // ==================================== END WARNING ==================================== export function getAddressFields( section: string, - addressCase: EventLocationAddressCases | AddressCases + addressCase: EventLocationAddressCases | AddressCases, + addressHierarchy: string[] ): SerializedFormField[] { let useCase = addressCase as string let placeOfEventFields: SerializedFormField[] = [] @@ -289,7 +335,7 @@ export function getAddressFields( }) }, // Required // Select fields are added for each administrative location level from Humdata - ...getAdminLevelSelects(section, useCase), // Required + ...getAdminLevelSelects(section, useCase, addressHierarchy), // Required { name: `city${sentenceCase(useCase)}${sentenceCase(section)}`, type: 'TEXT', diff --git a/src/form/index.ts b/src/form/index.ts index 1dc89b8ab..4fc80de45 100644 --- a/src/form/index.ts +++ b/src/form/index.ts @@ -8,14 +8,21 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ - +import { Request } from '@hapi/hapi' import { decorateFormsWithAddresses } from '../utils/address-utils' import { birthForm } from './birth' import { deathForm } from './death' import { marriageForm } from './marriage' import { IForms, Event } from './types/types' +import { getUserOfficeLocationHierarchy } from '@countryconfig/utils/users' -export async function formHandler(): Promise { +export async function formHandler(req: Request): Promise { + const addressHierarchy = ( + await getUserOfficeLocationHierarchy( + req.headers.authorization, + req.auth.credentials.sub as string + ) + ).map(({ id }) => id) // ====================== NOTE REGARDING MIGRATING FROM OPNCRVS v1.2 OR EARLIER ====================== // SIMPLY RETURN A JSON OF YOUR FULL FORM HERE, WITH THE ADDITION OF THE NEW MARRIAGE AND VERSION PROP @@ -51,8 +58,12 @@ export async function formHandler(): Promise { // THIS DECORATOR FUNCTION POPULATES ADDRESSES ACCORDING TO THE defaultAddressConfiguration in address-settings.ts // SO YOU ONLY NEED TO CONFIGURE ADDRESS FIELDS IN A SINGLE LOCATION FOR ALL DECORATED INSTANCES. - birth: decorateFormsWithAddresses(birthForm, Event.Birth), - death: decorateFormsWithAddresses(deathForm, Event.Death), - marriage: decorateFormsWithAddresses(marriageForm, Event.Marriage) + birth: decorateFormsWithAddresses(birthForm, Event.Birth, addressHierarchy), + death: decorateFormsWithAddresses(deathForm, Event.Death, addressHierarchy), + marriage: decorateFormsWithAddresses( + marriageForm, + Event.Marriage, + addressHierarchy + ) } } diff --git a/src/form/types/types.ts b/src/form/types/types.ts index 1cce9b9e6..657626242 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -528,7 +528,6 @@ export interface IDynamicOptions { jurisdictionType?: string resource?: string options?: { [key: string]: ISelectOption[] } - initialValue?: string } export type IFormFieldTemplateMapOperation = diff --git a/src/utils/address-utils.ts b/src/utils/address-utils.ts index fcfa198af..0272c9ee6 100644 --- a/src/utils/address-utils.ts +++ b/src/utils/address-utils.ts @@ -984,23 +984,26 @@ export const getAddressSubsection = ( // You should never need to edit this function. If there is a bug here raise an issue in [Github](https://github.com/opencrvs/opencrvs-farajaland) function getAddressFieldsByConfiguration( configuration: AllowedAddressConfigurations, - section: string + section: string, + addressHierarchy: string[] ): SerializedFormField[] { switch (configuration.config) { case EventLocationAddressCases.PLACE_OF_BIRTH: case EventLocationAddressCases.PLACE_OF_DEATH: case EventLocationAddressCases.PLACE_OF_MARRIAGE: - return getAddressFields('', configuration.config) + return getAddressFields('', configuration.config, addressHierarchy) case AddressCases.PRIMARY_ADDRESS: return getAddress( section, AddressCases.PRIMARY_ADDRESS, + addressHierarchy, configuration.conditionalCase ) case AddressCases.SECONDARY_ADDRESS: return getAddress( section, AddressCases.SECONDARY_ADDRESS, + addressHierarchy, configuration.conditionalCase ) case AddressCopyConfigCases.PRIMARY_ADDRESS_SAME_AS_OTHER_PRIMARY: @@ -1039,7 +1042,8 @@ function getAddressFieldsByConfiguration( // You should never need to edit this function. If there is a bug here raise an issue in [Github](https://github.com/opencrvs/opencrvs-farajaland) export function decorateFormsWithAddresses( defaultEventForm: ISerializedForm, - event: string + event: string, + addressHierarchy: string[] ): ISerializedForm { const newForm = cloneDeep(defaultEventForm) defaultAddressConfiguration.forEach( @@ -1052,7 +1056,11 @@ export function decorateFormsWithAddresses( let previewGroups: IPreviewGroup[] = [] configurations.forEach((configuration) => { addressFields = addressFields.concat( - getAddressFieldsByConfiguration(configuration, sectionId) + getAddressFieldsByConfiguration( + configuration, + sectionId, + addressHierarchy + ) ) previewGroups = previewGroups.concat(getPreviewGroups(configuration)) }) @@ -1079,11 +1087,13 @@ export function decorateFormsWithAddresses( function getAddress( section: string, addressCase: AddressCases, + addressHierarchy: string[], conditionalCase?: string ): SerializedFormField[] { const defaultFields: SerializedFormField[] = getAddressFields( section, - addressCase + addressCase, + addressHierarchy ) if (conditionalCase) { defaultFields.forEach((field) => { diff --git a/src/utils/users.ts b/src/utils/users.ts new file mode 100644 index 000000000..c1ecea7a1 --- /dev/null +++ b/src/utils/users.ts @@ -0,0 +1,97 @@ +import { APPLICATION_CONFIG_URL, GATEWAY_URL } from '@countryconfig/constants' +import fetch from 'node-fetch' +import gql from 'graphql-tag' +import { print } from 'graphql/language/printer' +import { URL } from 'url' + +type GetUser = { + primaryOffice?: { + id: string + } +} + +type Location = { + id: string +} + +async function getUser(token: string, userId: string): Promise { + const url = new URL('graphql', GATEWAY_URL) + const getUsersRes = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `${token}` + }, + body: JSON.stringify({ + operationName: 'fetchUser', + variables: { + userId: userId + }, + query: print(gql` + query fetchUser($userId: String!) { + getUser(userId: $userId) { + id + creationDate + username + practitionerId + mobile + systemRole + role { + _id + labels { + lang + label + __typename + } + __typename + } + status + name { + use + firstNames + familyName + __typename + } + primaryOffice { + id + name + alias + status + __typename + } + __typename + } + } + `) + }) + }) + + const res = (await getUsersRes.json()) as { + data: { getUser: GetUser } + } + + return res.data.getUser +} + +async function getLocationHierarchy(locationId: string): Promise { + const url = new URL( + `location/${locationId}/hierarchy`, + APPLICATION_CONFIG_URL + ) + const res = await fetch(url) + if (!res.ok) { + throw new Error('Unable to retrieve location hierarchy') + } + return res.json() +} + +export async function getUserOfficeLocationHierarchy( + token: string, + userId: string +): Promise { + const user = await getUser(token, userId) + if (!user.primaryOffice) { + throw new Error('No primary office found for user') + } + return getLocationHierarchy(user.primaryOffice.id) +} diff --git a/yarn.lock b/yarn.lock index 116200757..8d13b9a48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4157,7 +4157,7 @@ graphql-request@^6.0.0: "@graphql-typed-document-node/core" "^3.2.0" cross-fetch "^3.1.5" -graphql-tag@^2.11.0: +graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== @@ -4174,6 +4174,11 @@ graphql-ws@^5.4.1: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.11.2.tgz#d5e0acae8b4d4a4cf7be410a24135cfcefd7ddc0" integrity sha512-4EiZ3/UXYcjm+xFGP544/yW1+DVI8ZpKASFbzrV5EDTFWJp0ZvLl4Dy2fSZAzz9imKp5pZMIcjB0x/H69Pv/6w== +graphql@^16.3.0: + version "16.8.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" + integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== + handlebars@*: version "4.7.8" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" From c09042bb042a09c0ae062389c748159ff8d61594 Mon Sep 17 00:00:00 2001 From: Tahmid Rahman <42269993+tahmidrahman-dsi@users.noreply.github.com> Date: Tue, 7 May 2024 12:16:10 +0600 Subject: [PATCH 004/146] [HOTFIX] Mass email subject & content styles (#983) * Update body content styles to use linebreaks * Forward subject from variables instead of template --- src/api/notification/email-templates/index.ts | 3 ++- .../email-templates/other/all-user-notification.html | 6 +++++- src/api/notification/handler.ts | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/api/notification/email-templates/index.ts b/src/api/notification/email-templates/index.ts index e65463e73..acdfe7e1d 100644 --- a/src/api/notification/email-templates/index.ts +++ b/src/api/notification/email-templates/index.ts @@ -119,7 +119,7 @@ type RejectionDeclarationVariables = DeclarationCommonVariables & { name: string } -type AllUserNotificationVariables = { +export type AllUserNotificationVariables = { subject: string body: string } @@ -262,3 +262,4 @@ export type TemplateVariables = | InReviewDeclarationVariables | RegistrationDeclarationVariables | RejectionDeclarationVariables + | AllUserNotificationVariables diff --git a/src/api/notification/email-templates/other/all-user-notification.html b/src/api/notification/email-templates/other/all-user-notification.html index 7e2b34391..0b9da50c7 100644 --- a/src/api/notification/email-templates/other/all-user-notification.html +++ b/src/api/notification/email-templates/other/all-user-notification.html @@ -24,6 +24,10 @@ margin-bottom: 24px; } + .mail-body { + white-space: pre-line; + } + i { color: #666; font-weight: 400; @@ -37,7 +41,7 @@ max-width: 100%;">

{{subject}}

-

+

{{body}}


diff --git a/src/api/notification/handler.ts b/src/api/notification/handler.ts index 5cf1443c1..e28947d13 100644 --- a/src/api/notification/handler.ts +++ b/src/api/notification/handler.ts @@ -16,6 +16,7 @@ import { COUNTRY_LOGO_URL, LOGIN_URL, SENDER_EMAIL_ADDRESS } from './constant' import { sendEmail } from './email-service' import { SMSTemplateType, sendSMS } from './sms-service' import { + AllUserNotificationVariables, EmailTemplateType, TemplateVariables, getTemplate, @@ -105,7 +106,10 @@ export async function notificationHandler( logger.info(`Notification method is email and recipient ${recipient.email}`) const template = getTemplate(templateName.email) - const emailSubject = template.subject + const emailSubject = + template.type === 'allUserNotification' + ? (variables as AllUserNotificationVariables).subject + : template.subject const emailBody = renderTemplate(template, { ...variables, From 290d4f5aba579ea7f7c2b8a2a40dee0ce40a0907 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 7 May 2024 17:32:10 +0600 Subject: [PATCH 005/146] fix: change gateway web url to internal swarm one (#990) --- infrastructure/docker-compose.deploy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index b8fdc06ea..2d34292bc 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -581,7 +581,9 @@ services: - APN_SERVICE_URL=http://apm-server:8200 - COUNTRY_CONFIG_URL=https://countryconfig.{{hostname}} - LOGIN_URL=https://login.{{hostname}} - - GATEWAY_URL=https://gateway.{{hostname}} + # This needs to be the internal swarm url + # as containers cannot connect to the web + - GATEWAY_URL=http://gateway:7070 - CLIENT_APP_URL=https://register.{{hostname}} - NOTIFICATION_TRANSPORT=${NOTIFICATION_TRANSPORT} - ALERT_EMAIL=${ALERT_EMAIL:-""} From 06f683e450776b6dcb74730e1c92abf02d74b965 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 14 May 2024 15:51:19 +0600 Subject: [PATCH 006/146] docs: update CHANGELOG --- CHANGELOG.md | 111 +++++++++------------------------------------------ 1 file changed, 19 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1a33c796..a1c38a063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,21 @@ # Changelog ## 1.5.0 (TBD) +- Change auth URLs to access them via gateway +- Add hearth URL to search service +- Include an endpoint for serving individual certificates in development mode +- Include compositionId in confirm registration payload +- Move individual configuration options to feature flags +- Remove logrocket refrences +- Upgrade to node 18 +- Enable gzip compression in client & login +- Make SENTRY_DSN variable optional +- Use docker compose v2 in github workflows +- Mass email from national system admin - Remove dependency on openhim. The openhim db is kept for backwards compatibility reasons and will be removed in v1.6 - -## [1.3.4](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.3.3...v1.3.4) - -## Breaking changes - -## New features - -## Bug fixes - -- Fix typo in certificate handlebar names - -See [Releases](https://github.com/opencrvs/opencrvs-countryconfig/releases) for release notes of older releases. +- Add smtp environment variables in qa compose file +- Use image tag instead of patterns in certificate SVGs +- Generate default address according to logged-in user's location ## [1.4.1](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.0...v1.4.1) @@ -69,90 +71,15 @@ In the next OpenCRVS release v1.5.0, there will be two significant changes: - The `infrastructure` directory and related pipelines will be moved to a new repository. - Both the new infrastructure repository and the OpenCRVS country resource package repositories will start following their own release cycles, mostly independent from the core's release cycle. From this release forward, both packages are released as "OpenCRVS minor compatible" releases, meaning that the OpenCRVS countryconfig 1.3.0- is compatible with OpenCRVS 1.3.0, 1.3.1, 1.3.2, etc. This allows for the release of new hotfix versions of the core without having to publish a new version of the infrastructure or countryconfig. -### Bug fixes - -## [1.3.3](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.3.2...v1.3.3) - -## Breaking changes - -## New features - -- #### Greater customizability of location data in certificates - - The various admin level handlebars e.g. **statePlaceofbirth**, - **districtPrimaryMother** only contained the name of that location which was - not able to take advantage of all the information OpenCRVS had available - about the various admin levels e.g. the name of that location in the - secondary language. So we are introducing a new set of admin level - handlebars that would contain the **id** of that location which we can - resolve into a value of the shape - - ``` - { - name: string - alias: string - } - ``` - - using the new **"location"** handlebar helper. Here name is the primary - label of the location and alias being the secondary one. Currently only - these 2 fields are available but we will be adding more fields depending on - various countries requirements. If previously the certificate svg used to - contain `{{districtPlaceofbirth}}` then now we can replace it with - `{{location districtPlaceofbirthId 'name'}}`. To access alias, the `'name'` - needs to be replaced with `'alias'`. - - Below is a list of all the new handlebars that are meant to be used with the - "location" handlebar helper. - - - statePrimaryInformantId - - districtPrimaryInformantId - - statePlaceofbirthId - - districtPlaceofbirthId - - statePrimaryMotherId - - districtPrimaryMotherId - - statePrimaryFatherId - - districtPrimaryFatherId - - statePrimaryDeceasedId - - districtPrimaryDeceasedId - - statePlaceofdeathId - - districtPlaceofdeathId - - statePrimaryGroomId - - districtPrimaryGroomId - - statePrimaryBrideId - - districtPrimaryBrideId - - statePlaceofmarriageId - - districtPlaceofmarriageId - - registrar.stateId - - registrar.districtId - - registrar.officeId - - registrationAgent.stateId - - registrationAgent.districtId - - registrationAgent.officeId - - ##### We will be deprecating the counterpart of the above mentioned handlebars that contains only the label of the specified location in a future version so we highly recommend that implementers update their certificates to use these new ones. - -- #### "Spouse" section in Farajaland death form - - Spouse section is an optional section in death form. Going forward it will be included in Farajaland example configuration. - -- #### Type of ID dropdown - Farajaland forms will now include a dropdown to select the type of ID an individual is providing e.g. National ID, Driving License etc. instead of being restricted to only national ID number. -- #### Number of dependents of deceased field - As an example of custom field, the deceased section in death form will now include the **numberOfDependants** field. -- #### Reason for late registration field - The birth & death forms will include another custom field, **reasonForLateRegistration**, which makes use of "LATE_REGISTRATION_TARGET" configuration option in it's visibility conditional. +## [1.3.4](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.3.3...v1.3.4) -## Bug fixes +### Bug fixes -- Updated translations for form introduction page and sending for approval to reflect the default notification method being email. -- # Remove hard-coded conditionals from "occupation" field to make it usable in the deceased form +- Fix typo in certificate handlebar names ## [1.3.3](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.3.2...v1.3.3) -## Breaking changes - -## New features +### New features - #### Greater customizability of location data in certificates @@ -220,7 +147,7 @@ In the next OpenCRVS release v1.5.0, there will be two significant changes: - #### Reason for late registration field The birth & death forms will include another custom field, **reasonForLateRegistration**, which makes use of "LATE_REGISTRATION_TARGET" configuration option in it's visibility conditional. -## Bug fixes +### Bug fixes - Updated translations for form introduction page and sending for approval to reflect the default notification method being email. - Remove hard-coded conditionals from "occupation" field to make it usable in the deceased form From 7be22de4d2bf8821329f5122e436958f7eff8872 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 15 May 2024 15:18:33 +0300 Subject: [PATCH 007/146] set owner for backup server authorized_keys to be the backup user --- infrastructure/server-setup/backups.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure/server-setup/backups.yml b/infrastructure/server-setup/backups.yml index fa8717219..c690e2f8a 100644 --- a/infrastructure/server-setup/backups.yml +++ b/infrastructure/server-setup/backups.yml @@ -111,6 +111,8 @@ marker: '# {mark} ANSIBLE MANAGED BLOCK docker-manager-first {{ manager_hostname }}' create: yes mode: 0600 + owner: '{{ external_backup_server_user }}' + tags: - backups From 12845b44f39328dc29d9a0aa07165cff213990f9 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Fri, 17 May 2024 14:34:03 +0300 Subject: [PATCH 008/146] minor fix to how download script cleans backup directories before applying downloaded backups --- infrastructure/backups/download.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/infrastructure/backups/download.sh b/infrastructure/backups/download.sh index 5e4ca8391..0d87a1783 100644 --- a/infrastructure/backups/download.sh +++ b/infrastructure/backups/download.sh @@ -99,7 +99,13 @@ mkdir -p $BACKUP_RAW_FILES_DIR/extract tar -xvf $BACKUP_RAW_FILES_DIR/${LABEL}.tar.gz -C $BACKUP_RAW_FILES_DIR/extract # Delete previous days restore(s) and move the newly downloaded one in place -rm -rf /data/backups/* +for BACKUP_DIR in /data/backups/*; do + if [ -d "$BACKUP_DIR" ]; then + rm -rf $BACKUP_DIR/* + fi +done + + mv $BACKUP_RAW_FILES_DIR/extract/elasticsearch /data/backups/elasticsearch mv $BACKUP_RAW_FILES_DIR/extract/influxdb /data/backups/influxdb/${LABEL} From e3253dd7a8907d869c67fa6ce3f120b43be6476c Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" <32668488+Nil20@users.noreply.github.com> Date: Mon, 20 May 2024 15:41:28 +0600 Subject: [PATCH 009/146] chore!: move configuration options (#1005) --- src/api/application/application-config-default.ts | 6 +++--- src/form/common/common-optional-fields.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/application/application-config-default.ts b/src/api/application/application-config-default.ts index 28db8ebf4..9b2db665a 100644 --- a/src/api/application/application-config-default.ts +++ b/src/api/application/application-config-default.ts @@ -46,10 +46,10 @@ export const defaultApplicationConfig = { MARRIAGE_REGISTRATION: false, EXTERNAL_VALIDATION_WORKQUEUE: false, INFORMANT_SIGNATURE: false, - PRINT_DECLARATION: false + PRINT_DECLARATION: false, + DATE_OF_BIRTH_UNKNOWN: true, + INFORMANT_SIGNATURE_REQUIRED: false }, - DATE_OF_BIRTH_UNKNOWN: true, - INFORMANT_SIGNATURE_REQUIRED: false, USER_NOTIFICATION_DELIVERY_METHOD: 'email', // or 'sms', or '' ... You can use 'sms' for WhatsApp INFORMANT_NOTIFICATION_DELIVERY_METHOD: 'email', // or 'sms', or '' ... You can use 'sms' for WhatsApp SIGNATURE_REQUIRED_FOR_ROLES: ['LOCAL_REGISTRAR', 'NATIONAL_REGISTRAR'] diff --git a/src/form/common/common-optional-fields.ts b/src/form/common/common-optional-fields.ts index 419125a8a..d37f8ff84 100644 --- a/src/form/common/common-optional-fields.ts +++ b/src/form/common/common-optional-fields.ts @@ -22,7 +22,7 @@ import { Validator } from '../types/validators' const exactDobConditional: Conditional[] = [ { action: 'hide', - expression: '!window.config.DATE_OF_BIRTH_UNKNOWN' + expression: '!window.config.FEATURES.DATE_OF_BIRTH_UNKNOWN' } ] From a4a37a58bf708df619fc6bf05646f2238920c425 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Tue, 21 May 2024 19:06:56 +0600 Subject: [PATCH 010/146] Remove authentication from dashboard queries endpoint and update traefik rules --- infrastructure/docker-compose.deploy.yml | 17 +++++++++++------ src/index.ts | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 2d34292bc..82fdfe861 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -554,7 +554,7 @@ services: deploy: labels: - 'traefik.enable=true' - - 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`) && !Path(`/notification`)' + - 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`) && !Path(`/notification`) &!Path(`/dashboards/queries.json`)' - 'traefik.http.services.countryconfig.loadbalancer.server.port=3040' - 'traefik.http.routers.countryconfig.tls=true' - 'traefik.http.routers.countryconfig.tls.certresolver=certResolver' @@ -567,13 +567,15 @@ services: - 'traefik.http.middlewares.countryconfig.headers.stsseconds=31536000' - 'traefik.http.middlewares.countryconfig.headers.stsincludesubdomains=true' - 'traefik.http.middlewares.countryconfig.headers.stspreload=true' - # This is an invalid IP range, effectively blocking all IPs from accessing /email path. + # This is an invalid IP range, effectively blocking all IPs from accessing below paths. # It's only meant to be accessed from the internal docker network. - - 'traefik.http.middlewares.block-email.ipwhitelist.sourcerange=255.255.255.255' + - 'traefik.http.middlewares.block-internal-routes.ipwhitelist.sourcerange=255.255.255.255' - 'traefik.http.routers.block-email.rule=Host(`countryconfig.{{hostname}}`) && Path(`/email`)' - - 'traefik.http.routers.block-email.middlewares=block-email' + - 'traefik.http.routers.block-email.middlewares=block-internal-routes' - 'traefik.http.routers.block-notification.rule=Host(`countryconfig.{{hostname}}`) && Path(`/notification`)' - - 'traefik.http.routers.block-notification.middlewares=block-email' + - 'traefik.http.routers.block-notification.middlewares=block-internal-routes' + - 'traefik.http.routers.block-dashboard-queries.rule=Host(`countryconfig.{{hostname}}`) && Path(`/dashboards/queries.json`)' + - 'traefik.http.routers.block-dashboard-queries.middlewares=block-internal-routes' replicas: 1 environment: - MONGO_URL=mongodb://mongo1/user-mgnt?replicaSet=rs0 @@ -848,7 +850,7 @@ services: deploy: labels: - 'traefik.enable=true' - - 'traefik.http.routers.config.rule=Host(`config.{{hostname}}`)' + - 'traefik.http.routers.config.rule=Host(`config.{{hostname}}`) && !Path(`/dashboardQueries`)' - 'traefik.http.services.config.loadbalancer.server.port=2021' - 'traefik.http.routers.config.tls=true' - 'traefik.http.routers.config.tls.certresolver=certResolver' @@ -860,6 +862,9 @@ services: - 'traefik.http.middlewares.config.headers.stsseconds=31536000' - 'traefik.http.middlewares.config.headers.stsincludesubdomains=true' - 'traefik.http.middlewares.config.headers.stspreload=true' + - 'traefik.http.middlewares.block-internal-routes.ipwhitelist.sourcerange=255.255.255.255' + - 'traefik.http.routers.block-dashboard-queries.rule=Host(`countryconfig.{{hostname}}`) && Path(`/dashboardQueries`)' + - 'traefik.http.routers.block-dashboard-queries.middlewares=block-internal-routes' replicas: 1 networks: - overlay_net diff --git a/src/index.ts b/src/index.ts index e7c6bc94c..56b69f257 100644 --- a/src/index.ts +++ b/src/index.ts @@ -374,6 +374,7 @@ export async function createServer() { handler: dashboardQueriesHandler, options: { tags: ['api'], + auth: false, description: 'Serves dashboard view refresher queries' } }) From f2e2816b0ba1427a73b5da556ad27b45bc614b2d Mon Sep 17 00:00:00 2001 From: Tahmid Rahman <42269993+tahmidrahman-dsi@users.noreply.github.com> Date: Mon, 27 May 2024 14:02:16 +0600 Subject: [PATCH 011/146] fix: remove authentication from dashboard queries endpoint and update traefik rules (#120) --- infrastructure/docker-compose.deploy.yml | 17 +++++++++++------ src/index.ts | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 2d34292bc..82fdfe861 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -554,7 +554,7 @@ services: deploy: labels: - 'traefik.enable=true' - - 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`) && !Path(`/notification`)' + - 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`) && !Path(`/notification`) &!Path(`/dashboards/queries.json`)' - 'traefik.http.services.countryconfig.loadbalancer.server.port=3040' - 'traefik.http.routers.countryconfig.tls=true' - 'traefik.http.routers.countryconfig.tls.certresolver=certResolver' @@ -567,13 +567,15 @@ services: - 'traefik.http.middlewares.countryconfig.headers.stsseconds=31536000' - 'traefik.http.middlewares.countryconfig.headers.stsincludesubdomains=true' - 'traefik.http.middlewares.countryconfig.headers.stspreload=true' - # This is an invalid IP range, effectively blocking all IPs from accessing /email path. + # This is an invalid IP range, effectively blocking all IPs from accessing below paths. # It's only meant to be accessed from the internal docker network. - - 'traefik.http.middlewares.block-email.ipwhitelist.sourcerange=255.255.255.255' + - 'traefik.http.middlewares.block-internal-routes.ipwhitelist.sourcerange=255.255.255.255' - 'traefik.http.routers.block-email.rule=Host(`countryconfig.{{hostname}}`) && Path(`/email`)' - - 'traefik.http.routers.block-email.middlewares=block-email' + - 'traefik.http.routers.block-email.middlewares=block-internal-routes' - 'traefik.http.routers.block-notification.rule=Host(`countryconfig.{{hostname}}`) && Path(`/notification`)' - - 'traefik.http.routers.block-notification.middlewares=block-email' + - 'traefik.http.routers.block-notification.middlewares=block-internal-routes' + - 'traefik.http.routers.block-dashboard-queries.rule=Host(`countryconfig.{{hostname}}`) && Path(`/dashboards/queries.json`)' + - 'traefik.http.routers.block-dashboard-queries.middlewares=block-internal-routes' replicas: 1 environment: - MONGO_URL=mongodb://mongo1/user-mgnt?replicaSet=rs0 @@ -848,7 +850,7 @@ services: deploy: labels: - 'traefik.enable=true' - - 'traefik.http.routers.config.rule=Host(`config.{{hostname}}`)' + - 'traefik.http.routers.config.rule=Host(`config.{{hostname}}`) && !Path(`/dashboardQueries`)' - 'traefik.http.services.config.loadbalancer.server.port=2021' - 'traefik.http.routers.config.tls=true' - 'traefik.http.routers.config.tls.certresolver=certResolver' @@ -860,6 +862,9 @@ services: - 'traefik.http.middlewares.config.headers.stsseconds=31536000' - 'traefik.http.middlewares.config.headers.stsincludesubdomains=true' - 'traefik.http.middlewares.config.headers.stspreload=true' + - 'traefik.http.middlewares.block-internal-routes.ipwhitelist.sourcerange=255.255.255.255' + - 'traefik.http.routers.block-dashboard-queries.rule=Host(`countryconfig.{{hostname}}`) && Path(`/dashboardQueries`)' + - 'traefik.http.routers.block-dashboard-queries.middlewares=block-internal-routes' replicas: 1 networks: - overlay_net diff --git a/src/index.ts b/src/index.ts index e7c6bc94c..56b69f257 100644 --- a/src/index.ts +++ b/src/index.ts @@ -374,6 +374,7 @@ export async function createServer() { handler: dashboardQueriesHandler, options: { tags: ['api'], + auth: false, description: 'Serves dashboard view refresher queries' } }) From 58fb8cc607021ca07248723c627e8ad105a21bc3 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 27 May 2024 14:47:25 +0600 Subject: [PATCH 012/146] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee4672b07..0907be5da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Changelog + ## [1.5.0] - Change auth URLs to access them via gateway @@ -12,10 +13,12 @@ - Make SENTRY_DSN variable optional - Use docker compose v2 in github workflows - Mass email from national system admin -- Remove dependency on openhim. The openhim db is kept for backwards compatibility reasons and will be removed in v1.6 +- Remove dependency on openhim. The openhim db is kept for backwards compatibility reasons and will be removed in + v1.6 - Add smtp environment variables in qa compose file - Use image tag instead of patterns in certificate SVGs - Generate default address according to logged-in user's location +- Remove authentication from dashboard queries route ## [1.4.1](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.0...v1.4.1) From 549acefdd49dc5c0fd44573f8926eb3e5d7eca27 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 27 May 2024 14:53:46 +0600 Subject: [PATCH 013/146] Remove unintended formatting changes --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0907be5da..5d7a76a4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,7 @@ - Make SENTRY_DSN variable optional - Use docker compose v2 in github workflows - Mass email from national system admin -- Remove dependency on openhim. The openhim db is kept for backwards compatibility reasons and will be removed in - v1.6 +- Remove dependency on openhim. The openhim db is kept for backwards compatibility reasons and will be removed in v1.6 - Add smtp environment variables in qa compose file - Use image tag instead of patterns in certificate SVGs - Generate default address according to logged-in user's location From b6832e8cc246856583898582eb60bc8d7a0d20a9 Mon Sep 17 00:00:00 2001 From: Niko Kurtti Date: Mon, 27 May 2024 12:20:17 +0300 Subject: [PATCH 014/146] Update jq images to official ones to avoid deprecation breakage (#122) --- infrastructure/monitoring/kibana/setup-config.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index 38f439bd7..a2f5cca78 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -23,7 +23,7 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net stedolan/jq -r '.data[].id' | while read -r id; do +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id" done @@ -31,7 +31,7 @@ done $docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net stedolan/jq -r '.data[].id' | while read -r id; do +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_disable" $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_enable" done From d94f0e40e13712d0c6509262d9d36fd7070de23b Mon Sep 17 00:00:00 2001 From: Niko Kurtti Date: Mon, 27 May 2024 12:20:40 +0300 Subject: [PATCH 015/146] Update Docker and checkout actions to get rid of warnings (#121) --- .github/workflows/deploy-prod.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/publish-release.yml | 4 ++-- .github/workflows/publish-to-dockerhub.yml | 6 +++--- .github/workflows/test.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 69189010c..8d75aa765 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -75,7 +75,7 @@ jobs: echo "KNOWN_HOSTS=" >> $GITHUB_ENV - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9cfa54135..9630a00f9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -77,7 +77,7 @@ jobs: echo "KNOWN_HOSTS=" >> $GITHUB_ENV - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index b3d0fe56e..3ffcabf0c 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -15,7 +15,7 @@ jobs: base: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: ref: '${{ github.event.inputs.branch_name }}' @@ -47,7 +47,7 @@ jobs: custom_tag: ${{ env.TAG }} - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} diff --git a/.github/workflows/publish-to-dockerhub.yml b/.github/workflows/publish-to-dockerhub.yml index 49ccfc90a..11739fc01 100644 --- a/.github/workflows/publish-to-dockerhub.yml +++ b/.github/workflows/publish-to-dockerhub.yml @@ -16,20 +16,20 @@ jobs: push: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 if: github.event_name == 'workflow_dispatch' with: fetch-depth: 2 ref: '${{ github.event.inputs.branch_name }}' - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 if: github.event_name == 'push' - name: Get tags run: git fetch --tags origin - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f0c74dcc..cb7a42e5a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: steps: - name: Checking out git repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Use Node.js 18.19 uses: actions/setup-node@v2 From 5042dc908c87d41558bc9d49206b124775564b77 Mon Sep 17 00:00:00 2001 From: Anamul Haque Date: Thu, 30 May 2024 12:16:50 +0600 Subject: [PATCH 016/146] fix: add translations for different informants in print, issue & correction flow (#127) * french translation added for informants * updated changelog.md --- CHANGELOG.md | 1 + src/translations/client.csv | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7a76a4d..a02e0d0c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Use image tag instead of patterns in certificate SVGs - Generate default address according to logged-in user's location - Remove authentication from dashboard queries route +- Added french translation of informant for print certificate flow, issue certificate flow & correction flow ## [1.4.1](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.0...v1.4.1) diff --git a/src/translations/client.csv b/src/translations/client.csv index b1f1f9398..ceac4fc70 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -383,7 +383,7 @@ constants.issueConfirmationMessage,Confirmation of issuance,Please confirm that constants.issueToBride,Issuing to bride,Issue to bride,Délivré à) la mariée constants.issueToFather,Issuing to father,Issue to father,Délivré au père constants.issueToGroom,Issuing to groom,Issue to groom,délivré au marié -constants.issueToInformant,Issuance of death to informant,Issue to informant ({informant}),Delivré à l'informateur +constants.issueToInformant,Issuance of death to informant,"Issue to informant ({informant, select, MOTHER {Mother} FATHER {Father} GRANDFATHER {Grandfather} GRANDMOTHER {Grandmother} BROTHER {Brother} SISTER {Sister} LEGAL_GUARDIAN {Legal guardian} BRIDE {Bride} GROOM {Groom} HEAD_OF_GROOM_FAMILY {Head of groom's family} HEAD_OF_BRIDE_FAMILY {Head of bride's family} SPOUSE {Spouse} SON {Son} DAUGHTER {Daughter} SON_IN_LAW {Son in law} DAUGHTER_IN_LAW {Daughter in law} GRANDSON {Grandson} GRANDDAUGHTER {Granddaughter} other {{informant}}})","Delivré à l'informateur ({informant, select, MOTHER {Mère} FATHER {Père} GRANDFATHER {Grand-père} GRANDMOTHER {Grand-mère} BROTHER {Frère} SISTER {Sœur} LEGAL_GUARDIAN {Tuteur légal} BRIDE {Mariée} GROOM {Marié} HEAD_OF_GROOM_FAMILY {Chef de la famille du marié} HEAD_OF_BRIDE_FAMILY {Chef de la famille de la mariée} SPOUSE {Conjoint} SON {Fils} DAUGHTER {Fille} SON_IN_LAW {Beau-fils} DAUGHTER_IN_LAW {Belle-fille} GRANDSON {Petit-fils} GRANDDAUGHTER {Petite-fille} other {{informant}}})" constants.issueToMother,Issuing to mother,Issue to mother,Délivré à la mère constants.issueToSomeoneElse,Issuing to someone else,Issue to someone else,Délivrer à quelqu'un d'autre constants.issuedBy,The issued by sec text,Issued by,Délivré par @@ -520,7 +520,7 @@ correction.corrector.groom,Label for groom option in certificate correction form correction.corrector.idCheck,,Check proof of ID,Vérifiez la preuve d'identité. Correspond-elle aux détails suivants ? correction.corrector.idCheckVerify,,Yes,Oui correction.corrector.idCheckWithoutVerify,,No,Non -correction.corrector.informant,Label for informant option in certificate correction form,Informant ({informant}),Informateur +correction.corrector.informant,Label for informant option in certificate correction form,"Informant ({informant, select, MOTHER {Mother} FATHER {Father} GRANDFATHER {Grandfather} GRANDMOTHER {Grandmother} BROTHER {Brother} SISTER {Sister} LEGAL_GUARDIAN {Legal guardian} BRIDE {Bride} GROOM {Groom} HEAD_OF_GROOM_FAMILY {Head of groom's family} HEAD_OF_BRIDE_FAMILY {Head of bride's family} SPOUSE {Spouse} SON {Son} DAUGHTER {Daughter} SON_IN_LAW {Son in law} DAUGHTER_IN_LAW {Daughter in law} GRANDSON {Grandson} GRANDDAUGHTER {Granddaughter} other {{informant}}})","Informateur ({informant, select, MOTHER {Mère} FATHER {Père} GRANDFATHER {Grand-père} GRANDMOTHER {Grand-mère} BROTHER {Frère} SISTER {Sœur} LEGAL_GUARDIAN {Tuteur légal} BRIDE {Mariée} GROOM {Marié} HEAD_OF_GROOM_FAMILY {Chef de la famille du marié} HEAD_OF_BRIDE_FAMILY {Chef de la famille de la mariée} SPOUSE {Conjoint} SON {Fils} DAUGHTER {Fille} SON_IN_LAW {Beau-fils} DAUGHTER_IN_LAW {Belle-fille} GRANDSON {Petit-fils} GRANDDAUGHTER {Petite-fille} other {{informant}}})" correction.corrector.legalGuardian,Label for legal guardian option in certificate correction form,Legal guardian,Tuteur légal correction.corrector.me,Label for registrar option in certificate correction form,Me,Moi correction.corrector.mother,Label for mother option in certificate correction form,Mother,Mère @@ -904,7 +904,7 @@ form.field.label.ageOfInformant,,Age of informant,Âge de l'informateur form.field.label.ageOfMother,,Age of mother,Âge de la mère form.field.label.ageOfSpouse,,Age of spouse,Âge du conjoint form.field.label.app.certifyRecordTo.father,,Print and issue to father,Imprimer et remettre au père -form.field.label.app.certifyRecordTo.informant,,Print and issue to informant ({informant}),Imprimer et remettre à l'informateur ({informant}) +form.field.label.app.certifyRecordTo.informant,,"Print and issue to informant ({informant, select, MOTHER {Mother} FATHER {Father} GRANDFATHER {Grandfather} GRANDMOTHER {Grandmother} BROTHER {Brother} SISTER {Sister} LEGAL_GUARDIAN {Legal guardian} BRIDE {Bride} GROOM {Groom} HEAD_OF_GROOM_FAMILY {Head of groom's family} HEAD_OF_BRIDE_FAMILY {Head of bride's family} SPOUSE {Spouse} SON {Son} DAUGHTER {Daughter} SON_IN_LAW {Son in law} DAUGHTER_IN_LAW {Daughter in law} GRANDSON {Grandson} GRANDDAUGHTER {Granddaughter} other {{informant}}})","Imprimer et remettre à l'informateur ({informant, select, MOTHER {Mère} FATHER {Père} GRANDFATHER {Grand-père} GRANDMOTHER {Grand-mère} BROTHER {Frère} SISTER {Sœur} LEGAL_GUARDIAN {Tuteur légal} BRIDE {Mariée} GROOM {Marié} HEAD_OF_GROOM_FAMILY {Chef de la famille du marié} HEAD_OF_BRIDE_FAMILY {Chef de la famille de la mariée} SPOUSE {Conjoint} SON {Fils} DAUGHTER {Fille} SON_IN_LAW {Beau-fils} DAUGHTER_IN_LAW {Belle-fille} GRANDSON {Petit-fils} GRANDDAUGHTER {Petite-fille} other {{informant}}})" form.field.label.app.certifyRecordTo.mother,,Print and issue to mother,Imprimer et remettre à la mère form.field.label.app.phoneVerWarn,,Check with the informant that the mobile phone number you have entered is correct,Vérifiez auprès de l'informateur que le numéro de téléphone mobile que vous avez indiqué est correct. form.field.label.app.whoContDet.app,,Informant,Informateur From 635774875176147a1f4f0a7bcfb2d5e6c805a0bc Mon Sep 17 00:00:00 2001 From: Tareq Date: Thu, 30 May 2024 16:31:11 +0600 Subject: [PATCH 017/146] Fix Correct item names in update modal for marriage declaration --- CHANGELOG.md | 3 ++- src/form/marriage/index.ts | 2 +- src/translations/client.csv | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a02e0d0c9..c2ca59342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ - Use image tag instead of patterns in certificate SVGs - Generate default address according to logged-in user's location - Remove authentication from dashboard queries route -- Added french translation of informant for print certificate flow, issue certificate flow & correction flow +- Added french translation of informant for print certificate flow, issue certificate flow & correction flow +- Groom's and Bride's name, printIssue translation variables updated [#124](https://github.com/opencrvs/opencrvs-countryconfig/pull/124) ## [1.4.1](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.0...v1.4.1) diff --git a/src/form/marriage/index.ts b/src/form/marriage/index.ts index c88cab80b..213ffdeff 100644 --- a/src/form/marriage/index.ts +++ b/src/form/marriage/index.ts @@ -84,7 +84,7 @@ export const marriageForm: ISerializedForm = { { id: 'informant', viewType: 'form', - name: formMessageDescriptors.registrationName, + name: formMessageDescriptors.informantName, title: formMessageDescriptors.informantTitle, groups: [ { diff --git a/src/translations/client.csv b/src/translations/client.csv index ceac4fc70..b1ce67b03 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -903,7 +903,9 @@ form.field.label.ageOfGroom,,Age of groom,Âge du marié form.field.label.ageOfInformant,,Age of informant,Âge de l'informateur form.field.label.ageOfMother,,Age of mother,Âge de la mère form.field.label.ageOfSpouse,,Age of spouse,Âge du conjoint +form.field.label.app.certifyRecordTo.bride,,Print and issue to bride,Imprimer et envoyer à la mariée form.field.label.app.certifyRecordTo.father,,Print and issue to father,Imprimer et remettre au père +form.field.label.app.certifyRecordTo.groom,,Print and issue to groom,Imprimer et envoyer au marié form.field.label.app.certifyRecordTo.informant,,"Print and issue to informant ({informant, select, MOTHER {Mother} FATHER {Father} GRANDFATHER {Grandfather} GRANDMOTHER {Grandmother} BROTHER {Brother} SISTER {Sister} LEGAL_GUARDIAN {Legal guardian} BRIDE {Bride} GROOM {Groom} HEAD_OF_GROOM_FAMILY {Head of groom's family} HEAD_OF_BRIDE_FAMILY {Head of bride's family} SPOUSE {Spouse} SON {Son} DAUGHTER {Daughter} SON_IN_LAW {Son in law} DAUGHTER_IN_LAW {Daughter in law} GRANDSON {Grandson} GRANDDAUGHTER {Granddaughter} other {{informant}}})","Imprimer et remettre à l'informateur ({informant, select, MOTHER {Mère} FATHER {Père} GRANDFATHER {Grand-père} GRANDMOTHER {Grand-mère} BROTHER {Frère} SISTER {Sœur} LEGAL_GUARDIAN {Tuteur légal} BRIDE {Mariée} GROOM {Marié} HEAD_OF_GROOM_FAMILY {Chef de la famille du marié} HEAD_OF_BRIDE_FAMILY {Chef de la famille de la mariée} SPOUSE {Conjoint} SON {Fils} DAUGHTER {Fille} SON_IN_LAW {Beau-fils} DAUGHTER_IN_LAW {Belle-fille} GRANDSON {Petit-fils} GRANDDAUGHTER {Petite-fille} other {{informant}}})" form.field.label.app.certifyRecordTo.mother,,Print and issue to mother,Imprimer et remettre à la mère form.field.label.app.phoneVerWarn,,Check with the informant that the mobile phone number you have entered is correct,Vérifiez auprès de l'informateur que le numéro de téléphone mobile que vous avez indiqué est correct. @@ -1290,7 +1292,7 @@ form.section.accountDetails,,Account details,Coordonnées du compte form.section.assignedRegistrationOffice,,Assign to a registration office,A quel bureau voulez-vous affecter un nouvel utilisateur ? form.section.assignedRegistrationOfficeGroupTitle,,Assigned registration office,Bureau d'enregistrement assigné form.section.bride.headOfBrideFamily,,Head of bride's family,Chef de la famille de la mariée -form.section.bride.name,,Print and issue to bride,Imprimer et envoyer à la mariée +form.section.bride.name,,Bride,Mariée form.section.bride.title,,Bride's details,Détails de la mariée form.section.causeOfDeath.name,,Cause of Death,Cause du décès form.section.causeOfDeath.title,,What is the medically certified cause of death?,Quelle est la cause de décès médicalement certifiée ? @@ -1327,7 +1329,7 @@ form.section.documents.uploadImage,,Upload a photo of the supporting document,T form.section.father.name,,Father,Père form.section.father.title,,Father's details,Information du père form.section.groom.headOfGroomFamily,,Head of groom's family,Chef de la famille du marié -form.section.groom.name,,Print and issue to groom,Imprimer et envoyer au marié +form.section.groom.name,,Groom,Marié form.section.groom.title,,Groom's details,Détails du marié form.section.informant.name,,Informant,Informateur form.section.informant.title,,Informant's details,information de l'informateur From aba81fe780815c6d37ec8ad61861005a435322a4 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Tue, 4 Jun 2024 16:18:23 +0600 Subject: [PATCH 018/146] chore: update default users mobile format --- .../employees/source/default-employees.csv | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/data-seeding/employees/source/default-employees.csv b/src/data-seeding/employees/source/default-employees.csv index aa77561c8..1888c3a03 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,0911111111,k.bwalya,kalushabwalya17@gmail.com,test -CRVS_OFFICE_JWMRGwDBXK,Felix,Katongo,REGISTRATION_AGENT,Registration Agent,0922222222,f.katongo,kalushabwalya17+@gmail.com,test -CRVS_OFFICE_JWMRGwDBXK,Kennedy,Mweene,LOCAL_REGISTRAR,Local Registrar,0933333333,k.mweene,kalushabwalya1.7@gmail.com,test -CRVS_OFFICE_JWMRGwDBXK,Emmanuel,Mayuka,LOCAL_SYSTEM_ADMIN,Local System Admin,0921681112,e.mayuka,kalushabwalya.17@gmail.com,test -CRVS_OFFICE_2OKicPQMNI,Jonathan,Campbell,NATIONAL_SYSTEM_ADMIN,National System Admin,0921111111,j.campbell,kalushabwaly.a17@gmail.com,test -CRVS_OFFICE_okQp4uKCz0,Patrick,Gondwe,FIELD_AGENT,Local Leader,0912121212,p.gondwe,kalushabwal.ya17@gmail.com,test -CRVS_OFFICE_okQp4uKCz0,Joshua,Mutale,REGISTRATION_AGENT,Registration Agent,0923232323,j.mutale,kalushabwa.lya17@gmail.com,test -CRVS_OFFICE_okQp4uKCz0,Derrick,Bulaya,LOCAL_REGISTRAR,Local Registrar,0934343434,d.bulaya,kalushabw.alya17@gmail.com,test -CRVS_OFFICE_okQp4uKCz0,Alex,Ngonga,LOCAL_SYSTEM_ADMIN,Local System Admin,0978787878,a.ngonga,kalushab.walya17@gmail.com,test -CRVS_OFFICE_2OKicPQMNI,Edgar,Kazembe,PERFORMANCE_MANAGEMENT,Performance Manager,0977777777,e.kazembe,kalusha.bwalya17@gmail.com,test -CRVS_OFFICE_2OKicPQMNI,Joseph,Musonda,NATIONAL_REGISTRAR,National Registrar,0915151515,j.musonda,kalush.abwalya17@gmail.com,test +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 From 4027ae2667329ff980db3884864f55bd1e2b01ea Mon Sep 17 00:00:00 2001 From: Pyry Rouvila Date: Wed, 5 Jun 2024 13:57:00 +0300 Subject: [PATCH 019/146] don't require regLastLocation in Postman Event Notification (#115) --- postman/Event Notification.postman_collection.json | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/postman/Event Notification.postman_collection.json b/postman/Event Notification.postman_collection.json index ab3408626..bbe519238 100644 --- a/postman/Event Notification.postman_collection.json +++ b/postman/Event Notification.postman_collection.json @@ -12,7 +12,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"meta\": {\n \"lastUpdated\": \"2022-08-14T14:43:47.000Z\"\n },\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"8f793c5a-3d53-4c9b-898b-1c04759716c6\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"final\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"birth-notification\"\n }\n ],\n \"text\": \"Birth Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n },\n \"date\": \"2022-08-14T14:43:47.000Z\",\n \"author\": [],\n \"title\": \"Birth Notification\",\n \"section\": [\n {\n \"title\": \"Child details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"child-details\"\n }\n ],\n \"text\": \"Child details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n }\n ]\n },\n {\n \"title\": \"Birth encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"birth-encounter\"\n }\n ],\n \"text\": \"Birth encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n }\n ]\n },\n {\n \"title\": \"Mother's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"mother-details\"\n }\n ],\n \"text\": \"Mother's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n ]\n },\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\"\n }\n ]\n },\n {\n \"title\": \"Father's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"father-details\"\n }\n ],\n \"text\": \"Father's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:8546aaf3-8a60-4150-bc24-ab5579bc0fa2\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"intent\": \"unknown\",\n \"identifier\": [],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"BIRTH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\"\n },\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"MOTHER\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260759205190\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastLocation\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeLocationId}}\"\n }\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Tatke\"\n ],\n \"given\": [\n \"Harney\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"2022-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"use\": \"official\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n },\n \"value\": \"3624667568\"\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Mom\"\n ]\n }\n ],\n \"gender\": \"female\",\n \"telecom\": [\n {\n \"use\": \"mobile\",\n \"system\": \"phone\",\n \"value\": \"+260759205190\"\n }\n ],\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthInteger\": 2,\n \"maritalStatus\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/StructureDefinition/marital-status\",\n \"code\": \"M\"\n }\n ],\n \"text\": \"MARRIED\"\n },\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghanland\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/patient-occupation\",\n \"valueString\": \"Housewife\"\n },\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/educational-attainment\",\n \"valueString\": \"POST_SECONDARY_ISCED_4\"\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"MOTHER\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"use\": \"official\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n },\n \"value\": \"6848901132\"\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Dad\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"telecom\": [\n {\n \"use\": \"mobile\",\n \"system\": \"phone\",\n \"value\": \"+260759205190\"\n }\n ],\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthInteger\": 2,\n \"maritalStatus\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/StructureDefinition/marital-status\",\n \"code\": \"M\"\n }\n ],\n \"text\": \"MARRIED\"\n },\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Madgeland\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/patient-occupation\",\n \"valueString\": \"Businessman\"\n },\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"FAR\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/educational-attainment\",\n \"valueString\": \"POST_SECONDARY_ISCED_4\"\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:45c68568-2ca0-4932-9731-535dd4180fe0\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"procedure\",\n \"display\": \"Procedure\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"57722-1\",\n \"display\": \"Birth plurality of Pregnancy\"\n }\n ]\n },\n \"valueQuantity\": {\n \"value\": \"SINGLE\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d2d3d5b8-658e-4c29-9ec5-cb2431b4ddf3\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"3141-9\",\n \"display\": \"Body weight Measured\"\n }\n ]\n },\n \"valueQuantity\": {\n \"value\": 4,\n \"unit\": \"kg\",\n \"system\": \"http://unitsofmeasure.org\",\n \"code\": \"kg\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:706905cf-7e5d-4d9f-866a-a3795780a990\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"procedure\",\n \"display\": \"Procedure\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"73764-3\",\n \"display\": \"Birth attendant title\"\n }\n ]\n },\n \"valueString\": \"PHYSICIAN\"\n }\n }\n ]\n}", + "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"meta\": {\n \"lastUpdated\": \"2022-08-14T14:43:47.000Z\"\n },\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"8f793c5a-3d53-4c9b-898b-1c04759716c6\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"final\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"birth-notification\"\n }\n ],\n \"text\": \"Birth Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n },\n \"date\": \"2022-08-14T14:43:47.000Z\",\n \"author\": [],\n \"title\": \"Birth Notification\",\n \"section\": [\n {\n \"title\": \"Child details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"child-details\"\n }\n ],\n \"text\": \"Child details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n }\n ]\n },\n {\n \"title\": \"Birth encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"birth-encounter\"\n }\n ],\n \"text\": \"Birth encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n }\n ]\n },\n {\n \"title\": \"Mother's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"mother-details\"\n }\n ],\n \"text\": \"Mother's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n ]\n },\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\"\n }\n ]\n },\n {\n \"title\": \"Father's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"father-details\"\n }\n ],\n \"text\": \"Father's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:8546aaf3-8a60-4150-bc24-ab5579bc0fa2\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"intent\": \"unknown\",\n \"identifier\": [],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"BIRTH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\"\n },\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"MOTHER\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260759205190\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Tatke\"\n ],\n \"given\": [\n \"Harney\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"2022-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"use\": \"official\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n },\n \"value\": \"3624667568\"\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Mom\"\n ]\n }\n ],\n \"gender\": \"female\",\n \"telecom\": [\n {\n \"use\": \"mobile\",\n \"system\": \"phone\",\n \"value\": \"+260759205190\"\n }\n ],\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthInteger\": 2,\n \"maritalStatus\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/StructureDefinition/marital-status\",\n \"code\": \"M\"\n }\n ],\n \"text\": \"MARRIED\"\n },\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghanland\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/patient-occupation\",\n \"valueString\": \"Housewife\"\n },\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/educational-attainment\",\n \"valueString\": \"POST_SECONDARY_ISCED_4\"\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"MOTHER\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"use\": \"official\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n },\n \"value\": \"6848901132\"\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Dad\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"telecom\": [\n {\n \"use\": \"mobile\",\n \"system\": \"phone\",\n \"value\": \"+260759205190\"\n }\n ],\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthInteger\": 2,\n \"maritalStatus\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/StructureDefinition/marital-status\",\n \"code\": \"M\"\n }\n ],\n \"text\": \"MARRIED\"\n },\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Madgeland\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/patient-occupation\",\n \"valueString\": \"Businessman\"\n },\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"FAR\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/educational-attainment\",\n \"valueString\": \"POST_SECONDARY_ISCED_4\"\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:45c68568-2ca0-4932-9731-535dd4180fe0\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"procedure\",\n \"display\": \"Procedure\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"57722-1\",\n \"display\": \"Birth plurality of Pregnancy\"\n }\n ]\n },\n \"valueQuantity\": {\n \"value\": \"SINGLE\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d2d3d5b8-658e-4c29-9ec5-cb2431b4ddf3\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"3141-9\",\n \"display\": \"Body weight Measured\"\n }\n ]\n },\n \"valueQuantity\": {\n \"value\": 4,\n \"unit\": \"kg\",\n \"system\": \"http://unitsofmeasure.org\",\n \"code\": \"kg\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:706905cf-7e5d-4d9f-866a-a3795780a990\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"procedure\",\n \"display\": \"Procedure\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"73764-3\",\n \"display\": \"Birth attendant title\"\n }\n ]\n },\n \"valueString\": \"PHYSICIAN\"\n }\n }\n ]\n}", "options": { "raw": { "language": "json" @@ -38,7 +38,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"meta\": {\n \"lastUpdated\": \"2022-08-14T14:43:47.000Z\"\n },\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"8f793c5a-3d53-4c9b-898b-1c04759716c6\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"final\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"birth-notification\"\n }\n ],\n \"text\": \"Birth Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n },\n \"date\": \"2022-08-14T14:43:47.000Z\",\n \"title\": \"Birth Notification\",\n \"section\": [\n {\n \"title\": \"Child details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"child-details\"\n }\n ],\n \"text\": \"Child details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n }\n ]\n },\n {\n \"title\": \"Birth encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"birth-encounter\"\n }\n ],\n \"text\": \"Birth encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n }\n ]\n },\n {\n \"title\": \"Mother's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"mother-details\"\n }\n ],\n \"text\": \"Mother's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n ]\n },\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\"\n }\n ]\n },\n {\n \"title\": \"Father's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"father-details\"\n }\n ],\n \"text\": \"Father's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:8546aaf3-8a60-4150-bc24-ab5579bc0fa2\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"intent\": \"unknown\",\n \"identifier\": [],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"BIRTH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\"\n },\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"MOTHER\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260759205190\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastLocation\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeLocationId}}\"\n }\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Min\"\n ],\n \"given\": [\n \"Child\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"2022-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Mom\"\n ]\n }\n ],\n \"gender\": \"female\",\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"MOTHER\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Dad\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n }\n ]\n}", + "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"meta\": {\n \"lastUpdated\": \"2022-08-14T14:43:47.000Z\"\n },\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"8f793c5a-3d53-4c9b-898b-1c04759716c6\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"final\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"birth-notification\"\n }\n ],\n \"text\": \"Birth Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n },\n \"date\": \"2022-08-14T14:43:47.000Z\",\n \"title\": \"Birth Notification\",\n \"section\": [\n {\n \"title\": \"Child details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"child-details\"\n }\n ],\n \"text\": \"Child details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\"\n }\n ]\n },\n {\n \"title\": \"Birth encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"birth-encounter\"\n }\n ],\n \"text\": \"Birth encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\"\n }\n ]\n },\n {\n \"title\": \"Mother's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"mother-details\"\n }\n ],\n \"text\": \"Mother's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n ]\n },\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\"\n }\n ]\n },\n {\n \"title\": \"Father's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"father-details\"\n }\n ],\n \"text\": \"Father's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:8546aaf3-8a60-4150-bc24-ab5579bc0fa2\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"intent\": \"unknown\",\n \"identifier\": [],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"BIRTH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:37dd8e55-69c0-493d-b1a0-b7462a1d806a\"\n },\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"MOTHER\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260759205190\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:760c393e-4dc3-4572-83f6-b70765963ef1\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Min\"\n ],\n \"given\": [\n \"Child\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"2022-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Mom\"\n ]\n }\n ],\n \"gender\": \"female\",\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:b74fbd0e-8536-4c11-833d-781e89a4b553\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"MOTHER\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:d9d3a8c8-6a47-47a1-be86-0493a4ec55a7\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:ad1e15bb-51da-449a-8a12-c7dae10728e4\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"family\": [\n \"Ratke\"\n ],\n \"given\": [\n \"Dad\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"2002-06-29\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:7cb1d9cc-ea4b-4046-bea0-38bdf3082f56\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n }\n ]\n}", "options": { "raw": { "language": "json" @@ -64,7 +64,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:56e7d617-1011-4ca1-863f-3b7262709329\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"56e7d617-1011-4ca1-863f-3b7262709329\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"preliminary\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"death-notification\"\n }\n ],\n \"text\": \"Death Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"title\": \"Death Notification\",\n \"section\": [\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:9ae636db-f7ad-47aa-9e8a-5bb651482084\"\n }\n ]\n },\n {\n \"title\": \"Deceased details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"deceased-details\"\n }\n ],\n \"text\": \"Deceased details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:5c192954-d798-402f-a539-31aada34d005\"\n }\n ]\n },\n {\n \"title\": \"Death encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"death-encounter\"\n }\n ],\n \"text\": \"Death encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:8de42635-6dac-4d59-96b4-943ceb40ad1e\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d3bdcf50-f6b9-40aa-a30b-d9515275becc\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"DEATH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:56e7d617-1011-4ca1-863f-3b7262709329\"\n },\n \"identifier\": [],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"SPOUSE\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260712345679\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastLocation\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeLocationId}}\"\n }\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:9ae636db-f7ad-47aa-9e8a-5bb651482084\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"SPOUSE\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:398afc2a-09c9-4b7b-bc45-2bef06501cf0\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:5c192954-d798-402f-a539-31aada34d005\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Salam\"\n ],\n \"family\": [\n \"Ahmed\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"1975-12-12\",\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"deceasedBoolean\": true,\n \"deceasedDateTime\": \"2021-11-12\",\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:398afc2a-09c9-4b7b-bc45-2bef06501cf0\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Anjum\"\n ],\n \"family\": [\n \"Begum\"\n ]\n }\n ],\n \"birthDate\": \"1985-12-12\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:8de42635-6dac-4d59-96b4-943ceb40ad1e\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:6a0bd2db-4fa4-4665-ad62-24821dd01f2a\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:8de42635-6dac-4d59-96b4-943ceb40ad1e\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"cause-of-death-established\",\n \"display\": \"Cause of death established\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/cause-of-death-established\",\n \"code\": \"false\"\n }\n ]\n }\n }\n }\n ],\n \"meta\": {\n \"lastUpdated\": \"2022-11-30T05:59:51.196Z\"\n }\n}", + "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:56e7d617-1011-4ca1-863f-3b7262709329\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"56e7d617-1011-4ca1-863f-3b7262709329\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"preliminary\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"death-notification\"\n }\n ],\n \"text\": \"Death Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"title\": \"Death Notification\",\n \"section\": [\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:9ae636db-f7ad-47aa-9e8a-5bb651482084\"\n }\n ]\n },\n {\n \"title\": \"Deceased details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"deceased-details\"\n }\n ],\n \"text\": \"Deceased details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:5c192954-d798-402f-a539-31aada34d005\"\n }\n ]\n },\n {\n \"title\": \"Death encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"death-encounter\"\n }\n ],\n \"text\": \"Death encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:8de42635-6dac-4d59-96b4-943ceb40ad1e\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:d3bdcf50-f6b9-40aa-a30b-d9515275becc\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"DEATH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:56e7d617-1011-4ca1-863f-3b7262709329\"\n },\n \"identifier\": [],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"SPOUSE\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260712345679\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:9ae636db-f7ad-47aa-9e8a-5bb651482084\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"SPOUSE\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:398afc2a-09c9-4b7b-bc45-2bef06501cf0\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:5c192954-d798-402f-a539-31aada34d005\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Salam\"\n ],\n \"family\": [\n \"Ahmed\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"1975-12-12\",\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"deceasedBoolean\": true,\n \"deceasedDateTime\": \"2021-11-12\",\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:398afc2a-09c9-4b7b-bc45-2bef06501cf0\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Anjum\"\n ],\n \"family\": [\n \"Begum\"\n ]\n }\n ],\n \"birthDate\": \"1985-12-12\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"\",\n \"\",\n \"\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:8de42635-6dac-4d59-96b4-943ceb40ad1e\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:6a0bd2db-4fa4-4665-ad62-24821dd01f2a\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:8de42635-6dac-4d59-96b4-943ceb40ad1e\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"cause-of-death-established\",\n \"display\": \"Cause of death established\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/cause-of-death-established\",\n \"code\": \"false\"\n }\n ]\n }\n }\n }\n ],\n \"meta\": {\n \"lastUpdated\": \"2022-11-30T05:59:51.196Z\"\n }\n}", "options": { "raw": { "language": "json" @@ -90,7 +90,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:7109f150-6262-4798-835e-55b3e4dd4cfb\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"7109f150-6262-4798-835e-55b3e4dd4cfb\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"preliminary\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"death-notification\"\n }\n ],\n \"text\": \"Death Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"title\": \"Death Notification\",\n \"section\": [\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:1363f62f-e34f-4c03-90f3-5fa1d40d0892\"\n }\n ]\n },\n {\n \"title\": \"Deceased details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"deceased-details\"\n }\n ],\n \"text\": \"Deceased details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:233b56c8-aa3d-4d32-9d42-7afe1200fc4f\"\n }\n ]\n },\n {\n \"title\": \"Death encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"death-encounter\"\n }\n ],\n \"text\": \"Death encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:cbce7430-3c63-457e-a6d8-ed3b35a97a64\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"DEATH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:7109f150-6262-4798-835e-55b3e4dd4cfb\"\n },\n \"identifier\": [],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"SPOUSE\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260712345679\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastLocation\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeLocationId}}\"\n }\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:1363f62f-e34f-4c03-90f3-5fa1d40d0892\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"SPOUSE\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:42061dc8-0d97-493a-94e4-e60d0f3fc070\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:233b56c8-aa3d-4d32-9d42-7afe1200fc4f\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"value\": \"1234123421\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n }\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Bashir\"\n ],\n \"family\": [\n \"Ahmed\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"1971-12-12\",\n \"maritalStatus\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/StructureDefinition/marital-status\",\n \"code\": \"M\"\n }\n ],\n \"text\": \"MARRIED\"\n },\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"deceasedBoolean\": true,\n \"deceasedDateTime\": \"2021-12-12\",\n \"multipleBirthBoolean\": false,\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:42061dc8-0d97-493a-94e4-e60d0f3fc070\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"value\": \"4321234257\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n }\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Selena\"\n ],\n \"family\": [\n \"Begum\"\n ]\n }\n ],\n \"birthDate\": \"1980-12-12\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:7b4fcd33-5b39-463a-bbd4-4ac2e41e5f99\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"uncertified-manner-of-death\",\n \"display\": \"Uncertified manner of death\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/manner-of-death\",\n \"code\": \"NATURAL_CAUSES\"\n }\n ]\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:89c0605f-8eb8-4f58-98ff-317efe64e91f\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"cause-of-death-method\",\n \"display\": \"Cause of death method\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/cause-of-death-method\",\n \"code\": \"PHYSICIAN\"\n }\n ]\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:6a0bd2db-4fa4-4665-ad62-24821dd01f2a\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"cause-of-death-established\",\n \"display\": \"Cause of death established\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/cause-of-death-established\",\n \"code\": \"true\"\n }\n ]\n }\n }\n }\n ],\n \"meta\": {\n \"lastUpdated\": \"2022-11-30T05:50:26.175Z\"\n }\n}", + "raw": "{\n \"resourceType\": \"Bundle\",\n \"type\": \"document\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:7109f150-6262-4798-835e-55b3e4dd4cfb\",\n \"resource\": {\n \"identifier\": {\n \"system\": \"urn:ietf:rfc:3986\",\n \"value\": \"7109f150-6262-4798-835e-55b3e4dd4cfb\"\n },\n \"resourceType\": \"Composition\",\n \"status\": \"preliminary\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-types\",\n \"code\": \"death-notification\"\n }\n ],\n \"text\": \"Death Notification\"\n },\n \"class\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-classes\",\n \"code\": \"crvs-document\"\n }\n ],\n \"text\": \"CRVS Document\"\n },\n \"title\": \"Death Notification\",\n \"section\": [\n {\n \"title\": \"Informant's details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"informant-details\"\n }\n ],\n \"text\": \"Informant's details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:1363f62f-e34f-4c03-90f3-5fa1d40d0892\"\n }\n ]\n },\n {\n \"title\": \"Deceased details\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/doc-sections\",\n \"code\": \"deceased-details\"\n }\n ],\n \"text\": \"Deceased details\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:233b56c8-aa3d-4d32-9d42-7afe1200fc4f\"\n }\n ]\n },\n {\n \"title\": \"Death encounter\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/sections\",\n \"code\": \"death-encounter\"\n }\n ],\n \"text\": \"Death encounter\"\n },\n \"entry\": [\n {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:cbce7430-3c63-457e-a6d8-ed3b35a97a64\",\n \"resource\": {\n \"resourceType\": \"Task\",\n \"status\": \"draft\",\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/types\",\n \"code\": \"DEATH\"\n }\n ]\n },\n \"focus\": {\n \"reference\": \"urn:uuid:7109f150-6262-4798-835e-55b3e4dd4cfb\"\n },\n \"identifier\": [],\n \"extension\": [\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person\",\n \"valueString\": \"SPOUSE\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-phone-number\",\n \"valueString\": \"+260712345679\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/contact-person-email\",\n \"valueString\": \"axon@gmail.com\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/timeLoggedMS\",\n \"valueInteger\": 0\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/in-complete-fields\",\n \"valueString\": \"N/A\"\n },\n {\n \"url\": \"http://opencrvs.org/specs/extension/regLastOffice\",\n \"valueReference\": {\n \"reference\": \"Location/{{officeId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:1363f62f-e34f-4c03-90f3-5fa1d40d0892\",\n \"resource\": {\n \"resourceType\": \"RelatedPerson\",\n \"relationship\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype\",\n \"code\": \"SPOUSE\"\n }\n ]\n },\n \"patient\": {\n \"reference\": \"urn:uuid:42061dc8-0d97-493a-94e4-e60d0f3fc070\"\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:233b56c8-aa3d-4d32-9d42-7afe1200fc4f\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"value\": \"1234123421\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n }\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Bashir\"\n ],\n \"family\": [\n \"Ahmed\"\n ]\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"1971-12-12\",\n \"maritalStatus\": {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/StructureDefinition/marital-status\",\n \"code\": \"M\"\n }\n ],\n \"text\": \"MARRIED\"\n },\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"deceasedBoolean\": true,\n \"deceasedDateTime\": \"2021-12-12\",\n \"multipleBirthBoolean\": false,\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:42061dc8-0d97-493a-94e4-e60d0f3fc070\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"active\": true,\n \"identifier\": [\n {\n \"value\": \"4321234257\",\n \"type\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/identifier-type\",\n \"code\": \"NATIONAL_ID\"\n }\n ]\n }\n }\n ],\n \"name\": [\n {\n \"use\": \"en\",\n \"given\": [\n \"Selena\"\n ],\n \"family\": [\n \"Begum\"\n ]\n }\n ],\n \"birthDate\": \"1980-12-12\",\n \"deceasedBoolean\": false,\n \"multipleBirthBoolean\": false,\n \"address\": [\n {\n \"type\": \"PRIMARY_ADDRESS\",\n \"line\": [\n \"12\",\n \"Usual Street\",\n \"Usual Residental Area\",\n \"\",\n \"\",\n \"URBAN\"\n ],\n \"city\": \"Meghnan\",\n \"district\": \"{{districtId}}\",\n \"state\": \"{{stateId}}\",\n \"postalCode\": \"52275\",\n \"country\": \"{{countryCode}}\"\n }\n ],\n \"extension\": [\n {\n \"url\": \"http://hl7.org/fhir/StructureDefinition/patient-nationality\",\n \"extension\": [\n {\n \"url\": \"code\",\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"urn:iso:std:iso:3166\",\n \"code\": \"{{countryCode}}\"\n }\n ]\n }\n },\n {\n \"url\": \"period\",\n \"valuePeriod\": {\n \"start\": \"\",\n \"end\": \"\"\n }\n }\n ]\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\",\n \"resource\": {\n \"resourceType\": \"Encounter\",\n \"status\": \"finished\",\n \"location\": [\n {\n \"location\": {\n \"reference\": \"Location/{{facilityId}}\"\n }\n }\n ]\n }\n },\n {\n \"fullUrl\": \"urn:uuid:7b4fcd33-5b39-463a-bbd4-4ac2e41e5f99\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"uncertified-manner-of-death\",\n \"display\": \"Uncertified manner of death\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/manner-of-death\",\n \"code\": \"NATURAL_CAUSES\"\n }\n ]\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:89c0605f-8eb8-4f58-98ff-317efe64e91f\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"cause-of-death-method\",\n \"display\": \"Cause of death method\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/cause-of-death-method\",\n \"code\": \"PHYSICIAN\"\n }\n ]\n }\n }\n },\n {\n \"fullUrl\": \"urn:uuid:6a0bd2db-4fa4-4665-ad62-24821dd01f2a\",\n \"resource\": {\n \"resourceType\": \"Observation\",\n \"status\": \"final\",\n \"context\": {\n \"reference\": \"urn:uuid:e9e011f5-78dc-4e2f-8ce2-ea80e04a510f\"\n },\n \"category\": [\n {\n \"coding\": [\n {\n \"system\": \"http://hl7.org/fhir/observation-category\",\n \"code\": \"vital-signs\",\n \"display\": \"Vital Signs\"\n }\n ]\n }\n ],\n \"code\": {\n \"coding\": [\n {\n \"system\": \"http://loinc.org\",\n \"code\": \"cause-of-death-established\",\n \"display\": \"Cause of death established\"\n }\n ]\n },\n \"valueCodeableConcept\": {\n \"coding\": [\n {\n \"system\": \"http://opencrvs.org/specs/cause-of-death-established\",\n \"code\": \"true\"\n }\n ]\n }\n }\n }\n ],\n \"meta\": {\n \"lastUpdated\": \"2022-11-30T05:50:26.175Z\"\n }\n}", "options": { "raw": { "language": "json" @@ -141,10 +141,6 @@ } ], "variable": [ - { - "key": "officeLocationId", - "value": "c24c0b72-11b5-4c1a-bbb7-61112fa6f481" - }, { "key": "officeId", "value": "0be1dc9b-5c8b-4d20-a88c-08cafc71c99a" From 61bad39395cd8f262c9f428c35aea6fc76e7085e Mon Sep 17 00:00:00 2001 From: Pyry Rouvila Date: Wed, 5 Jun 2024 13:57:20 +0300 Subject: [PATCH 020/146] fix: simplify hierarchy now that it can be queried through graphql (#116) --- src/form/index.ts | 12 ++++---- src/utils/users.ts | 74 ++++++++-------------------------------------- 2 files changed, 18 insertions(+), 68 deletions(-) diff --git a/src/form/index.ts b/src/form/index.ts index 4fc80de45..51323c305 100644 --- a/src/form/index.ts +++ b/src/form/index.ts @@ -14,15 +14,13 @@ import { birthForm } from './birth' import { deathForm } from './death' import { marriageForm } from './marriage' import { IForms, Event } from './types/types' -import { getUserOfficeLocationHierarchy } from '@countryconfig/utils/users' +import { fetchUserLocationHierarchy } from '@countryconfig/utils/users' export async function formHandler(req: Request): Promise { - const addressHierarchy = ( - await getUserOfficeLocationHierarchy( - req.headers.authorization, - req.auth.credentials.sub as string - ) - ).map(({ id }) => id) + const addressHierarchy = await fetchUserLocationHierarchy( + req.headers.authorization, + req.auth.credentials.sub as string + ) // ====================== NOTE REGARDING MIGRATING FROM OPNCRVS v1.2 OR EARLIER ====================== // SIMPLY RETURN A JSON OF YOUR FULL FORM HERE, WITH THE ADDITION OF THE NEW MARRIAGE AND VERSION PROP diff --git a/src/utils/users.ts b/src/utils/users.ts index c1ecea7a1..415c77bb5 100644 --- a/src/utils/users.ts +++ b/src/utils/users.ts @@ -1,20 +1,20 @@ -import { APPLICATION_CONFIG_URL, GATEWAY_URL } from '@countryconfig/constants' +import { GATEWAY_URL } from '@countryconfig/constants' import fetch from 'node-fetch' import gql from 'graphql-tag' import { print } from 'graphql/language/printer' import { URL } from 'url' type GetUser = { - primaryOffice?: { - id: string + primaryOffice: { + hierarchy: Array<{ + id: string + }> } } - -type Location = { - id: string -} - -async function getUser(token: string, userId: string): Promise { +export async function fetchUserLocationHierarchy( + token: string, + userId: string +) { const url = new URL('graphql', GATEWAY_URL) const getUsersRes = await fetch(url, { method: 'POST', @@ -30,36 +30,11 @@ async function getUser(token: string, userId: string): Promise { query: print(gql` query fetchUser($userId: String!) { getUser(userId: $userId) { - id - creationDate - username - practitionerId - mobile - systemRole - role { - _id - labels { - lang - label - __typename - } - __typename - } - status - name { - use - firstNames - familyName - __typename - } primaryOffice { - id - name - alias - status - __typename + hierarchy { + id + } } - __typename } } `) @@ -70,28 +45,5 @@ async function getUser(token: string, userId: string): Promise { data: { getUser: GetUser } } - return res.data.getUser -} - -async function getLocationHierarchy(locationId: string): Promise { - const url = new URL( - `location/${locationId}/hierarchy`, - APPLICATION_CONFIG_URL - ) - const res = await fetch(url) - if (!res.ok) { - throw new Error('Unable to retrieve location hierarchy') - } - return res.json() -} - -export async function getUserOfficeLocationHierarchy( - token: string, - userId: string -): Promise { - const user = await getUser(token, userId) - if (!user.primaryOffice) { - throw new Error('No primary office found for user') - } - return getLocationHierarchy(user.primaryOffice.id) + return res.data.getUser.primaryOffice.hierarchy.map(({ id }) => id) } From 81b1da8e154c9f7fe9768d733d1f8a248da55288 Mon Sep 17 00:00:00 2001 From: Tahmid Rahman <42269993+tahmidrahman-dsi@users.noreply.github.com> Date: Wed, 5 Jun 2024 19:46:30 +0600 Subject: [PATCH 021/146] [OCRVS-7016] Fix typo in traefik router rule (#130) * fix: typo in countryconfig traefik rule * fix: remove duplicate traefik route definition in config --- infrastructure/docker-compose.deploy.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 82fdfe861..bce5ed74f 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -554,7 +554,7 @@ services: deploy: labels: - 'traefik.enable=true' - - 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`) && !Path(`/notification`) &!Path(`/dashboards/queries.json`)' + - 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`) && !Path(`/notification`) && !Path(`/dashboards/queries.json`)' - 'traefik.http.services.countryconfig.loadbalancer.server.port=3040' - 'traefik.http.routers.countryconfig.tls=true' - 'traefik.http.routers.countryconfig.tls.certresolver=certResolver' @@ -850,7 +850,7 @@ services: deploy: labels: - 'traefik.enable=true' - - 'traefik.http.routers.config.rule=Host(`config.{{hostname}}`) && !Path(`/dashboardQueries`)' + - 'traefik.http.routers.config.rule=Host(`config.{{hostname}}`)' - 'traefik.http.services.config.loadbalancer.server.port=2021' - 'traefik.http.routers.config.tls=true' - 'traefik.http.routers.config.tls.certresolver=certResolver' @@ -862,9 +862,6 @@ services: - 'traefik.http.middlewares.config.headers.stsseconds=31536000' - 'traefik.http.middlewares.config.headers.stsincludesubdomains=true' - 'traefik.http.middlewares.config.headers.stspreload=true' - - 'traefik.http.middlewares.block-internal-routes.ipwhitelist.sourcerange=255.255.255.255' - - 'traefik.http.routers.block-dashboard-queries.rule=Host(`countryconfig.{{hostname}}`) && Path(`/dashboardQueries`)' - - 'traefik.http.routers.block-dashboard-queries.middlewares=block-internal-routes' replicas: 1 networks: - overlay_net From b972563b75e4861fec4b8d505f4aa6aa059fed2e Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 6 Jun 2024 12:17:38 +0600 Subject: [PATCH 022/146] chore: remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field (#114) * chore: remove splitView prop * docs: update CHANGELOG --- CHANGELOG.md | 4 ++++ src/form/types/types.ts | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ca59342..83ce038b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.6.0 (TBD) + +- Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field + ## [1.5.0] - Change auth URLs to access them via gateway diff --git a/src/form/types/types.ts b/src/form/types/types.ts index 657626242..c54c6d5a9 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -413,7 +413,6 @@ export interface IDocumentUploaderWithOptionsFormField extends IFormFieldBase { type: typeof DOCUMENT_UPLOADER_WITH_OPTION options: ISelectOption[] hideOnEmptyOption?: boolean - splitView?: boolean } export interface ISimpleDocumentUploaderFormField extends IFormFieldBase { type: typeof SIMPLE_DOCUMENT_UPLOADER From ddaf0e69cbd5c9bcd99daf1fa4991b5c399f9ea2 Mon Sep 17 00:00:00 2001 From: Anamul Haque Date: Fri, 7 Jun 2024 12:56:03 +0600 Subject: [PATCH 023/146] fix: show correct 'Place of Certification' in certificates (#131) * in place of certification, loggedin user used * changelog updated * modified for death and marriage --- CHANGELOG.md | 2 ++ .../certificates/source/Farajaland-birth-certificate-v2.svg | 2 +- .../certificates/source/Farajaland-death-certificate-v2.svg | 2 +- .../certificates/source/Farajaland-marriage-certificate-v2.svg | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ce038b3..e0438f326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ - Use image tag instead of patterns in certificate SVGs - Generate default address according to logged-in user's location - Remove authentication from dashboard queries route +- Added french translation of informant for print certificate flow, issue certificate flow & correction flow +- In the certificate, the 'Place of Certification' now accurately reflects the correct location. - Added french translation of informant for print certificate flow, issue certificate flow & correction flow - Groom's and Bride's name, printIssue translation variables updated [#124](https://github.com/opencrvs/opencrvs-countryconfig/pull/124) diff --git a/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg index 4e7dc7f2d..6d66223f4 100644 --- a/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg @@ -26,7 +26,7 @@ {{certificateDate}} Date of certification / Date de délivrance -{{location registrar.officeId 'name'}}
{{location registrar.districtId 'name'}}, {{location registrar.stateId 'name'}}, Farajaland +{{location loggedInUser.officeId 'name'}}
{{location loggedInUser.districtId 'name'}}, {{location loggedInUser.stateId 'name'}}, Farajaland Place of certification / Lieu de délivrance {{mosipAid}} diff --git a/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg index 086490d8b..5ed6a20cd 100644 --- a/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg @@ -8,7 +8,7 @@ {{certificateDate}} Date of certification / Date de délivrance -{{location registrar.officeId 'name'}}{{location registrar.districtId 'name'}}, {{location registrar.stateId 'name'}}, Farajaland +{{location loggedInUser.officeId 'name'}}{{location loggedInUser.districtId 'name'}}, {{location loggedInUser.stateId 'name'}}, Farajaland Place of certification / Lieu de certification Registrar / L'Officier de l'État Civil diff --git a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg index cda9f1845..3c0dca946 100644 --- a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg @@ -27,7 +27,7 @@ {{certificateDate}} Date of certification / Date de délivrance -{{location registrar.officeId 'name'}}{{location registrar.districtId 'name'}}, {{location registrar.stateId 'name'}}, Farajaland +{{location loggedInUser.officeId 'name'}}{{location loggedInUser.districtId 'name'}}, {{location loggedInUser.stateId 'name'}}, Farajaland Place of certification / Lieu de certification Registrar / L'Officier de l'État Civil From db82ae6842d606e90115d8e0045e978c63340a97 Mon Sep 17 00:00:00 2001 From: Tahmid Rahman <42269993+tahmidrahman-dsi@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:07:21 +0600 Subject: [PATCH 024/146] feat: support for image compression configuration (#132) * Language updates for dynamic maxSize for document uploader input * chore: update types for new properties * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/form/death/required-sections.ts | 2 +- src/form/types/types.ts | 2 ++ src/translations/client.csv | 6 +++--- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0438f326..bc3b463db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - In the certificate, the 'Place of Certification' now accurately reflects the correct location. - Added french translation of informant for print certificate flow, issue certificate flow & correction flow - Groom's and Bride's name, printIssue translation variables updated [#124](https://github.com/opencrvs/opencrvs-countryconfig/pull/124) +- Add support for image compression configuration ## [1.4.1](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.0...v1.4.1) diff --git a/src/form/death/required-sections.ts b/src/form/death/required-sections.ts index d3fab203c..eeabd9930 100644 --- a/src/form/death/required-sections.ts +++ b/src/form/death/required-sections.ts @@ -177,4 +177,4 @@ export const documentsSection = { ] } ] -} as ISerializedFormSection +} satisfies ISerializedFormSection diff --git a/src/form/types/types.ts b/src/form/types/types.ts index c54c6d5a9..ab93bc784 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -413,6 +413,8 @@ export interface IDocumentUploaderWithOptionsFormField extends IFormFieldBase { type: typeof DOCUMENT_UPLOADER_WITH_OPTION options: ISelectOption[] hideOnEmptyOption?: boolean + compressImagesToSizeMB?: number + maxSizeMB?: number } export interface ISimpleDocumentUploaderFormField extends IFormFieldBase { type: typeof SIMPLE_DOCUMENT_UPLOADER diff --git a/src/translations/client.csv b/src/translations/client.csv index b1ce67b03..5ef7cdcb1 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -1375,9 +1375,9 @@ home.header.typeName,Search menu name type,Name,Nom home.header.typePhone,Search menu phone no type,Phone no.,N° de téléphone home.header.typeRN,Search menu brn drn type,Registration no.,N ° d'enregistrement. imageUploadOption.upload.documentType,Show error message if the document type is not selected,Please select the type of document first,Veuillez d'abord sélectionner le type de document -imageUploadOption.upload.error,Show error messages while uploading,File format not supported. Please upload a .png or .jpg (max 5mb),"Format de fichier non pris en charge. Veuillez joindre un png, jpg (max 5mb)" -imageUploadOption.upload.imageFormat,Show error message if the selected image type is not supported,File format not supported. Please upload a .png or .jpg (max 5mb),Le format d'image n'est pas supporté. Veuillez joindre un png ou un jpg (max 5mb) -imageUploadOption.upload.overSized,Error message for Attachment size greater than 5mb.,File is too large. Please upload a file less than 5mb,Le fichier est trop volumineux. Veuillez joindre un fichier de moins de 5mb +imageUploadOption.upload.error,Show error messages while uploading,File format not supported. Please upload a .png or .jpg (max {maxSize}mb),"Format de fichier non pris en charge. Veuillez joindre un png, jpg (max {maxSize}mb)" +imageUploadOption.upload.imageFormat,Show error message if the selected image type is not supported,File format not supported. Please upload a .png or .jpg (max {maxSize}mb),Le format d'image n'est pas supporté. Veuillez joindre un png ou un jpg (max {maxSize}mb) +imageUploadOption.upload.overSized,Error message for Attachment size greater than defined max size.,File is too large. Please upload a file less than {maxSize}mb,Le fichier est trop volumineux. Veuillez joindre un fichier de moins de {maxSize}mb informant.name,Informant section name,Informant,Informateur informant.title,Informant section title,Informant type,Sélectionner l'informateur integrations.activate,Label for activate option,Activate,Activer From e83abd618e22aa25e201322d9a948700a0364c41 Mon Sep 17 00:00:00 2001 From: Tahmid Rahman <42269993+tahmidrahman-dsi@users.noreply.github.com> Date: Tue, 11 Jun 2024 18:38:40 +0600 Subject: [PATCH 025/146] [OCRVS-6578] Metabase admin credentials environment variables (#128) * Add env variable metabase super user email * add env metabase admin password, rename env variable metabase admin email * update setup-environment with metabase related questions * update CHANGELOG.md * refactor: make metabase admin variable as secret complying with project convention * chore: update setup environment script to ask metabase admin related questions --- CHANGELOG.md | 5 +-- infrastructure/docker-compose.deploy.yml | 2 ++ .../environments/setup-environment.ts | 33 ++++++++++++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc3b463db..4fd0ccd6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,11 +22,12 @@ - Use image tag instead of patterns in certificate SVGs - Generate default address according to logged-in user's location - Remove authentication from dashboard queries route -- Added french translation of informant for print certificate flow, issue certificate flow & correction flow -- In the certificate, the 'Place of Certification' now accurately reflects the correct location. +- Added french translation of informant for print certificate flow, issue certificate flow & correction flow +- In the certificate, the 'Place of Certification' now accurately reflects the correct location. - Added french translation of informant for print certificate flow, issue certificate flow & correction flow - Groom's and Bride's name, printIssue translation variables updated [#124](https://github.com/opencrvs/opencrvs-countryconfig/pull/124) - Add support for image compression configuration +- Provide env variables for metabase admin credentials ## [1.4.1](https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.0...v1.4.1) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index bce5ed74f..4a3e03cbc 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -972,6 +972,8 @@ services: - OPENCRVS_METABASE_MAP_URL=http://countryconfig:3040/content/farajaland-map.geojson - OPENCRVS_METABASE_MAP_REGION_KEY=State - OPENCRVS_METABASE_MAP_REGION_NAME=State + - OPENCRVS_METABASE_ADMIN_EMAIL=${OPENCRVS_METABASE_ADMIN_EMAIL} + - OPENCRVS_METABASE_ADMIN_PASSWORD=${OPENCRVS_METABASE_ADMIN_PASSWORD} deploy: labels: - 'traefik.enable=true' diff --git a/infrastructure/environments/setup-environment.ts b/infrastructure/environments/setup-environment.ts index 2f8a80d4b..60fdc1fa0 100644 --- a/infrastructure/environments/setup-environment.ts +++ b/infrastructure/environments/setup-environment.ts @@ -23,6 +23,7 @@ import { writeFileSync } from 'fs' import { exec as callbackExec } from 'child_process' import { promisify } from 'util' import { join } from 'path' +import { initial } from 'lodash' const exec = promisify(callbackExec) @@ -718,6 +719,28 @@ const derivedVariables = [ } ] as const +const metabaseAdminQuestions = [ + { + valueType: 'SECRET' as const, + name: 'OPENCRVS_METABASE_ADMIN_EMAIL', + type: 'text' as const, + message: + 'Email for Metabase super admin. Used as a username when logging in to the dashboard', + valueLabel: 'OPENCRVS_METABASE_ADMIN_EMAIL', + scope: 'ENVIRONMENT' as const, + initial: 'user@opencrvs.org' + }, + { + valueType: 'SECRET' as const, + name: 'OPENCRVS_METABASE_ADMIN_PASSWORD', + type: 'text' as const, + message: 'Password for Metabase super admin.', + valueLabel: 'OPENCRVS_METABASE_ADMIN_PASSWORD', + scope: 'ENVIRONMENT' as const, + initial: generateLongPassword() + } +] + ALL_QUESTIONS.push( ...dockerhubQuestions, ...sshQuestions, @@ -731,7 +754,8 @@ ALL_QUESTIONS.push( ...vpnQuestions, ...vpnHostQuestions, ...sentryQuestions, - ...derivedVariables + ...derivedVariables, + ...metabaseAdminQuestions ) ;(async () => { const { type } = await prompts( @@ -950,6 +974,13 @@ ALL_QUESTIONS.push( } } + log('\n', kleur.bold().underline('METABASE ADMIN')) + await promptAndStoreAnswer( + environment, + metabaseAdminQuestions, + existingValues + ) + log('\n', kleur.bold().underline('SMTP')) await promptAndStoreAnswer(environment, emailQuestions, existingValues) From 4a85a6ffd4fac581e997a1e7f180895b5d04a21b Mon Sep 17 00:00:00 2001 From: Jaakko Hirvioja <98736266+jhirvioja@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:02:59 +0300 Subject: [PATCH 026/146] Update setup-environment.ts & deploy.sh to support non-default SSH port (#125) * Remove hardcoded SSH_PORT * Add SSH_PORT to validate_options * Add --ssh_port to print_usage_and_exit * Add sshPort to sshQuestions * Update "standard" -> "default" * Add --ssh_port to deployment workflows --- .github/workflows/deploy-prod.yml | 1 + .github/workflows/deploy.yml | 1 + infrastructure/deployment/deploy.sh | 8 ++++++-- infrastructure/environments/setup-environment.ts | 11 +++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 8d75aa765..b43fd166a 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -143,6 +143,7 @@ jobs: --environment=${{ github.event.inputs.environment }} \ --host=${{ vars.DOMAIN }} \ --ssh_host=${{ secrets.SSH_HOST }} \ + --ssh_port=${{ secrets.SSH_PORT }} \ --ssh_user=${{ secrets.SSH_USER }} \ --version=${{ github.event.inputs.core-image-tag }} \ --country_config_version=${{ github.event.inputs.countryconfig-image-tag }} \ diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9630a00f9..5b757f9d1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -147,6 +147,7 @@ jobs: --environment=${{ github.event.inputs.environment }} \ --host=${{ vars.DOMAIN }} \ --ssh_host=${{ secrets.SSH_HOST }} \ + --ssh_port=${{ secrets.SSH_PORT }} \ --ssh_user=${{ secrets.SSH_USER }} \ --version=${{ github.event.inputs.core-image-tag }} \ --country_config_version=${{ github.event.inputs.countryconfig-image-tag }} \ diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 0838d8c8f..ab14495be 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -63,7 +63,6 @@ done # Default values -SSH_PORT=22 SSH_ARGS=${SSH_ARGS:-""} LOG_LOCATION=${LOG_LOCATION:-/var/log} @@ -90,7 +89,7 @@ function trapint { } print_usage_and_exit () { - echo 'Usage: ./deploy.sh --host --environment --ssh_host --ssh_user --version --country_config_version --replicas' + echo 'Usage: ./deploy.sh --host --environment --ssh_host --ssh_port --ssh_user --version --country_config_version --replicas' echo " --environment can be 'production', 'development', 'qa' or similar" echo ' --host is the server to deploy to' echo " --version can be any OpenCRVS Core docker image tag or 'latest'" @@ -120,6 +119,11 @@ validate_options() { print_usage_and_exit fi + if [ -z "$SSH_PORT" ] ; then + echo 'Error: Argument --ssh_port is required.' + print_usage_and_exit + fi + if [ -z "$SSH_USER" ] ; then echo 'Error: Argument --ssh_user is required.' print_usage_and_exit diff --git a/infrastructure/environments/setup-environment.ts b/infrastructure/environments/setup-environment.ts index 60fdc1fa0..2a488d7fb 100644 --- a/infrastructure/environments/setup-environment.ts +++ b/infrastructure/environments/setup-environment.ts @@ -313,6 +313,17 @@ const sshQuestions = [ initial: process.env.SSH_USER || 'provision', scope: 'ENVIRONMENT' as const }, + { + name: 'sshPort', + type: 'number' as const, + message: + 'What port number is used in establishing the SSH connection? This usually is the default 22. If you are an advanced user, and have set a different port, provide it here.', + valueType: 'SECRET' as const, + validate: notEmpty, + valueLabel: 'SSH_PORT', + initial: process.env.SSH_PORT ? parseInt(process.env.SSH_PORT) : 22, + scope: 'ENVIRONMENT' as const + }, { name: 'sshArgs', type: 'text' as const, From be0eaf15b9b3525fd3e87e7bf896f4403cf4ef3d Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Thu, 13 Jun 2024 14:11:13 +0300 Subject: [PATCH 027/146] Remove noise from development time logs (#138) * reduce noise from development logs related to https://github.com/opencrvs/opencrvs-core/pull/7022 * order deps alphabetically * remove debug log items --- package.json | 15 +- src/form/custom-fields.ts | 11 +- src/index.ts | 14 +- src/logger.ts | 14 +- src/utils/mapping/field-mapping-utils.ts | 5 +- yarn.lock | 354 ++++++++++------------- 6 files changed, 184 insertions(+), 229 deletions(-) diff --git a/package.json b/package.json index 363a7f8b4..bf2072b87 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "@graphql-codegen/add": "^3.1.1", "@graphql-codegen/cli": "^3.3.1", "@graphql-codegen/introspection": "^3.0.1", - "@graphql-codegen/typescript": "^3.0.4", "@graphql-codegen/typescript-operations": "^3.0.4", + "@graphql-codegen/typescript": "^3.0.4", "@inquirer/editor": "^1.2.13", "@octokit/core": "4.2.1", "@types/google-libphonenumber": "^7.4.23", @@ -49,15 +49,16 @@ "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "cypress-xpath": "^2.0.1", - "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", + "eslint": "^8.43.0", "husky": "1.0.0-rc.13", "inquirer": "^9.2.12", "kleur": "^4.1.5", "libsodium-wrappers": "^0.7.13", "lint-staged": "^7.1.0", "nodemon": "^2.0.22", + "pino-pretty": "^11.0.0", "prettier": "^2.8.8", "react-intl": "^6.4.3" }, @@ -70,15 +71,13 @@ "@types/csv2json": "^1.4.0", "@types/fhir": "^0.0.30", "@types/geojson": "^7946.0.4", - "@types/hapi-pino": "^6.3.0", "@types/hapi__boom": "^9.0.1", "@types/hapi__hapi": "^20.0.0", "@types/jwt-decode": "^2.2.1", "@types/lodash": "^4.14.117", - "@types/node": "^10.12.5", "@types/node-fetch": "^2.6.2", + "@types/node": "^10.12.5", "@types/nodemailer": "^6.4.14", - "@types/pino": "^5.14.0", "app-module-path": "^2.2.0", "chalk": "^2.4.1", "country-data": "^0.0.31", @@ -89,11 +88,11 @@ "dotenv": "^6.1.0", "esbuild": "^0.18.9", "google-libphonenumber": "^3.2.32", - "graphql": "^16.3.0", "graphql-tag": "^2.12.6", + "graphql": "^16.3.0", "handlebars": "^4.7.7", "hapi-auth-jwt2": "10.4.0", - "hapi-pino": "^6.3.0", + "hapi-pino": "^9.0.0", "hapi-sentry": "^3.1.0", "joi": "^17.4.0", "jwt-decode": "^2.2.0", @@ -103,7 +102,7 @@ "nodemailer": "^6.9.8", "opener": "^1.5.1", "p-queue": "^6.6.2", - "pino": "^5.14.0", + "pino": "^7.0.0", "prompts": "^2.4.2", "short-uid": "^0.1.0", "ts-node": "^10.9.1", diff --git a/src/form/custom-fields.ts b/src/form/custom-fields.ts index e6d713d33..d43fd8aa1 100644 --- a/src/form/custom-fields.ts +++ b/src/form/custom-fields.ts @@ -8,13 +8,13 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ +import { uppercaseFirstLetter } from '@countryconfig/utils' +import { getCustomFieldMapping } from '@countryconfig/utils/mapping/field-mapping-utils' +import { camelCase } from 'lodash' import { MessageDescriptor } from 'react-intl' +import { getNationalIDValidators } from './common/default-validation-conditionals' import { formMessageDescriptors } from './common/messages' import { Conditional, SerializedFormField } from './types/types' -import { getCustomFieldMapping } from '@countryconfig/utils/mapping/field-mapping-utils' -import { getNationalIDValidators } from './common/default-validation-conditionals' -import { camelCase } from 'lodash' -import { uppercaseFirstLetter } from '@countryconfig/utils' // ======================= CUSTOM FIELD CONFIGURATION ======================= @@ -183,8 +183,7 @@ export function getIDNumber( camelCase(idValue) )}` const validators = getValidators(sectionId, idValue) - // eslint-disable-next-line no-console - console.log('Custom field addded with handlebar: ', fieldName) + return { name: fieldName, required, diff --git a/src/index.ts b/src/index.ts index 56b69f257..7ff65f615 100644 --- a/src/index.ts +++ b/src/index.ts @@ -69,18 +69,18 @@ export interface ITokenPayload { } export default function getPlugins() { - const plugins: any[] = [ - inert, - JWT, - { + const plugins: any[] = [inert, JWT] + + if (process.env.NODE_ENV === 'production') { + plugins.push({ plugin: Pino, options: { prettyPrint: false, logPayload: false, instance: logger } - } - ] + }) + } if (SENTRY_DSN) { plugins.push({ @@ -165,7 +165,7 @@ async function getPublicKey(): Promise { const response = await fetch(`${AUTH_URL}/.well-known`) return response.text() } catch (error) { - console.log( + logger.error( `Failed to fetch public key from Core. Make sure Core is running, and you are able to connect to ${AUTH_URL}/.well-known.` ) if (process.env.NODE_ENV === 'production') { diff --git a/src/logger.ts b/src/logger.ts index a042cb232..c17ff7774 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -9,7 +9,19 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import pino from 'pino' -export const logger = pino() +export const logger = + process.env.NODE_ENV === 'production' + ? pino() + : pino({ + transport: { + target: 'pino-pretty', + options: { + colorize: true, + ignore: 'pid,hostname' + } + } + }) + if (process.env.NODE_ENV === 'test') { logger.level = 'silent' } diff --git a/src/utils/mapping/field-mapping-utils.ts b/src/utils/mapping/field-mapping-utils.ts index ef5f5823a..a4a4451ae 100644 --- a/src/utils/mapping/field-mapping-utils.ts +++ b/src/utils/mapping/field-mapping-utils.ts @@ -316,10 +316,7 @@ export function getFieldMapping( export function getCustomFieldMapping(fieldId: string): IFormFieldMapping { const customFieldCertificateHandlebar = createCustomFieldHandlebarName(fieldId) - console.log( - 'Custom field addded with handlebar: ', - customFieldCertificateHandlebar - ) + return { mutation: { operation: 'customFieldToQuestionnaireTransformer' diff --git a/yarn.lock b/yarn.lock index 8d13b9a48..72d987e52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1406,11 +1406,6 @@ resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-2.1.0.tgz#66aff77094dc3080bd5df44ec63881f2676eb020" integrity sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q== -"@hapi/bourne@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" - integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== - "@hapi/call@^8.0.0": version "8.0.1" resolved "https://registry.yarnpkg.com/@hapi/call/-/call-8.0.1.tgz#9e64cd8ba6128eb5be6e432caaa572b1ed8cd7c0" @@ -1494,16 +1489,11 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-10.0.1.tgz#ee9da297fabc557e1c040a0f44ee89c266ccc306" integrity sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw== -"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.0.4", "@hapi/hoek@^9.1.0": +"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.0.4", "@hapi/hoek@^9.1.0", "@hapi/hoek@^9.2.1": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== -"@hapi/hoek@^8.3.0": - version "8.5.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" - integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== - "@hapi/inert@^6.0.3": version "6.0.5" resolved "https://registry.yarnpkg.com/@hapi/inert/-/inert-6.0.5.tgz#0c5a28e9b5a637d3d47419859bb7163d0b194a61" @@ -2021,11 +2011,6 @@ dependencies: "@types/node" "*" -"@types/events@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== - "@types/fhir@^0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/fhir/-/fhir-0.0.30.tgz#a0aec3b2d7dd2a7584474dac446ede5f9a4d7a13" @@ -2048,15 +2033,6 @@ dependencies: handlebars "*" -"@types/hapi-pino@^6.3.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@types/hapi-pino/-/hapi-pino-6.4.0.tgz#3e1103918812d8bd8d4f189b68d08c086eafe74c" - integrity sha512-5okQF+hl3P1DX1S/rR/zWQpEYP8qzwj/LcXPqRx5QT00h6BcViM8HAO87cqho2y8LsVzLAHfOeQp1/6jgV8E7A== - dependencies: - "@types/hapi__hapi" "*" - "@types/node" "*" - "@types/pino" "*" - "@types/hapi__boom@^9.0.1": version "9.0.1" resolved "https://registry.yarnpkg.com/@types/hapi__boom/-/hapi__boom-9.0.1.tgz#8fbf719d0358e361b831802fb05fb4feced2ac29" @@ -2069,7 +2045,7 @@ resolved "https://registry.yarnpkg.com/@types/hapi__catbox/-/hapi__catbox-10.2.4.tgz#4d0531a6c2d0e45024f724020d536041ef8ffe30" integrity sha512-A6ivRrXD5glmnJna1UAGw87QNZRp/vdFO9U4GS+WhOMWzHnw+oTGkMvg0g6y1930CbeheGOCm7A1qHsqH7AXqg== -"@types/hapi__hapi@*", "@types/hapi__hapi@^20.0.0": +"@types/hapi__hapi@*", "@types/hapi__hapi@^20.0.0", "@types/hapi__hapi@^20.0.10": version "20.0.13" resolved "https://registry.yarnpkg.com/@types/hapi__hapi/-/hapi__hapi-20.0.13.tgz#ea8ce83c192f6e8106f6e76e40f795e7e36d0615" integrity sha512-LP4IPfhIO5ZPVOrJo7H8c8Slc0WYTFAUNQX1U0LBPKyXioXhH5H2TawIgxKujIyOhbwoBbpvOsBf6o5+ToJIrQ== @@ -2211,21 +2187,6 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/pino@*": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@types/pino/-/pino-7.0.5.tgz#1c84a81b924a6a9e263dbb581dffdbad7a3c60c4" - integrity sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg== - dependencies: - pino "*" - -"@types/pino@^5.14.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@types/pino/-/pino-5.20.0.tgz#d4bafa71d992d223d29c857c11988e8c3abc66f0" - integrity sha512-gz3Ahvx1UDEveXViOQtYqnUkjSVQFdoJqpZTW/63spEHwOGRJRJIi3JMJSClp5Sk1x1ljn9tHWjGczmP6s/rvg== - dependencies: - "@types/events" "*" - "@types/node" "*" - "@types/prompts@^2.4.9": version "2.4.9" resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.4.9.tgz#8775a31e40ad227af511aa0d7f19a044ccbd371e" @@ -2429,10 +2390,10 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -abstract-logging@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-1.0.0.tgz#8b7deafd310559bc28f77724dd1bb30177278c1b" - integrity sha512-CWDjsyA74oOOK6ekFOE00fEUR/twE2SUmXWFQpF1J1fxaq9wSI2tnK3z0vAhpEcmCqw8xD/+A2M2a2M+3bCe8A== +abstract-logging@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" + integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA== acorn-jsx@^5.3.2: version "5.3.2" @@ -2555,16 +2516,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -args@^5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/args/-/args-5.0.3.tgz#943256db85021a85684be2f0882f25d796278702" - integrity sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA== - dependencies: - camelcase "5.0.0" - chalk "2.4.2" - leven "2.1.0" - mri "1.1.4" - arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -2846,11 +2797,6 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" -camelcase@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== - camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -2875,15 +2821,6 @@ chalk@*: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.1.2.tgz#d957f370038b75ac572471e83be4c5ca9f8e8c45" integrity sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ== -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2895,6 +2832,15 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -3113,6 +3059,11 @@ colorette@^2.0.16: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +colorette@^2.0.7: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -3302,10 +3253,10 @@ date-fns@^2.28.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== -dateformat@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" - integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== +dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== debounce@^1.2.0: version "1.2.1" @@ -3463,6 +3414,16 @@ duplexify@^4.1.1: readable-stream "^3.1.1" stream-shift "^1.0.0" +duplexify@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.3.tgz#a07e1c0d0a2c001158563d32592ba58bddb0236f" + integrity sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.2" + ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -3764,6 +3725,11 @@ extract-files@^11.0.0: resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-11.0.0.tgz#b72d428712f787eef1f5193aff8ab5351ca8469a" integrity sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ== +fast-copy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" + integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== + fast-decode-uri-component@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" @@ -3807,17 +3773,12 @@ fast-querystring@^1.1.1: dependencies: fast-decode-uri-component "^1.0.1" -fast-redact@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-2.1.0.tgz#dfe3c1ca69367fb226f110aa4ec10ec85462ffdf" - integrity sha512-0LkHpTLyadJavq9sRzzyqIoMZemWli77K2/MGOkafrR64B9ItrvZ9aT+jluvNDsv0YEHjSNhlMBtbokuoqii4A== - -fast-redact@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" - integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== +fast-redact@^3.0.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== -fast-safe-stringify@^2.0.7: +fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== @@ -3956,11 +3917,6 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flatstr@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" - integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== - flatted@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" @@ -4212,15 +4168,16 @@ hapi-auth-jwt2@10.4.0: cookie "^0.4.0" jsonwebtoken "^9.0.0" -hapi-pino@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/hapi-pino/-/hapi-pino-6.5.0.tgz#33100f07626e7d8cef2b36965cbfb68cdb39c937" - integrity sha512-262F+AJpNHCCGIPpqugPtVWU2plXyCcjeXkbcrD60LRg/tcobLAHuzR6usNcKCansJbrcCy+/kBXYcKQGae7+g== +hapi-pino@^9.0.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/hapi-pino/-/hapi-pino-9.4.0.tgz#4fa400ffd8cdf8777176d5cf2f3e6e0e11688c63" + integrity sha512-QGxTgsAOw2siV8/vspYmFc19/lCeoPe/LLafSJ6NiUQNPTQmo6/PbiEtKVHjL1Mw1REokQCDZUOuOA80einRKQ== dependencies: - "@hapi/hoek" "^8.3.0" - abstract-logging "^1.0.0" - pino "^5.13.5" - pino-pretty "^3.2.2" + "@hapi/hoek" "^9.2.1" + "@types/hapi__hapi" "^20.0.10" + abstract-logging "^2.0.1" + get-caller-file "^2.0.5" + pino "^7.8.1" hapi-sentry@^3.1.0: version "3.2.0" @@ -4318,6 +4275,11 @@ header-case@^2.0.4: capital-case "^1.0.4" tslib "^2.0.3" +help-me@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" + integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== + hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -4790,11 +4752,6 @@ jiti@^1.17.1: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.18.2.tgz#80c3ef3d486ebf2450d9335122b32d121f2a83cd" integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== -jmespath@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" - integrity sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w== - joi@^17.2.1, joi@^17.3.0, joi@^17.4.0: version "17.6.4" resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.4.tgz#4d9536a059ef0762c718ae98673016b3ec151abd" @@ -4811,10 +4768,10 @@ jose@^4.11.4: resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.1.tgz#74f12a621ea2ef850bf0dd8405e2ee4041aea934" integrity sha512-SgjXLpP7jhQkUNKL6RpowoR/IF4QKE+WjLDMpNnh2vmhiFs67NftrNpvFtgbwpvRdtueFliahYYWz9E+XZZQlg== -joycon@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615" - integrity sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ== +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -4959,7 +4916,7 @@ kleur@^4.1.5: resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== -leven@2.1.0, leven@^2.1.0: +leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" integrity sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA== @@ -5297,11 +5254,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mri@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" - integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -5510,6 +5462,11 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +on-exit-leak-free@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" + integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== + on-exit-leak-free@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz#5c703c968f7e7f851885f6459bf8a8a57edc9cc4" @@ -5780,69 +5737,63 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== -pino-abstract-transport@v1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" - integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA== +pino-abstract-transport@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" + integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== dependencies: readable-stream "^4.0.0" split2 "^4.0.0" -pino-pretty@^3.2.2: - version "3.6.1" - resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-3.6.1.tgz#61aabf9bf851d0ed831f7e9b813dea20f61116a3" - integrity sha512-S3bal+Yd313OEaPijbf7V+jPxVaTaRO5RQX8S/Mwdtb/8+JOgo1KolDeJTfMDTU2/k6+MHvEbxv+T1ZRfGlnjA== +pino-abstract-transport@v0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" + integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ== dependencies: - "@hapi/bourne" "^1.3.2" - args "^5.0.1" - chalk "^2.4.2" - dateformat "^3.0.3" - fast-safe-stringify "^2.0.7" - jmespath "^0.15.0" - joycon "^2.2.5" - pump "^3.0.0" - readable-stream "^3.4.0" - split2 "^3.1.1" - strip-json-comments "^3.0.1" + duplexify "^4.1.2" + split2 "^4.0.0" -pino-std-serializers@^2.4.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.5.0.tgz#40ead781c65a0ce7ecd9c1c33f409d31fe712315" - integrity sha512-wXqbqSrIhE58TdrxxlfLwU9eDhrzppQDvGhBEr1gYbzzM4KKo3Y63gSjiDXRKLVS2UOXdPNR2v+KnQgNrs+xUg== +pino-pretty@^11.0.0: + version "11.2.1" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-11.2.1.tgz#de9a42ff8ea7b26da93506bb9e49d0b566c5ae96" + integrity sha512-O05NuD9tkRasFRWVaF/uHLOvoRDFD7tb5VMertr78rbsYFjYp48Vg3477EshVAF5eZaEw+OpDl/tu+B0R5o+7g== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^3.0.2" + fast-safe-stringify "^2.1.1" + help-me "^5.0.0" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.0.0" + pump "^3.0.0" + readable-stream "^4.0.0" + secure-json-parse "^2.4.0" + sonic-boom "^4.0.1" + strip-json-comments "^3.1.1" -pino-std-serializers@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz#4c20928a1bafca122fdc2a7a4a171ca1c5f9c526" - integrity sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ== +pino-std-serializers@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" + integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== -pino@*: - version "8.7.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-8.7.0.tgz#58621608a3d8540ae643cdd9194cdd94130c78d9" - integrity sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA== +pino@^7.0.0, pino@^7.8.1: + version "7.11.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" + integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg== dependencies: atomic-sleep "^1.0.0" - fast-redact "^3.1.1" - on-exit-leak-free "^2.1.0" - pino-abstract-transport v1.0.0 - pino-std-serializers "^6.0.0" - process-warning "^2.0.0" + fast-redact "^3.0.0" + on-exit-leak-free "^0.2.0" + pino-abstract-transport v0.5.0 + pino-std-serializers "^4.0.0" + process-warning "^1.0.0" quick-format-unescaped "^4.0.3" - real-require "^0.2.0" - safe-stable-stringify "^2.3.1" - sonic-boom "^3.1.0" - thread-stream "^2.0.0" - -pino@^5.13.5, pino@^5.14.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-5.17.0.tgz#b9def314e82402154f89a25d76a31f20ca84b4c8" - integrity sha512-LqrqmRcJz8etUjyV0ddqB6OTUutCgQULPFg2b4dtijRHUsucaAdBgSUW58vY6RFSX+NT8963F+q0tM6lNwGShA== - dependencies: - fast-redact "^2.0.0" - fast-safe-stringify "^2.0.7" - flatstr "^1.0.12" - pino-std-serializers "^2.4.2" - quick-format-unescaped "^3.0.3" - sonic-boom "^0.7.5" + real-require "^0.1.0" + safe-stable-stringify "^2.1.0" + sonic-boom "^2.2.1" + thread-stream "^0.15.1" pkg-dir@^3.0.0: version "3.0.0" @@ -5888,10 +5839,10 @@ pretty-format@^23.6.0: ansi-regex "^3.0.0" ansi-styles "^3.2.0" -process-warning@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.0.0.tgz#341dbeaac985b90a04ebcd844d50097c7737b2ee" - integrity sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww== +process-warning@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" + integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== process@^0.11.10: version "0.11.10" @@ -5974,11 +5925,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -quick-format-unescaped@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-3.0.3.tgz#fb3e468ac64c01d22305806c39f121ddac0d1fb9" - integrity sha512-dy1yjycmn9blucmJLXOfZDx1ikZJUi6E8bBZLnhPG5gBrVhHXx2xVyqqgKBubVNEXmx51dBACMHpoMQK/N/AXQ== - quick-format-unescaped@^4.0.3: version "4.0.4" resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" @@ -6023,7 +5969,7 @@ read-pkg@^4.0.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^3.0.0, readable-stream@^3.4.0: +readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -6049,10 +5995,10 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -real-require@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" - integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== +real-require@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" + integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== regenerator-runtime@^0.13.4: version "0.13.10" @@ -6233,10 +6179,10 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -safe-stable-stringify@^2.3.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz#34694bd8a30575b7f94792aa51527551bd733d61" - integrity sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA== +safe-stable-stringify@^2.1.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" @@ -6248,6 +6194,11 @@ scuid@^1.1.0: resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== +secure-json-parse@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -6447,18 +6398,17 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -sonic-boom@^0.7.5: - version "0.7.7" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-0.7.7.tgz#d921de887428208bfa07b0ae32c278de043f350a" - integrity sha512-Ei5YOo5J64GKClHIL/5evJPgASXFVpfVYbJV9PILZQytTK6/LCwHvsZJW2Ig4p9FMC2OrBrMnXKgRN/OEoAWfg== +sonic-boom@^2.2.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" + integrity sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg== dependencies: atomic-sleep "^1.0.0" - flatstr "^1.0.12" -sonic-boom@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.2.0.tgz#ce9f2de7557e68be2e52c8df6d9b052e7d348143" - integrity sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA== +sonic-boom@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.0.1.tgz#515b7cef2c9290cb362c4536388ddeece07aed30" + integrity sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ== dependencies: atomic-sleep "^1.0.0" @@ -6521,13 +6471,6 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -split2@^3.1.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" - integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== - dependencies: - readable-stream "^3.0.0" - split2@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" @@ -6563,6 +6506,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +stream-shift@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" + integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== + streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -6666,7 +6614,7 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== -strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "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== @@ -6712,12 +6660,12 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -thread-stream@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.2.0.tgz#310c03a253f729094ce5d4638ef5186dfa80a9e8" - integrity sha512-rUkv4/fnb4rqy/gGy7VuqK6wE1+1DOCOWy4RMeaV69ZHMP11tQKZvZSip1yTgrKCMZzEMcCL/bKfHvSfDHx+iQ== +thread-stream@^0.15.1: + version "0.15.2" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.15.2.tgz#fb95ad87d2f1e28f07116eb23d85aba3bc0425f4" + integrity sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA== dependencies: - real-require "^0.2.0" + real-require "^0.1.0" through2@^3.0.1: version "3.0.2" From e7efba5e0d3f093ff2f53800866e88e53620dd56 Mon Sep 17 00:00:00 2001 From: Anamul Haque Date: Fri, 14 Jun 2024 12:26:51 +0600 Subject: [PATCH 028/146] fix: add query mapper for International Postal Code field * international postal code added * changelog updated --------- Co-authored-by: Tameem Bin Haider --- CHANGELOG.md | 1 + src/utils/address-utils.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd0ccd6f..dd5df7628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - In the certificate, the 'Place of Certification' now accurately reflects the correct location. - Added french translation of informant for print certificate flow, issue certificate flow & correction flow - Groom's and Bride's name, printIssue translation variables updated [#124](https://github.com/opencrvs/opencrvs-countryconfig/pull/124) +- Add query mapper for International Postal Code field - Add support for image compression configuration - Provide env variables for metabase admin credentials diff --git a/src/utils/address-utils.ts b/src/utils/address-utils.ts index 0272c9ee6..25462de5f 100644 --- a/src/utils/address-utils.ts +++ b/src/utils/address-utils.ts @@ -695,6 +695,10 @@ function getQueryMapping( `city${sentenceCase(useCase)}${sentenceCase(section)}` || fieldName === `postalCode${sentenceCase(useCase)}${sentenceCase(section)}` || + fieldName === + `internationalPostalCode${sentenceCase(useCase)}${sentenceCase( + section + )}` || fieldName === `internationalState${sentenceCase(useCase)}${sentenceCase( section From 59fe2a9f6291b270d2dd609cca3e19813f492d07 Mon Sep 17 00:00:00 2001 From: Jamil Date: Fri, 14 Jun 2024 12:28:17 +0600 Subject: [PATCH 029/146] fix: remove extra quotes in translation file (#129) --- src/translations/client.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/translations/client.csv b/src/translations/client.csv index 5ef7cdcb1..9acf85484 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -665,7 +665,7 @@ countries.GTM,,Guatemala,Guatemala countries.GUF,,French Guiana,Guyane française countries.GUM,,Guam,Guam countries.GUY,,Guyana,Guyane -countries.HKG,,"""China, Hong Kong Special Administrative Region"",""Chine, région administrative spéciale de Hong Kong", +countries.HKG,,"China, Hong Kong Special Administrative Region","Chine, région administrative spéciale de Hong Kong", countries.HMD,,Heard Island and McDonald Islands,Île Heard et îles McDonald countries.HND,,Honduras,Honduras countries.HRV,,Croatia,Croatie @@ -703,7 +703,7 @@ countries.LSO,,Lesotho,Lesotho countries.LTU,,Lithuania,Lituanie countries.LUX,,Luxembourg,Luxembourg countries.LVA,,Latvia,Lettonie -countries.MAC,,"""China, Macao Special Administrative Region"",""Chine, Région administrative spéciale de Macao", +countries.MAC,,"China, Macao Special Administrative Region","Chine, Région administrative spéciale de Macao", countries.MAF,,Saint Martin (French Part),Saint Martin (partie française) countries.MAR,,Morocco,Maroc countries.MCO,,Monaco,Monaco From bc4740ff2e0ae90a1f617596b228b77d4defaa88 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Mon, 17 Jun 2024 11:48:55 +0300 Subject: [PATCH 030/146] [OCRVS-6986][OCRVS-7015][OCRVS-6590] Automate setting up SSH credentials between application & backup server (#126) * enable setting up backup environment with environment creator * Read backup server SSH key from backup Github environment. Do the same for production backup encryption key when setting up staging to periodically restore backups. * add deprecation comments * remove hidden type from environment setup questions * make default values for environment variable empty instead of an empty string empty string got interpreted as ''which broke all truthy checks in our code * add error to deployment script if user hasnt set up known hosts properly * remove default wireguard setup * run interactive shell in environment creator when fetching ssh fingerprint from servers * separate backups and jump groups grom backups-host and jump-host, other small improvements * remove references to vpn host address * remove environment cli parameter, fix environment files not persisting properly * add defaults to many questions like ssh user that we always use the same username for * remove tmate debugging * do not ask backup passphrase explicitly * remove jump user from qa template * reorganize environment list to match our intended order of setup * handle env file not existing * remove rest of VPN related questions * some fixes for how the script detects automated variables * remove rest of debug references * fix wrong name for staging environment * do not pick up commented lines when checking if backup servers are wanting to be used for staging * improved SSSH_PORT support for new additions * take custom ssh port into account in provision firewall setup * add deprecation comment * fix known host checking if port is the default 22 in that case we do not want to search for an item [ip]:22 but just ip instead as that's how ssh-keyscan writes it * make HTTPS challenge the default again * Updated comments * add a wait for apt lock so if there are two processes running for instance to provision the same machine, the other one waits until the first one is finished * combine dependency update commands into just one command, guarded by lock file detection this way there's nothing stopping someone from running provisioning for the same environment simultaneously * only generate database secrets for non-backup servers, store automatically generated secrets in .env files * ensure user tasks are ran also to the backup machine --------- Co-authored-by: euanmillar --- .github/workflows/clear-environment.yml | 11 +- .github/workflows/deploy-prod.yml | 4 +- .github/workflows/deploy.yml | 13 +- .../workflows/get-secret-from-environment.yml | 87 ++ .github/workflows/provision.yml | 129 ++- .github/workflows/reset-2fa.yml | 2 +- CHANGELOG.md | 13 + infrastructure/deployment/deploy.sh | 20 +- infrastructure/docker-compose.deploy.yml | 18 +- .../docker-compose.development-deploy.yml | 20 +- .../docker-compose.production-deploy.yml | 42 +- infrastructure/docker-compose.qa-deploy.yml | 65 +- .../docker-compose.staging-deploy.yml | 42 +- infrastructure/environments/github.ts | 8 +- infrastructure/environments/logger.ts | 9 + .../environments/setup-environment.ts | 796 +++++++++++------- infrastructure/environments/ssh.ts | 18 + .../environments/update-known-hosts.sh | 24 +- infrastructure/server-setup/backups.yml | 91 +- .../server-setup/group_vars/all.yml | 6 +- .../server-setup/inventory/backup.yml | 33 + .../{ => inventory}/development.yml | 0 .../server-setup/inventory/jump.yml | 30 + .../{ => inventory}/production.yml | 37 +- .../server-setup/{ => inventory}/qa.yml | 11 - .../server-setup/{ => inventory}/staging.yml | 32 +- infrastructure/server-setup/jump.yml | 100 +++ infrastructure/server-setup/playbook.yml | 26 +- .../server-setup/tasks/backups/crontab.yml | 29 +- infrastructure/server-setup/tasks/checks.yml | 10 + infrastructure/server-setup/tasks/docker.yml | 14 + infrastructure/server-setup/tasks/ufw.yml | 10 +- infrastructure/server-setup/tasks/updates.yml | 24 +- infrastructure/server-setup/tasks/users.yml | 14 - package.json | 3 +- yarn.lock | 101 ++- 36 files changed, 1297 insertions(+), 595 deletions(-) create mode 100644 .github/workflows/get-secret-from-environment.yml create mode 100644 infrastructure/environments/logger.ts create mode 100644 infrastructure/environments/ssh.ts create mode 100644 infrastructure/server-setup/inventory/backup.yml rename infrastructure/server-setup/{ => inventory}/development.yml (100%) create mode 100644 infrastructure/server-setup/inventory/jump.yml rename infrastructure/server-setup/{ => inventory}/production.yml (52%) rename infrastructure/server-setup/{ => inventory}/qa.yml (65%) rename infrastructure/server-setup/{ => inventory}/staging.yml (55%) create mode 100644 infrastructure/server-setup/jump.yml diff --git a/.github/workflows/clear-environment.yml b/.github/workflows/clear-environment.yml index d48c9c602..25f360477 100644 --- a/.github/workflows/clear-environment.yml +++ b/.github/workflows/clear-environment.yml @@ -49,7 +49,12 @@ jobs: HOST: ${{ vars.DOMAIN }} ENV: ${{ vars.ENVIRONMENT_TYPE }} SSH_USER: ${{ secrets.SSH_USER }} - SSH_HOST: ${{ secrets.SSH_HOST }} + # SSH_HOST was moved from a secret to a variable in OpenCRVS 1.5.0 + # @todo @deprecated remove the fallback to secrets.SSH_HOST in OpenCRVS 1.7.0 + SSH_HOST: ${{ vars.SSH_HOST || secrets.SSH_HOST }} + # SSH_PORT was moved from a secret to a variable in OpenCRVS 1.5.0 + # @todo @deprecated remove the fallback to secrets.SSH_PORT in OpenCRVS 1.7.0 + SSH_PORT: ${{ vars.SSH_PORT || secrets.SSH_PORT }} REPLICAS: ${{ vars.REPLICAS }} MONGODB_ADMIN_USER: ${{ secrets.MONGODB_ADMIN_USER }} MONGODB_ADMIN_PASSWORD: ${{ secrets.MONGODB_ADMIN_PASSWORD }} @@ -58,7 +63,7 @@ jobs: MINIO_ROOT_PASSWORD: ${{ secrets.MINIO_ROOT_PASSWORD }} SSH_ARGS: ${{ vars.SSH_ARGS }} run: | - ssh $SSH_USER@$SSH_HOST $SSH_ARGS " + ssh -p $SSH_PORT $SSH_USER@$SSH_HOST $SSH_ARGS " ELASTICSEARCH_ADMIN_USER=elastic \ ELASTICSEARCH_ADMIN_PASSWORD=$ELASTICSEARCH_SUPERUSER_PASSWORD \ MONGODB_ADMIN_USER=$MONGODB_ADMIN_USER \ @@ -69,7 +74,7 @@ jobs: echo "Running migrations..." echo - ssh $SSH_USER@$SSH_HOST $SSH_ARGS " + ssh -p $SSH_PORT $SSH_USER@$SSH_HOST $SSH_ARGS " ELASTICSEARCH_ADMIN_USER=elastic \ ELASTICSEARCH_ADMIN_PASSWORD=$ELASTICSEARCH_SUPERUSER_PASSWORD \ /opt/opencrvs/infrastructure/run-migrations.sh" diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index b43fd166a..a60139597 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -142,8 +142,8 @@ jobs: --clear_data=no \ --environment=${{ github.event.inputs.environment }} \ --host=${{ vars.DOMAIN }} \ - --ssh_host=${{ secrets.SSH_HOST }} \ - --ssh_port=${{ secrets.SSH_PORT }} \ + --ssh_host=${{ vars.SSH_HOST || secrets.SSH_HOST }} \ + --ssh_port=${{ vars.SSH_PORT || secrets.SSH_PORT }} \ --ssh_user=${{ secrets.SSH_USER }} \ --version=${{ github.event.inputs.core-image-tag }} \ --country_config_version=${{ github.event.inputs.countryconfig-image-tag }} \ diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5b757f9d1..a747682bb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -23,10 +23,6 @@ on: type: boolean description: Reset the environment default: false - debug: - type: boolean - description: Open SSH session to the runner after deployment - default: false jobs: deploy: environment: ${{ github.event.inputs.environment }} @@ -139,24 +135,19 @@ jobs: - name: Deploy to ${{ github.event.inputs.environment }} id: deploy - continue-on-error: ${{ github.event.inputs.debug == 'true' }} run: | cd ./${{ github.event.repository.name }} yarn deploy \ --clear_data=no \ --environment=${{ github.event.inputs.environment }} \ --host=${{ vars.DOMAIN }} \ - --ssh_host=${{ secrets.SSH_HOST }} \ - --ssh_port=${{ secrets.SSH_PORT }} \ + --ssh_host=${{ vars.SSH_HOST || secrets.SSH_HOST }} \ + --ssh_port=${{ vars.SSH_PORT || secrets.SSH_PORT }} \ --ssh_user=${{ secrets.SSH_USER }} \ --version=${{ github.event.inputs.core-image-tag }} \ --country_config_version=${{ github.event.inputs.countryconfig-image-tag }} \ --replicas=${{ vars.REPLICAS }} - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - if: ${{ github.event.inputs.debug == 'true' }} - reset: needs: deploy if: ${{ github.event.inputs.reset == 'true' && needs.deploy.outputs.outcome == 'success' }} diff --git a/.github/workflows/get-secret-from-environment.yml b/.github/workflows/get-secret-from-environment.yml new file mode 100644 index 000000000..79fe4c73a --- /dev/null +++ b/.github/workflows/get-secret-from-environment.yml @@ -0,0 +1,87 @@ +name: Reusable Fetch Secret Workflow + +on: + workflow_call: + inputs: + secret_name: + required: true + type: string + env_name: + required: true + type: string + outputs: + secret_value: + description: 'Secret value, encrypted with the encryption key' + value: ${{ jobs.fetch-credentials.outputs.secret_value }} + environment_exists: + description: 'Whether the environment exists or not' + value: ${{ jobs.check-environment.outputs.environment_exists }} + secrets: + gh_token: + required: true + encryption_key: + required: true + # All secrets that are we want to allow access to need + # to be defined in this list + BACKUP_ENCRYPTION_PASSPHRASE: + required: false + SSH_KEY: + required: false + +jobs: + check-environment: + name: Check if Environment Exists + runs-on: ubuntu-22.04 + outputs: + environment_exists: ${{ steps.check-env.outputs.exists }} + steps: + - name: Check if GITHUB_TOKEN is set + id: check-token + run: | + if [ -z "${{ secrets.gh_token }}" ]; then + echo "Environment secret GITHUB_TOKEN is not set. Make sure you add a correct Github API token before running this pipeline." + exit 1 + fi + + - name: Check if environment exists + id: check-env + run: | + ENV_NAME="${{ inputs.env_name }}" + RESPONSE=$(curl -s -H "Authorization: Bearer ${{ secrets.gh_token }}" \ + "https://api.github.com/repos/${{ github.repository }}/environments/$ENV_NAME") + if echo "$RESPONSE" | grep -q '"name": "'$ENV_NAME'"'; then + echo "Environment $ENV_NAME exists." + echo "::set-output name=exists::true" + else + echo "Environment $ENV_NAME does not exist." + echo "::set-output name=exists::false" + fi + + fetch-credentials: + name: Fetch Secret + runs-on: ubuntu-22.04 + environment: ${{ inputs.env_name }} + needs: check-environment + # Without this Github actions will create the environment when it doesnt exist + if: needs.check-environment.outputs.environment_exists == 'true' + outputs: + secret_value: ${{ steps.fetch-credentials.outputs.secret_value }} + steps: + - name: Fetch the secret + id: fetch-credentials + env: + SECRET_NAME: ${{ inputs.secret_name }} + run: | + SECRET_VALUE="${{ secrets[env.SECRET_NAME] }}" + if [ -z "$SECRET_VALUE" ]; then + echo "Secret ${{ inputs.secret_name }} is empty. Usually this means you have not explicitly stated the secrets" + echo "in both the workflow file get-secrets-from-environment and in the file you are using the reusable workflow from." + echo "Please make sure you have added the secret to the workflow files and retry." + exit 1 + fi + echo -n "$SECRET_VALUE" | openssl enc -aes-256-cbc -pbkdf2 -salt -k "${{ secrets.encryption_key }}" -out encrypted_key.bin + ENCODED_ENCRYPTED_SECRET=$(base64 < encrypted_key.bin) + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "secret_value<<$EOF" >> $GITHUB_OUTPUT + echo "$ENCODED_ENCRYPTED_SECRET" >> $GITHUB_OUTPUT + echo "$EOF" >> $GITHUB_OUTPUT diff --git a/.github/workflows/provision.yml b/.github/workflows/provision.yml index 160c9138c..9ed6743f8 100644 --- a/.github/workflows/provision.yml +++ b/.github/workflows/provision.yml @@ -13,37 +13,73 @@ on: - staging - qa - production + - backup + - jump tag: type: choice description: Select group tag you want to execute default: all options: - all + - application - backups - checks - - updates - - application - - tools - - docker - - deployment - - users - crontab - - mongodb - data-partition - - swap - - ufw - - fail2ban - decrypt - - swarm + - deployment + - docker - elasticsearch + - fail2ban + - jump + - mongodb + - swap + - swarm + - tools - traefik - debug: - type: boolean - description: Open SSH session to the runner after deployment - default: false + - ufw + - updates + - users jobs: + get-backup-ssh-key: + name: Get backup SSH key + uses: ./.github/workflows/get-secret-from-environment.yml + with: + secret_name: 'SSH_KEY' + env_name: 'backup' + secrets: + gh_token: ${{ secrets.GH_TOKEN }} + encryption_key: ${{ secrets.GH_ENCRYPTION_PASSWORD }} + SSH_KEY: ${{ secrets.SSH_KEY }} + + get-jump-ssh-key: + name: Get jump SSH key + uses: ./.github/workflows/get-secret-from-environment.yml + with: + secret_name: 'SSH_KEY' + env_name: 'jump' + secrets: + gh_token: ${{ secrets.GH_TOKEN }} + encryption_key: ${{ secrets.GH_ENCRYPTION_PASSWORD }} + SSH_KEY: ${{ secrets.SSH_KEY }} + + get-production-encryption-key: + name: Get production backup encryption key + if: github.event.inputs.environment == 'staging' + uses: ./.github/workflows/get-secret-from-environment.yml + with: + secret_name: 'BACKUP_ENCRYPTION_PASSPHRASE' + env_name: 'production' + secrets: + gh_token: ${{ secrets.GH_TOKEN }} + encryption_key: ${{ secrets.GH_ENCRYPTION_PASSWORD }} + BACKUP_ENCRYPTION_PASSPHRASE: ${{ secrets.BACKUP_ENCRYPTION_PASSPHRASE }} + provision: + name: Provision ${{ github.event.inputs.environment }} environment: ${{ github.event.inputs.environment }} + needs: [get-backup-ssh-key, get-production-encryption-key, get-jump-ssh-key] + if: always() runs-on: ubuntu-22.04 outputs: outcome: ${{ steps.deploy.outcome }} @@ -55,7 +91,16 @@ jobs: fetch-depth: 0 path: './${{ github.event.repository.name }}' - - name: Set variables for ansible in production environments + - name: Insert production encryption key to environment variables + if: github.event.inputs.environment == 'staging' && needs.get-production-encryption-key.outputs.environment_exists == 'true' + run: | + echo "${{ needs.get-production-encryption-key.outputs.secret_value }}" | base64 --decode | \ + openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.GH_ENCRYPTION_PASSWORD }}" -out /tmp/backup_encryption_key + BACKUP_RESTORE_ENCRYPTION_PASSPHRASE=$(cat /tmp/backup_encryption_key) + echo "backup_restore_encryption_passphrase=$BACKUP_RESTORE_ENCRYPTION_PASSPHRASE" >> $GITHUB_ENV + echo "::add-mask::$BACKUP_RESTORE_ENCRYPTION_PASSPHRASE" + + - name: Set variables for ansible id: ansible-variables run: | JSON_WITH_NEWLINES=$(cat<" + echo "" + echo "or" + echo "" + echo "sh ./infrastructure/environments/update-known-hosts.sh " + exit 1 +fi + echo echo "Deploying VERSION $VERSION to $SSH_HOST..." echo @@ -346,6 +363,7 @@ echo "Deploying COUNTRY_CONFIG_VERSION $COUNTRY_CONFIG_VERSION to $SSH_HOST..." echo echo "Syncing configuration files to the target server" + configured_rsync -rlD $PROJECT_ROOT/infrastructure $SSH_USER@$SSH_HOST:/opt/opencrvs/ --delete --no-perms --omit-dir-times --verbose configured_rsync -rlD /tmp/docker-compose.yml /tmp/docker-compose.deps.yml $SSH_USER@$SSH_HOST:/opt/opencrvs/infrastructure --no-perms --omit-dir-times --verbose diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 4a3e03cbc..9e43290f4 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -588,15 +588,15 @@ services: - GATEWAY_URL=http://gateway:7070 - CLIENT_APP_URL=https://register.{{hostname}} - NOTIFICATION_TRANSPORT=${NOTIFICATION_TRANSPORT} - - ALERT_EMAIL=${ALERT_EMAIL:-""} - - SMTP_HOST=${SMTP_HOST:-""} - - SMTP_PORT=${SMTP_PORT:-""} - - SMTP_USERNAME=${SMTP_USERNAME:-""} - - SMTP_PASSWORD=${SMTP_PASSWORD:-""} - - SMTP_SECURE=${SMTP_SECURE:-""} - - INFOBIP_GATEWAY_ENDPOINT=${INFOBIP_GATEWAY_ENDPOINT:-""} - - INFOBIP_API_KEY=${INFOBIP_API_KEY:-""} - - INFOBIP_SENDER_ID=${INFOBIP_SENDER_ID:-""} + - ALERT_EMAIL=${ALERT_EMAIL:-} + - SMTP_HOST=${SMTP_HOST:-} + - SMTP_PORT=${SMTP_PORT:-} + - SMTP_USERNAME=${SMTP_USERNAME:-} + - SMTP_PASSWORD=${SMTP_PASSWORD:-} + - SMTP_SECURE=${SMTP_SECURE:-} + - INFOBIP_GATEWAY_ENDPOINT=${INFOBIP_GATEWAY_ENDPOINT:-} + - INFOBIP_API_KEY=${INFOBIP_API_KEY:-} + - INFOBIP_SENDER_ID=${INFOBIP_SENDER_ID:-} - DOMAIN={{hostname}} networks: - overlay_net diff --git a/infrastructure/docker-compose.development-deploy.yml b/infrastructure/docker-compose.development-deploy.yml index 95c892336..b60f37cab 100644 --- a/infrastructure/docker-compose.development-deploy.yml +++ b/infrastructure/docker-compose.development-deploy.yml @@ -4,7 +4,7 @@ services: notification: environment: - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production @@ -22,7 +22,7 @@ services: - CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration - CHECK_INVALID_TOKEN=true - MONGO_URL=mongodb://mongo1/user-mgnt?replicaSet=rs0 - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - SENDER_EMAIL_ADDRESS=${SENDER_EMAIL_ADDRESS} - ALERT_EMAIL=${ALERT_EMAIL} - SMTP_HOST=${SMTP_HOST} @@ -42,7 +42,7 @@ services: gateway: environment: - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - COUNTRY=FAR - QA_ENV=true - NODE_ENV=production @@ -50,43 +50,43 @@ services: workflow: environment: - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production search: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production metrics: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production auth: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production user-mgnt: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production webhooks: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production config: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production diff --git a/infrastructure/docker-compose.production-deploy.yml b/infrastructure/docker-compose.production-deploy.yml index 06217603c..c063e9429 100644 --- a/infrastructure/docker-compose.production-deploy.yml +++ b/infrastructure/docker-compose.production-deploy.yml @@ -7,18 +7,13 @@ version: '3.3' # Before you deploy staging or production environments, make sure the application servers are # either in an internal network or protected with a firewall. No ports should be exposed to the internet. # -# The VPN_HOST_ADDRESS environment variable should be set to the IP address where all inbound traffic is coming from. -# In most cases, this is the VPN server's public IP address. -# -# ${VPN_HOST_ADDRESS} -# services: gateway: environment: - NODE_ENV=production - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 2 @@ -26,21 +21,21 @@ services: environment: - NODE_ENV=production - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 2 search: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 2 metrics: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://metrics:${METRICS_MONGODB_PASSWORD}@mongo1,mongo2/metrics?replicaSet=rs0 - HEARTH_MONGO_URL=mongodb://hearth:${HEARTH_MONGODB_PASSWORD}@mongo1,mongo2/hearth-dev?replicaSet=rs0 - DASHBOARD_MONGO_URL=mongodb://performance:${PERFORMANCE_MONGODB_PASSWORD}@mongo1,mongo2/performance?replicaSet=rs0 @@ -48,14 +43,14 @@ services: auth: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 2 user-mgnt: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://user-mgnt:${USER_MGNT_MONGODB_PASSWORD}@mongo1,mongo2/user-mgnt?replicaSet=rs0 deploy: replicas: 2 @@ -64,7 +59,7 @@ services: environment: - NODE_ENV=production - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://notification:${NOTIFICATION_MONGODB_PASSWORD}@mongo1,mongo2/notification?replicaSet=rs0 deploy: replicas: 2 @@ -72,7 +67,7 @@ services: webhooks: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://webhooks:${WEBHOOKS_MONGODB_PASSWORD}@mongo1,mongo2/webhooks?replicaSet=rs0 deploy: replicas: 2 @@ -80,7 +75,7 @@ services: config: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://config:${CONFIG_MONGODB_PASSWORD}@mongo1,mongo2/application-config?replicaSet=rs0 deploy: replicas: 2 @@ -106,7 +101,7 @@ services: - APPLICATION_CONFIG_URL=http://config:2021 - CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration - CHECK_INVALID_TOKEN=true - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - SENDER_EMAIL_ADDRESS=${SENDER_EMAIL_ADDRESS} - ALERT_EMAIL=${ALERT_EMAIL} - SMTP_HOST=${SMTP_HOST} @@ -192,7 +187,15 @@ services: # These templates use an Automatic Certificate Management Environment (Let's Encrypt). # This makes sure that the HTTPS certificates are automatically generated and renewed without manual maintenance. # - # For your country to do this, your domain's DNS provider must be one of the ones listed here + # This default configuration will only work if OpenCRVS is directly accessible from the internet. + # + # WE STRONGLY RECOMMEND THAT YOU DO NOT EXPOSE PRODUCTION OPENCRVS TO THE INTERNET! + # + # If you are deploying OpenCRVS in a private network, you have two options: + # 1. Use a DNS provider that supports ACME DNS-01 challenges. + # 2. Use a manually renewed certificate file. + + # For your country to use the DNS-01 challenge, your domain's DNS provider must be one of the ones listed here # https://doc.traefik.io/traefik/https/acme/#providers # # If your DNS provider is not listed, you can use manually renewed certificate files instead of Let's Encrypt. @@ -210,13 +213,12 @@ services: # - --tls.stores.default.defaultcertificate.certfile=/certs/crvs.cm.crt # - --tls.stores.default.defaultcertificate.keyfile=/certs/crvs.cm.key - environment: - - GOOGLE_DOMAINS_ACCESS_TOKEN=${GOOGLE_DOMAINS_ACCESS_TOKEN} command: - - --certificatesresolvers.certResolver.acme.dnschallenge=true - - --certificatesresolvers.certResolver.acme.dnschallenge.provider=googledomains - --certificatesresolvers.certResolver.acme.email=riku@opencrvs.org - --certificatesresolvers.certResolver.acme.storage=acme.json + - --certificatesresolvers.certResolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory + - --certificatesresolvers.certResolver.acme.httpchallenge.entrypoint=web + - --certificatesresolvers.certResolver.acme.httpchallenge=true - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 diff --git a/infrastructure/docker-compose.qa-deploy.yml b/infrastructure/docker-compose.qa-deploy.yml index 14e3524d8..f01395d40 100644 --- a/infrastructure/docker-compose.qa-deploy.yml +++ b/infrastructure/docker-compose.qa-deploy.yml @@ -4,7 +4,6 @@ services: traefik: networks: - overlay_net - - vpn command: # Use HTTP-01 challenge as the web server is publicly available # https://doc.traefik.io/traefik/https/acme/#httpchallenge @@ -34,7 +33,7 @@ services: notification: environment: - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production @@ -52,7 +51,7 @@ services: - CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration - CHECK_INVALID_TOKEN=true - MONGO_URL=mongodb://mongo1/user-mgnt?replicaSet=rs0 - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - SENDER_EMAIL_ADDRESS=${SENDER_EMAIL_ADDRESS} - ALERT_EMAIL=${ALERT_EMAIL} - SMTP_HOST=${SMTP_HOST} @@ -72,7 +71,7 @@ services: gateway: environment: - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - COUNTRY=FAR - QA_ENV=true - NODE_ENV=production @@ -80,43 +79,43 @@ services: workflow: environment: - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production search: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production metrics: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production auth: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production user-mgnt: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production webhooks: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production config: environment: - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - QA_ENV=true - NODE_ENV=production @@ -129,47 +128,3 @@ services: environment: - QA_ENV=true - NODE_ENV=production - - wg-easy: - image: ghcr.io/wg-easy/wg-easy:8 - environment: - - WG_HOST=vpn.{{hostname}} - - PASSWORD=${VPN_ADMIN_PASSWORD} - - WG_DEFAULT_ADDRESS=10.13.13.x - - WG_ALLOWED_IPS=0.0.0.0/0 - - WG_PORT=51822 - - WG_POST_UP=iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE - - WG_POST_DOWN=iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE - volumes: - - /data/wireguard:/etc/wireguard - ports: - - '51822:51820/udp' - cap_add: - - NET_ADMIN - - SYS_MODULE - sysctls: - - net.ipv4.conf.all.src_valid_mark=1 - - net.ipv4.ip_forward=1 - deploy: - labels: - - 'traefik.enable=true' - - 'traefik.http.routers.vpn.rule=Host(`vpn.{{hostname}}`)' - - 'traefik.http.services.vpn.loadbalancer.server.port=51821' - - 'traefik.http.routers.vpn.tls=true' - - 'traefik.http.routers.vpn.tls.certresolver=certResolver' - - 'traefik.http.routers.vpn.entrypoints=web,websecure' - - 'traefik.docker.network=opencrvs_vpn' - - 'traefik.http.middlewares.vpn.headers.customresponseheaders.Pragma=no-cache' - - 'traefik.http.middlewares.vpn.headers.customresponseheaders.Cache-control=no-store' - - 'traefik.http.middlewares.vpn.headers.customresponseheaders.X-Robots-Tag=none' - - 'traefik.http.middlewares.vpn.headers.stsseconds=31536000' - - 'traefik.http.middlewares.vpn.headers.stsincludesubdomains=true' - - 'traefik.http.middlewares.vpn.headers.stspreload=true' - restart: unless-stopped - networks: - - vpn - -networks: - vpn: - driver: overlay - attachable: false diff --git a/infrastructure/docker-compose.staging-deploy.yml b/infrastructure/docker-compose.staging-deploy.yml index eb7e05695..3b7a25cc0 100644 --- a/infrastructure/docker-compose.staging-deploy.yml +++ b/infrastructure/docker-compose.staging-deploy.yml @@ -7,18 +7,13 @@ version: '3.3' # Before you deploy staging or production environments, make sure the application servers are # either in an internal network or protected with a firewall. No ports should be exposed to the internet. # -# The VPN_HOST_ADDRESS environment variable should be set to the IP address where all inbound traffic is coming from. -# In most cases, this is the VPN server's public IP address. -# -# ${VPN_HOST_ADDRESS} -# services: gateway: environment: - NODE_ENV=production - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 1 @@ -26,21 +21,21 @@ services: environment: - NODE_ENV=production - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 1 search: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 1 metrics: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://metrics:${METRICS_MONGODB_PASSWORD}@mongo1/metrics?replicaSet=rs0 - HEARTH_MONGO_URL=mongodb://hearth:${HEARTH_MONGODB_PASSWORD}@mongo1/hearth-dev?replicaSet=rs0 - DASHBOARD_MONGO_URL=mongodb://performance:${PERFORMANCE_MONGODB_PASSWORD}@mongo1/performance?replicaSet=rs0 @@ -48,14 +43,14 @@ services: auth: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} deploy: replicas: 1 user-mgnt: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://user-mgnt:${USER_MGNT_MONGODB_PASSWORD}@mongo1/user-mgnt?replicaSet=rs0 deploy: replicas: 1 @@ -64,7 +59,7 @@ services: environment: - NODE_ENV=production - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://notification:${NOTIFICATION_MONGODB_PASSWORD}@mongo1/notification?replicaSet=rs0 deploy: replicas: 1 @@ -72,7 +67,7 @@ services: webhooks: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://webhooks:${WEBHOOKS_MONGODB_PASSWORD}@mongo1/webhooks?replicaSet=rs0 deploy: replicas: 1 @@ -80,7 +75,7 @@ services: config: environment: - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - MONGO_URL=mongodb://config:${CONFIG_MONGODB_PASSWORD}@mongo1/application-config?replicaSet=rs0 deploy: replicas: 1 @@ -106,7 +101,7 @@ services: - APPLICATION_CONFIG_URL=http://config:2021 - CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration - CHECK_INVALID_TOKEN=true - - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_DSN=${SENTRY_DSN:-} - SENDER_EMAIL_ADDRESS=${SENDER_EMAIL_ADDRESS} - ALERT_EMAIL=${ALERT_EMAIL} - SMTP_HOST=${SMTP_HOST} @@ -162,7 +157,15 @@ services: # These templates use an Automatic Certificate Management Environment (Let's Encrypt). # This makes sure that the HTTPS certificates are automatically generated and renewed without manual maintenance. # - # For your country to do this, your domain's DNS provider must be one of the ones listed here + # This default configuration will only work if OpenCRVS is directly accessible from the internet. + # + # WE STRONGLY RECOMMEND THAT YOU DO NOT EXPOSE PRODUCTION OPENCRVS TO THE INTERNET! + # + # If you are deploying OpenCRVS in a private network, you have two options: + # 1. Use a DNS provider that supports ACME DNS-01 challenges. + # 2. Use a manually renewed certificate file. + + # For your country to use the DNS-01 challenge, your domain's DNS provider must be one of the ones listed here # https://doc.traefik.io/traefik/https/acme/#providers # # If your DNS provider is not listed, you can use manually renewed certificate files instead of Let's Encrypt. @@ -180,13 +183,12 @@ services: # - --tls.stores.default.defaultcertificate.certfile=/certs/crvs.cm.crt # - --tls.stores.default.defaultcertificate.keyfile=/certs/crvs.cm.key - environment: - - GOOGLE_DOMAINS_ACCESS_TOKEN=${GOOGLE_DOMAINS_ACCESS_TOKEN} command: - - --certificatesresolvers.certResolver.acme.dnschallenge=true - - --certificatesresolvers.certResolver.acme.dnschallenge.provider=googledomains - --certificatesresolvers.certResolver.acme.email=riku@opencrvs.org - --certificatesresolvers.certResolver.acme.storage=acme.json + - --certificatesresolvers.certResolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory + - --certificatesresolvers.certResolver.acme.httpchallenge.entrypoint=web + - --certificatesresolvers.certResolver.acme.httpchallenge=true - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 diff --git a/infrastructure/environments/github.ts b/infrastructure/environments/github.ts index b16b10462..d95184430 100644 --- a/infrastructure/environments/github.ts +++ b/infrastructure/environments/github.ts @@ -1,5 +1,6 @@ const sodium = require('libsodium-wrappers') import { Octokit } from '@octokit/core' +import { error } from './logger' export async function createVariable( octokit: Octokit, @@ -211,7 +212,12 @@ export async function createEnvironment( ) return true } catch (err) { - throw new Error('Cannot create environment') + error( + `Cannot create environment: [${err.status}] ${err.response.data.message}` + ) + throw new Error( + `Cannot create environment: [${err.status}] ${err.response.data.message}` + ) } } diff --git a/infrastructure/environments/logger.ts b/infrastructure/environments/logger.ts new file mode 100644 index 000000000..9458cdc7e --- /dev/null +++ b/infrastructure/environments/logger.ts @@ -0,0 +1,9 @@ +import kleur from 'kleur' + +/* eslint-disable no-console */ +export const log = console.log +export const warn = console.warn +export const success = (...args: any[]) => + console.log(kleur.green(args.join(' '))) +export const info = console.info +export const error = (...args: any[]) => console.log(kleur.red(args.join(' '))) diff --git a/infrastructure/environments/setup-environment.ts b/infrastructure/environments/setup-environment.ts index 2a488d7fb..b7b2159cc 100644 --- a/infrastructure/environments/setup-environment.ts +++ b/infrastructure/environments/setup-environment.ts @@ -1,35 +1,52 @@ import { Octokit } from '@octokit/core' -import { PromptObject } from 'prompts' -import prompts from 'prompts' -import minimist from 'minimist' - +import { spawn } from 'child_process' +import dotenv from 'dotenv' import kleur from 'kleur' +import prompts, { PromptObject } from 'prompts' import { - Variable, Secret, + Variable, + createEnvironment, + createEnvironmentSecret, + createRepositorySecret, + createVariable, getRepositoryId, listEnvironmentSecrets, listEnvironmentVariables, - createRepositorySecret, - createEnvironmentSecret, - createVariable, - updateVariable, - createEnvironment, - listRepositorySecrets + listRepositorySecrets, + updateVariable } from './github' import editor from '@inquirer/editor' -import { writeFileSync } from 'fs' -import { exec as callbackExec } from 'child_process' -import { promisify } from 'util' +import { readFileSync, writeFileSync } from 'fs' import { join } from 'path' -import { initial } from 'lodash' - -const exec = promisify(callbackExec) +import { error, info, log, success, warn } from './logger' +import { verifyConnection } from './ssh' const notEmpty = (value: string | number) => value.toString().trim().length > 0 ? true : 'Please enter a value' +function runInteractiveShell( + command: string, + args: string[] = [] +): Promise { + return new Promise((resolve, reject) => { + const shell = spawn(command, args, { stdio: 'inherit' }) + + shell.on('close', (code) => { + if (code === 0) { + resolve(`Shell exited with code ${code}`) + } else { + reject(new Error(`Shell exited with code ${code}`)) + } + }) + + shell.on('error', (err) => { + reject(err) + }) + }) +} + type Question = PromptObject & { name: T valueType?: 'SECRET' | 'VARIABLE' @@ -54,8 +71,18 @@ type VariableAnswer = { name: string didExist: Variable | undefined value: string + scope: 'ENVIRONMENT' | 'REPOSITORY' } -type Answers = (SecretAnswer | VariableAnswer)[] + +type Answer = SecretAnswer | VariableAnswer +type Answers = Answer[] +type AnswerWithNullValue = + | (Omit & { + value: SecretAnswer['value'] | null + }) + | (Omit & { + value: VariableAnswer['value'] | null + }) function questionToPrompt({ // eslint-disable-next-line no-unused-vars @@ -69,25 +96,9 @@ function questionToPrompt({ return promptOptions } -// eslint-disable-next-line no-console -const log = console.log -const warn = console.warn - const ALL_QUESTIONS: Array> = [] const ALL_ANSWERS: Array> = [] -const { environment } = minimist(process.argv.slice(2)) - -if (!environment || typeof environment !== 'string') { - console.error('Please specify an environment name with --environment=') - process.exit(1) -} - -// Read users .env file based on the environment name they gave above, e.g. .env.production -require('dotenv').config({ - path: `${process.cwd()}/.env.${environment}` -}) - function findExistingValue( name: string, type: T, @@ -104,7 +115,7 @@ function findExistingValue( async function promptAndStoreAnswer( environment: string, - questions: Array>, + questions: Array>, existingValues: Array ) { log('') @@ -180,13 +191,17 @@ async function promptAndStoreAnswer( } return questionWithVariableLabel }) - const foo = processedQuestions.map(questionToPrompt) - const result = await prompts(foo, { + + const promptQuestions = processedQuestions.map(questionToPrompt) + + const result = await prompts(promptQuestions, { onCancel: () => { process.exit(1) } }) + ALL_ANSWERS.push(result) + storeSecrets(environment, getAnswers(existingValues)) const existingValuesForQuestions = questions @@ -215,10 +230,25 @@ function generateLongPassword() { } function storeSecrets(environment: string, answers: Answers) { - writeFileSync( - `.env.${environment}`, - answers.map((update) => `${update.name}="${update.value}"`).join('\n') + let envConfig: Record = {} + try { + envConfig = dotenv.parse( + readFileSync(`${process.cwd()}/.env.${environment}`) + ) + } catch (error) { + envConfig = {} + } + + const linesFromAnswers = answers.map( + (update) => `${update.name}="${update.value}"` ) + const linesFromEnvConfig = Object.entries(envConfig) + .filter(([name]) => !answers.find((update) => update.name === name)) + .map(([name, value]) => `${name}="${value}"`) + + const allLines = [...linesFromEnvConfig, ...linesFromAnswers].sort() + + writeFileSync(`.env.${environment}`, allLines.join('\n')) } const githubQuestions = [ @@ -237,14 +267,18 @@ const githubQuestions = [ validate: notEmpty, initial: process.env.GITHUB_REPOSITORY, scope: 'REPOSITORY' as const - }, + } +] +const githubTokenQuestion = [ { name: 'githubToken', type: 'text' as const, message: 'What is your Github token?', validate: notEmpty, initial: process.env.GITHUB_TOKEN, - scope: 'REPOSITORY' as const + valueType: 'SECRET' as const, + scope: 'REPOSITORY' as const, + valueLabel: 'GH_TOKEN' } ] @@ -297,28 +331,18 @@ const sshQuestions = [ type: 'text' as const, message: 'What is the target server IP address? Note: For "production" environment server clusters of (2, 3 or 5 replicas) this is always the IP address for just 1 manager server', - valueType: 'SECRET' as const, + valueType: 'VARIABLE' as const, validate: notEmpty, valueLabel: 'SSH_HOST', initial: process.env.SSH_HOST, scope: 'ENVIRONMENT' as const }, - { - name: 'sshUser', - type: 'text' as const, - message: 'What is the SSH login user to be used for provisioning?', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'SSH_USER', - initial: process.env.SSH_USER || 'provision', - scope: 'ENVIRONMENT' as const - }, { name: 'sshPort', type: 'number' as const, message: 'What port number is used in establishing the SSH connection? This usually is the default 22. If you are an advanced user, and have set a different port, provide it here.', - valueType: 'SECRET' as const, + valueType: 'VARIABLE' as const, validate: notEmpty, valueLabel: 'SSH_PORT', initial: process.env.SSH_PORT ? parseInt(process.env.SSH_PORT) : 22, @@ -341,7 +365,7 @@ const sshKeyQuestions = [ { name: 'sshKey', type: 'text' as const, - message: `Paste the SSH private key for SSH_USER here:`, + message: `Paste the SSH private key for SSH_USER (provision) here:`, valueType: 'SECRET' as const, validate: notEmpty, valueLabel: 'SSH_KEY', @@ -406,77 +430,6 @@ const databaseAndMonitoringQuestions = [ valueLabel: 'KIBANA_PASSWORD', initial: process.env.KIBANA_PASSWORD || generateLongPassword(), scope: 'ENVIRONMENT' as const - }, - { - name: 'elasticsearchSuperuserPassword', - type: 'text' as const, - message: 'Input the password for the Elasticsearch superuser', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'ELASTICSEARCH_SUPERUSER_PASSWORD', - initial: - process.env.ELASTICSEARCH_SUPERUSER_PASSWORD || generateLongPassword(), - scope: 'ENVIRONMENT' as const - }, - { - name: 'minioRootUser', - type: 'text' as const, - message: 'Input the username for the Minio root user', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'MINIO_ROOT_USER', - initial: process.env.MINIO_ROOT_USER || generateLongPassword(), - scope: 'ENVIRONMENT' as const - }, - { - name: 'minioRootPassword', - type: 'text' as const, - message: 'Input the password for the Minio root user', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'MINIO_ROOT_PASSWORD', - initial: process.env.MINIO_ROOT_PASSWORD || generateLongPassword(), - scope: 'ENVIRONMENT' as const - }, - { - name: 'mongodbAdminUser', - type: 'text' as const, - message: 'Input the username for the MongoDB admin user', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'MONGODB_ADMIN_USER', - initial: process.env.MONGODB_ADMIN_USER || generateLongPassword(), - scope: 'ENVIRONMENT' as const - }, - { - name: 'mongodbAdminPassword', - type: 'text' as const, - message: 'Input the password for the MongoDB admin user', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'MONGODB_ADMIN_PASSWORD', - initial: process.env.MONGODB_ADMIN_PASSWORD || generateLongPassword(), - scope: 'ENVIRONMENT' as const - }, - { - name: 'superUserPassword', - type: 'text' as const, - message: 'Input the password for the OpenCRVS super user', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'SUPER_USER_PASSWORD', - initial: process.env.SUPER_USER_PASSWORD || generateLongPassword(), - scope: 'ENVIRONMENT' as const - }, - { - name: 'encryptionKey', - type: 'text' as const, - message: 'Input the password for the disk encryption key', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'ENCRYPTION_KEY', - initial: process.env.ENCRYPTION_KEY || generateLongPassword(), - scope: 'ENVIRONMENT' as const } ] @@ -619,40 +572,6 @@ const emailQuestions = [ } ] -const backupQuestions = [ - { - name: 'backupHost', - type: 'text' as const, - message: 'What is your backup host IP address?', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'BACKUP_HOST', - initial: process.env.BACKUP_HOST, - scope: 'ENVIRONMENT' as const - }, - { - name: 'backupEncryptionPassprase', - type: 'text' as const, - message: 'Input a long random passphrase to be used for encrypting backups', - valueType: 'SECRET' as const, - validate: notEmpty, - valueLabel: 'BACKUP_ENCRYPTION_PASSPHRASE', - initial: process.env.BACKUP_ENCRYPTION_PASSPHRASE || generateLongPassword(), - scope: 'ENVIRONMENT' as const - } -] -const vpnQuestions = [ - { - name: 'vpnHostAddress', - type: 'text' as const, - message: `Please enter the source IP address for users connecting to this environment. This is the public IP address of the VPN server.`, - initial: process.env.VPN_HOST_ADDRESS || '', - validate: notEmpty, - valueType: 'VARIABLE' as const, - valueLabel: 'VPN_HOST_ADDRESS', - scope: 'ENVIRONMENT' as const - } -] const vpnHostQuestions = [ { name: 'vpnAdminPassword', @@ -680,52 +599,123 @@ const sentryQuestions = [ const derivedVariables = [ { - valueType: 'VARIABLE', name: 'ACTIVATE_USERS', - type: 'disabled', valueLabel: 'ACTIVATE_USERS', + valueType: 'VARIABLE', + type: 'disabled', scope: 'ENVIRONMENT' }, { - valueType: 'VARIABLE', name: 'AUTH_HOST', - type: 'disabled', valueLabel: 'AUTH_HOST', + valueType: 'VARIABLE', + type: 'disabled', scope: 'ENVIRONMENT' }, { - valueType: 'VARIABLE', name: 'COUNTRY_CONFIG_HOST', - type: 'disabled', valueLabel: 'COUNTRY_CONFIG_HOST', + valueType: 'VARIABLE', + type: 'disabled', scope: 'ENVIRONMENT' }, { - valueType: 'VARIABLE', name: 'GATEWAY_HOST', - type: 'disabled', valueLabel: 'GATEWAY_HOST', + valueType: 'VARIABLE', + type: 'disabled', scope: 'ENVIRONMENT' }, { - valueType: 'VARIABLE', name: 'CONTENT_SECURITY_POLICY_WILDCARD', - type: 'disabled', valueLabel: 'CONTENT_SECURITY_POLICY_WILDCARD', + valueType: 'VARIABLE', + type: 'disabled', scope: 'ENVIRONMENT' }, { - valueType: 'VARIABLE', name: 'CLIENT_APP_URL', - type: 'disabled', valueLabel: 'CLIENT_APP_URL', + valueType: 'VARIABLE', + type: 'disabled', scope: 'ENVIRONMENT' }, { - valueType: 'VARIABLE', name: 'LOGIN_URL', - type: 'disabled', valueLabel: 'LOGIN_URL', + valueType: 'VARIABLE', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'ELASTICSEARCH_SUPERUSER_PASSWORD', + valueLabel: 'ELASTICSEARCH_SUPERUSER_PASSWORD', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'MINIO_ROOT_USER', + valueLabel: 'MINIO_ROOT_USER', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'MINIO_ROOT_PASSWORD', + valueLabel: 'MINIO_ROOT_PASSWORD', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'MONGODB_ADMIN_USER', + valueLabel: 'MONGODB_ADMIN_USER', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'MONGODB_ADMIN_PASSWORD', + valueLabel: 'MONGODB_ADMIN_PASSWORD', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'SUPER_USER_PASSWORD', + valueLabel: 'SUPER_USER_PASSWORD', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'ENCRYPTION_KEY', + valueLabel: 'ENCRYPTION_KEY', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, + { + name: 'GH_ENCRYPTION_PASSWORD', + valueLabel: 'GH_ENCRYPTION_PASSWORD', + valueType: 'SECRET', + type: 'disabled', + scope: 'REPOSITORY' + }, + { + name: 'SSH_USER', + valueLabel: 'SSH_USER', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT', + value: 'provision' + }, + { + name: 'BACKUP_ENCRYPTION_PASSPHRASE', + valueLabel: 'BACKUP_ENCRYPTION_PASSPHRASE', + valueType: 'SECRET', + type: 'disabled', scope: 'ENVIRONMENT' } ] as const @@ -753,6 +743,7 @@ const metabaseAdminQuestions = [ ] ALL_QUESTIONS.push( + ...githubTokenQuestion, ...dockerhubQuestions, ...sshQuestions, ...sshKeyQuestions, @@ -761,15 +752,21 @@ ALL_QUESTIONS.push( ...notificationTransportQuestions, ...smsQuestions, ...emailQuestions, - ...backupQuestions, - ...vpnQuestions, ...vpnHostQuestions, ...sentryQuestions, ...derivedVariables, ...metabaseAdminQuestions ) + +/* + * These environment only need a subset of the environment variables + * as they are not used for application hosting + */ + +const SPECIAL_NON_APPLICATION_ENVIRONMENTS = ['jump', 'backup'] + ;(async () => { - const { type } = await prompts( + const { type: environment } = await prompts( [ { name: 'type', @@ -777,24 +774,31 @@ ALL_QUESTIONS.push( scope: 'ENVIRONMENT' as const, message: 'Purpose for the environment?', choices: [ + { title: 'Quality assurance (no PII data)', value: 'qa' }, { - title: 'Production (hosts PII data, requires frequent backups)', - value: 'production' + title: 'Staging (hosts PII data, no backups)', + value: 'staging' }, + { title: 'Backup', value: 'backup' }, { - title: 'Staging (hosts PII data, no backups)', + title: 'Production (hosts PII data, requires frequent backups)', value: 'production' }, - { title: 'Quality assurance (no PII data)', value: 'qa' }, + { title: 'Jump / Bastion', value: 'jump' }, { title: 'Other', value: 'development' } ] } ].map(questionToPrompt) ) + // Read users .env file based on the environment name they gave above, e.g. .env.production + dotenv.config({ + path: `${process.cwd()}/.env.${environment}` + }) + log('\n', kleur.bold().underline('Github')) - const { githubOrganisation, githubRepository, githubToken } = await prompts( + const { githubOrganisation, githubRepository } = await prompts( githubQuestions.map(questionToPrompt), { onCancel: () => { @@ -803,6 +807,12 @@ ALL_QUESTIONS.push( } ) + const { githubToken } = await promptAndStoreAnswer( + environment, + githubTokenQuestion, + [] + ) + const octokit = new Octokit({ auth: githubToken }) @@ -862,12 +872,12 @@ ALL_QUESTIONS.push( log(kleur.green('\nSuccessfully logged in to Github\n')) } - log('\n', kleur.bold().underline('Docker Hub')) - - await promptAndStoreAnswer(environment, dockerhubQuestions, existingValues) - log('\n', kleur.bold().underline('SSH')) - await promptAndStoreAnswer(environment, sshQuestions, existingValues) + const { sshPort, sshArgs, sshHost } = await promptAndStoreAnswer( + environment, + sshQuestions, + existingValues + ) const SSH_KEY_EXISTS = existingValues.find( (value) => value.name === 'SSH_KEY' && value.scope === 'ENVIRONMENT' @@ -875,136 +885,129 @@ ALL_QUESTIONS.push( if (!SSH_KEY_EXISTS) { const sshKey = await editor({ - message: `Paste the SSH private key for ${kleur.cyan('SSH_USER')} here:` + message: `Paste the SSH private key for ${kleur.cyan( + 'SSH_USER (provision)' + )} here:` }) const formattedSSHKey = sshKey.endsWith('\n') ? sshKey : sshKey + '\n' ALL_ANSWERS.push({ sshKey: formattedSSHKey }) + /* + * ssh2 library for Node.js doesn't support the same command line parameters + * as we store in the secrets, thus we cannot reliably do the connection verification here. + */ + if (!sshArgs) { + info('Testing SSH connection...') + try { + await verifyConnection(sshHost, sshPort, 'provision', formattedSSHKey) + } catch (err) { + error( + 'Failed to connect to the target server.', + 'Please try again.', + 'If connecting to the server requires a VPN connection, please connect your VPN client before trying again.', + 'If your connection is via a jump server, please specify the jump server in the SSH_ARGS variable.' + ) + error('Reason:', err.message) + process.exit(1) + } + success( + "Successfully connected to the target server's SSH. Now closing connection..." + ) + } } - log('\n', kleur.bold().underline('Server setup')) - const { domain } = await promptAndStoreAnswer( - environment, - infrastructureQuestions, - existingValues - ) + log('\n', kleur.bold().underline('Docker Hub')) - const { updateHosts } = await prompts( - [ - { - name: 'updateHosts', - type: 'confirm' as const, - message: `Do you want to update the hosts file entry for ${domain}?`, - scope: 'REPOSITORY' as const, - initial: true - } - ].map(questionToPrompt) - ) + await promptAndStoreAnswer(environment, dockerhubQuestions, existingValues) - if (updateHosts) { + if (SPECIAL_NON_APPLICATION_ENVIRONMENTS.includes(environment)) { try { - const res = await exec( - `sh ${join(__dirname, './update-known-hosts.sh')} ${domain}` - ) - console.log(res.stdout) + await runInteractiveShell(`sh`, [ + join(__dirname, './update-known-hosts.sh'), + sshHost, + sshPort + ]) } catch (error) { warn( 'Failed to update hosts file. Notice that unknown domains will cause a "host key verification failed" error on deployment.' ) } - } - - log('\n', kleur.bold().underline('Databases & monitoring')) - - await promptAndStoreAnswer( - environment, - databaseAndMonitoringQuestions, - existingValues - ) - log('\n', kleur.bold().underline('Sentry')) - const sentryDSNExists = findExistingValue( - 'SENTRY_DSN', - 'SECRET', - 'ENVIRONMENT', - existingValues - ) - - if (sentryDSNExists) { - await promptAndStoreAnswer(environment, sentryQuestions, existingValues) } else { - const { useSentry } = await prompts( - [ - { - name: 'useSentry', - type: 'confirm' as const, - message: 'Do you want to use Sentry?', - scope: 'ENVIRONMENT' as const, - initial: Boolean(process.env.SENTRY_DNS) - } - ].map(questionToPrompt) + log('\n', kleur.bold().underline('Server setup')) + const { domain } = await promptAndStoreAnswer( + environment, + infrastructureQuestions, + existingValues ) - if (useSentry) { - await promptAndStoreAnswer(environment, sentryQuestions, existingValues) + try { + await runInteractiveShell(`sh`, [ + join(__dirname, './update-known-hosts.sh'), + domain, + sshPort + ]) + } catch (error) { + warn( + 'Failed to update hosts file. Notice that unknown domains will cause a "host key verification failed" error on deployment.' + ) } - } - - if (['production', 'staging'].includes(type)) { - log('\n', kleur.bold().underline('Backups')) - await promptAndStoreAnswer(environment, backupQuestions, existingValues) - log('\n', kleur.bold().underline('VPN')) + log('\n', kleur.bold().underline('Databases & monitoring')) - await promptAndStoreAnswer(environment, vpnQuestions, existingValues) - } - - const vpnAdminPasswordExists = findExistingValue( - 'VPN_ADMIN_PASSWORD', - 'SECRET', - 'ENVIRONMENT', - existingValues - ) - - if (vpnAdminPasswordExists) { - await promptAndStoreAnswer(environment, vpnHostQuestions, existingValues) - } else { - const { isVPNHost } = await prompts( - [ - { - name: 'isVPNHost', - type: 'confirm' as const, - message: `Is this environment going to be used as the VPN server (Wireguard)?`, - scope: 'ENVIRONMENT' as const, - initial: true - } - ].map(questionToPrompt) + await promptAndStoreAnswer( + environment, + databaseAndMonitoringQuestions, + existingValues + ) + log('\n', kleur.bold().underline('Sentry')) + const sentryDSNExists = findExistingValue( + 'SENTRY_DSN', + 'SECRET', + 'ENVIRONMENT', + existingValues ) - if (isVPNHost) { - await promptAndStoreAnswer(environment, vpnHostQuestions, existingValues) + if (sentryDSNExists) { + await promptAndStoreAnswer(environment, sentryQuestions, existingValues) + } else { + const { useSentry } = await prompts( + [ + { + name: 'useSentry', + type: 'confirm' as const, + message: 'Do you want to use Sentry?', + scope: 'ENVIRONMENT' as const, + initial: Boolean(process.env.SENTRY_DNS) + } + ].map(questionToPrompt) + ) + + if (useSentry) { + await promptAndStoreAnswer(environment, sentryQuestions, existingValues) + } } - } - log('\n', kleur.bold().underline('METABASE ADMIN')) - await promptAndStoreAnswer( - environment, - metabaseAdminQuestions, - existingValues - ) + log('\n', kleur.bold().underline('METABASE ADMIN')) + await promptAndStoreAnswer( + environment, + metabaseAdminQuestions, + existingValues + ) - log('\n', kleur.bold().underline('SMTP')) - await promptAndStoreAnswer(environment, emailQuestions, existingValues) + log('\n', kleur.bold().underline('SMTP')) + await promptAndStoreAnswer(environment, emailQuestions, existingValues) - log('\n', kleur.bold().underline('Notification')) + log('\n', kleur.bold().underline('Notification')) - const { notificationTransport } = await promptAndStoreAnswer( - environment, - notificationTransportQuestions, - existingValues - ) + const { notificationTransport } = await promptAndStoreAnswer( + environment, + notificationTransportQuestions, + existingValues + ) - if (notificationTransport.includes('sms')) { - await promptAndStoreAnswer(environment, smsQuestions, existingValues) + if (notificationTransport.includes('sms')) { + await promptAndStoreAnswer(environment, smsQuestions, existingValues) + } } const allAnswers = ALL_ANSWERS.reduce((acc, answer) => { @@ -1020,7 +1023,189 @@ ALL_QUESTIONS.push( fn: (value: string | undefined) => string ) => fn(variable || existingValue?.value) || '' - const derivedUpdates = [ + function findExistingOrDefine( + name: string, + type: 'SECRET' | 'VARIABLE', + scope: 'REPOSITORY' | 'ENVIRONMENT', + newValue: string + ) { + return findExistingValue(name, type, scope, existingValues) + ? null + : process.env[name] || newValue + } + + const derivedUpdates: AnswerWithNullValue[] = [ + { + name: 'GH_ENCRYPTION_PASSWORD', + type: 'SECRET' as const, + didExist: findExistingValue( + 'GH_ENCRYPTION_PASSWORD', + 'SECRET', + 'REPOSITORY', + existingValues + ), + value: findExistingOrDefine( + 'GH_ENCRYPTION_PASSWORD', + 'SECRET', + 'REPOSITORY', + generateLongPassword() + ), + scope: 'REPOSITORY' as const + }, + { + name: 'SSH_USER', + type: 'SECRET' as const, + scope: 'ENVIRONMENT' as const, + value: 'provision', + didExist: findExistingValue( + 'SSH_USER', + 'SECRET', + 'ENVIRONMENT', + existingValues + ) + } + ] + + if (['production', 'staging'].includes(environment)) { + derivedUpdates.push({ + name: 'BACKUP_ENCRYPTION_PASSPHRASE', + type: 'SECRET' as const, + didExist: findExistingValue( + 'BACKUP_ENCRYPTION_PASSPHRASE', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'BACKUP_ENCRYPTION_PASSPHRASE', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }) + } + + const applicationServerUpdates = [ + { + name: 'ELASTICSEARCH_SUPERUSER_PASSWORD', + type: 'SECRET' as const, + didExist: findExistingValue( + 'ELASTICSEARCH_SUPERUSER_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'ELASTICSEARCH_SUPERUSER_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, + { + name: 'MINIO_ROOT_USER', + type: 'SECRET' as const, + didExist: findExistingValue( + 'MINIO_ROOT_USER', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'MINIO_ROOT_USER', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, + { + name: 'MINIO_ROOT_PASSWORD', + type: 'SECRET' as const, + didExist: findExistingValue( + 'MINIO_ROOT_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'MINIO_ROOT_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, + { + name: 'MONGODB_ADMIN_USER', + type: 'SECRET' as const, + didExist: findExistingValue( + 'MONGODB_ADMIN_USER', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'MONGODB_ADMIN_USER', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, + { + name: 'MONGODB_ADMIN_PASSWORD', + type: 'SECRET' as const, + didExist: findExistingValue( + 'MONGODB_ADMIN_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'MONGODB_ADMIN_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, + { + name: 'SUPER_USER_PASSWORD', + type: 'SECRET' as const, + didExist: findExistingValue( + 'SUPER_USER_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'SUPER_USER_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, + { + name: 'ENCRYPTION_KEY', + type: 'SECRET' as const, + didExist: findExistingValue( + 'ENCRYPTION_KEY', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'ENCRYPTION_KEY', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, { type: 'VARIABLE' as const, name: 'ACTIVATE_USERS', @@ -1131,8 +1316,16 @@ ALL_QUESTIONS.push( } ] - const updates: Answers = getAnswers(existingValues) - .concat(...derivedUpdates) + if (!SPECIAL_NON_APPLICATION_ENVIRONMENTS.includes(environment)) { + derivedUpdates.push(...applicationServerUpdates) + } + + const updates = getAnswers(existingValues) + .concat( + ...derivedUpdates.filter( + (update): update is Answer => update.value !== null + ) + ) .filter( (variable) => Boolean(variable.value) && @@ -1141,6 +1334,8 @@ ALL_QUESTIONS.push( variable.value !== variable.didExist?.value) ) + storeSecrets(environment, updates) + /* * List out all updates to the variables and confirm with the user */ @@ -1179,9 +1374,20 @@ ALL_QUESTIONS.push( `The following secrets will be added to Github for environment ${environment}:` ) ) - newSecrets.forEach((secret) => { - log(secret.name, '=', secret.value) - }) + newSecrets + .filter(({ scope }) => scope === 'ENVIRONMENT') + .forEach((secret) => { + log(secret.name, '=', secret.value) + }) + log('') + log( + kleur.yellow(`The following secrets will be added to Github repository:`) + ) + newSecrets + .filter(({ scope }) => scope === 'REPOSITORY') + .forEach((secret) => { + log(secret.name, '=', secret.value) + }) log('') } if (updatedSecrets.length > 0) { diff --git a/infrastructure/environments/ssh.ts b/infrastructure/environments/ssh.ts new file mode 100644 index 000000000..9294a5c14 --- /dev/null +++ b/infrastructure/environments/ssh.ts @@ -0,0 +1,18 @@ +import { NodeSSH } from 'node-ssh' + +export async function verifyConnection( + host: string, + port: number, + username: string, + privateKey: string +) { + const ssh = new NodeSSH() + + await ssh.connect({ + host, + username, + port, + privateKey: privateKey ? privateKey : undefined + }) + await ssh.dispose() +} diff --git a/infrastructure/environments/update-known-hosts.sh b/infrastructure/environments/update-known-hosts.sh index 65fc7c29a..b84ff0f0b 100644 --- a/infrastructure/environments/update-known-hosts.sh +++ b/infrastructure/environments/update-known-hosts.sh @@ -3,29 +3,41 @@ set -e # Check if a domain is provided -if [ "$#" -ne 1 ]; then - echo "Usage: $0 " +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " exit 1 fi DOMAIN=$1 +PORT=$2 IP=$(dig +short $DOMAIN) + +if [ -z "$IP" ]; then + IP=$DOMAIN +fi + KNOWN_HOSTS_FILE="infrastructure/known-hosts" # Ensure the known-hosts file exist touch "$KNOWN_HOSTS_FILE" # Remove existing entry for the domain from the known-hosts file -ssh-keygen -R "$DOMAIN" -f "$KNOWN_HOSTS_FILE" || true -ssh-keygen -R "$IP" -f "$KNOWN_HOSTS_FILE" || true +ssh-keygen -R "[$DOMAIN]:$PORT" -f "$KNOWN_HOSTS_FILE" || true +ssh-keygen -R "[$IP]:$PORT" -f "$KNOWN_HOSTS_FILE" || true # Initialize keyscan result variable KEYSCAN_RESULT="" # Attempt to fetch the new SSH public key for the domain while [ -z "$KEYSCAN_RESULT" ]; do - # Use `|| true` to prevent script exit if ssh-keyscan fails - KEYSCAN_RESULT=$(ssh-keyscan "$DOMAIN" "$IP" 2>/dev/null) || true + + if [ "$DOMAIN" == "$IP" ]; then + # Use `|| true` to prevent script exit if ssh-keyscan fails + KEYSCAN_RESULT=$(ssh-keyscan -p $PORT "$IP" 2>/dev/null) || true + else + # Use `|| true` to prevent script exit if ssh-keyscan fails + KEYSCAN_RESULT=$(ssh-keyscan -p $PORT "$DOMAIN" "$IP" 2>/dev/null) || true + fi # Check if ssh-keyscan was successful if [ -z "$KEYSCAN_RESULT" ]; then diff --git a/infrastructure/server-setup/backups.yml b/infrastructure/server-setup/backups.yml index c690e2f8a..04ef87f3b 100644 --- a/infrastructure/server-setup/backups.yml +++ b/infrastructure/server-setup/backups.yml @@ -16,9 +16,6 @@ - hosts: docker-manager-first become: yes - vars: - crontab_user: root - tasks: - name: Get crontab user home directory getent: @@ -81,54 +78,38 @@ tags: - backups -- hosts: backups +- hosts: backups-host become: yes become_method: sudo - vars: - manager_hostname: "{{ groups['docker-manager-first'][0] }}" - crontab_user: root tasks: - name: Ensure backup user is present user: - name: '{{ external_backup_server_user }}' + name: '{{ backup_server_user }}' state: present create_home: true - home: '/home/{{ external_backup_server_user }}' + home: '{{ backup_server_user_home }}' shell: /bin/bash tags: - backups - set_fact: - external_backup_server_user_home: '/home/{{ external_backup_server_user }}' - tags: - - backups - - - name: Ensure backup application servers can login to backup server - blockinfile: - path: '{{ external_backup_server_user_home }}/.ssh/authorized_keys' - block: | - {{ lookup('file', '/tmp/docker-manager-first_id_rsa.pub') }} - marker: '# {mark} ANSIBLE MANAGED BLOCK docker-manager-first {{ manager_hostname }}' - create: yes - mode: 0600 - owner: '{{ external_backup_server_user }}' - + backup_server_user_home: '/home/{{ backup_server_user }}' tags: - backups - name: 'Create backup directory' file: - path: '{{ external_backup_server_remote_directory }}' + path: '{{ backup_server_remote_target_directory }}' state: directory - owner: '{{ external_backup_server_user }}' + owner: '{{ backup_server_user }}' tags: - backups - - name: Copy rotate_backups.sh file to external_backup_server_user's home directory + - name: Copy rotate_backups.sh file to backup_server_user's home directory copy: src: ../backups/rotate_backups.sh - dest: '{{ external_backup_server_user_home }}/rotate_backups.sh' - owner: '{{ external_backup_server_user }}' + dest: '{{ backup_server_user_home }}/rotate_backups.sh' + owner: '{{ backup_server_user }}' mode: 0755 tags: - backups @@ -136,10 +117,60 @@ - name: 'Setup backup rotation' cron: user: '{{ crontab_user }}' - name: 'rotate backups' + name: 'rotate backups in {{ backup_server_remote_target_directory }}' minute: '0' hour: '0' - job: 'bash {{ external_backup_server_user_home }}/rotate_backups.sh --backup_dir={{ external_backup_server_remote_directory }} --amount_to_keep={{ amount_of_backups_to_keep }} >> /var/log/opencrvs-rotate-backups.log 2>&1' + job: 'bash {{ backup_server_user_home }}/rotate_backups.sh --backup_dir={{ backup_server_remote_target_directory }} --amount_to_keep={{ amount_of_backups_to_keep }} >> /var/log/opencrvs-rotate-backups.log 2>&1' state: "{{ 'present' if (amount_of_backups_to_keep) else 'absent' }}" tags: - backups + +- hosts: backups + become: yes + become_method: sudo + tasks: + - name: Get manager node hostname + set_fact: + manager_hostname: "{{ groups['docker-manager-first'][0] }}" + when: "'docker-manager-first' in groups" + tags: + - backups + + - name: Ensure application servers can login to backup server + blockinfile: + path: '{{ backup_server_user_home }}/.ssh/authorized_keys' + block: | + {{ lookup('file', '/tmp/docker-manager-first_id_rsa.pub') }} + marker: '# {mark} ANSIBLE MANAGED BLOCK docker-manager-first {{ manager_hostname }}' + create: yes + mode: 0600 + owner: '{{ backup_server_user }}' + when: "'docker-manager-first' in groups" + tags: + - backups + +- hosts: docker-manager-first + become: yes + tasks: + - name: Set destination server + when: "'backups' in groups and groups['backups'] | length > 0" + set_fact: + destination_server: "{{ hostvars[groups['backups'][0]].ansible_host }}" + tags: + - backups + + - name: Check SSH connection to destination server + shell: ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 {{ backup_server_user }}@{{ destination_server }} 'echo Connection successful' + remote_user: '{{ crontab_user }}' + register: ssh_test + when: "'backups' in groups and groups['backups'] | length > 0" + ignore_errors: yes + tags: + - backups + + - name: Fail if SSH connection test failed + fail: + msg: 'SSH connection to the backup server failed' + when: "'backups' in groups and groups['backups'] | length > 0 and ssh_test.rc != 0" + tags: + - backups diff --git a/infrastructure/server-setup/group_vars/all.yml b/infrastructure/server-setup/group_vars/all.yml index 1529c0347..50603f70a 100644 --- a/infrastructure/server-setup/group_vars/all.yml +++ b/infrastructure/server-setup/group_vars/all.yml @@ -10,5 +10,7 @@ ansible_python_interpreter: /usr/bin/python3 encrypt_data: False swap_file_path: /swapfile swap_file_size_mb: 8000 -external_backup_server_remote_directory: /home/backup/backups -external_backup_server_user: 'backup' +backup_server_user: 'backup' +backup_server_user_home: '/home/backup' +crontab_user: root +provisioning_user: provision diff --git a/infrastructure/server-setup/inventory/backup.yml b/infrastructure/server-setup/inventory/backup.yml new file mode 100644 index 000000000..41ab8e839 --- /dev/null +++ b/infrastructure/server-setup/inventory/backup.yml @@ -0,0 +1,33 @@ +# 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. + +# Example IP addressses used in this file: +# 22.22.22.22 - Target staging server's IP address +# 33.33.33.33 - Second worker node in this 2-server setup +# 55.55.55.55 - This IP address is both the VPN server the server used as a bastion / jump host +# 66.66.66.66 - This IP address is the address of a backup server + +all: + vars: + # @todo how many days to store backups for? + amount_of_backups_to_keep: 7 + backup_server_remote_target_directory: /home/backup/backups + users: + # @todo this is where you define which development team members have access to the server. + # If you need to remove access from someone, do not remove them from this list, but instead set their state: absent + - name: my-user + ssh_keys: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABg...Z/rhU= user@example.com + state: present + sudoer: true + +backups-host: + hosts: + backup: # @todo set this to be the hostname of your backup server + ansible_host: '66.66.66.66' diff --git a/infrastructure/server-setup/development.yml b/infrastructure/server-setup/inventory/development.yml similarity index 100% rename from infrastructure/server-setup/development.yml rename to infrastructure/server-setup/inventory/development.yml diff --git a/infrastructure/server-setup/inventory/jump.yml b/infrastructure/server-setup/inventory/jump.yml new file mode 100644 index 000000000..639a9d8dd --- /dev/null +++ b/infrastructure/server-setup/inventory/jump.yml @@ -0,0 +1,30 @@ +# 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. + +# Example IP addressses used in this file: +# 22.22.22.22 - Target staging server's IP address +# 33.33.33.33 - Second worker node in this 2-server setup +# 55.55.55.55 - This IP address is both the VPN server the server used as a bastion / jump host +# 66.66.66.66 - This IP address is the address of a backup server + +all: + vars: + users: + # @todo this is where you define which development team members have access to the server. + # If you need to remove access from someone, do not remove them from this list, but instead set their state: absent + - name: my-user + ssh_keys: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABg...Z/rhU= user@example.com + state: present + sudoer: true + +jump-host: + hosts: + backup: # @todo set this to be the hostname of your backup server + ansible_host: '55.55.55.55' diff --git a/infrastructure/server-setup/production.yml b/infrastructure/server-setup/inventory/production.yml similarity index 52% rename from infrastructure/server-setup/production.yml rename to infrastructure/server-setup/inventory/production.yml index f23e64fcd..255ef8e48 100644 --- a/infrastructure/server-setup/production.yml +++ b/infrastructure/server-setup/inventory/production.yml @@ -15,13 +15,18 @@ all: vars: - # This configuration variable blocks all access to the server, including SSH, except from the IP addresses specified below. - # This should always be set when configuring a production server if there is no other firewall in front of the server. - # SSH and other services should never be exposed to the public internet. + # "enable_backups" is set to true on production. Production will always backup it's own data. enable_backups: true + # This configuration variable blocks all access to the server, including SSH, except from the IP addresses specified below. + # In most cases these should be left empty, as the VPN server should be the only source of traffic. + # OpenCRVS should never be exposed to the public internet. If this is not possible to organise, + # then this should always be set when configuring a production server if there is no other firewall in front of the server. + # BE CAREFUL! This will block all SSH traffic except from the IP addresses listed here. only_allow_access_from_addresses: - # @todo place the IP address of your VPN server or other explicitly allowed traffic sources here - - 55.55.55.55 # example VPN server IP address + # @todo place the IP address of your jump server or other explicitly allowed traffic sources here + #- 55.55.55.55 # example jump server IP address + [] + backup_server_remote_target_directory: /home/backup/backups users: # @todo this is where you define which development team members have access to the server. # If you need to remove access from someone, do not remove them from this list, but instead set their state: absent @@ -34,23 +39,31 @@ all: docker-manager-first: hosts: prod-01: # @todo set this to be the hostname of your target server - ansible_host: '22.22.22.22' # todo set this to be the hostname of your target server + ansible_host: '22.22.22.22' # @todo set this to be the hostname of your target server data_label: data1 - # @todo as production servers are not directly accessible from the internet, you need to use a jump server to access them. - ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' + # If you are using a jump server to access this environment, enter other SSH args here. + # ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' # @todo We recommend you add 2-4 workers for a scaled production deployment # This should depend on the size of your country and the number of end users. +# If you are only using one production worker for very small countries or small pilot projects, replace with an empty block like so: docker-workers: {} docker-workers: hosts: prod-02: # @todo set this to be the hostname of your target server ansible_host: '33.33.33.33' - data_label: data2 - ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' + # If you are using a jump server to access this environment, enter other SSH args here. + # ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' backups: hosts: backup: # @todo set this to be the hostname of your backup server ansible_host: '66.66.66.66' - # @todo how many days to store backups for? - amount_of_backups_to_keep: 3 + # Written by provision pipeline. Assumes "backup" environment + # exists in Github environments + ansible_ssh_private_key_file: /tmp/backup_ssh_private_key + +# If you are using a jump server to access this environment +# jump: +# hosts: +# backup: # @todo set this to be the hostname of your backup server +# ansible_host: '55.55.55.55' diff --git a/infrastructure/server-setup/qa.yml b/infrastructure/server-setup/inventory/qa.yml similarity index 65% rename from infrastructure/server-setup/qa.yml rename to infrastructure/server-setup/inventory/qa.yml index d584e5987..840662637 100644 --- a/infrastructure/server-setup/qa.yml +++ b/infrastructure/server-setup/inventory/qa.yml @@ -17,17 +17,6 @@ all: state: present sudoer: true - # @todo will this host be used as a bastion / jump server for the CI/CD pipeline to access production servers? - # if no, then you can remove this user. - - name: jump - state: present - sudoer: false - two_factor: false - ssh_keys: - # if yes, then this should list the public keys of the private keys that are used when connecting to the production servers. - - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABg...Z/rhU= provision@github-runner-243 # example provision user key - - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABg...Z/rhU= provision@github-runner-244 # example provision user key - docker-manager-first: hosts: qa: # @todo set this to be the hostname of your target server diff --git a/infrastructure/server-setup/staging.yml b/infrastructure/server-setup/inventory/staging.yml similarity index 55% rename from infrastructure/server-setup/staging.yml rename to infrastructure/server-setup/inventory/staging.yml index dd7a5a1ca..3f7c069e5 100644 --- a/infrastructure/server-setup/staging.yml +++ b/infrastructure/server-setup/inventory/staging.yml @@ -15,13 +15,20 @@ all: vars: # This configuration variable blocks all access to the server, including SSH, except from the IP addresses specified below. - # This should always be set when configuring a production server if there is no other firewall in front of the server. - # SSH and other services should never be exposed to the public internet. + # In most cases these should be left empty, as the VPN server should be the only source of traffic. + # OpenCRVS should never be exposed to the public internet. If this is not possible to organise, + # then this should always be set when configuring a production server if there is no other firewall in front of the server. + # BE CAREFUL! This will block all SSH traffic except from the IP addresses listed here. only_allow_access_from_addresses: - # @todo place the IP address of your VPN server or other explicitly allowed traffic sources here - - 55.55.55.55 # example VPN server IP address + # If you are using a jump server to access this environment, place the IP address of the jump server or other explicitly allowed traffic sources here + #- 55.55.55.55 # example jump server IP address + [] + # "enable_backups" is set to false on staging. Staging will not backup it's own data. enable_backups: false + # "periodic_restore_from_backup" restores daily production backups to your staging server. + # For this you need to first setup a backup environment periodic_restore_from_backup: true + backup_server_remote_source_directory: /home/backup/backups users: # @todo this is where you define which development team members have access to the server. # If you need to remove access from someone, do not remove them from this list, but instead set their state: absent @@ -34,10 +41,10 @@ all: docker-manager-first: hosts: staging: # @todo set this to be the hostname of your target server - ansible_host: '11.11.11.11' # todo set this to be the hostname of your target server + ansible_host: '11.11.11.11' # @todo set this to be the hostname of your target server data_label: data1 - # @todo as production servers are not directly accessible from the internet, you need to use a jump server to access them. - ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' + # If you are using a jump server to access this environment, enter other SSH args here. + # ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' # This staging servers is configured to only use one server docker-workers: {} @@ -48,5 +55,12 @@ backups: hosts: backup: # @todo set this to be the hostname of your backup server ansible_host: '66.66.66.66' # set this to be the IP address of your backup server - # @todo how many days to store backups for? - amount_of_backups_to_keep: 3 + # Written by provision pipeline. Assumes "backup" environment + # exists in Github environments + ansible_ssh_private_key_file: /tmp/backup_ssh_private_key + +# If you are using a jump server to access this environment +# jump: +# hosts: +# backup: # @todo set this to be the hostname of your backup server +# ansible_host: '55.55.55.55' diff --git a/infrastructure/server-setup/jump.yml b/infrastructure/server-setup/jump.yml new file mode 100644 index 000000000..b04bee3f1 --- /dev/null +++ b/infrastructure/server-setup/jump.yml @@ -0,0 +1,100 @@ +# 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. +--- +- hosts: docker-manager-first + become: yes + tasks: + - name: Fetch the public SSH key so it can be transferred to the jump machine + fetch: + src: '/home/{{ provisioning_user }}/.ssh/authorized_keys' + dest: '/tmp/docker-manager-first_id_rsa.pub' + flat: yes + tags: + - jump + +- hosts: jump-host + become: yes + become_method: sudo + tasks: + - name: Ensure jump user is present + user: + name: 'jump' + state: present + create_home: true + home: '/home/jump' + shell: /bin/bash + tags: + - jump + + - name: Only require public key from the user jump + blockinfile: + path: /etc/ssh/sshd_config + block: | + Match User jump + PasswordAuthentication no + AuthenticationMethods publickey + marker: '# {mark} ANSIBLE MANAGED BLOCK FOR USER jump' + become: yes + tags: + - jump + +- hosts: jump + become: yes + become_method: sudo + tasks: + - name: Get manager node hostname + set_fact: + manager_hostname: "{{ groups['docker-manager-first'][0] }}" + when: "'docker-manager-first' in groups and groups['docker-manager-first'] | length > 0" + tags: + - jump + + - name: Ensure application servers can login to jump server + blockinfile: + path: '/home/jump/.ssh/authorized_keys' + block: | + {{ lookup('file', '/tmp/docker-manager-first_id_rsa.pub') }} + marker: '# {mark} ANSIBLE MANAGED BLOCK {{ manager_hostname }}' + create: yes + mode: 0600 + owner: 'jump' + when: "'docker-manager-first' in groups and groups['docker-manager-first'] | length > 0" + tags: + - jump + +- hosts: localhost + tasks: + - name: Define application_server_using_jump + set_fact: + application_server_using_jump: "{{ 'docker-manager-first' in groups and 'jump' in groups and groups['jump'] | length > 0 }}" + tags: + - jump + + - name: Set destination server + when: 'application_server_using_jump' + set_fact: + destination_server: "{{ hostvars[groups['jump'][0]].ansible_host }}" + tags: + - jump + + # This would use the CI runner machine and the currently installed SSH key which should be provision user's key + - name: Check SSH connection to destination server + shell: ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -o ConnectTimeout=5 jump@{{ destination_server }} 'echo Connection successful' + when: 'application_server_using_jump' + register: ssh_test + ignore_errors: yes + tags: + - jump + + - name: Fail if SSH connection test failed + fail: + msg: 'SSH connection to the jump server failed' + when: 'application_server_using_jump and ssh_test.rc != 0' + tags: + - jump diff --git a/infrastructure/server-setup/playbook.yml b/infrastructure/server-setup/playbook.yml index b12b2c4c3..0a5bd1eff 100644 --- a/infrastructure/server-setup/playbook.yml +++ b/infrastructure/server-setup/playbook.yml @@ -14,6 +14,8 @@ - hosts: all become: yes + gather_facts: yes + ignore_unreachable: false become_method: sudo tasks: - include_tasks: @@ -29,14 +31,20 @@ apply: tags: - updates + + - include_tasks: + file: tasks/users.yml + apply: + tags: + - users tags: - - updates + - users -- hosts: docker-manager-first, docker-workers +- import_playbook: jump.yml + +- hosts: docker-manager-first:docker-workers become: yes - become_method: sudo - vars: - crontab_user: root + become_method: sudo tasks: - include_tasks: file: tasks/application.yml @@ -72,14 +80,6 @@ - deployment-user - users - - include_tasks: - file: tasks/users.yml - apply: - tags: - - users - tags: - - users - - include_tasks: file: tasks/mongodb.yml apply: diff --git a/infrastructure/server-setup/tasks/backups/crontab.yml b/infrastructure/server-setup/tasks/backups/crontab.yml index 1370e3180..8abd21d67 100644 --- a/infrastructure/server-setup/tasks/backups/crontab.yml +++ b/infrastructure/server-setup/tasks/backups/crontab.yml @@ -1,4 +1,12 @@ -- name: Copy backups.sh file to external_backup_server_user's home directory +- name: Set destination server + set_fact: + backup_hostname: "{{ hostvars[groups['backups'][0]].ansible_host }}" + backup_port: "{{ hostvars[groups['backups'][0]].ansible_port | default('22') }}" + tags: + - backups + when: "'backups' in groups and groups['backups'] | length == 1" + +- name: Copy backups.sh file to backup_server_user's home directory copy: src: ../backups/backup.sh dest: '{{ crontab_user_home }}/backup.sh' @@ -6,13 +14,14 @@ mode: 0755 - name: 'Setup crontab to backup the opencrvs data' + when: enable_backups | default(false) cron: user: '{{ crontab_user }}' name: 'backup opencrvs' minute: '0' hour: '0' - job: 'bash {{ crontab_user_home }}/backup.sh --passphrase={{ backup_encryption_passphrase }} --ssh_user={{ external_backup_server_user }} --ssh_host={{ external_backup_server_ip }} --ssh_port={{ external_backup_server_ssh_port }} --remote_dir={{ external_backup_server_remote_directory }} --replicas=1 >> /var/log/opencrvs-backup.log 2>&1' - state: "{{ 'present' if (external_backup_server_ip is defined and backup_encryption_passphrase and (enable_backups | default(false))) else 'absent' }}" + job: 'bash {{ crontab_user_home }}/backup.sh --passphrase={{ backup_encryption_passphrase }} --ssh_user={{ backup_server_user }} --ssh_host={{ backup_hostname }} --ssh_port={{ backup_port }} --remote_dir={{ backup_server_remote_target_directory }} --replicas=1 >> /var/log/opencrvs-backup.log 2>&1' + state: "{{ 'present' if (backup_hostname is defined and backup_encryption_passphrase and (enable_backups | default(false))) else 'absent' }}" ## # For machines that periodically restore from backup (staging) @@ -23,25 +32,27 @@ periodic_restore_from_backup: false when: periodic_restore_from_backup is not defined -- name: Throw an error if periodic_restore_from_backup is true but backup_encryption_passphrase is not defined +- name: Throw an error if periodic_restore_from_backup is true but backup_restore_encryption_passphrase is not defined fail: - msg: 'Error: backup_encryption_passphrase is not defined. It usually means you have not set backup_encryption_passphrase in your staging environments secrets' - when: periodic_restore_from_backup and backup_encryption_passphrase is not defined + msg: "Error: backup_restore_encryption_passphrase is not defined. This usually means you have enabled periodic restore from production but you haven't set up a production environment yet. Please set up a production environment first." + when: periodic_restore_from_backup and backup_restore_encryption_passphrase is not defined - name: 'Setup crontab to download a backup periodically the opencrvs data' + when: periodic_restore_from_backup cron: user: '{{ crontab_user }}' name: 'download opencrvs backup' minute: '30' hour: '0' - job: 'cd / && bash /opt/opencrvs/infrastructure/backups/download.sh --passphrase={{ backup_encryption_passphrase }} --ssh_user={{ external_backup_server_user }} --ssh_host={{ external_backup_server_ip }} --ssh_port={{ external_backup_server_ssh_port }} --remote_dir={{ external_backup_server_remote_directory }} >> /var/log/opencrvs-restore.log 2>&1' - state: "{{ 'present' if (external_backup_server_ip is defined and backup_encryption_passphrase and periodic_restore_from_backup) else 'absent' }}" + job: 'cd / && bash /opt/opencrvs/infrastructure/backups/download.sh --passphrase={{ backup_restore_encryption_passphrase }} --ssh_user={{ backup_server_user }} --ssh_host={{ backup_hostname }} --ssh_port={{ backup_port }} --remote_dir={{ backup_server_remote_source_directory }} >> /var/log/opencrvs-restore.log 2>&1' + state: "{{ 'present' if (backup_hostname is defined and backup_restore_encryption_passphrase and periodic_restore_from_backup) else 'absent' }}" - name: 'Setup crontab to restore the opencrvs data' + when: periodic_restore_from_backup cron: user: '{{ crontab_user }}' name: 'restore opencrvs' minute: '0' hour: '1' job: 'cd / && bash /opt/opencrvs/infrastructure/backups/restore.sh --replicas=1 >> /var/log/opencrvs-restore.log 2>&1' - state: "{{ 'present' if (external_backup_server_ip is defined and backup_encryption_passphrase and periodic_restore_from_backup) else 'absent' }}" + state: "{{ 'present' if (backup_hostname is defined and backup_restore_encryption_passphrase and periodic_restore_from_backup) else 'absent' }}" diff --git a/infrastructure/server-setup/tasks/checks.yml b/infrastructure/server-setup/tasks/checks.yml index ee292c05f..86c87e31a 100644 --- a/infrastructure/server-setup/tasks/checks.yml +++ b/infrastructure/server-setup/tasks/checks.yml @@ -1,3 +1,13 @@ +- name: Check if external_backup_server_user is set + fail: + msg: 'external_backup_server_user variable was deprecated in OpenCRVS 1.5. Please rename the variable to backup_server_remote_target_directory' + when: external_backup_server_user is defined + +- name: Check if external_backup_server_remote_directory is set + fail: + msg: 'external_backup_server_remote_directory variable was deprecated in OpenCRVS 1.5. Please rename the variable to backup_server_remote_target_directory' + when: external_backup_server_remote_directory is defined + - name: 'Check mandatory variables are defined' assert: that: diff --git a/infrastructure/server-setup/tasks/docker.yml b/infrastructure/server-setup/tasks/docker.yml index 663ddb4c2..addb66800 100644 --- a/infrastructure/server-setup/tasks/docker.yml +++ b/infrastructure/server-setup/tasks/docker.yml @@ -48,3 +48,17 @@ minute: '0' hour: '0' job: '/usr/bin/docker system prune -af >> /var/log/docker-prune.log' + +- name: Check if Docker group exists + command: getent group docker + register: docker_group + ignore_errors: yes + +- name: Add user to Docker group + when: docker_group.rc == 0 and item.state == 'present' + with_items: '{{ users }}' + ignore_errors: yes + user: + name: '{{ item.name }}' + groups: docker + append: yes diff --git a/infrastructure/server-setup/tasks/ufw.yml b/infrastructure/server-setup/tasks/ufw.yml index 67b644523..2d67cc6d6 100644 --- a/infrastructure/server-setup/tasks/ufw.yml +++ b/infrastructure/server-setup/tasks/ufw.yml @@ -12,17 +12,23 @@ loop: '{{ only_allow_access_from_addresses }}' when: only_allow_access_from_addresses is defined and only_allow_access_from_addresses | length > 0 +- name: Set default SSH port + set_fact: + ssh_port: '{{ ansible_port | default(22) }}' + - name: Remove general OpenSSH allow rule ufw: rule: allow - name: OpenSSH + port: '{{ ssh_port }}' + proto: tcp delete: yes when: only_allow_access_from_addresses is defined and only_allow_access_from_addresses | length > 0 - name: Allow OpenSSH through UFW universally ufw: rule: allow - name: OpenSSH + port: '{{ ssh_port }}' + proto: tcp when: only_allow_access_from_addresses is undefined or only_allow_access_from_addresses | length == 0 # Docker swarm ports - Note: all published docker container port will override UFW rules! diff --git a/infrastructure/server-setup/tasks/updates.yml b/infrastructure/server-setup/tasks/updates.yml index 2317bbde1..b270761fa 100644 --- a/infrastructure/server-setup/tasks/updates.yml +++ b/infrastructure/server-setup/tasks/updates.yml @@ -1,23 +1,15 @@ - name: Update apt repository apt: update_cache: yes + force_apt_get: yes -- name: Upgrade all packages to the latest version - apt: - upgrade: dist - -- name: Remove unnecessary packages - apt: - autoremove: yes - -- name: Clean up apt cache - apt: - autoclean: yes - -- name: Install unattended-upgrades package - apt: - name: unattended-upgrades - state: present +- name: Manage apt updates, upgrades, and package installation + shell: | + while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 5; done + apt-get update && apt-get dist-upgrade -y + apt-get install -y unattended-upgrades + apt-get autoremove -y + apt-get autoclean -y - name: Configure Unattended Upgrades ansible.builtin.copy: diff --git a/infrastructure/server-setup/tasks/users.yml b/infrastructure/server-setup/tasks/users.yml index 6ee8cf9db..d8aa84a38 100644 --- a/infrastructure/server-setup/tasks/users.yml +++ b/infrastructure/server-setup/tasks/users.yml @@ -46,20 +46,6 @@ when: item.state == 'present' with_items: '{{ users }}' -- name: Check if Docker group exists - command: getent group docker - register: docker_group - ignore_errors: yes - -- name: Add user to Docker group - when: docker_group.rc == 0 and item.state == 'present' - with_items: '{{ users }}' - ignore_errors: yes - user: - name: '{{ item.name }}' - groups: docker - append: yes - - name: Ensure 2FA setup lines are present in .profile for each user blockinfile: path: '/home/{{ item.name }}/.profile' diff --git a/package.json b/package.json index bf2072b87..6969d4376 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "kleur": "^4.1.5", "libsodium-wrappers": "^0.7.13", "lint-staged": "^7.1.0", + "node-ssh": "^13.2.0", "nodemon": "^2.0.22", "pino-pretty": "^11.0.0", "prettier": "^2.8.8", @@ -85,7 +86,7 @@ "csv-stringify": "^6.4.6", "csv2json": "^2.0.2", "date-fns": "^2.28.0", - "dotenv": "^6.1.0", + "dotenv": "^16.4.5", "esbuild": "^0.18.9", "google-libphonenumber": "^3.2.32", "graphql-tag": "^2.12.6", diff --git a/yarn.lock b/yarn.lock index 72d987e52..584e11b1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2546,6 +2546,13 @@ asap@~2.0.3: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +asn1@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + asn1js@^3.0.1, asn1js@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" @@ -2646,6 +2653,13 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +bcrypt-pbkdf@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -2734,6 +2748,11 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +buildcheck@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" + integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== + busboy@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -3154,6 +3173,14 @@ country-data@^0.0.31: currency-symbol-map "~2" underscore ">1.4.4" +cpu-features@~0.0.9: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" + integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== + dependencies: + buildcheck "~0.0.6" + nan "^2.19.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -3394,10 +3421,10 @@ dotenv@^16.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== -dotenv@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" - integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== dset@^3.1.2: version "3.1.2" @@ -4666,6 +4693,11 @@ is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-unc-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" @@ -5146,6 +5178,13 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@^1.1.1, make-error@^1.3.2: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -5279,6 +5318,11 @@ mute-stream@1.0.0, mute-stream@^1.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== +nan@^2.18.0, nan@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0" + integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -5351,6 +5395,18 @@ node-releases@^2.0.6: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== +node-ssh@^13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/node-ssh/-/node-ssh-13.2.0.tgz#06823c4817a5d31249c125a47d821d473f61491d" + integrity sha512-7vsKR2Bbs66th6IWCy/7SN4MSwlVt+G6QrHB631BjRUM8/LmvDugtYhi0uAmgvHS/+PVurfNBOmELf30rm0MZg== + dependencies: + is-stream "^2.0.0" + make-dir "^3.1.0" + sb-promise-queue "^2.1.0" + sb-scandir "^3.1.0" + shell-escape "^0.2.0" + ssh2 "^1.14.0" + nodemailer@^6.9.8: version "6.9.8" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.8.tgz#29601e80440f2af7aa62b32758fdac7c6b784143" @@ -6184,11 +6240,23 @@ safe-stable-stringify@^2.1.0: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sb-promise-queue@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sb-promise-queue/-/sb-promise-queue-2.1.0.tgz#7e44bebef643f75d809a3db7f605b815d877a04d" + integrity sha512-zwq4YuP1FQFkGx2Q7GIkZYZ6PqWpV+bg0nIO1sJhWOyGyhqbj0MsTvK6lCFo5TQwX5pZr6SCQ75e8PCDCuNvkg== + +sb-scandir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/sb-scandir/-/sb-scandir-3.1.0.tgz#31c346abb5184b73c5a25b286858f4299aa8756c" + integrity sha512-70BVm2xz9jn94zSQdpvYrEG101/UV9TVGcfWr9T5iob3QhCK4lYXeculfBqPGFv3XTeKgx4dpWyYIDeZUqo4kg== + dependencies: + sb-promise-queue "^2.1.0" + scuid@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" @@ -6209,7 +6277,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -6290,6 +6358,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-escape@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133" + integrity sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw== + shell-quote@^1.7.3: version "1.8.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" @@ -6488,6 +6561,17 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +ssh2@^1.14.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.15.0.tgz#2f998455036a7f89e0df5847efb5421748d9871b" + integrity sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw== + dependencies: + asn1 "^0.2.6" + bcrypt-pbkdf "^1.0.2" + optionalDependencies: + cpu-features "~0.0.9" + nan "^2.18.0" + staged-git-files@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.1.tgz#37c2218ef0d6d26178b1310719309a16a59f8f7b" @@ -6799,6 +6883,11 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tweetnacl@^0.14.3: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From 11bea8b3998d4992312cf5e5b84c7c89371ad8f8 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 18 Jun 2024 15:01:51 +0300 Subject: [PATCH 031/146] add auto release pr pipeline --- .github/workflows/auto-pr-to-release.yml | 161 +++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 .github/workflows/auto-pr-to-release.yml diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml new file mode 100644 index 000000000..cd9a11880 --- /dev/null +++ b/.github/workflows/auto-pr-to-release.yml @@ -0,0 +1,161 @@ +# 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. + +name: Auto PR to Release Branch + +on: + pull_request: + types: [closed] + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to process' + required: true + default: '' + +jobs: + create-pr: + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' }} + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Get PR details from workflow dispatch + if: ${{ github.event_name == 'workflow_dispatch' }} + id: get_pr_details_dispatch + run: | + PR_NUMBER=${{ github.event.inputs.pr_number }} + PR_DATA=$(gh pr view $PR_NUMBER --json number,headRefName,baseRefName,mergedBy,mergeCommit,author,milestone --jq '{number: .number, headRefName: .headRefName, baseRefName: .baseRefName, merger: .mergedBy.login, author: .author.login, milestone: .milestone.title}') + echo "PR_ID=$(echo $PR_DATA | jq -r '.number')" >> $GITHUB_ENV + echo "PR_AUTHOR=$(echo $PR_DATA | jq -r '.author')" >> $GITHUB_ENV + echo "PR_MERGER=$(echo $PR_DATA | jq -r '.merger')" >> $GITHUB_ENV + echo "MILESTONE=$(echo $PR_DATA | jq -r '.milestone')" >> $GITHUB_ENV + echo "BASE_BRANCH=$(echo $PR_DATA | jq -r '.baseRefName')" >> $GITHUB_ENV + echo "HEAD_BRANCH=$(echo $PR_DATA | jq -r '.headRefName')" >> $GITHUB_ENV + + LATEST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid') + FIRST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[0].oid') + echo "LATEST_COMMIT_SHA=${LATEST_COMMIT_SHA}" >> $GITHUB_ENV + echo "FIRST_COMMIT_SHA=${FIRST_COMMIT_SHA}" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get PR details from event + if: ${{ github.event_name == 'pull_request' }} + id: get_pr_details_event + run: | + PR_NUMBER=${{ github.event.pull_request.number }} + echo "PR_ID=${{ github.event.pull_request.number }}" >> $GITHUB_ENV + echo "PR_AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV + echo "MILESTONE=${{ github.event.pull_request.milestone.title }}" >> $GITHUB_ENV + echo "BASE_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV + echo "HEAD_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV + + LATEST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid') + FIRST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[0].oid') + echo "LATEST_COMMIT_SHA=${LATEST_COMMIT_SHA}" >> $GITHUB_ENV + echo "FIRST_COMMIT_SHA=${FIRST_COMMIT_SHA}" >> $GITHUB_ENV + + PR_DETAILS=$(gh pr view $PR_NUMBER --json mergedBy) + MERGED_BY_LOGIN=$(echo "$PR_DETAILS" | jq -r '.mergedBy.login') + echo "PR_MERGER=$MERGED_BY_LOGIN" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check for milestone and if release branch exists + id: check_release_branch + run: | + if [ -z "${{ env.MILESTONE }}" ]; then + echo "No milestone set. Exiting." + exit 0 + fi + + RELEASE_BRANCH="release-${{ env.MILESTONE }}" + + # Check if the release branch exists + if git ls-remote --heads origin $RELEASE_BRANCH | grep -q "refs/heads/$RELEASE_BRANCH"; then + echo "RELEASE_BRANCH=${RELEASE_BRANCH}" >> $GITHUB_ENV + else + echo "Release branch $RELEASE_BRANCH does not exist. Exiting." + exit 0 + fi + + - name: Create and push the new branch for the PR + run: | + PR_TITLE="🍒 Merge changes from PR #${{ env.PR_ID }} to ${{ env.RELEASE_BRANCH }}" + PR_BODY="Automated PR to merge changes from develop to ${{ env.RELEASE_BRANCH }}" + + # Configure git + git config user.name "github-actions" + git config user.email "github-actions@github.com" + git config advice.mergeConflict false + + # Fetch and checkout the release branch + git fetch origin + git checkout ${{ env.RELEASE_BRANCH }} + + # Create a new branch for the PR + NEW_BRANCH="auto-pr-${{ env.RELEASE_BRANCH }}-${{ env.PR_ID }}-$RANDOM" + git checkout -b $NEW_BRANCH + + echo "First commit: ${{ env.FIRST_COMMIT_SHA }}" + echo "Latest commit: ${{ env.LATEST_COMMIT_SHA }}" + COMMIT_RANGE="${{ env.FIRST_COMMIT_SHA }}..${{ env.LATEST_COMMIT_SHA }}" + + if [ "${{ env.FIRST_COMMIT_SHA }}" == "${{ env.LATEST_COMMIT_SHA }}" ]; then + COMMIT_RANGE=${{ env.FIRST_COMMIT_SHA }} + fi + + echo "Commit range: $COMMIT_RANGE" + + # Attempt to cherry-pick the commits from the original PR + CHERRY_PICK_OUTPUT=$(git cherry-pick $COMMIT_RANGE 2>&1) || { + git cherry-pick --abort || true + # If cherry-pick fails, create a placeholder commit + echo "Cherry-pick failed. Creating placeholder commit." + git reset --hard + git commit --allow-empty -m "Placeholder commit for PR #${{ env.PR_ID }}" + + # Add manual cherry-pick commands to the PR body + PR_BODY="${PR_BODY} + + **I failed to cherry-pick the changes automatically because of the following:** + + \`\`\` + $CHERRY_PICK_OUTPUT + \`\`\` + + **To continue manually you can use these commands:** + \`\`\` + git fetch origin pull/${{ env.PR_ID }}/head:pr-${{ env.PR_ID }}-branch + git checkout $NEW_BRANCH + git reset --hard HEAD~1 # Remove placeholder commit + git cherry-pick $COMMIT_RANGE + \`\`\` + " + } + + # Push the new branch + git push origin $NEW_BRANCH + + # Create a pull request and assign the original PR author as the reviewer + AUTHOR=${{ env.PR_AUTHOR }} + + if [[ $AUTHOR == *renovate* ]]; then + if [ -z "${{ env.PR_MERGER }}" ]; then + AUTHOR="" + else + AUTHOR=${{ env.PR_MERGER }} + fi + fi + gh pr create --title "$PR_TITLE" --body "$PR_BODY" --head $NEW_BRANCH --base ${{ env.RELEASE_BRANCH }} --reviewer $AUTHOR + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From c5f9bedbf57122ac3f431f654ec0dc4dcbd18772 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 18 Jun 2024 15:03:32 +0300 Subject: [PATCH 032/146] Fix permission error when removing google authenticator file (#144) --- .github/workflows/reset-2fa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reset-2fa.yml b/.github/workflows/reset-2fa.yml index 45386d246..7ed86fb52 100644 --- a/.github/workflows/reset-2fa.yml +++ b/.github/workflows/reset-2fa.yml @@ -47,4 +47,4 @@ jobs: - name: Remove 2FA run: | - ssh ${{ secrets.SSH_USER }}@${{ vars.SSH_HOST || secrets.SSH_HOST }} -p ${{ vars.SSH_PORT || secrets.SSH_PORT }} ${{ vars.SSH_ARGS }} "rm /home/${{ github.event.inputs.user }}/.google_authenticator" + ssh ${{ secrets.SSH_USER }}@${{ vars.SSH_HOST || secrets.SSH_HOST }} -p ${{ vars.SSH_PORT || secrets.SSH_PORT }} ${{ vars.SSH_ARGS }} "sudo rm /home/${{ github.event.inputs.user }}/.google_authenticator" From 85d60ad92d25667fd440e5bd6124cdf026d566dd Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 18 Jun 2024 15:13:16 +0300 Subject: [PATCH 033/146] fix cherry-pick instructions --- .github/workflows/auto-pr-to-release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index cd9a11880..4ebd9327d 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -135,7 +135,9 @@ jobs: **To continue manually you can use these commands:** \`\`\` - git fetch origin pull/${{ env.PR_ID }}/head:pr-${{ env.PR_ID }}-branch + git fetch origin $NEW_BRANCH:$NEW_BRANCH + git fetch origin ${{ env.HEAD_BRANCH }}:${{ env.HEAD_BRANCH }} + git checkout $NEW_BRANCH git reset --hard HEAD~1 # Remove placeholder commit git cherry-pick $COMMIT_RANGE From f63a90d10d5b732123d3e5487a31c328bac8f58b Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 18 Jun 2024 15:20:13 +0300 Subject: [PATCH 034/146] Auto PR: explicitly define head branch for cherry-pick --- .github/workflows/auto-pr-to-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 4ebd9327d..89d739fb6 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -117,7 +117,7 @@ jobs: echo "Commit range: $COMMIT_RANGE" # Attempt to cherry-pick the commits from the original PR - CHERRY_PICK_OUTPUT=$(git cherry-pick $COMMIT_RANGE 2>&1) || { + CHERRY_PICK_OUTPUT=$(git cherry-pick ${{ env.HEAD_BRANCH }} $COMMIT_RANGE 2>&1) || { git cherry-pick --abort || true # If cherry-pick fails, create a placeholder commit echo "Cherry-pick failed. Creating placeholder commit." @@ -140,7 +140,7 @@ jobs: git checkout $NEW_BRANCH git reset --hard HEAD~1 # Remove placeholder commit - git cherry-pick $COMMIT_RANGE + git cherry-pick ${{ env.HEAD_BRANCH }} $COMMIT_RANGE \`\`\` " } From 59cbf9966930858d0c61f3a1ebd7640823dfcce0 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 18 Jun 2024 15:25:38 +0300 Subject: [PATCH 035/146] Auto PR: fetch all heads before trying auto cherry-pick --- .github/workflows/auto-pr-to-release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 89d739fb6..326c59016 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -99,7 +99,7 @@ jobs: git config advice.mergeConflict false # Fetch and checkout the release branch - git fetch origin + git fetch --all git checkout ${{ env.RELEASE_BRANCH }} # Create a new branch for the PR @@ -117,7 +117,7 @@ jobs: echo "Commit range: $COMMIT_RANGE" # Attempt to cherry-pick the commits from the original PR - CHERRY_PICK_OUTPUT=$(git cherry-pick ${{ env.HEAD_BRANCH }} $COMMIT_RANGE 2>&1) || { + CHERRY_PICK_OUTPUT=$(git cherry-pick $COMMIT_RANGE 2>&1) || { git cherry-pick --abort || true # If cherry-pick fails, create a placeholder commit echo "Cherry-pick failed. Creating placeholder commit." @@ -140,7 +140,7 @@ jobs: git checkout $NEW_BRANCH git reset --hard HEAD~1 # Remove placeholder commit - git cherry-pick ${{ env.HEAD_BRANCH }} $COMMIT_RANGE + git cherry-pick $COMMIT_RANGE \`\`\` " } From 23397ef7accccf5d3443a20cf33ea94161ffaab7 Mon Sep 17 00:00:00 2001 From: Tahmid Rahman <42269993+tahmidrahman-dsi@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:20:33 +0600 Subject: [PATCH 036/146] chore: update dashboard query with the recent correction flow changes (#139) --- src/api/dashboards/queries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/dashboards/queries.ts b/src/api/dashboards/queries.ts index a79948ddc..827690ef4 100644 --- a/src/api/dashboards/queries.ts +++ b/src/api/dashboards/queries.ts @@ -486,7 +486,7 @@ const declarations = ({ lastUpdatedAt }: { lastUpdatedAt: string }) => ({ { $match: { 'meta.lastUpdated': { $gte: lastUpdatedAt }, - 'extension.url': 'http://opencrvs.org/specs/extension/requestCorrection' + 'extension.url': 'http://opencrvs.org/specs/extension/makeCorrection' } }, { From 49e8ca6362080f77917692baf6c5687c0e0e5f9d Mon Sep 17 00:00:00 2001 From: Pyry Rouvila Date: Thu, 20 Jun 2024 13:00:27 +0300 Subject: [PATCH 037/146] feat: handle semantic commit's in release auto-PRs (#152) --- .github/workflows/auto-pr-to-release.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 326c59016..db76a3872 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -33,13 +33,14 @@ jobs: id: get_pr_details_dispatch run: | PR_NUMBER=${{ github.event.inputs.pr_number }} - PR_DATA=$(gh pr view $PR_NUMBER --json number,headRefName,baseRefName,mergedBy,mergeCommit,author,milestone --jq '{number: .number, headRefName: .headRefName, baseRefName: .baseRefName, merger: .mergedBy.login, author: .author.login, milestone: .milestone.title}') + PR_DATA=$(gh pr view $PR_NUMBER --json number,headRefName,baseRefName,mergedBy,mergeCommit,author,milestone,title --jq '{number: .number, headRefName: .headRefName, baseRefName: .baseRefName, merger: .mergedBy.login, author: .author.login, milestone: .milestone.title, title: .title}') echo "PR_ID=$(echo $PR_DATA | jq -r '.number')" >> $GITHUB_ENV echo "PR_AUTHOR=$(echo $PR_DATA | jq -r '.author')" >> $GITHUB_ENV echo "PR_MERGER=$(echo $PR_DATA | jq -r '.merger')" >> $GITHUB_ENV echo "MILESTONE=$(echo $PR_DATA | jq -r '.milestone')" >> $GITHUB_ENV echo "BASE_BRANCH=$(echo $PR_DATA | jq -r '.baseRefName')" >> $GITHUB_ENV echo "HEAD_BRANCH=$(echo $PR_DATA | jq -r '.headRefName')" >> $GITHUB_ENV + echo "PR_TITLE=$(echo $PR_DATA | jq -r '.title')" >> $GITHUB_ENV LATEST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid') FIRST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[0].oid') @@ -58,6 +59,7 @@ jobs: echo "MILESTONE=${{ github.event.pull_request.milestone.title }}" >> $GITHUB_ENV echo "BASE_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV echo "HEAD_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV + echo "PR_TITLE=${{ github.event.pull_request.title }}" >> $GITHUB_ENV LATEST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid') FIRST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[0].oid') @@ -90,7 +92,15 @@ jobs: - name: Create and push the new branch for the PR run: | - PR_TITLE="🍒 Merge changes from PR #${{ env.PR_ID }} to ${{ env.RELEASE_BRANCH }}" + SEMANTIC_PR_TITLE="${{ env.PR_TITLE }}" + + # Check for semantic prefix + if [[ $SEMANTIC_PR_TITLE =~ ^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert|wip|merge)\: ]]; then + SEMANTIC_PR_TITLE="${BASH_REMATCH[1]}(${MILESTONE}): ${SEMANTIC_PR_TITLE#*: }" + else + SEMANTIC_PR_TITLE="🍒 Merge changes from PR #${{ env.PR_ID }} to ${{ env.RELEASE_BRANCH }}" + fi + PR_BODY="Automated PR to merge changes from develop to ${{ env.RELEASE_BRANCH }}" # Configure git @@ -158,6 +168,6 @@ jobs: AUTHOR=${{ env.PR_MERGER }} fi fi - gh pr create --title "$PR_TITLE" --body "$PR_BODY" --head $NEW_BRANCH --base ${{ env.RELEASE_BRANCH }} --reviewer $AUTHOR + gh pr create --title "$SEMANTIC_PR_TITLE" --body "$PR_BODY" --head $NEW_BRANCH --base ${{ env.RELEASE_BRANCH }} --reviewer $AUTHOR env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a8c80e04c74f6677338f291104a71310570ce648 Mon Sep 17 00:00:00 2001 From: Jamil Date: Thu, 20 Jun 2024 19:35:26 +0600 Subject: [PATCH 038/146] feat: add missing translation for: `correction requested`, `retry`, `failed to send` (#151) --- src/translations/client.csv | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/translations/client.csv b/src/translations/client.csv index 9acf85484..6feacc965 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -17,6 +17,7 @@ advancedSearch.form.recordStatusInReview,Option for form field: status of record advancedSearch.form.recordStatusInprogress,Option for form field: status of record,In progress,En cours advancedSearch.form.recordStatusRegistered,Option for form field: status of record,Registered,Inscrit advancedSearch.form.recordStatusRequireUpdate,Option for form field: status of record,Requires updates,Nécessite des mises à jour +advancedSearch.form.recordStatusCorrectionRequested,Option for form field: status of record,Correction requested,Correction demandée advancedSearch.form.registrationDetails,The title of Registration details accordion,Registration details,Détails d'inscription advancedSearch.form.statusOfRecordLabel,Label for input Status of record,Status of record,Etat d'avancement du dossier advancedSearchResult.pill.childDoB,The label for child d.o.b in active advancedSearchParams,Child d.o.b,Date de naissance enfant @@ -1738,6 +1739,8 @@ regHome.outbox.statusWaitingToReject,Label for declaration status waiting for re regHome.outbox.statusWaitingToRequestCorrection,Label for declaration status waiting for request correction,Waiting to correct,En attente de correction regHome.outbox.statusWaitingToSubmit,Label for declaration status waiting for reject,Waiting to send,En attente d'envoi regHome.outbox.statusWaitingToValidate,Label for declaration status waiting for validate,Waiting to send for approval,En attente d'envoi pour approbation +regHome.outbox.failed,Label for declaration status failed,Failed to send,Échec de l'envoi +regHome.outbox.retry,Label for Retry button in Outbox shown for records that failed to send,Retry,Réessayer regHome.outbox.waitingToRetry,Label for declaration status waiting for connection,Waiting to retry,Attendre de réessayer regHome.queryError,,An error occurred while searching,Une erreur s'est produite lors de la recherche regHome.readyForReview,The title of ready for review,Ready for review,Prêt pour l'examen From d638373d675d35c069fe5f3fc7ec12f8640ea41c Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" <32668488+Nil20@users.noreply.github.com> Date: Thu, 20 Jun 2024 19:42:35 +0600 Subject: [PATCH 039/146] fix: update certified at message id for verify certificate (#150) * fix: update certified at message id for verify certificate * Revert "fix: update certified at message id for verify certificate" This reverts commit 84f5f17d4ec13a986decc4c4c4ade464c78162cd. * fix: update certified at message id for verify certificate --- src/translations/client.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/client.csv b/src/translations/client.csv index 6feacc965..f8b50c9f1 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -2112,7 +2112,7 @@ validations.validNationalIDLengths,Nid valid lengths,9,9 validations.validNationalId,The error message that appears when an invalid value is used as nid,The National ID can only be numeric and must be 10 digits long,Le numéro d'identification national ne peut être que numérique et doit avoir une longueur de 9 chiffres. validations.validPassportNumber,The error message that appears when an invalid value is used as passport number,The Passport Number can only be alpha numeric and must be {validLength} characters long,Le numéro de passeport ne peut être qu'alphanumérique et doit comporter {validLength} caractères. verifyCertificate.brn,Label for Birth Registration Number,BRN,Numéro de régistre de naissance -verifyCertificate.createdAt,Label for date of certification,Date of certification,Date de l'émission du certificat de naissance +verifyCertificate.certifiedAt,Label for date of certification,Date of certification,Date de l'émission du certificat de naissance verifyCertificate.dateOfBirth,Label for date of birth,Date of birth,Date de naissance verifyCertificate.dateOfDeath,Label for date of death,Date of death,Date du décès verifyCertificate.drn,Label for Death Registration Number,DRN,Numéro de régistre de décès From 29c8365ae1ce19cdae90047860f96d9be8771477 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Fri, 21 Jun 2024 12:56:01 +0600 Subject: [PATCH 040/146] fix: add missing translations (#155) * fix: remove extra comma * fix: add missing translations --- src/translations/client.csv | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/translations/client.csv b/src/translations/client.csv index f8b50c9f1..a2184c3c5 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -13,11 +13,11 @@ advancedSearch.form.placeOfRegistrationHelperText,Helper text for input Place of advancedSearch.form.recordStatusAchived,Option for form field: status of record,Archived,Archivé advancedSearch.form.recordStatusAny,Option for form field: status of record,Any status,Tout statut advancedSearch.form.recordStatusCertified,Option for form field: status of record,Certified,Agréé +advancedSearch.form.recordStatusCorrectionRequested,Option for form field: status of record,Correction requested,Correction demandée advancedSearch.form.recordStatusInReview,Option for form field: status of record,In review,En cours d'examen advancedSearch.form.recordStatusInprogress,Option for form field: status of record,In progress,En cours advancedSearch.form.recordStatusRegistered,Option for form field: status of record,Registered,Inscrit advancedSearch.form.recordStatusRequireUpdate,Option for form field: status of record,Requires updates,Nécessite des mises à jour -advancedSearch.form.recordStatusCorrectionRequested,Option for form field: status of record,Correction requested,Correction demandée advancedSearch.form.registrationDetails,The title of Registration details accordion,Registration details,Détails d'inscription advancedSearch.form.statusOfRecordLabel,Label for input Status of record,Status of record,Etat d'avancement du dossier advancedSearchResult.pill.childDoB,The label for child d.o.b in active advancedSearchParams,Child d.o.b,Date de naissance enfant @@ -509,6 +509,10 @@ correction.certificate.corrector.idCheck,The title for id check component,Verify correction.certificate.corrector.idCheckVerify,The label for id check component action when verify details,Verified,Vérifié correction.certificate.corrector.idCheckWithoutVerify,The label for id check component action when does not verify details,Identity does not match,L'identité ne correspond pas correction.certificate.corrector.otherIdCheck,The title for other id check component,Check proof of ID,Avez-vous vérifié leur preuve d'identification ? +correction.correctionForApprovalDialog.actions.cancel,The cancel button for the dialog when record correction sent by registration agent for approval,Cancel,Annuler +correction.correctionForApprovalDialog.actions.send,The send button for the dialog when record correction sent by registration agent for approval,Confirm,Confirmer +correction.correctionForApprovalDialog.description,The description for the dialog when record correction sent by registration agent for approval,The Registrar will be notified of this correction request and a record of this request will be recorded,Le greffier sera informé de cette demande de correction et un enregistrement de cette demande sera effectué +correction.correctionForApprovalDialog.title,The title for the dialog when record correction sent by registration agent for approval,Send record correction for approval ?,Envoyer la correction de l'enregistrement pour approbation ? correction.corrector.anotherAgent,Label for another registration or field agent option in certificate correction form,Another registration agent or field agent,Un autre agent d'enregistrement ou un agent de terrain correction.corrector.birth.note,Birth correction note,Note: In the case that the child is now of legal age (18) then only they should be able to request a change to their birth record.,"Note : Si l'enfant a atteint l'âge légal (18 ans), il est le seul à pouvoir demander une modification de son acte de naissance." correction.corrector.bride,Label for bride option in certificate correction form,Bride,Mariée @@ -549,12 +553,15 @@ correction.summary.feesRequiredPositive,Positive label for Fees required for cer correction.summary.idCheck,ID check header for certificate correction summary,ID check,Contrôle d'identité correction.summary.idCheckForCorrection,The title for the dialog when details of the collector not verified for correction,Correct without proof of ID?,Correct sans pièce d'identité ? correction.summary.item,Corrected item table header for certificate correction summary,Item,Point +correction.summary.office,Office where certificate correction summary was submitted,Office,Bureau correction.summary.original,Original value table header for certificate correction summary,Original,Original correction.summary.proofOfPayment,Proof of payment label fees payment document,Proof of payment,Preuve de paiement correction.summary.proofOfPaymentRequired,Validation for proof of payment document for certificate correction summary,Proof of payment is required,Une preuve de paiement est requise correction.summary.reasonForRequest,Reason for request header for certificate correction summary,Reason for request,Raison de la demande correction.summary.requestedBy,Requested by header for certificate correction summary,Requested by,Demandée par +correction.summary.requestedOn,Date when certificate correction summary was submitted,Requested on,Demandé le correction.summary.required,Payment and proof of payment input error,Required for correction,Nécessaire pour la correction +correction.summary.submitter,Submitter of certificate correction summary,Submitter,Déposant correction.summary.supportingDocuments,Supporting documents header for certificate correction summary,Supporting documents,Documents à l'appui correction.summary.title,Title for certificate correction summary,Correction summary,Résumé de la correction correction.summary.totalPaymentLabel,Label of total payment in correction summary,Total {currency},Total {currency} @@ -666,7 +673,7 @@ countries.GTM,,Guatemala,Guatemala countries.GUF,,French Guiana,Guyane française countries.GUM,,Guam,Guam countries.GUY,,Guyana,Guyane -countries.HKG,,"China, Hong Kong Special Administrative Region","Chine, région administrative spéciale de Hong Kong", +countries.HKG,,"China, Hong Kong Special Administrative Region","Chine, région administrative spéciale de Hong Kong" countries.HMD,,Heard Island and McDonald Islands,Île Heard et îles McDonald countries.HND,,Honduras,Honduras countries.HRV,,Croatia,Croatie @@ -704,7 +711,7 @@ countries.LSO,,Lesotho,Lesotho countries.LTU,,Lithuania,Lituanie countries.LUX,,Luxembourg,Luxembourg countries.LVA,,Latvia,Lettonie -countries.MAC,,"China, Macao Special Administrative Region","Chine, Région administrative spéciale de Macao", +countries.MAC,,"China, Macao Special Administrative Region","Chine, Région administrative spéciale de Macao" countries.MAF,,Saint Martin (French Part),Saint Martin (partie française) countries.MAR,,Morocco,Maroc countries.MCO,,Monaco,Monaco @@ -1036,7 +1043,7 @@ form.field.label.educationAttainmentISCED6,,Second stage tertiary,Second degré form.field.label.educationAttainmentNone,,No schooling,Pas de scolarité form.field.label.educationAttainmentNotStated,,Not stated,Non déclaré form.field.label.email,,Email,e-mail -form.field.label.empty,empty string, , +form.field.label.empty,empty string,, form.field.label.exactDateOfBirthUnknown,,Exact date of birth unknown,Date de naissance exacte inconnue form.field.label.familyName,,Last name,Nom de famille form.field.label.father.nationality,,Nationality,Nationalité @@ -1701,6 +1708,7 @@ recordAudit.regAction.verified,Verified action,Certificate verified,Certificat v recordAudit.regAction.viewed,,Viewed,Vu recordAudit.regStatus.archived,,Archived,Archivé recordAudit.regStatus.certified,,Certified,Certifié +recordAudit.regStatus.correctionRequested,Label for when someone requested correction,Correction requested,Correction demandée recordAudit.regStatus.declared,,Declaration started,Déclaration initiée recordAudit.regStatus.declared.sentNotification,Field agent sent notification,Sent notification for review,Envoi d'une notification pour examen recordAudit.regStatus.issued,,Issued,Émis @@ -1721,6 +1729,8 @@ regHome.inPro.selector.hospital.drafts,The title of In progress Hospitals,Health regHome.inPro.selector.own.drafts,The title of In progress own drafts,My drafts,Mes brouillons regHome.inProgress,The title of In progress,In progress,En cours regHome.issued,Label for registration status issued,Issued,Émis +regHome.outbox.failed,Label for declaration status failed,Failed to send,Échec de l'envoi +regHome.outbox.retry,Label for Retry button in Outbox shown for records that failed to send,Retry,Réessayer regHome.outbox.statusArchiving,Label for application status Archiving,Archiving...,Archivage en cours regHome.outbox.statusCertifying,Label for declaration status Certifying,Certifying...,Certification en cours... regHome.outbox.statusIssuing,Label for declaration status Issuing,Issuing...,En cours d'émission... @@ -1739,8 +1749,6 @@ regHome.outbox.statusWaitingToReject,Label for declaration status waiting for re regHome.outbox.statusWaitingToRequestCorrection,Label for declaration status waiting for request correction,Waiting to correct,En attente de correction regHome.outbox.statusWaitingToSubmit,Label for declaration status waiting for reject,Waiting to send,En attente d'envoi regHome.outbox.statusWaitingToValidate,Label for declaration status waiting for validate,Waiting to send for approval,En attente d'envoi pour approbation -regHome.outbox.failed,Label for declaration status failed,Failed to send,Échec de l'envoi -regHome.outbox.retry,Label for Retry button in Outbox shown for records that failed to send,Retry,Réessayer regHome.outbox.waitingToRetry,Label for declaration status waiting for connection,Waiting to retry,Attendre de réessayer regHome.queryError,,An error occurred while searching,Une erreur s'est produite lors de la recherche regHome.readyForReview,The title of ready for review,Ready for review,Prêt pour l'examen @@ -1794,12 +1802,16 @@ register.form.modal.areYouReadyToSubmit,,Are you ready to submit?,Êtes-vous pr register.form.modal.desc.deleteDeclarationConfirm,Description for delete declaration confirmation modal,"Are you certain you want to delete this draft declaration form? Please note, this action cant be undone.",Êtes-vous certain de vouloir supprimer ce projet de formulaire de déclaration? Veuillez noter que cette action ne peut pas être annulée. register.form.modal.desc.exitWithoutSavingDeclarationConfirm,Description for exit declaration without saving confirmation modal,You have unsaved changes on your declaration form. Are you sure you want to exit without saving?,Vous avez des modifications non enregistrées sur votre formulaire de déclaration. Voulez-vous vraiment quitter sans enregistrer ? register.form.modal.desc.exitWithoutSavingModalForCorrection,Description for exit declaration without saving confirmation modal for correct record,Are you sure you want to exit? Any corrections you have made will not be saved.,Êtes-vous sûr de vouloir quitter ? Les corrections que vous avez apportées ne seront pas sauvegardées. +register.form.modal.desc.saveCorrectionConfirm,Description for save correction confirmation modal,The declarant will be notified of this correction and a record of this decision will be recorded,Le déclarant sera informé de cette correction et un procès-verbal de cette décision sera enregistré +register.form.modal.desc.saveCorrectionReject,Description for reject correction modal,The declarant will be notified of this decision and a record of this decision will be recorded,Cette décision sera notifiée au déclarant et un procès-verbal de cette décision sera enregistré register.form.modal.desc.saveDeclarationConfirm,Description for save declaration confirmation modal,All inputted data will be kept secure for future editing. Are you ready to save any changes to this declaration form?,Toutes les données saisies seront conservées en toute sécurité pour de futures modifications. Êtes-vous prêt à enregistrer les modifications apportées à ce formulaire de déclaration? register.form.modal.desc.validateConfirmation,Description for validate confirmation modal,This declaration will be sent for approval prior to registration.,Cette déclaration sera envoyée pour approbation avant l'enregistrement. register.form.modal.submitDescription,Submit description text on submit modal,By clicking “Submit” you confirm that the informant has read and reviewed the information and understands that this information will be shared with Civil Registration authorities.,"En cliquant sur Soumettre, vous confirmez que l'informateur a lu et revu les informations et qu'il comprend que ces informations seront partagées avec les autorités de l'état civil." register.form.modal.title.deleteDeclarationConfirm,Title for delete declaration confirmation modal,Delete draft?,Supprimer le brouillon? register.form.modal.title.exitWithoutSavingDeclarationConfirm,Title for exit declaration without saving confirmation modal,Exit without saving changes?,Quitter sans enregistrer les modifications? register.form.modal.title.exitWithoutSavingModalForCorrection,Title for exiting correction record without saving confirmation,Exit correct record?,Quitter l'enregistrement correct ? +register.form.modal.title.saveCorrectionConfirm,Title for save correction confirmation modal,Approve correction?,Approuver la correction ? +register.form.modal.title.saveCorrectionReject,Title for reject correction modal,Reject correction?,Rejeter la correction ? register.form.modal.title.saveDeclarationConfirm,Title for save declaration confirmation modal,Save & exit?,Enregistrer et quitter? register.form.modal.title.submitConfirmation,,"{completeDeclaration, select, true {Send for review?} other {Send for review?}}","{completeDeclaration, select, true {Envoyer une déclaration pour examen ?} other {Envoyer une déclaration incomplète ?}}" register.form.modal.title.validateConfirmation,Title for validate confirmation modal,Send for approval?,Envoyer pour approbation ? @@ -2018,6 +2030,7 @@ user.profile.audit.description.updated,Description for updated declaration,Updat user.profile.audit.description.validated,Description for validated declaration,Sent for approval,Déclaration envoyée pour approbation user.profile.audit.description.waiting_validation,,Sent declaration for external system validation,Déclaration envoyée pour la validation du système externe user.profile.audit.list.noDataFound,Text for audit list,No audits to display,Aucun audit à afficher +user.profile.auditList.approvedCorrectionAuditAction,Description for record correction being approved,Approved correction request,Demande de correction approuvée user.profile.auditList.archived,Description for declaration archived,Archived,Archivé user.profile.auditList.assigned,Description for declaration assignment,Assigned,Assigné user.profile.auditList.corrected,Description for declaration corrected,Corrected record,dossier corrigé @@ -2033,6 +2046,8 @@ user.profile.auditList.phoneNumberChanged,Description for user change phoneNumbe user.profile.auditList.reInstatedToInProgress,Description for sending registration from Reinstated to In progress audit action,Reinstated to in progress,Réintégré dans les travaux en cours user.profile.auditList.reInstatedToInReview,Description for sending registration from Reinstated to In review audit action,Reinstated to ready for review,Réintégré à pret à l'examen user.profile.auditList.reInstatedToUpdate,Description for sending registration from Reinstated to require updates audit action,Reinstated to requires updates,Réintégré pour demander des mises à jour +user.profile.auditList.rejectedCorrectedAuditAction,Description for record correction being rejected,Rejected correction request,Demande de correction rejetée +user.profile.auditList.requestedCorrectionAuditAction,Description for record correction being requested,Requested correction,Correction demandée user.profile.auditList.retrieved,Description for declaration retrieved audit action,Retrieved,Récupéré user.profile.auditList.sentForApproval,Description for sending registration for approval audit action,Sent for approval,Envoyé pour approbation user.profile.auditList.showMore,Label for show more link,Show next {pageSize} of {totalItems},Afficher le prochain {pageSize} de {totalItems From 0bd4c61226dd14c5ccb223f754d8b5617f0245aa Mon Sep 17 00:00:00 2001 From: Niko Kurtti Date: Tue, 25 Jun 2024 09:33:09 +0300 Subject: [PATCH 041/146] Ignore baseimage when deploying (#160) --- infrastructure/deployment/deploy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 707755df2..5d8725915 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -224,6 +224,8 @@ get_docker_tags_from_compose_files() { IMAGE_TAG_LIST=$(cat $SPACE_SEPARATED_COMPOSE_FILE_LIST \ `# Select rows with the image tag` \ | grep image: \ + `# Ignore the baseimage file as its not used directly` \ + | grep -v ocrvs-base \ `# Only keep the image version` \ | sed "s/image://") From fdc3a31c1507e89efb577e93a89c591f7424066c Mon Sep 17 00:00:00 2001 From: Euan Millar Date: Tue, 25 Jun 2024 12:42:47 +0100 Subject: [PATCH 042/146] Enforce token for seedign roles and users (#165) --- src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 7ff65f615..acfd768e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -505,7 +505,6 @@ export async function createServer() { path: '/roles', handler: rolesHandler, options: { - auth: false, tags: ['api', 'user-roles'], description: 'Returns user roles metadata' } @@ -516,7 +515,6 @@ export async function createServer() { path: '/users', handler: usersHandler, options: { - auth: false, tags: ['api', 'users'], description: 'Returns users metadata' } From 6830099648b79d9cdaafa957528597314c1aa5f5 Mon Sep 17 00:00:00 2001 From: Pyry Rouvila Date: Tue, 25 Jun 2024 16:15:34 +0300 Subject: [PATCH 043/146] fix: sms not being sent (#164) * fix: sms templates not being to able to parse json * fix: the missing await --- src/api/notification/sms-service.ts | 30 +++++++++++------------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/api/notification/sms-service.ts b/src/api/notification/sms-service.ts index 5a613f04f..a4d2016cb 100644 --- a/src/api/notification/sms-service.ts +++ b/src/api/notification/sms-service.ts @@ -15,10 +15,9 @@ import { } from './constant' import { logger } from '@countryconfig/logger' import fetch from 'node-fetch' -import { readFileSync } from 'fs' import * as Handlebars from 'handlebars' -import { join } from 'path' import { internal } from '@hapi/boom' +import { getLanguages } from '../content/service' export const informantTemplates = { birthInProgressNotification: 'birthInProgressNotification', @@ -43,19 +42,13 @@ export type SMSTemplateType = | keyof typeof otherTemplates | keyof typeof informantTemplates -interface ISMSNotificationTemplate { - lang: string - displayName: string - messages: Record -} - export async function sendSMS( type: SMSTemplateType, variables: Record, recipient: string, locale: string ) { - const message = compileMessages(type, variables, locale) + const message = await compileMessages(type, variables, locale) const body = JSON.stringify({ messages: [ { @@ -98,20 +91,19 @@ export async function sendSMS( } } -function compileMessages( +const compileMessages = async ( templateName: SMSTemplateType, variables: Record, locale: string -): string { - const smsNotificationTemplate = JSON.parse( - readFileSync( - join(__dirname, '../languages/content/notification/notification.json') - ).toString() - ).data as ISMSNotificationTemplate[] +) => { + const languages = await getLanguages('notification') + const language = languages.find(({ lang }) => lang === locale) - const language = smsNotificationTemplate.filter((obj) => { - return obj.lang === locale - })[0] + if (!language) { + throw new Error( + `Locale "${locale}" not found while compiling notification messages.` + ) + } const template = Handlebars.compile(language.messages[templateName]) return template(variables) From e86d964bcb6fde169a9a5e2256a7767fedc9c7bb Mon Sep 17 00:00:00 2001 From: Niko Kurtti Date: Wed, 26 Jun 2024 13:57:25 +0300 Subject: [PATCH 044/146] Add .nvmrc for easier dev (#161) --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..123b05279 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.20.2 From 86eda1abfd865bc500d15ecaaaaba797d3b67f66 Mon Sep 17 00:00:00 2001 From: Pyry Rouvila Date: Wed, 26 Jun 2024 17:38:10 +0300 Subject: [PATCH 045/146] fix: rename geojson endpoint to not tie implementations to example country name (#168) * fix: rename geojson endpoint to not tie implementations to example country names * fix: rename the file as well * refactor: use readfile instead of readfilesync to not block main thread --- CHANGELOG.md | 1 + README.md | 4 ++-- infrastructure/docker-compose.deploy.yml | 2 +- infrastructure/metabase/metabase.init.db.sql | 2 +- .../dashboards/file/{farajaland-map.geojson => map.geojson} | 0 src/api/dashboards/handler.ts | 4 ++-- src/index.ts | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) rename src/api/dashboards/file/{farajaland-map.geojson => map.geojson} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69407afc8..ec6955fa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - Add query mapper for International Postal Code field - Add support for image compression configuration - Provide env variables for metabase admin credentials +- Rename `farajaland-map.geojson` to `map.geojson` to not tie implementations into example country naming **Infrastructure** diff --git a/README.md b/README.md index e6984b71b..c50a9ed20 100644 --- a/README.md +++ b/README.md @@ -113,9 +113,9 @@ When the OpenCRVS Core servers start up with un-seeded databases they call the f - The country logo is loaded into HTML emails so must be hosted -9. `GET /content/farajaland-map.geojson` +9. `GET /content/map.geojson` -- A map of the country in GeoJSON must be hosted as it is loaded into OpenCVS Core Metabase Dashboards as a UI component +- A map of the country in GeoJSON must be hosted as it is loaded into OpenCRVS Core Metabase Dashboards as a UI component 10. `GET /ping` diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 9e43290f4..007b784af 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -969,7 +969,7 @@ services: - OPENCRVS_METABASE_SITE_NAME=OpenCRVS Dashboards - OPENCRVS_METABASE_SITE_URL=http://metabase.{{hostname}} - OPENCRVS_METABASE_MAP_NAME=Farajaland - - OPENCRVS_METABASE_MAP_URL=http://countryconfig:3040/content/farajaland-map.geojson + - OPENCRVS_METABASE_MAP_URL=http://countryconfig:3040/content/map.geojson - OPENCRVS_METABASE_MAP_REGION_KEY=State - OPENCRVS_METABASE_MAP_REGION_NAME=State - OPENCRVS_METABASE_ADMIN_EMAIL=${OPENCRVS_METABASE_ADMIN_EMAIL} diff --git a/infrastructure/metabase/metabase.init.db.sql b/infrastructure/metabase/metabase.init.db.sql index e8aabc19f..b92452237 100644 --- a/infrastructure/metabase/metabase.init.db.sql +++ b/infrastructure/metabase/metabase.init.db.sql @@ -785,7 +785,7 @@ INSERT INTO "PUBLIC"."SETTING" VALUES ('version-info', SYSTEM_COMBINE_CLOB(0)), ('site-name', 'OpenCRVS Local'), ('site-url', 'http://localhost:4444'), -('custom-geojson', '{"cdc1d5eb-c7f8-8b01-b296-eda34d06b6da":{"name":"Farajaland","url":"http://localhost:3040/content/farajaland-map.geojson","region_key":"State","region_name":"State"}}'), +('custom-geojson', '{"cdc1d5eb-c7f8-8b01-b296-eda34d06b6da":{"name":"Farajaland","url":"http://localhost:3040/content/map.geojson","region_key":"State","region_name":"State"}}'), ('startup-time-millis', '6453'), ('settings-last-updated', '2024-02-29 13:43:53.376747+02'); CREATE CACHED TABLE "PUBLIC"."METRIC_IMPORTANT_FIELD"( diff --git a/src/api/dashboards/file/farajaland-map.geojson b/src/api/dashboards/file/map.geojson similarity index 100% rename from src/api/dashboards/file/farajaland-map.geojson rename to src/api/dashboards/file/map.geojson diff --git a/src/api/dashboards/handler.ts b/src/api/dashboards/handler.ts index 4db96a351..adb549da1 100644 --- a/src/api/dashboards/handler.ts +++ b/src/api/dashboards/handler.ts @@ -17,8 +17,8 @@ export async function mapGeojsonHandler( request: Hapi.Request, h: Hapi.ResponseToolkit ) { - const filePath = join(__dirname, './file/farajaland-map.geojson') - const fileContents = fs.readFileSync(filePath, 'utf8') + const filePath = join(__dirname, './file/map.geojson') + const fileContents = await fs.promises.readFile(filePath, 'utf8') return h.response(fileContents).type('text/plain') } diff --git a/src/index.ts b/src/index.ts index acfd768e2..771371b8b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -391,7 +391,7 @@ export async function createServer() { server.route({ method: 'GET', - path: '/content/farajaland-map.geojson', + path: '/content/map.geojson', handler: mapGeojsonHandler, options: { auth: false, From 86de93de9859be5e032d28c0e6e45bda96d82c3f Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Thu, 27 Jun 2024 12:01:21 +0300 Subject: [PATCH 046/146] [OCRVS-7259] Fix elasticsearch directories getting nested in the backup restoration source directory (#170) * Fix elasticsearch directories getting nested in the backup restoration source directory * Update infrastructure/backups/download.sh Co-authored-by: Tameem Bin Haider --------- Co-authored-by: Tameem Bin Haider --- infrastructure/backups/download.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/backups/download.sh b/infrastructure/backups/download.sh index 0d87a1783..b2c2d1bdb 100644 --- a/infrastructure/backups/download.sh +++ b/infrastructure/backups/download.sh @@ -106,7 +106,7 @@ for BACKUP_DIR in /data/backups/*; do done -mv $BACKUP_RAW_FILES_DIR/extract/elasticsearch /data/backups/elasticsearch +mv $BACKUP_RAW_FILES_DIR/extract/elasticsearch/* /data/backups/elasticsearch/ mv $BACKUP_RAW_FILES_DIR/extract/influxdb /data/backups/influxdb/${LABEL} mv $BACKUP_RAW_FILES_DIR/extract/minio/ocrvs-${LABEL}.tar.gz /data/backups/minio/ From 609a8b89da3633cb47fcd054fde722c33750d6ec Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Fri, 28 Jun 2024 10:12:18 +0300 Subject: [PATCH 047/146] Change jumpserver hostnames in the inventory examples (#174) --- infrastructure/server-setup/inventory/production.yml | 3 +-- infrastructure/server-setup/inventory/staging.yml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/infrastructure/server-setup/inventory/production.yml b/infrastructure/server-setup/inventory/production.yml index 255ef8e48..924597414 100644 --- a/infrastructure/server-setup/inventory/production.yml +++ b/infrastructure/server-setup/inventory/production.yml @@ -61,9 +61,8 @@ backups: # Written by provision pipeline. Assumes "backup" environment # exists in Github environments ansible_ssh_private_key_file: /tmp/backup_ssh_private_key - # If you are using a jump server to access this environment # jump: # hosts: -# backup: # @todo set this to be the hostname of your backup server +# your-jumpserver-hostname-here: # @todo set this to be the hostname of your backup server # ansible_host: '55.55.55.55' diff --git a/infrastructure/server-setup/inventory/staging.yml b/infrastructure/server-setup/inventory/staging.yml index 3f7c069e5..8f484aef0 100644 --- a/infrastructure/server-setup/inventory/staging.yml +++ b/infrastructure/server-setup/inventory/staging.yml @@ -58,9 +58,8 @@ backups: # Written by provision pipeline. Assumes "backup" environment # exists in Github environments ansible_ssh_private_key_file: /tmp/backup_ssh_private_key - # If you are using a jump server to access this environment # jump: # hosts: -# backup: # @todo set this to be the hostname of your backup server +# your-jumpserver-hostname-here: # @todo set this to be the hostname of your backup server # ansible_host: '55.55.55.55' From 6229580977d1f59efb654e115cbc99fcc08e6631 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 1 Jul 2024 09:41:09 +0300 Subject: [PATCH 048/146] replace texts where jump machine is referred as backup --- infrastructure/server-setup/inventory/jump.yml | 2 +- infrastructure/server-setup/inventory/production.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/infrastructure/server-setup/inventory/jump.yml b/infrastructure/server-setup/inventory/jump.yml index 639a9d8dd..50d96e16f 100644 --- a/infrastructure/server-setup/inventory/jump.yml +++ b/infrastructure/server-setup/inventory/jump.yml @@ -26,5 +26,5 @@ all: jump-host: hosts: - backup: # @todo set this to be the hostname of your backup server + your-jumpserver-hostname-here: # @todo set this to be the hostname of your jump server ansible_host: '55.55.55.55' diff --git a/infrastructure/server-setup/inventory/production.yml b/infrastructure/server-setup/inventory/production.yml index 924597414..7951bcb3c 100644 --- a/infrastructure/server-setup/inventory/production.yml +++ b/infrastructure/server-setup/inventory/production.yml @@ -64,5 +64,5 @@ backups: # If you are using a jump server to access this environment # jump: # hosts: -# your-jumpserver-hostname-here: # @todo set this to be the hostname of your backup server +# your-jumpserver-hostname-here: # @todo set this to be the hostname of your jump server # ansible_host: '55.55.55.55' From 0fcbe7d37749a2050f6288aaf089ef9033e978a5 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 1 Jul 2024 09:42:24 +0300 Subject: [PATCH 049/146] add missing reference to gh ci private key file in jump context --- infrastructure/server-setup/inventory/production.yml | 1 + infrastructure/server-setup/inventory/staging.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/infrastructure/server-setup/inventory/production.yml b/infrastructure/server-setup/inventory/production.yml index 7951bcb3c..40ac49c8f 100644 --- a/infrastructure/server-setup/inventory/production.yml +++ b/infrastructure/server-setup/inventory/production.yml @@ -65,4 +65,5 @@ backups: # jump: # hosts: # your-jumpserver-hostname-here: # @todo set this to be the hostname of your jump server +# ansible_ssh_private_key_file: /tmp/jump_ssh_private_key # Written by provision pipeline. Assumes "jump" environment exists in Github environments # ansible_host: '55.55.55.55' diff --git a/infrastructure/server-setup/inventory/staging.yml b/infrastructure/server-setup/inventory/staging.yml index 8f484aef0..4106d3b03 100644 --- a/infrastructure/server-setup/inventory/staging.yml +++ b/infrastructure/server-setup/inventory/staging.yml @@ -62,4 +62,5 @@ backups: # jump: # hosts: # your-jumpserver-hostname-here: # @todo set this to be the hostname of your backup server +# ansible_ssh_private_key_file: /tmp/jump_ssh_private_key # Written by provision pipeline. Assumes "jump" environment exists in Github environments # ansible_host: '55.55.55.55' From 02942a5715a0191e474297560333bdeaa49c870b Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 1 Jul 2024 10:08:48 +0300 Subject: [PATCH 050/146] add todo comment for easier search experience --- infrastructure/server-setup/inventory/production.yml | 2 +- infrastructure/server-setup/inventory/staging.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/infrastructure/server-setup/inventory/production.yml b/infrastructure/server-setup/inventory/production.yml index 40ac49c8f..4176ff701 100644 --- a/infrastructure/server-setup/inventory/production.yml +++ b/infrastructure/server-setup/inventory/production.yml @@ -42,7 +42,7 @@ docker-manager-first: ansible_host: '22.22.22.22' # @todo set this to be the hostname of your target server data_label: data1 # If you are using a jump server to access this environment, enter other SSH args here. - # ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' + # ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' # @todo set this to be the IP address of your jump server # @todo We recommend you add 2-4 workers for a scaled production deployment # This should depend on the size of your country and the number of end users. diff --git a/infrastructure/server-setup/inventory/staging.yml b/infrastructure/server-setup/inventory/staging.yml index 4106d3b03..50a6cb216 100644 --- a/infrastructure/server-setup/inventory/staging.yml +++ b/infrastructure/server-setup/inventory/staging.yml @@ -44,7 +44,7 @@ docker-manager-first: ansible_host: '11.11.11.11' # @todo set this to be the hostname of your target server data_label: data1 # If you are using a jump server to access this environment, enter other SSH args here. - # ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' + # ansible_ssh_common_args: '-J jump@55.55.55.55 -o StrictHostKeyChecking=no' # @todo set this to be the IP address of your jump server # This staging servers is configured to only use one server docker-workers: {} From daf881edc5942c950b1bdcbd44329db3172e322e Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 1 Jul 2024 10:09:08 +0300 Subject: [PATCH 051/146] unify language by replacing replica with server --- infrastructure/environments/setup-environment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infrastructure/environments/setup-environment.ts b/infrastructure/environments/setup-environment.ts index b7b2159cc..12f8a9bfe 100644 --- a/infrastructure/environments/setup-environment.ts +++ b/infrastructure/environments/setup-environment.ts @@ -330,7 +330,7 @@ const sshQuestions = [ name: 'sshHost', type: 'text' as const, message: - 'What is the target server IP address? Note: For "production" environment server clusters of (2, 3 or 5 replicas) this is always the IP address for just 1 manager server', + 'What is the target server IP address? (Note: For "production" environment with 2, 3 or 5 servers, this is the IP address of the manager server', valueType: 'VARIABLE' as const, validate: notEmpty, valueLabel: 'SSH_HOST', @@ -401,7 +401,7 @@ const infrastructureQuestions = [ name: 'replicas', type: 'number' as const, message: - 'What is the number of replicas? EDIT: This should be 1 for qa, staging and backup environments. For "production" environment server clusters of (2, 3 or 5 replicas), set to 2, 3 or 5 as appropriate.', + 'What is the number of servers? Note: This should be 1 for qa, staging and backup environments. For "production" environment server cluster should consists of 2, 3 or 5 servers.', valueType: 'VARIABLE' as const, validate: notEmpty, valueLabel: 'REPLICAS', From 6855381d800a1326400967f74e64eeacaa93af59 Mon Sep 17 00:00:00 2001 From: Jamil Date: Tue, 2 Jul 2024 11:29:12 +0600 Subject: [PATCH 052/146] feat: add missing translations for texts in `correct record?` modal (#163) * feat: add missing translations for texts in `correct record?` modal * chore: remove extra comma at eof * amend: remove unused translation --- src/translations/client.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/translations/client.csv b/src/translations/client.csv index a2184c3c5..e94d488fe 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -509,6 +509,8 @@ correction.certificate.corrector.idCheck,The title for id check component,Verify correction.certificate.corrector.idCheckVerify,The label for id check component action when verify details,Verified,Vérifié correction.certificate.corrector.idCheckWithoutVerify,The label for id check component action when does not verify details,Identity does not match,L'identité ne correspond pas correction.certificate.corrector.otherIdCheck,The title for other id check component,Check proof of ID,Avez-vous vérifié leur preuve d'identification ? +correction.correctRecordDialog.description,The description for the dialog when record correction sent by a registrar,The informant will be notified of this correction and a record of this decision will be recorded,L'informateur sera informé de cette correction et une trace de cette décision sera enregistrée. +correction.correctRecordDialog.title,The title for the dialog when record correction sent by a registrar,Correct record ?,enregistrement correct ? correction.correctionForApprovalDialog.actions.cancel,The cancel button for the dialog when record correction sent by registration agent for approval,Cancel,Annuler correction.correctionForApprovalDialog.actions.send,The send button for the dialog when record correction sent by registration agent for approval,Confirm,Confirmer correction.correctionForApprovalDialog.description,The description for the dialog when record correction sent by registration agent for approval,The Registrar will be notified of this correction request and a record of this request will be recorded,Le greffier sera informé de cette demande de correction et un enregistrement de cette demande sera effectué From 00a9bf892a5d09cd78c7bea91ac4e8e76e50e09a Mon Sep 17 00:00:00 2001 From: Muhammed Tareq Aziz Date: Tue, 2 Jul 2024 11:41:15 +0600 Subject: [PATCH 053/146] fix: improve informant name formatting for inProgress declaration emails (#162) * Fix email template to handle missing informantName - Add conditional check for informantName in the greeting - Display "Hello," if informantName is null or empty to avoid extra space * docs: update CHANGELOG --------- Co-authored-by: Tameem Bin Haider --- CHANGELOG.md | 1 + src/api/notification/email-templates/birth/inProgress.html | 6 +++++- src/api/notification/email-templates/death/inProgress.html | 6 +++++- .../notification/email-templates/marriage/inProgress.html | 6 +++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec6955fa5..d92697d95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - Add query mapper for International Postal Code field - Add support for image compression configuration - Provide env variables for metabase admin credentials +- Improved formatting of informant name for inProgress declaration emails - Rename `farajaland-map.geojson` to `map.geojson` to not tie implementations into example country naming **Infrastructure** diff --git a/src/api/notification/email-templates/birth/inProgress.html b/src/api/notification/email-templates/birth/inProgress.html index 105bdf7c5..574bdabb8 100644 --- a/src/api/notification/email-templates/birth/inProgress.html +++ b/src/api/notification/email-templates/birth/inProgress.html @@ -38,7 +38,11 @@

Complete your birth declaration

- Hello {{informantName}}, + {{#if informantName}} + Hello {{informantName}}, + {{else}} + Hello, + {{/if}}

Please visit {{crvsOffice}} to complete your birth declaration. diff --git a/src/api/notification/email-templates/death/inProgress.html b/src/api/notification/email-templates/death/inProgress.html index ad01aa9a2..0474606a5 100644 --- a/src/api/notification/email-templates/death/inProgress.html +++ b/src/api/notification/email-templates/death/inProgress.html @@ -37,7 +37,11 @@ max-width: 100%;">

Complete your death declaration

- Hello {{informantName}}, + {{#if informantName}} + Hello {{informantName}}, + {{else}} + Hello, + {{/if}}

Please visit {{crvsOffice}} to complete your death declaration. Your death registration tracking ID is diff --git a/src/api/notification/email-templates/marriage/inProgress.html b/src/api/notification/email-templates/marriage/inProgress.html index 481f90142..7b18e1e2b 100644 --- a/src/api/notification/email-templates/marriage/inProgress.html +++ b/src/api/notification/email-templates/marriage/inProgress.html @@ -37,7 +37,11 @@ max-width: 100%;">

Complete your marriage declaration

- Hello {{informantName}}, + {{#if informantName}} + Hello {{informantName}}, + {{else}} + Hello, + {{/if}}

Please visit {{crvsOffice}} to complete your marriage declaration. Your marriage registration tracking ID is From 5246098dab96183668f3dae1450ae8eab10eba24 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 2 Jul 2024 12:19:12 +0300 Subject: [PATCH 054/146] Make default production docker-compose assume a single-node cluster (#172) the same assumption is made in inventory files so with current docker-compose the setup would fail --- infrastructure/docker-compose.deploy.yml | 29 ++++ .../docker-compose.production-deploy.yml | 157 ------------------ 2 files changed, 29 insertions(+), 157 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 007b784af..c36df5adc 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -491,6 +491,7 @@ services: # Configure webapps and add traefik config client: environment: + - DECLARED_DECLARATION_SEARCH_QUERY_COUNT=100 - COUNTRY_CONFIG_URL=https://countryconfig.{{hostname}} - CONTENT_SECURITY_POLICY_WILDCARD=${CONTENT_SECURITY_POLICY_WILDCARD} - MINIO_URL=https://minio.{{hostname}} @@ -578,6 +579,11 @@ services: - 'traefik.http.routers.block-dashboard-queries.middlewares=block-internal-routes' replicas: 1 environment: + - NODE_ENV=production + - FHIR_URL=http://hearth:3447/fhir + - AUTH_URL=http://auth:4040 + - APPLICATION_CONFIG_URL=http://config:2021 + - CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration - MONGO_URL=mongodb://mongo1/user-mgnt?replicaSet=rs0 - CONFIG_MONGO_URL=mongodb://mongo1/application-config?replicaSet=rs0 - APN_SERVICE_URL=http://apm-server:8200 @@ -639,6 +645,8 @@ services: - jwt-public-key.{{ts}} - jwt-private-key.{{ts}} environment: + - NODE_ENV=production + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - CERT_PRIVATE_KEY_PATH=/run/secrets/jwt-private-key.{{ts}} - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} @@ -673,6 +681,8 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - RECORD_SEARCH_QUOTA=2000 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} @@ -692,6 +702,9 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production + - LANGUAGES=en,fr + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} - MONGO_URL=mongodb://notification:${NOTIFICATION_MONGODB_PASSWORD}@mongo1/notification?replicaSet=rs0 @@ -710,6 +723,9 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production + - LANGUAGES=en,fr + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} - LOGIN_URL=https://login.{{hostname}} @@ -743,6 +759,9 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production + - LANGUAGES=en,fr + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} deploy: @@ -760,6 +779,8 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production + - SENTRY_DSN=${SENTRY_DSN:-} - ES_HOST=search-user:${ROTATING_SEARCH_ELASTIC_PASSWORD}@elasticsearch:9200 - APN_SERVICE_URL=http://apm-server:8200 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} @@ -781,6 +802,8 @@ services: volumes: - /data/vsexport:/usr/src/app/packages/metrics/src/scripts environment: + - NODE_ENV=production + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} - MONGO_URL=mongodb://metrics:${METRICS_MONGODB_PASSWORD}@mongo1/metrics?replicaSet=rs0 @@ -802,6 +825,7 @@ services: tag: 'metrics' scheduler: environment: + - NODE_ENV=production - OPENHIM_MONGO_URL=mongodb://openhim:${OPENHIM_MONGODB_PASSWORD}@mongo1/openhim-dev?replicaSet=rs0 deploy: replicas: 1 @@ -822,6 +846,7 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production - APN_SERVICE_URL=http://apm-server:8200 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} - MINIO_ACCESS_KEY=${MINIO_ROOT_USER} @@ -841,6 +866,8 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} - MONGO_URL=mongodb://config:${CONFIG_MONGODB_PASSWORD}@mongo1/application-config?replicaSet=rs0 @@ -925,6 +952,8 @@ services: secrets: - jwt-public-key.{{ts}} environment: + - NODE_ENV=production + - SENTRY_DSN=${SENTRY_DSN:-} - APN_SERVICE_URL=http://apm-server:8200 - MONGO_URL=mongodb://webhooks:${WEBHOOKS_MONGODB_PASSWORD}@mongo1/webhooks?replicaSet=rs0 - CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}} diff --git a/infrastructure/docker-compose.production-deploy.yml b/infrastructure/docker-compose.production-deploy.yml index c063e9429..b568d488b 100644 --- a/infrastructure/docker-compose.production-deploy.yml +++ b/infrastructure/docker-compose.production-deploy.yml @@ -9,86 +9,6 @@ version: '3.3' # services: - gateway: - environment: - - NODE_ENV=production - - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-} - deploy: - replicas: 2 - - workflow: - environment: - - NODE_ENV=production - - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-} - deploy: - replicas: 2 - - search: - environment: - - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-} - deploy: - replicas: 2 - - metrics: - environment: - - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-} - - MONGO_URL=mongodb://metrics:${METRICS_MONGODB_PASSWORD}@mongo1,mongo2/metrics?replicaSet=rs0 - - HEARTH_MONGO_URL=mongodb://hearth:${HEARTH_MONGODB_PASSWORD}@mongo1,mongo2/hearth-dev?replicaSet=rs0 - - DASHBOARD_MONGO_URL=mongodb://performance:${PERFORMANCE_MONGODB_PASSWORD}@mongo1,mongo2/performance?replicaSet=rs0 - - auth: - environment: - - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-} - deploy: - replicas: 2 - - user-mgnt: - environment: - - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-} - - MONGO_URL=mongodb://user-mgnt:${USER_MGNT_MONGODB_PASSWORD}@mongo1,mongo2/user-mgnt?replicaSet=rs0 - deploy: - replicas: 2 - - notification: - environment: - - NODE_ENV=production - - LANGUAGES=en,fr - - SENTRY_DSN=${SENTRY_DSN:-} - - MONGO_URL=mongodb://notification:${NOTIFICATION_MONGODB_PASSWORD}@mongo1,mongo2/notification?replicaSet=rs0 - deploy: - replicas: 2 - - webhooks: - environment: - - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-} - - MONGO_URL=mongodb://webhooks:${WEBHOOKS_MONGODB_PASSWORD}@mongo1,mongo2/webhooks?replicaSet=rs0 - deploy: - replicas: 2 - - config: - environment: - - NODE_ENV=production - - SENTRY_DSN=${SENTRY_DSN:-} - - MONGO_URL=mongodb://config:${CONFIG_MONGODB_PASSWORD}@mongo1,mongo2/application-config?replicaSet=rs0 - deploy: - replicas: 2 - - scheduler: - environment: - - NODE_ENV=production - - OPENHIM_MONGO_URL=mongodb://openhim:${OPENHIM_MONGODB_PASSWORD}@mongo1,mongo2/openhim-dev?replicaSet=rs0 - - documents: - environment: - - NODE_ENV=production - countryconfig: image: ${DOCKERHUB_ACCOUNT}/${DOCKERHUB_REPO}:${COUNTRY_CONFIG_VERSION} restart: unless-stopped @@ -96,10 +16,6 @@ services: - jwt-public-key.{{ts}} environment: - NODE_ENV=production - - FHIR_URL=http://hearth:3447/fhir - - AUTH_URL=http://auth:4040 - - APPLICATION_CONFIG_URL=http://config:2021 - - CONFIRM_REGISTRATION_URL=http://workflow:5050/confirm/registration - CHECK_INVALID_TOKEN=true - SENTRY_DSN=${SENTRY_DSN:-} - SENDER_EMAIL_ADDRESS=${SENDER_EMAIL_ADDRESS} @@ -109,79 +25,6 @@ services: - SMTP_USERNAME=${SMTP_USERNAME} - SMTP_PASSWORD=${SMTP_PASSWORD} - SMTP_SECURE=${SMTP_SECURE} - deploy: - replicas: 2 - - client: - environment: - - DECLARED_DECLARATION_SEARCH_QUERY_COUNT=100 - deploy: - replicas: 2 - - logstash: - deploy: - replicas: 2 - - apm-server: - deploy: - replicas: 2 - - components: - deploy: - replicas: 2 - - login: - deploy: - replicas: 2 - - hearth: - environment: - - mongodb__url=mongodb://hearth:${HEARTH_MONGODB_PASSWORD}@mongo1,mongo2/hearth-dev?replicaSet=rs0 - - deploy: - replicas: 2 - - migration: - environment: - - USER_MGNT_MONGO_URL=mongodb://user-mgnt:${USER_MGNT_MONGODB_PASSWORD}@mongo1,mongo2/user-mgnt?replicaSet=rs0 - - APPLICATION_CONFIG_MONGO_URL=mongodb://config:${CONFIG_MONGODB_PASSWORD}@mongo1,mongo2/application-config?replicaSet=rs0 - - PERFORMANCE_MONGO_URL=mongodb://performance:${PERFORMANCE_MONGODB_PASSWORD}@mongo1,mongo2/performance?replicaSet=rs0 - - HEARTH_MONGO_URL=mongodb://hearth:${HEARTH_MONGODB_PASSWORD}@mongo1,mongo2/hearth-dev?replicaSet=rs0 - - OPENHIM_MONGO_URL=mongodb://openhim:${OPENHIM_MONGODB_PASSWORD}@mongo1,mongo2/openhim-dev?replicaSet=rs0 - - WAIT_HOSTS=mongo1:27017,mongo2:27017,influxdb:8086,minio:9000,elasticsearch:9200 - - mongo2: - image: mongo:4.4 - hostname: 'mongo2' - restart: unless-stopped - command: mongod --auth --replSet rs0 --keyFile /etc/mongodb-keyfile - volumes: - - '/data/mongo:/data/db' - - '/mongodb-keyfile:/mongodb-keyfile' - entrypoint: - - bash - - -c - - | - cp /mongodb-keyfile /etc/mongodb-keyfile - chmod 400 /etc/mongodb-keyfile - chown 999:999 /etc/mongodb-keyfile - exec docker-entrypoint.sh $$@ - environment: - - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ADMIN_USER} - - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ADMIN_PASSWORD} - deploy: - labels: - - 'traefik.enable=false' - replicas: 1 - placement: - constraints: - - node.labels.data2 == true - networks: - - overlay_net - - mongo-on-update: - environment: - - REPLICAS=2 traefik: # These templates use an Automatic Certificate Management Environment (Let's Encrypt). From f472d5cad6424b368e5592eb28490d0639e9f981 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 2 Jul 2024 12:21:11 +0300 Subject: [PATCH 055/146] add assignee for cherry-pick PRs --- .github/workflows/auto-pr-to-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index db76a3872..968ebba47 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Get PR details from workflow dispatch if: ${{ github.event_name == 'workflow_dispatch' }} @@ -168,6 +168,6 @@ jobs: AUTHOR=${{ env.PR_MERGER }} fi fi - gh pr create --title "$SEMANTIC_PR_TITLE" --body "$PR_BODY" --head $NEW_BRANCH --base ${{ env.RELEASE_BRANCH }} --reviewer $AUTHOR + gh pr create --title "$SEMANTIC_PR_TITLE" --body "$PR_BODY" --head "$NEW_BRANCH" --base "${{ env.RELEASE_BRANCH }}" --assignee "$AUTHOR" --reviewer "$AUTHOR" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 32dc29383d59ef49e091b44891efb5e8354b16bc Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 2 Jul 2024 16:58:37 +0300 Subject: [PATCH 056/146] fix(ocrvs-5086): show different label when father address matches Allow passing array of conditionals, create helper --- src/form/addresses/address-fields.ts | 16 +++++----- src/form/addresses/index.ts | 19 ++++++++++-- .../common/default-validation-conditionals.ts | 13 ++++++++ src/form/common/messages.ts | 2 +- src/form/types/types.ts | 2 +- src/utils/address-utils.ts | 30 +++++++++++-------- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/form/addresses/address-fields.ts b/src/form/addresses/address-fields.ts index b53dff6b6..17ab505ae 100644 --- a/src/form/addresses/address-fields.ts +++ b/src/form/addresses/address-fields.ts @@ -3,6 +3,7 @@ import { AddressCases, AddressCopyConfigCases, AddressSubsections, + Conditional, EventLocationAddressCases, SerializedFormField } from '../types/types' @@ -18,13 +19,14 @@ import { yesNoRadioOptions } from '../common/select-options' import { ADMIN_LEVELS } from '.' import { getPlaceOfBirthFields } from '../birth/required-fields' import { getPlaceOfDeathFields } from '../death/required-fields' +import { expressionToConditional } from '../common/default-validation-conditionals' // A radio group field that allows you to select an address from another section export const getXAddressSameAsY = ( xComparisonSection: string, yComparisonSection: string, label: MessageDescriptor, - conditionalCase?: string + conditionals?: Conditional[] | string ): SerializedFormField[] => { const copyAddressField: SerializedFormField = { name: AddressCopyConfigCases.PRIMARY_ADDRESS_SAME_AS_OTHER_PRIMARY, @@ -32,16 +34,12 @@ export const getXAddressSameAsY = ( label, required: true, initialValue: true, - previewGroup: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, validator: [], options: yesNoRadioOptions, - conditionals: conditionalCase - ? [ - { - action: 'hide', - expression: `${conditionalCase}` - } - ] + conditionals: conditionals + ? typeof conditionals === 'string' + ? [expressionToConditional(conditionals)] + : conditionals : [], mapping: { mutation: { diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index e8bc1efb1..b28d56767 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -14,6 +14,7 @@ import { MOTHER_DETAILS_DONT_EXIST, SPOUSE_DETAILS_DONT_EXIST, detailsDontExist, + expressionToConditional, hideIfInformantBrideOrGroom, hideIfInformantSpouse, informantNotMotherOrFather, @@ -121,14 +122,28 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ { config: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, label: formMessageDescriptors.primaryAddress, - conditionalCase: `${FATHER_DETAILS_DONT_EXIST}` + conditionalCase: [ + expressionToConditional(FATHER_DETAILS_DONT_EXIST), + expressionToConditional( + `${FATHER_DETAILS_DONT_EXIST} || ${primaryAddressSameAsOtherPrimaryAddress}`, + 'hideInPreview' + ) + ] }, { config: AddressCopyConfigCases.PRIMARY_ADDRESS_SAME_AS_OTHER_PRIMARY, label: formMessageDescriptors.primaryAddressSameAsOtherPrimary, xComparisonSection: 'father', yComparisonSection: 'mother', - conditionalCase: `(${detailsDontExist} || ${mothersDetailsDontExistOnOtherPage})` + conditionalCase: [ + expressionToConditional( + `(${detailsDontExist} || ${mothersDetailsDontExistOnOtherPage})` + ), + expressionToConditional( + `(${detailsDontExist} || ${mothersDetailsDontExistOnOtherPage} || !${primaryAddressSameAsOtherPrimaryAddress})`, + 'hideInPreview' + ) + ] }, { config: AddressCases.PRIMARY_ADDRESS, diff --git a/src/form/common/default-validation-conditionals.ts b/src/form/common/default-validation-conditionals.ts index 093c2b7a8..3a23fcf9f 100644 --- a/src/form/common/default-validation-conditionals.ts +++ b/src/form/common/default-validation-conditionals.ts @@ -12,6 +12,19 @@ import { Conditional } from '../types/types' import { IntegratingSystemType } from '../types/types' import { Validator } from '../types/validators' +/** + * Turns a string expression into a Conditional object + * @param expression conditional expression + * @param action conditional action. e.g. 'hide' |'HideInPreview'. Defaults to 'hide' + */ +export const expressionToConditional = ( + expression: string, + action: string = 'hide' +): Conditional => ({ + action, + expression: `${expression}` +}) + export const isValidChildBirthDate = [ { operation: 'isValidChildBirthDate' diff --git a/src/form/common/messages.ts b/src/form/common/messages.ts index 9c2798f6f..11b5cb78e 100644 --- a/src/form/common/messages.ts +++ b/src/form/common/messages.ts @@ -128,7 +128,7 @@ export const informantMessageDescriptors = { export const formMessageDescriptors = { primaryAddress: { defaultMessage: 'Usual place of residence', - description: 'Title of the primary adress ', + description: 'Title of the primary adress', id: 'form.field.label.primaryAddress' }, spouseSectionName: { diff --git a/src/form/types/types.ts b/src/form/types/types.ts index ab93bc784..c182795ed 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -927,7 +927,7 @@ export type AllowedAddressConfigurations = { label?: MessageDescriptor xComparisonSection?: string yComparisonSection?: string - conditionalCase?: string + conditionalCase?: string | Conditional[] } export type AdministrativeLevel = 1 | 2 | 3 | 4 | 5 diff --git a/src/utils/address-utils.ts b/src/utils/address-utils.ts index 25462de5f..178b8298b 100644 --- a/src/utils/address-utils.ts +++ b/src/utils/address-utils.ts @@ -34,6 +34,7 @@ import { } from '../form/addresses/address-fields' import { getPreviewGroups } from '../form/common/preview-groups' import { cloneDeep } from 'lodash' +import { expressionToConditional } from '../form/common/default-validation-conditionals' // Use this function to edit the visibility of fields depending on user input function getLocationLevelConditionals( @@ -961,7 +962,7 @@ export function isUseCaseForPlaceOfEvent(useCase: string): Boolean { export const getAddressSubsection = ( previewGroup: AddressSubsections, label: MessageDescriptor, - conditionalCase?: string + conditionalCase?: string | Conditional[] ): SerializedFormField[] => { const fields: SerializedFormField[] = [] const subsection: SerializedFormField = { @@ -974,13 +975,12 @@ export const getAddressSubsection = ( } if (conditionalCase) { - subsection['conditionals'] = [ - { - action: 'hide', - expression: `${conditionalCase}` - } - ] + subsection['conditionals'] = + typeof conditionalCase === 'string' + ? [expressionToConditional(conditionalCase)] + : conditionalCase } + fields.push(subsection) return fields } @@ -1092,7 +1092,7 @@ function getAddress( section: string, addressCase: AddressCases, addressHierarchy: string[], - conditionalCase?: string + conditionalCase?: string | Conditional[] ): SerializedFormField[] { const defaultFields: SerializedFormField[] = getAddressFields( section, @@ -1102,12 +1102,15 @@ function getAddress( if (conditionalCase) { defaultFields.forEach((field) => { let conditional - if (conditionalCase) { - conditional = { - action: 'hide', - expression: `${conditionalCase}` - } + if (typeof conditionalCase === 'string') { + conditional = expressionToConditional(conditionalCase) } + + // @TODO Update this to handle multiple conditionals if the changes are otherwise good + if (Array.isArray(conditionalCase)) { + conditional = conditionalCase[0] + } + if ( conditional && field.conditionals && @@ -1121,5 +1124,6 @@ function getAddress( } }) } + return defaultFields } From 13b9bee4913b55daa739313a47c44473cbc12367 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 3 Jul 2024 09:20:02 +0300 Subject: [PATCH 057/146] fix(ocrvs-5086): refactor getAddress to accept arrays for conditionals --- .../common/default-validation-conditionals.ts | 2 +- src/utils/address-utils.ts | 45 +++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/form/common/default-validation-conditionals.ts b/src/form/common/default-validation-conditionals.ts index 3a23fcf9f..2210c63c4 100644 --- a/src/form/common/default-validation-conditionals.ts +++ b/src/form/common/default-validation-conditionals.ts @@ -15,7 +15,7 @@ import { Validator } from '../types/validators' /** * Turns a string expression into a Conditional object * @param expression conditional expression - * @param action conditional action. e.g. 'hide' |'HideInPreview'. Defaults to 'hide' + * @param action conditional action. e.g. 'hide' |'hideInPreview'. Defaults to 'hide' */ export const expressionToConditional = ( expression: string, diff --git a/src/utils/address-utils.ts b/src/utils/address-utils.ts index 178b8298b..b3bfe57bd 100644 --- a/src/utils/address-utils.ts +++ b/src/utils/address-utils.ts @@ -1100,30 +1100,29 @@ function getAddress( addressHierarchy ) if (conditionalCase) { - defaultFields.forEach((field) => { - let conditional - if (typeof conditionalCase === 'string') { - conditional = expressionToConditional(conditionalCase) - } - - // @TODO Update this to handle multiple conditionals if the changes are otherwise good - if (Array.isArray(conditionalCase)) { - conditional = conditionalCase[0] - } - - if ( - conditional && - field.conditionals && - field.conditionals.filter( - (conditional) => conditional.expression === conditionalCase - ).length === 0 - ) { - field.conditionals.push(conditional) - } else if (conditional && !field.conditionals) { - field.conditionals = [conditional] - } - }) + return addConditionalsToFields(defaultFields, conditionalCase) } return defaultFields } + +/** + * Adds provided conditionals to each field. Mutates the given array. + */ +function addConditionalsToFields( + fields: SerializedFormField[], + conditionalCase: string | Conditional[] +) { + fields.forEach((field) => { + const conditionals = + typeof conditionalCase === 'string' + ? [expressionToConditional(conditionalCase)] + : conditionalCase + + if (conditionals.length > 0) { + field.conditionals = [...(field.conditionals || []), ...conditionals] + } + }) + + return fields +} From d71bf5cbc14d02c544726617763bacf0bad0aab2 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" <32668488+Nil20@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:09:35 +0600 Subject: [PATCH 058/146] chore: add invalid date validation message (#184) --- src/translations/client.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/src/translations/client.csv b/src/translations/client.csv index e94d488fe..be22b95dd 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -2103,6 +2103,7 @@ validations.englishOnlyNameFormat,"The error message that appears when a non Eng validations.facilityMustBeSelected,The error message appears when no valid facility is selected,No facility selected,Aucun établissement sélectionné validations.greaterThanZero,The error message appears when input is less than or equal to 0,Must be a greater than zero,Doit être supérieur à zéro validations.illegalMarriageAge,The error message appears when the birth date is not old enough to register for marriage,Illegal age of marriage,Age illègale pour mariage +validations.invalidDate,The error message that appears when a date field is invalid,Invalid date field,Champ de date invalide validations.isDateNotAfterDeath,The error message appears when the given date of death is not valid,Date of birth must be before date of death,La date doit être antérieure à la date de décès validations.isDateNotBeforeBirth,The error message appears when the given date of death is not valid,Date of death must be after deceased date of birth,La date doit être postérieure à la date de naissance du défunt validations.isInformantOfLegalAge,The error message appears when the informant is not old enough to register an event,Informant is not of legal age,L'informateur n'est pas majeur From 3e3cf5f1cf04f6ae9dfb310a61f46b46bd7deea6 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" <32668488+Nil20@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:28:52 +0600 Subject: [PATCH 059/146] fix: update witness section names (#185) --- src/form/common/messages.ts | 13 +++++++++---- src/form/marriage/index.ts | 4 ++-- src/translations/client.csv | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/form/common/messages.ts b/src/form/common/messages.ts index 9c2798f6f..743d0c1a4 100644 --- a/src/form/common/messages.ts +++ b/src/form/common/messages.ts @@ -776,10 +776,15 @@ export const formMessageDescriptors = { description: 'Label for form field: Place of occurrence of marriage', id: 'form.field.label.placeOfMarriage' }, - witnessName: { - defaultMessage: 'Witness', - description: 'Form section name for Witness', - id: 'form.section.witness.name' + witnessOneName: { + defaultMessage: 'Witness 1', + description: 'Form section name for Witness one', + id: 'form.section.witnessOne.name' + }, + witnessTwoName: { + defaultMessage: 'Witness 2', + description: 'Form section name for Witness two', + id: 'form.section.witnessTwo.name' }, witnessOneTitle: { defaultMessage: 'What are the witnesses one details?', diff --git a/src/form/marriage/index.ts b/src/form/marriage/index.ts index 213ffdeff..7fc51ef6f 100644 --- a/src/form/marriage/index.ts +++ b/src/form/marriage/index.ts @@ -269,7 +269,7 @@ export const marriageForm: ISerializedForm = { { id: 'witnessOne', viewType: 'form', - name: formMessageDescriptors.witnessName, + name: formMessageDescriptors.witnessOneName, title: formMessageDescriptors.witnessOneTitle, groups: [ { @@ -296,7 +296,7 @@ export const marriageForm: ISerializedForm = { { id: 'witnessTwo', viewType: 'form', - name: formMessageDescriptors.witnessName, + name: formMessageDescriptors.witnessTwoName, title: formMessageDescriptors.witnessTwoTitle, groups: [ { diff --git a/src/translations/client.csv b/src/translations/client.csv index be22b95dd..0d4cd861a 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -1363,8 +1363,9 @@ form.section.upload.documentsTitle,,Upload supporting documents,Joindre des docu form.section.user.preview.title,,Confirm details,Confirmer les détails form.section.user.title,,Create new user,Créer un nouvel utilisateur form.section.userDetails,,User details,information utilisateur -form.section.witness.name,,Witness,Témoin +form.section.witnessOne.name,Form section name for Witness one,Witness 1,Témoin 1 form.section.witnessOne.title,,Witness 1 details,Détails du témoin 1 +form.section.witnessTwo.name,Form section name for Witness two,Witness 2,Témoin 2 form.section.witnessTwo.title,,Witness 2 details,Détails du témoin 2 home.header.advancedSearch,Search menu advanced search type,Advanced Search,Recherche avancée home.header.email,Search email type,Email,E-mail From e00238e6d283c6e4d6a86431bbc1881b94ae7fa0 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Wed, 3 Jul 2024 17:51:17 +0600 Subject: [PATCH 060/146] fix: enable auth for certs endpoint (#188) --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 771371b8b..c7ea2459b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -494,7 +494,6 @@ export async function createServer() { path: '/certificates', handler: certificateHandler, options: { - auth: false, tags: ['api', 'certificates'], description: 'Returns certificate metadata' } From b55d3fb62bb0ac75adc111b5d727aff2a40cd304 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 3 Jul 2024 14:57:58 +0300 Subject: [PATCH 061/146] fix(ocrvs-5086): update primary address preview copy for informant and spouse --- src/form/addresses/index.ts | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index b28d56767..97bee6497 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -191,14 +191,28 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ { config: AddressCopyConfigCases.PRIMARY_ADDRESS_SAME_AS_OTHER_PRIMARY, label: formMessageDescriptors.primaryAddressSameAsDeceasedsPrimary, - conditionalCase: `${isInformantSpouse}`, + conditionalCase: [ + expressionToConditional(isInformantSpouse), + expressionToConditional( + `${isInformantSpouse} || !${primaryAddressSameAsOtherPrimaryAddress}`, + 'hideInPreview' + ) + ], xComparisonSection: 'informant', yComparisonSection: 'deceased' }, { config: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, label: formMessageDescriptors.informantPrimaryAddress, - conditionalCase: `${primaryAddressSameAsOtherPrimaryAddress} || ${hideIfInformantSpouse[0].expression}` + conditionalCase: [ + expressionToConditional( + `${primaryAddressSameAsOtherPrimaryAddress} || ${hideIfInformantSpouse[0].expression}` + ), + expressionToConditional( + `${primaryAddressSameAsOtherPrimaryAddress} || ${hideIfInformantSpouse[0].expression} || ${primaryAddressSameAsOtherPrimaryAddress}`, + 'hideInPreview' + ) + ] }, { config: AddressCases.PRIMARY_ADDRESS, @@ -257,14 +271,26 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ { config: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, label: formMessageDescriptors.primaryAddress, - conditionalCase: `${SPOUSE_DETAILS_DONT_EXIST}` + conditionalCase: [ + expressionToConditional(SPOUSE_DETAILS_DONT_EXIST), + expressionToConditional( + `${SPOUSE_DETAILS_DONT_EXIST} || ${primaryAddressSameAsOtherPrimaryAddress}`, + 'hideInPreview' + ) + ] }, { config: AddressCopyConfigCases.PRIMARY_ADDRESS_SAME_AS_OTHER_PRIMARY, label: formMessageDescriptors.primaryAddressSameAsDeceasedsPrimary, xComparisonSection: 'spouse', yComparisonSection: 'deceased', - conditionalCase: `${detailsDontExist}` + conditionalCase: [ + expressionToConditional(detailsDontExist), + expressionToConditional( + `${detailsDontExist} || !${primaryAddressSameAsOtherPrimaryAddress}`, + 'hideInPreview' + ) + ] }, { config: AddressCases.PRIMARY_ADDRESS, From 12f6204115c35f9969469b433962d255ad597975 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 3 Jul 2024 16:29:04 +0300 Subject: [PATCH 062/146] fix(ocrvs-7324): remove duplicate sentences from copy --- src/form/birth/index.ts | 2 +- src/form/death/index.ts | 2 +- src/translations/client.csv | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/form/birth/index.ts b/src/form/birth/index.ts index c112ac4c9..fb24a1bd6 100644 --- a/src/form/birth/index.ts +++ b/src/form/birth/index.ts @@ -138,7 +138,7 @@ export const birthForm: ISerializedForm = { }, { defaultMessage: - 'Once the declaration is processed you will receive you will receive an SMS to tell you when to visit the office to collect the certificate - Take your ID with you.', + 'Once the declaration is processed you will receive an SMS to tell you when to visit the office to collect the certificate - Take your ID with you.', description: 'Form information for birth', id: 'form.section.information.birth.bullet3' }, diff --git a/src/form/death/index.ts b/src/form/death/index.ts index 0c1d4d071..454568e65 100644 --- a/src/form/death/index.ts +++ b/src/form/death/index.ts @@ -143,7 +143,7 @@ export const deathForm = { }, { defaultMessage: - 'Once the declaration is processed you will receive you will receive an SMS to tell you when to visit the office to collect the certificate - Take your ID with you.', + 'Once the declaration is processed you will receive an SMS to tell you when to visit the office to collect the certificate - Take your ID with you.', description: 'Form information for birth', id: 'form.section.information.death.bullet3' }, diff --git a/src/translations/client.csv b/src/translations/client.csv index 0d4cd861a..525186ad2 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -1345,11 +1345,11 @@ form.section.informant.name,,Informant,Informateur form.section.informant.title,,Informant's details,information de l'informateur form.section.information.birth.bullet1,,I am going to help you make a declaration of birth.,Je vais vous aider à faire une déclaration de naissance. form.section.information.birth.bullet2,,As the legal Informant it is important that all the information provided by you is accurate.,"En tant qu'informateur légal, il est important que toutes les informations que vous fournissez soient exactes." -form.section.information.birth.bullet3,,Once the declaration is processed you will receive you will receive an email to tell you when to visit the office to collect the certificate - Take your ID with you.,"Une fois la déclaration traitée, vous recevrez un courriel vous indiquant quand vous rendre au bureau pour retirer le certificat - Munissez-vous d'une pièce d'identité." +form.section.information.birth.bullet3,,Once the declaration is processed you will receive an email to tell you when to visit the office to collect the certificate - Take your ID with you.,"Une fois la déclaration traitée, vous recevrez un courriel vous indiquant quand vous rendre au bureau pour retirer le certificat - Munissez-vous d'une pièce d'identité." form.section.information.birth.bullet4,,"Make sure you collect the certificate. A birth certificate is critical for this child, especially to make their life easy later on. It will help to access health services, school examinations and government benefits.","Veillez à récupérer le certificat. Un certificat de naissance est essentiel pour cet enfant, notamment pour lui faciliter la vie plus tard. Il l'aidera à accéder aux services de santé, aux examens scolaires et aux prestations de l'État." form.section.information.death.bullet1,,I am going to help you make a declaration of death.,Je vais vous aider à faire une déclaration de décès. form.section.information.death.bullet2,,As the legal Informant it is important that all the information provided by you is accurate.,"En tant qu'informateur légal, il est important que toutes les informations que vous fournissez soient exactes." -form.section.information.death.bullet3,,Once the declaration is processed you will receive you will receive an email to tell you when to visit the office to collect the certificate - Take your ID with you.,"Une fois la déclaration traitée, vous recevrez un courriel vous indiquant quand vous rendre au bureau pour retirer le certificat - Munissez-vous d'une pièce d'identité." +form.section.information.death.bullet3,,Once the declaration is processed you will receive an email to tell you when to visit the office to collect the certificate - Take your ID with you.,"Une fois la déclaration traitée, vous recevrez un courriel vous indiquant quand vous rendre au bureau pour retirer le certificat - Munissez-vous d'une pièce d'identité." form.section.information.death.bullet4,,Make sure you collect the certificate. A death certificate is critical to support with inheritance claims and to resolve the affairs of the deceased e.g. closing bank accounts and setting loans.,"Veillez à récupérer le certificat. Le certificat de décès est essentiel pour les demandes d'héritage et pour régler les affaires de la personne décédée, par exemple la fermeture des comptes bancaires et la mise en place des prêts." form.section.information.name,,Information,Informations form.section.marriageEvent.date,,Date of marriage,Date du mariage @@ -2146,7 +2146,7 @@ verifyCertificate.registrationCenter,Label for registration center,Registration verifyCertificate.sex,Label for sex,Sex,Sexe verifyCertificate.sexFemale,Option for form field: Sex name,Female,Femme verifyCertificate.sexMale,Option for form field: Sex name,Male,Homme -verifyCertificate.successMessage,message for success alert,Compare the partial details of the record below against those against those recorded on the certificate,Comparez les données partielles de l'enregistrement ci-dessous avec celles qui figurent sur le certificat. +verifyCertificate.successMessage,message for success alert,Compare the partial details of the record below against those recorded on the certificate,Comparez les données partielles de l'enregistrement ci-dessous avec celles qui figurent sur le certificat. verifyCertificate.successTitle,title for success alert,Valid QR code,Code QR valide verifyCertificate.successUrl,title for success alert for url validation,URL Verification,Vérification des URL verifyCertificate.timeOut,message when time out after few minute,You been timed out,Délais expirée From 574d43a7ba6c0cf70bf4aa0468af3c6a44180b14 Mon Sep 17 00:00:00 2001 From: Niko Kurtti Date: Fri, 5 Jul 2024 15:34:44 +0300 Subject: [PATCH 063/146] Remove deprecated version from compose files --- docker-compose.yml | 3 --- infrastructure/docker-compose.deploy.yml | 2 -- infrastructure/docker-compose.development-deploy.yml | 2 -- infrastructure/docker-compose.production-deploy.yml | 2 -- infrastructure/docker-compose.qa-deploy.yml | 2 -- infrastructure/docker-compose.staging-deploy.yml | 2 -- 6 files changed, 13 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3274fbcca..f7025fcab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,10 +6,7 @@ # & Healthcare Disclaimer located at http://opencrvs.org/license. # # Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. -version: "3.3" - services: - countryconfig: image: ${DOCKERHUB_ACCOUNT}/${DOCKERHUB_REPO}:${COUNTRY_CONFIG_VERSION} build: . diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 6ffd83733..8e51f6d5d 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -6,8 +6,6 @@ # & Healthcare Disclaimer located at http://opencrvs.org/license. # # Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. -version: '3.3' - services: # Configure reverse proxy for public endpoints # Note: these published port will override UFW rules as Docker manages it's own iptables diff --git a/infrastructure/docker-compose.development-deploy.yml b/infrastructure/docker-compose.development-deploy.yml index 276f4f8c5..27f337f4e 100644 --- a/infrastructure/docker-compose.development-deploy.yml +++ b/infrastructure/docker-compose.development-deploy.yml @@ -1,5 +1,3 @@ -version: '3.3' - services: notification: environment: diff --git a/infrastructure/docker-compose.production-deploy.yml b/infrastructure/docker-compose.production-deploy.yml index 0e9130b2a..e094eb3fe 100644 --- a/infrastructure/docker-compose.production-deploy.yml +++ b/infrastructure/docker-compose.production-deploy.yml @@ -1,5 +1,3 @@ -version: '3.3' - # # Production deployments of OpenCRVS should never be exposed to the internet. # Instead, they should be deployed on a private network and exposed to the internet via a VPN. diff --git a/infrastructure/docker-compose.qa-deploy.yml b/infrastructure/docker-compose.qa-deploy.yml index 541ceb731..0355e64e1 100644 --- a/infrastructure/docker-compose.qa-deploy.yml +++ b/infrastructure/docker-compose.qa-deploy.yml @@ -1,5 +1,3 @@ -version: '3.3' - services: traefik: networks: diff --git a/infrastructure/docker-compose.staging-deploy.yml b/infrastructure/docker-compose.staging-deploy.yml index f108abf50..3a6ac3bf5 100644 --- a/infrastructure/docker-compose.staging-deploy.yml +++ b/infrastructure/docker-compose.staging-deploy.yml @@ -1,5 +1,3 @@ -version: '3.3' - # # Production deployments of OpenCRVS should never be exposed to the internet. # Instead, they should be deployed on a private network and exposed to the internet via a VPN. From dfc63d5f758e31d7f934f3c2161713a655a1bb10 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 9 Jul 2024 11:43:41 +0600 Subject: [PATCH 064/146] feat: add the new required review/preview section (#137) * feat: add a dummy preview section * feat: add SIGNATURE form field type * feat: add informant's signature field * feat: add maxSizeMb & allowedFileFormats config * fix: update informant signature mapping params * feat: add review section to birth * refactor: move to required section file * feat: add signature fields for death & marriage * docs: update comment Co-authored-by: Pyry Rouvila * chore: define messageDescriptors for name & title * docs: update CHANGELOG --------- Co-authored-by: Pyry Rouvila --- CHANGELOG.md | 1 + src/form/birth/index.ts | 11 +- src/form/birth/required-sections.ts | 29 ++++- src/form/common/common-optional-fields.ts | 21 ++++ src/form/common/messages.ts | 20 ++++ src/form/death/index.ts | 11 +- src/form/death/required-sections.ts | 27 +++++ src/form/marriage/index.ts | 11 +- src/form/marriage/required-sections.ts | 126 +++++++++++++++++++++- src/form/types/types.ts | 9 ++ 10 files changed, 258 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d92697d95..c1407a662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.6.0 (TBD) - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field +- New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. ## [1.5.0] diff --git a/src/form/birth/index.ts b/src/form/birth/index.ts index fb24a1bd6..8fd20b3ae 100644 --- a/src/form/birth/index.ts +++ b/src/form/birth/index.ts @@ -69,7 +69,12 @@ import { exactDateOfBirthUnknownConditional, hideIfNidIntegrationEnabled } from '../common/default-validation-conditionals' -import { documentsSection, registrationSection } from './required-sections' +import { + documentsSection, + registrationSection, + previewSection, + reviewSection +} from './required-sections' import { certificateHandlebars } from './certificate-handlebars' import { getSectionMapping } from '@countryconfig/utils/mapping/section/birth/mapping-utils' import { getCommonSectionMapping } from '@countryconfig/utils/mapping/field-mapping-utils' @@ -460,6 +465,8 @@ export const birthForm: ISerializedForm = { ], mapping: getSectionMapping('father') }, - documentsSection // REQUIRED SECTION FOR DOCUMENT ATTACHMENTS + documentsSection, // REQUIRED SECTION FOR DOCUMENT ATTACHMENTS + previewSection, // REQUIRED SECTION TO PREVIEW DECLARATION BEFORE SUBMIT + reviewSection // REQUIRED SECTION TO REVIEW SUBMITTED DECLARATION ] } diff --git a/src/form/birth/required-sections.ts b/src/form/birth/required-sections.ts index 771ff05ba..a968430b8 100644 --- a/src/form/birth/required-sections.ts +++ b/src/form/birth/required-sections.ts @@ -2,6 +2,7 @@ import { getSectionMapping } from '@countryconfig/utils/mapping/section/birth/ma import { formMessageDescriptors } from '../common/messages' import { ISerializedFormSection } from '../types/types' import { getFieldMapping } from '@countryconfig/utils/mapping/field-mapping-utils' +import { informantsSignature } from '../common/common-optional-fields' export const registrationSection = { id: 'registration', // A hidden 'registration' section must be included to store identifiers in a form draft that are used in certificates @@ -208,4 +209,30 @@ export const documentsSection = { ] } ] -} as ISerializedFormSection +} satisfies ISerializedFormSection + +export const previewSection = { + id: 'preview', + viewType: 'preview', + name: formMessageDescriptors.previewName, + title: formMessageDescriptors.previewTitle, + groups: [ + { + id: 'preview-view-group', + fields: [informantsSignature] + } + ] +} satisfies ISerializedFormSection + +export const reviewSection = { + id: 'review', + viewType: 'review', + name: formMessageDescriptors.reviewName, + title: formMessageDescriptors.reviewTitle, + groups: [ + { + id: 'review-view-group', + fields: [informantsSignature] + } + ] +} satisfies ISerializedFormSection diff --git a/src/form/common/common-optional-fields.ts b/src/form/common/common-optional-fields.ts index d37f8ff84..ecc91ea09 100644 --- a/src/form/common/common-optional-fields.ts +++ b/src/form/common/common-optional-fields.ts @@ -200,3 +200,24 @@ export const getEducation = ( options: educationalAttainmentOptions, mapping: getFieldMapping('educationalAttainment', certificateHandlebar) }) + +export const informantsSignature = { + name: 'informantSignature', + label: { + defaultMessage: 'Signature of informant', + description: 'Label for informants signature input', + id: 'review.inputs.informantsSignature' + }, + validator: [], + type: 'SIGNATURE', + mapping: { + mutation: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'informantsSignature'] + }, + query: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'informantsSignature'] + } + } +} satisfies SerializedFormField diff --git a/src/form/common/messages.ts b/src/form/common/messages.ts index 8ffafad8d..17c2684a3 100644 --- a/src/form/common/messages.ts +++ b/src/form/common/messages.ts @@ -326,6 +326,26 @@ export const formMessageDescriptors = { description: 'Label for form field: Nationality', id: 'form.field.label.nationality' }, + previewName: { + defaultMessage: 'Preview', + description: 'Form section name for Preview', + id: 'register.form.section.preview.name' + }, + previewTitle: { + defaultMessage: 'Preview', + description: 'Form section title for Preview', + id: 'register.form.section.preview.title' + }, + reviewName: { + defaultMessage: 'Review', + description: 'Form section name for Review', + id: 'review.form.section.review.name' + }, + reviewTitle: { + defaultMessage: 'Review', + description: 'Form section title for Review', + id: 'review.form.section.review.title' + }, placeOfBirthPreview: { defaultMessage: 'Place of delivery', description: 'Title for place of birth sub section', diff --git a/src/form/death/index.ts b/src/form/death/index.ts index 454568e65..66181d0b2 100644 --- a/src/form/death/index.ts +++ b/src/form/death/index.ts @@ -64,7 +64,12 @@ import { hideIfInformantSpouse, hideIfNidIntegrationEnabled } from '../common/default-validation-conditionals' -import { documentsSection, registrationSection } from './required-sections' +import { + documentsSection, + previewSection, + registrationSection, + reviewSection +} from './required-sections' import { deceasedNameInEnglish, informantNameInEnglish /*, @@ -509,6 +514,8 @@ export const deathForm = { ], mapping: getSectionMapping('father') },*/ - documentsSection + documentsSection, + previewSection, + reviewSection ] } satisfies ISerializedForm diff --git a/src/form/death/required-sections.ts b/src/form/death/required-sections.ts index eeabd9930..5a7fbea52 100644 --- a/src/form/death/required-sections.ts +++ b/src/form/death/required-sections.ts @@ -2,6 +2,7 @@ import { getSectionMapping } from '@countryconfig/utils/mapping/section/death/ma import { formMessageDescriptors } from '../common/messages' import { ISerializedFormSection } from '../types/types' import { getFieldMapping } from '@countryconfig/utils/mapping/field-mapping-utils' +import { informantsSignature } from '../common/common-optional-fields' export const registrationSection = { id: 'registration', @@ -178,3 +179,29 @@ export const documentsSection = { } ] } satisfies ISerializedFormSection + +export const previewSection = { + id: 'preview', + viewType: 'preview', + name: formMessageDescriptors.previewName, + title: formMessageDescriptors.previewTitle, + groups: [ + { + id: 'preview-view-group', + fields: [informantsSignature] + } + ] +} satisfies ISerializedFormSection + +export const reviewSection = { + id: 'review', + viewType: 'review', + name: formMessageDescriptors.reviewName, + title: formMessageDescriptors.reviewTitle, + groups: [ + { + id: 'review-view-group', + fields: [informantsSignature] + } + ] +} satisfies ISerializedFormSection diff --git a/src/form/marriage/index.ts b/src/form/marriage/index.ts index 7fc51ef6f..3b77628e5 100644 --- a/src/form/marriage/index.ts +++ b/src/form/marriage/index.ts @@ -48,7 +48,12 @@ import { hideIfInformantBrideOrGroom, brideOrGroomBirthDateValidators } from '../common/default-validation-conditionals' -import { documentsSection, registrationSection } from './required-sections' +import { + documentsSection, + previewSection, + registrationSection, + reviewSection +} from './required-sections' import { brideNameInEnglish, groomNameInEnglish, @@ -320,6 +325,8 @@ export const marriageForm: ISerializedForm = { ], mapping: getCommonSectionMapping('informant') }, - documentsSection + documentsSection, + previewSection, + reviewSection ] } diff --git a/src/form/marriage/required-sections.ts b/src/form/marriage/required-sections.ts index 78123172b..dc57c50a5 100644 --- a/src/form/marriage/required-sections.ts +++ b/src/form/marriage/required-sections.ts @@ -1,7 +1,11 @@ import { getSectionMapping } from '@countryconfig/utils/mapping/section/marriage/mapping-utils' import { getInformantConditionalForMarriageDocUpload } from '../common/default-validation-conditionals' import { formMessageDescriptors } from '../common/messages' -import { ISelectOption, ISerializedFormSection } from '../types/types' +import { + ISelectOption, + ISerializedFormSection, + SerializedFormField +} from '../types/types' import { getDocUploaderForMarriage } from './required-fields' export const registrationSection = { @@ -100,3 +104,123 @@ export const documentsSection = { } ] } as ISerializedFormSection + +/* + * In this reference configuration the signature + * fields for both the preview & review section are same + * but they can potentially be different e.g. it could be + * made such that the signatures are only required when + * registering a submitted declaration + */ +const signatureFields = [ + { + name: 'brideSignature', + label: { + defaultMessage: 'Signature of Bride', + description: "Label for bride's signature input", + id: 'review.inputs.brideSignature' + }, + required: true, + validator: [], + type: 'SIGNATURE', + mapping: { + mutation: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'brideSignature'] + }, + query: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'brideSignature'] + } + } + }, + { + name: 'groomSignature', + label: { + defaultMessage: 'Signature of groom', + description: "Label for groom's signature input", + id: 'review.inputs.groomSignature' + }, + required: true, + validator: [], + type: 'SIGNATURE', + mapping: { + mutation: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'groomSignature'] + }, + query: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'groomSignature'] + } + } + }, + { + name: 'witnessOneSignature', + label: { + defaultMessage: 'Signature of witnessOne', + description: "Label for witnessOne's signature input", + id: 'review.inputs.witnessOneSignature' + }, + required: true, + validator: [], + type: 'SIGNATURE', + mapping: { + mutation: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'witnessOneSignature'] + }, + query: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'witnessOneSignature'] + } + } + }, + { + name: 'witnessTwoSignature', + label: { + defaultMessage: 'Signature of witnessTwo', + description: "Label for witnessTwo's signature input", + id: 'review.inputs.witnessTwoSignature' + }, + required: true, + validator: [], + type: 'SIGNATURE', + mapping: { + mutation: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'witnessTwoSignature'] + }, + query: { + operation: 'fieldValueSectionExchangeTransformer', + parameters: ['registration', 'witnessTwoSignature'] + } + } + } +] satisfies SerializedFormField[] + +export const previewSection = { + id: 'preview', + viewType: 'preview', + name: formMessageDescriptors.previewName, + title: formMessageDescriptors.previewTitle, + groups: [ + { + id: 'preview-view-group', + fields: signatureFields + } + ] +} satisfies ISerializedFormSection + +export const reviewSection = { + id: 'review', + viewType: 'review', + name: formMessageDescriptors.reviewName, + title: formMessageDescriptors.reviewTitle, + groups: [ + { + id: 'review-view-group', + fields: signatureFields + } + ] +} satisfies ISerializedFormSection diff --git a/src/form/types/types.ts b/src/form/types/types.ts index c182795ed..6ba5711ad 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -141,6 +141,8 @@ export const TIME = 'TIME' export const NID_VERIFICATION_BUTTON = 'NID_VERIFICATION_BUTTON' export const DIVIDER = 'DIVIDER' export const HEADING3 = 'HEADING3' +export const SIGNATURE = 'SIGNATURE' + export enum RadioSize { LARGE = 'large', NORMAL = 'normal' @@ -482,6 +484,12 @@ export interface IHeading3Field extends IFormFieldBase { type: typeof HEADING3 } +export interface ISignatureFormField extends IFormFieldBase { + type: typeof SIGNATURE + maxSizeMb?: number + allowedFileFormats?: ('png' | 'jpg' | 'jpeg' | 'svg')[] +} + export type IFormField = | ITextFormField | ITelFormField @@ -514,6 +522,7 @@ export type IFormField = | INidVerificationButton | IDividerField | IHeading3Field + | ISignatureFormField export interface SelectComponentOption { value: string From 7f3902d99781ae45209c4de148f321f4049c31e9 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 9 Jul 2024 11:53:11 +0600 Subject: [PATCH 065/146] fix: stop accessing response text multiple times (#195) As text is a stream it can only be accessed once otherwise it would error. --- src/api/notification/sms-service.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/api/notification/sms-service.ts b/src/api/notification/sms-service.ts index a4d2016cb..caffde3bb 100644 --- a/src/api/notification/sms-service.ts +++ b/src/api/notification/sms-service.ts @@ -80,15 +80,13 @@ export async function sendSMS( } const responseBody = await response.text() - logger.info(`Response from Infobip: ${JSON.stringify(responseBody)}`) if (!response.ok) { - logger.error( - `Failed to send sms to ${recipient}. Reason: ${response.text()}` - ) + logger.error(`Failed to send sms to ${recipient}. Reason: ${responseBody}`) throw internal( - `Failed to send notification to ${recipient}. Reason: ${response.text()}` + `Failed to send notification to ${recipient}. Reason: ${responseBody}` ) } + logger.info(`Response from Infobip: ${JSON.stringify(responseBody)}`) } const compileMessages = async ( From fa3ddde8092a13b4a0b1f62b1cd5f7f66ffb7b73 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 9 Jul 2024 12:18:05 +0600 Subject: [PATCH 066/146] chore: update weight at birth postfix (#201) Also remove inputFieldWidth prop --- src/form/birth/optional-fields.ts | 14 +++----------- src/form/common/common-optional-fields.ts | 1 - src/form/death/custom-fields.ts | 3 +-- src/form/types/types.ts | 1 - 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/form/birth/optional-fields.ts b/src/form/birth/optional-fields.ts index dddd1a421..38df50af2 100644 --- a/src/form/birth/optional-fields.ts +++ b/src/form/birth/optional-fields.ts @@ -61,12 +61,8 @@ export const weightAtBirth: SerializedFormField = { parameters: [0, 6] } ], - postfix: 'Kg', - mapping: getFieldMapping( - 'weightAtBirth', - certificateHandlebars.weightAtBirth - ), - inputFieldWidth: '78px' + postfix: 'kilograms (kg)', + mapping: getFieldMapping('weightAtBirth', certificateHandlebars.weightAtBirth) } export const multipleBirth: SerializedFormField = { @@ -94,9 +90,5 @@ export const multipleBirth: SerializedFormField = { parameters: [2] } ], - mapping: getFieldMapping( - 'multipleBirth', - certificateHandlebars.multipleBirth - ), - inputFieldWidth: '64px' + mapping: getFieldMapping('multipleBirth', certificateHandlebars.multipleBirth) } diff --git a/src/form/common/common-optional-fields.ts b/src/form/common/common-optional-fields.ts index ecc91ea09..3be7a5e25 100644 --- a/src/form/common/common-optional-fields.ts +++ b/src/form/common/common-optional-fields.ts @@ -66,7 +66,6 @@ export const getAgeOfIndividualInYears = ( validator: validators, conditionals, postfix: 'years', - inputFieldWidth: '78px', ...(certificateHandlebar && { mapping: getFieldMapping('ageOfIndividualInYears', certificateHandlebar) }) diff --git a/src/form/death/custom-fields.ts b/src/form/death/custom-fields.ts index 2b3add283..1d08e2e86 100644 --- a/src/form/death/custom-fields.ts +++ b/src/form/death/custom-fields.ts @@ -31,7 +31,6 @@ export function getNumberOfDependants(): SerializedFormField { initialValue: '', validator: [], mapping: getCustomFieldMapping(fieldId), - conditionals: [], - inputFieldWidth: '64px' + conditionals: [] } } diff --git a/src/form/types/types.ts b/src/form/types/types.ts index 6ba5711ad..28a95b547 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -348,7 +348,6 @@ export interface INumberFormField extends IFormFieldBase { type: typeof NUMBER step?: number max?: number - inputFieldWidth?: string inputWidth?: number } export interface IBigNumberFormField extends IFormFieldBase { From 94d79819268cd92a2ae32f82f624d7227426dc80 Mon Sep 17 00:00:00 2001 From: Pyry Rouvila Date: Tue, 9 Jul 2024 20:32:39 +0300 Subject: [PATCH 067/146] chore(changelog): underline breaking and important changes (#194) * chore(changelog): underline breaking and important changes * chore: add auth for cert endpoint --------- Co-authored-by: euanmillar --- CHANGELOG.md | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1407a662..8ca58cb38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,21 +5,39 @@ - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. -## [1.5.0] +## 1.5.0 +### Breaking changes + +- #### Update the certificate preview mechanism + + In effort of minimizing JavaScript-bundle size, we have streamlined the way how review certificate -page renders certificates. In case the images in your certificates are previewing blurry, you need to update your SVG-certificates to print QR-codes and other images directly with `` instead of the more complicated `` -paradigm. This doesn't affect printed certificates as they are still created as previously. + +- #### Application config items `DATE_OF_BIRTH_UNKNOWN` and `INFORMANT_SIGNATURE_REQUIRED` moved under `FEATURES` + + See https://github.com/opencrvs/opencrvs-farajaland/pull/1005 for details + +- #### Infrastructure + +- Treat backup host identically to other hosts. To migrate: + + 1. Move all inventory files (qa.yml, production.yml...) from `infrastructure/server-setup` to `infrastructure/server-setup/inventory` + 2. Run environment creator for your backup server `yarn environment:init --environment=backup` + +### Other changes + +- Upgrade Node.js to 18 +- Remove dependency OpenHIM. The OpenHIM database is kept for backwards compatibility reasons and will be removed in v1.6 - Change auth URLs to access them via gateway - Add hearth URL to search service - Include an endpoint for serving individual certificates in development mode - Include compositionId in confirm registration payload -- Move individual configuration options to feature flags - Remove logrocket refrences -- Upgrade to node 18 - Enable gzip compression in client & login - Make SENTRY_DSN variable optional - Use docker compose v2 in github workflows - Mass email from national system admin -- Remove dependency on openhim. The openhim db is kept for backwards compatibility reasons and will be removed in v1.6 -- Add smtp environment variables in qa compose file +- Add SMTP environment variables in qa compose file - Use image tag instead of patterns in certificate SVGs - Generate default address according to logged-in user's location - Remove authentication from dashboard queries route @@ -32,13 +50,10 @@ - Provide env variables for metabase admin credentials - Improved formatting of informant name for inProgress declaration emails - Rename `farajaland-map.geojson` to `map.geojson` to not tie implementations into example country naming +- Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field [#114](https://github.com/opencrvs/opencrvs-countryconfig/pull/114) +- Enable authentication for certificates endpoint [#188](https://github.com/opencrvs/opencrvs-countryconfig/pull/188) -**Infrastructure** - -- Treat backup host identically to other hosts. To migrate: - - 1. Move all inventory files (qa.yml, production.yml...) from `infrastructure/server-setup` to `infrastructure/server-setup/inventory` - 2. Run environment creator for your backup server `yarn environment:init --environment=backup` +- #### Infrastructure - Allow using staging to both period restore of production backup and also for backing up its own data to a different location using `backup_server_remote_target_directory` and `backup_server_remote_source_directory` ansible variables. This use case is mostly meant for OpenCRVS team internal use. From 5691206700c867f6fd7f7229444ef6679efc6001 Mon Sep 17 00:00:00 2001 From: Euan Millar Date: Tue, 9 Jul 2024 18:33:03 +0100 Subject: [PATCH 068/146] added backup environment for resetting tw factor auth on backup environments (#167) --- .github/workflows/reset-2fa.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/reset-2fa.yml b/.github/workflows/reset-2fa.yml index 7ed86fb52..729f9f47d 100644 --- a/.github/workflows/reset-2fa.yml +++ b/.github/workflows/reset-2fa.yml @@ -16,6 +16,7 @@ on: - staging - qa - production + - backup jobs: reset: From 7d2bdafbd71d3dc3054b35ac5c84a59a1642c42b Mon Sep 17 00:00:00 2001 From: Euan Millar Date: Tue, 9 Jul 2024 21:17:00 +0100 Subject: [PATCH 069/146] bump release number (#205) --- .github/workflows/deploy-prod.yml | 2 +- .github/workflows/deploy.yml | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index a60139597..93288c6e1 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -14,7 +14,7 @@ on: core-image-tag: description: Core DockerHub image tag required: true - default: 'v1.4.1' + default: 'v1.5.0' countryconfig-image-tag: description: Your Country Config DockerHub image tag required: true diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a747682bb..4c3d837ec 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,7 @@ on: core-image-tag: description: Core DockerHub image tag required: true - default: 'v1.4.1' + default: 'v1.5.0' countryconfig-image-tag: description: Your Country Config DockerHub image tag required: true diff --git a/package.json b/package.json index 6969d4376..dd2250442 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@opencrvs/countryconfig", - "version": "1.4.1", + "version": "1.5.0", "description": "OpenCRVS country configuration for reference data", "os": [ "darwin", From 05ab1999cbff7d7668a2db5d9fd305442514cf12 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Wed, 10 Jul 2024 15:27:21 +0600 Subject: [PATCH 070/146] docs: add breaking changes section (#207) --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ca58cb38..2410acbfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,11 @@ ## 1.6.0 (TBD) +### Breaking changes + - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. +- Remove `inputFieldWidth` from Number type form field ## 1.5.0 From 9154e2938bb9c5d31723b7134052b42a301e2733 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Wed, 10 Jul 2024 15:34:35 +0600 Subject: [PATCH 071/146] fix: skip PR creation when milestone not set --- .github/workflows/auto-pr-to-release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 968ebba47..0be40b8fb 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -73,11 +73,12 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check for milestone and if release branch exists + continue-on-error: true id: check_release_branch run: | if [ -z "${{ env.MILESTONE }}" ]; then echo "No milestone set. Exiting." - exit 0 + exit 1 fi RELEASE_BRANCH="release-${{ env.MILESTONE }}" @@ -87,10 +88,11 @@ jobs: echo "RELEASE_BRANCH=${RELEASE_BRANCH}" >> $GITHUB_ENV else echo "Release branch $RELEASE_BRANCH does not exist. Exiting." - exit 0 + exit 1 fi - name: Create and push the new branch for the PR + if: ${{ steps.check_release_branch.outcome == 'success' }} run: | SEMANTIC_PR_TITLE="${{ env.PR_TITLE }}" From 17b4166de274f830856b355dfebb5d373bf3df26 Mon Sep 17 00:00:00 2001 From: Euan Millar Date: Wed, 10 Jul 2024 16:53:06 +0100 Subject: [PATCH 072/146] remove special characters from the password generation (#209) --- infrastructure/environments/setup-environment.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/infrastructure/environments/setup-environment.ts b/infrastructure/environments/setup-environment.ts index 12f8a9bfe..83a0642bf 100644 --- a/infrastructure/environments/setup-environment.ts +++ b/infrastructure/environments/setup-environment.ts @@ -221,8 +221,7 @@ async function promptAndStoreAnswer( } function generateLongPassword() { - const chars = - '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_' + const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' let result = '' for (let i = 16; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)] From 88e08cacf6041ed5b1ab361c05e6eeec442c26cf Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 11 Jul 2024 12:44:47 +0600 Subject: [PATCH 073/146] chore: bump release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dd2250442..45f8ea317 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@opencrvs/countryconfig", - "version": "1.5.0", + "version": "1.6.0", "description": "OpenCRVS country configuration for reference data", "os": [ "darwin", From 516cf92de1bbb8cf21812609f1c09d898a8691c3 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 11 Jul 2024 13:47:55 +0600 Subject: [PATCH 074/146] docs: remove duplicate entries --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e7b4e86a..3bfa5bcef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,12 +33,6 @@ 1. Move all inventory files (qa.yml, production.yml...) from `infrastructure/server-setup` to `infrastructure/server-setup/inventory` 2. Run environment creator for your backup server `yarn environment:init --environment=backup` -- Allow using staging to both period restore of production backup and also for backing up its own data to a different location using `backup_server_remote_target_directory` and `backup_server_remote_source_directory` ansible variables. This use case is mostly meant for OpenCRVS team internal use. - -- Automate SSH key exchange between application and backup server. For staging servers, automatically fetch production backup encryption key if periodic restore is enabled - -- Improved support for non-22 SSH port - ### Other changes - Upgrade Node.js to 18 From 572ad2449905b7c282d2ee9eb6204be0ca43c538 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 11 Jul 2024 16:13:40 +0600 Subject: [PATCH 075/146] chore: bump deploy tags --- .github/workflows/deploy-prod.yml | 2 +- .github/workflows/deploy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 93288c6e1..30f89c55d 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -14,7 +14,7 @@ on: core-image-tag: description: Core DockerHub image tag required: true - default: 'v1.5.0' + default: 'v1.6.0' countryconfig-image-tag: description: Your Country Config DockerHub image tag required: true diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4c3d837ec..6e6998379 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,7 @@ on: core-image-tag: description: Core DockerHub image tag required: true - default: 'v1.5.0' + default: 'v1.6.0' countryconfig-image-tag: description: Your Country Config DockerHub image tag required: true From 1ebc0f1e71a2c6f993d969c3c654fc9d6e0fe196 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 11 Jul 2024 07:33:21 +0300 Subject: [PATCH 076/146] take off filesystem metric check from system and related alert --- infrastructure/monitoring/beats/metricbeat.yml | 16 ++++++++-------- .../monitoring/elastalert/rules/alert.yaml | 6 +++--- infrastructure/monitoring/kibana/config.ndjson | 1 - 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index cb4875457..520ed80bd 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -38,14 +38,14 @@ metricbeat.modules: cpu.metrics: ['percentages'] core.metrics: ['percentages'] - - module: system - period: 1m - metricsets: - - filesystem - - fsstat - processors: - - drop_event.when.regexp: - system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' + # - module: system + # period: 1m + # metricsets: + # - filesystem + # - fsstat + # processors: + # - drop_event.when.regexp: + # system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' - module: system period: 15m diff --git a/infrastructure/monitoring/elastalert/rules/alert.yaml b/infrastructure/monitoring/elastalert/rules/alert.yaml index d5bfbcb50..58a74d374 100644 --- a/infrastructure/monitoring/elastalert/rules/alert.yaml +++ b/infrastructure/monitoring/elastalert/rules/alert.yaml @@ -30,9 +30,9 @@ filter: - term: rule.name.keyword: value: 'CPU under heavy load' - - term: - rule.name.keyword: - value: 'Low on available disk space' + # - term: + # rule.name.keyword: + # value: 'Low on available disk space' minimum_should_match: 1 alert: post2 diff --git a/infrastructure/monitoring/kibana/config.ndjson b/infrastructure/monitoring/kibana/config.ndjson index a8f47219d..6c26fe491 100644 --- a/infrastructure/monitoring/kibana/config.ndjson +++ b/infrastructure/monitoring/kibana/config.ndjson @@ -5,6 +5,5 @@ {"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"query matched","params":{"documents":["{\"@timestamp\":\"2023-11-20T10:19:30.521Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":".es-query","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-20T09:12:19.237Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Successful SSH login","notifyWhen":"onActionGroupChange","params":{"esQuery":"{ \"query\": { \"bool\": { \"must\": [ \n { \"term\": { \"log.file.path\": \"/var/log/auth.log\" } },\n { \"term\": { \"event.outcome\": \"success\" }}\n ] } } }","index":["filebeat-*"],"size":100,"threshold":[1],"thresholdComparator":">=","timeField":"@timestamp","timeWindowSize":1,"timeWindowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:19.537Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"e79aaa90-8784-11ee-b9ba-89bbe73df7ff","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457367,232778],"type":"alert","updated_at":"2024-02-07T08:27:37.367Z","version":"WzQ5NTQ0MCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2022-06-20T06:16:33.414Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.084Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"CPU under heavy load","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":">","customMetric":{"aggregation":"avg","field":"","id":"alert-custom-metric","type":"custom"},"metric":"cpu","threshold":[70],"timeSize":1,"timeUnit":"m"}],"nodeType":"host","sourceId":"default"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:30.573Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f022bee0-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457362,232774],"type":"alert","updated_at":"2024-02-07T08:27:37.362Z","version":"WzQ5NTQzOCwxOV0="} -{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/mapper/cryptfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/mapper/cryptfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2023-06-15T07:57:35.954Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"],"indexOverride":"kibana-alert-history-services"}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.069Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:21.551Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f02b4a60-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457374,232780],"type":"alert","updated_at":"2024-02-07T08:27:37.374Z","version":"WzQ5NTQ0MSwxOV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":9,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file From 72c2b8ea4ee6a333f489147304aec1dc316e5001 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 11 Jul 2024 10:55:57 +0300 Subject: [PATCH 077/146] take up docker module --- .../monitoring/beats/metricbeat.yml | 37 ++----------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index 520ed80bd..98bf4ffdb 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -19,39 +19,10 @@ metricbeat.modules: #------------------------------- System Module ------------------------------- - module: system metricsets: - [ - 'cpu', - 'load', - 'memory', - 'network', - 'process', - 'process_summary', - 'core', - 'diskio', - 'socket' - ] - processes: ['.*'] - process.include_top_n: - by_cpu: 5 - by_memory: 5 - period: 10s - cpu.metrics: ['percentages'] - core.metrics: ['percentages'] - - # - module: system - # period: 1m - # metricsets: - # - filesystem - # - fsstat - # processors: - # - drop_event.when.regexp: - # system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' - - - module: system - period: 15m - metricsets: - - uptime - + - filesystem + processors: + - drop_event.when.regexp: + system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' #------------------------------- Docker Module ------------------------------- - module: docker metricsets: From 619c19bd663b2bc0c395a84dd8ef078aa7cc85ef Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 11 Jul 2024 16:37:10 +0300 Subject: [PATCH 078/146] Use kibana access token for connections --- infrastructure/docker-compose.deploy.yml | 6 ++++-- infrastructure/monitoring/kibana/kibana.yml | 2 +- infrastructure/monitoring/kibana/setup-config.sh | 15 ++++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 4d8a8473f..85ecfc2c5 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -77,6 +77,7 @@ services: - ELASTICSEARCH_HOST=elasticsearch:9200 - ELASTICSEARCH_USERNAME=elastic - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} + - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} - KIBANA_HOST=kibana:5601 command: ['--strict.perms=false'] deploy: @@ -127,8 +128,8 @@ services: entrypoint: [ 'curl', - '-u', - 'elastic:${ELASTICSEARCH_SUPERUSER_PASSWORD}', + '-H', + 'Authorization: Bearer ${KIBANA_ACCESS_TOKEN}', '-X', 'POST', 'http://kibana:5601/api/saved_objects/_import?overwrite=true', @@ -175,6 +176,7 @@ services: environment: - ELASTICSEARCH_USERNAME=elastic - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} + - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} configs: - source: kibana.{{ts}} target: /usr/share/kibana/config/kibana.yml diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 90404ecab..1530fc336 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -64,7 +64,7 @@ xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs # Kibana can also authenticate to Elasticsearch via "service account tokens". # Service account tokens are Bearer style tokens that replace the traditional username/password based configuration. # Use this token instead of a username/password. -# elasticsearch.serviceAccountToken: "my_token" +elasticsearch.serviceAccountToken: '{{KIBANA_ACCESS_TOKEN}}' # Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of # the elasticsearch.requestTimeout setting. diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index a2f5cca78..34fc10bd3 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,15 +23,16 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_enable" +$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_enable" done + From d02723285acdea5120dee041ef07f5f3b4a4fd2a Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 10:08:28 +0300 Subject: [PATCH 079/146] use kibana_system account instead of elastic --- infrastructure/deployment/deploy.sh | 3 +++ infrastructure/docker-compose.deploy.yml | 11 +++++------ infrastructure/elasticsearch/setup-users.sh | 1 + infrastructure/monitoring/kibana/kibana.yml | 1 - infrastructure/monitoring/kibana/setup-config.sh | 15 +++++++-------- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 5d8725915..9fe757fad 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -331,6 +331,9 @@ export ROTATING_METRICBEAT_ELASTIC_PASSWORD=`generate_password` # Used by APM for writing data to ElasticSearch export ROTATING_APM_ELASTIC_PASSWORD=`generate_password` +# Used by Kibana for writing data to ElasticSearch +export ROTATING_KIBANA_ELASTIC_PASSWORD=`generate_password` + # Download core compose files to /tmp/ for compose_file in ${COMPOSE_FILES_DOWNLOADED_FROM_CORE[@]}; do if [ ! -f $compose_file ]; then diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 85ecfc2c5..78580d51b 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -77,7 +77,6 @@ services: - ELASTICSEARCH_HOST=elasticsearch:9200 - ELASTICSEARCH_USERNAME=elastic - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} - - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} - KIBANA_HOST=kibana:5601 command: ['--strict.perms=false'] deploy: @@ -128,8 +127,8 @@ services: entrypoint: [ 'curl', - '-H', - 'Authorization: Bearer ${KIBANA_ACCESS_TOKEN}', + '-u', + 'kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD}', '-X', 'POST', 'http://kibana:5601/api/saved_objects/_import?overwrite=true', @@ -174,9 +173,8 @@ services: networks: - overlay_net environment: - - ELASTICSEARCH_USERNAME=elastic - - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} - - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} + - ELASTICSEARCH_USERNAME=kibana_system + - ELASTICSEARCH_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} configs: - source: kibana.{{ts}} target: /usr/share/kibana/config/kibana.yml @@ -367,6 +365,7 @@ services: - APM_ELASTIC_PASSWORD=${ROTATING_APM_ELASTIC_PASSWORD} - SEARCH_ELASTIC_USERNAME=search-user - SEARCH_ELASTIC_PASSWORD=${ROTATING_SEARCH_ELASTIC_PASSWORD} + - KIBANA_ELASTIC_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} - KIBANA_USERNAME=${KIBANA_USERNAME} - KIBANA_PASSWORD=${KIBANA_PASSWORD} volumes: diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 366090da1..05f6aae0f 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -25,6 +25,7 @@ users_passwords=( [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" + [kibana_system]="${KIBANA_ELASTIC_PASSWORD:-}" ) # ------------------------------------- diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 1530fc336..7b2f480c1 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -64,7 +64,6 @@ xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs # Kibana can also authenticate to Elasticsearch via "service account tokens". # Service account tokens are Bearer style tokens that replace the traditional username/password based configuration. # Use this token instead of a username/password. -elasticsearch.serviceAccountToken: '{{KIBANA_ACCESS_TOKEN}}' # Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of # the elasticsearch.requestTimeout setting. diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index 34fc10bd3..e6f8d864f 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,16 +23,15 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_enable" +$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" done - From eabdf4623e3c26b1f8f5ad64f900e32ffa469b48 Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 11:29:08 +0300 Subject: [PATCH 080/146] add kibana_system_password to top level env --- infrastructure/deployment/deploy.sh | 2 -- infrastructure/docker-compose.deploy.yml | 6 ++--- infrastructure/elasticsearch/setup-users.sh | 2 +- .../environments/setup-environment.ts | 24 +++++++++++++++++++ .../monitoring/kibana/setup-config.sh | 14 +++++------ 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 9fe757fad..e1e58fe10 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -331,8 +331,6 @@ export ROTATING_METRICBEAT_ELASTIC_PASSWORD=`generate_password` # Used by APM for writing data to ElasticSearch export ROTATING_APM_ELASTIC_PASSWORD=`generate_password` -# Used by Kibana for writing data to ElasticSearch -export ROTATING_KIBANA_ELASTIC_PASSWORD=`generate_password` # Download core compose files to /tmp/ for compose_file in ${COMPOSE_FILES_DOWNLOADED_FROM_CORE[@]}; do diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 78580d51b..ef35f1763 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -128,7 +128,7 @@ services: [ 'curl', '-u', - 'kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD}', + 'kibana_system:${KIBANA_SYSTEM_PASSWORD}', '-X', 'POST', 'http://kibana:5601/api/saved_objects/_import?overwrite=true', @@ -174,7 +174,7 @@ services: - overlay_net environment: - ELASTICSEARCH_USERNAME=kibana_system - - ELASTICSEARCH_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} + - ELASTICSEARCH_PASSWORD=${KIBANA_SYSTEM_PASSWORD} configs: - source: kibana.{{ts}} target: /usr/share/kibana/config/kibana.yml @@ -365,7 +365,7 @@ services: - APM_ELASTIC_PASSWORD=${ROTATING_APM_ELASTIC_PASSWORD} - SEARCH_ELASTIC_USERNAME=search-user - SEARCH_ELASTIC_PASSWORD=${ROTATING_SEARCH_ELASTIC_PASSWORD} - - KIBANA_ELASTIC_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} + - KIBANA_SYSTEM_PASSWORD=${KIBANA_SYSTEM_PASSWORD} - KIBANA_USERNAME=${KIBANA_USERNAME} - KIBANA_PASSWORD=${KIBANA_PASSWORD} volumes: diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 05f6aae0f..16c7534dd 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -25,7 +25,7 @@ users_passwords=( [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" - [kibana_system]="${KIBANA_ELASTIC_PASSWORD:-}" + [kibana_system]="${KIBANA_SYSTEM_PASSWORD:-}" ) # ------------------------------------- diff --git a/infrastructure/environments/setup-environment.ts b/infrastructure/environments/setup-environment.ts index 83a0642bf..aa5d3dd41 100644 --- a/infrastructure/environments/setup-environment.ts +++ b/infrastructure/environments/setup-environment.ts @@ -653,6 +653,13 @@ const derivedVariables = [ type: 'disabled', scope: 'ENVIRONMENT' }, + { + name: 'KIBANA_SYSTEM_PASSWORD', + valueLabel: 'KIBANA_SYSTEM_PASSWORD', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, { name: 'MINIO_ROOT_USER', valueLabel: 'MINIO_ROOT_USER', @@ -1103,6 +1110,23 @@ const SPECIAL_NON_APPLICATION_ENVIRONMENTS = ['jump', 'backup'] ), scope: 'ENVIRONMENT' as const }, + { + name: 'KIBANA_SYSTEM_PASSWORD', + type: 'SECRET' as const, + didExist: findExistingValue( + 'KIBANA_SYSTEM_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'KIBANA_SYSTEM_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, { name: 'MINIO_ROOT_USER', type: 'SECRET' as const, diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index e6f8d864f..e3876ae68 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,15 +23,15 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" +$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" done From 55d65625d421a060350097069e0a921b4e7839ae Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 11:39:37 +0300 Subject: [PATCH 081/146] remove reference to kibana_system from setup-users.sh --- infrastructure/elasticsearch/setup-users.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 16c7534dd..366090da1 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -25,7 +25,6 @@ users_passwords=( [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" - [kibana_system]="${KIBANA_SYSTEM_PASSWORD:-}" ) # ------------------------------------- From 07ebadd6f8dad63cd4786dc3d91e940fc2fee56b Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 12:30:14 +0300 Subject: [PATCH 082/146] use elastic user on setup-config kibana_system does not have access to kibana --- infrastructure/monitoring/kibana/setup-config.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index e3876ae68..b94cab21f 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,15 +23,15 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" -done +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_enable" +done \ No newline at end of file From 1f7462875df7e7a3ed753736ef83d1df6cd79a2c Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 12:41:04 +0300 Subject: [PATCH 083/146] remove deprecated dependencies from elastic and kibana --- infrastructure/docker-compose.deploy.yml | 1 - infrastructure/monitoring/kibana/kibana.yml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index ef35f1763..69d30bb7b 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -282,7 +282,6 @@ services: - path.repo=/data/backups/elasticsearch - cluster.name=docker-cluster - network.host=0.0.0.0 - - discovery.zen.minimum_master_nodes=1 - discovery.type=single-node - xpack.security.enabled=true - xpack.security.authc.api_key.enabled=true diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 7b2f480c1..ba87306dc 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -53,7 +53,6 @@ monitoring.ui.container.elasticsearch.enabled: true xpack.encryptedSavedObjects.encryptionKey: '{{KIBANA_ENCRYPTION_KEY}}' xpack.reporting.encryptionKey: '{{KIBANA_ENCRYPTION_KEY}}' xpack.actions.preconfiguredAlertHistoryEsIndex: true -xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs*,logstash*' # If your Elasticsearch is protected with basic authentication, these settings provide # the username and password that the Kibana server uses to perform maintenance on the Kibana # index at startup. Your Kibana users still need to authenticate with Elasticsearch, which @@ -103,7 +102,7 @@ xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs # =================== System: Logging =================== # Set the value of this setting to off to suppress all logging output, or to debug to log everything. Defaults to 'info' -logging.root.level: error +logging.root.level: debug # Enables you to specify a file where Kibana stores log output. #logging.appenders.default: # type: file From 1c294176b6db73b999e92ef1a0d3cf2ac500605a Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 13:36:45 +0300 Subject: [PATCH 084/146] upgrade kibana to 8.14.3 --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 69d30bb7b..cde5be71d 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -156,7 +156,7 @@ services: gelf-address: 'udp://127.0.0.1:12201' tag: 'setup-kibana-config' kibana: - image: docker.elastic.co/kibana/kibana:7.17.0 + image: docker.elastic.co/kibana/kibana:8.14.3 restart: always deploy: labels: From 08e2850b96855fd2d3374649ed033756d81a4dae Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 13:57:48 +0300 Subject: [PATCH 085/146] upgrade logstash to 8.14.3 --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index cde5be71d..152e48796 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -408,7 +408,7 @@ services: tag: 'elastalert' logstash: - image: logstash:7.17.0 + image: logstash:8.14.3 command: logstash -f /etc/logstash/logstash.conf --verbose ports: - '12201:12201' From 8855e07765fa2985646f61391bf536a41692181b Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 14:05:46 +0300 Subject: [PATCH 086/146] upgrade beats to 8.14.3 use monitoring instead of deprecated xpack.monitoring --- infrastructure/docker-compose.deploy.yml | 4 ++-- infrastructure/monitoring/beats/metricbeat.yml | 12 ++++++------ infrastructure/monitoring/filebeat/filebeat.yml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 152e48796..e72e52d63 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -60,7 +60,7 @@ services: - overlay_net filebeat: - image: docker.elastic.co/beats/filebeat:7.17.0 + image: docker.elastic.co/beats/filebeat:8.14.3 user: root networks: - overlay_net @@ -85,7 +85,7 @@ services: - 'traefik.enable=false' metricbeat: - image: docker.elastic.co/beats/metricbeat:7.17.13 + image: docker.elastic.co/beats/metricbeat:8.14.3 user: root cap_add: - SYS_PTRACE diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index 98bf4ffdb..bb0f9d01b 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -24,11 +24,11 @@ metricbeat.modules: - drop_event.when.regexp: system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' #------------------------------- Docker Module ------------------------------- - - module: docker - metricsets: - ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] - hosts: ['unix:///var/run/docker.sock'] - period: 10s + # - module: docker + # metricsets: + # ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] + # hosts: ['unix:///var/run/docker.sock'] + # period: 10s #================================ Processors =================================== processors: @@ -56,7 +56,7 @@ setup.kibana: password: ${KIBANA_PASSWORD} #============================== Xpack Monitoring =============================== -xpack.monitoring: +monitoring: enabled: true elasticsearch: username: ${BEATS_USERNAME} diff --git a/infrastructure/monitoring/filebeat/filebeat.yml b/infrastructure/monitoring/filebeat/filebeat.yml index d2ac41bcb..8f75ec4b9 100644 --- a/infrastructure/monitoring/filebeat/filebeat.yml +++ b/infrastructure/monitoring/filebeat/filebeat.yml @@ -60,7 +60,7 @@ setup.kibana: password: ${ELASTICSEARCH_PASSWORD} #============================== Xpack Monitoring =============================== -xpack.monitoring: +monitoring: enabled: true elasticsearch: From 449c858e023a747ad35bdece92f321f450d5d05b Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 14:42:18 +0300 Subject: [PATCH 087/146] upgrade apm server to 7.17.22 --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index e72e52d63..fdd49d7f4 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -431,7 +431,7 @@ services: - 'traefik.enable=false' replicas: 1 apm-server: - image: docker.elastic.co/apm/apm-server:7.15.2 + image: docker.elastic.co/apm/apm-server:7.17.22 cap_add: ['CHOWN', 'DAC_OVERRIDE', 'SETGID', 'SETUID'] cap_drop: ['ALL'] restart: always From 4548fb5476c3dc5bcb9e284ed2254e433eb73c4a Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 15:16:03 +0300 Subject: [PATCH 088/146] revert log changes after debugging --- .../monitoring/beats/metricbeat.yml | 39 ++++++++++++++++--- infrastructure/monitoring/kibana/kibana.yml | 2 +- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index bb0f9d01b..78bd940a7 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -18,17 +18,46 @@ metricbeat.autodiscover: metricbeat.modules: #------------------------------- System Module ------------------------------- - module: system + metricsets: + [ + 'cpu', + 'load', + 'memory', + 'network', + 'process', + 'process_summary', + 'core', + 'diskio', + 'socket' + ] + processes: ['.*'] + process.include_top_n: + by_cpu: 5 + by_memory: 5 + period: 10s + cpu.metrics: ['percentages'] + core.metrics: ['percentages'] + + - module: system + period: 1m metricsets: - filesystem + - fsstat processors: - drop_event.when.regexp: system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' + + - module: system + period: 15m + metricsets: + - uptime + #------------------------------- Docker Module ------------------------------- - # - module: docker - # metricsets: - # ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] - # hosts: ['unix:///var/run/docker.sock'] - # period: 10s + - module: docker + metricsets: + ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] + hosts: ['unix:///var/run/docker.sock'] + period: 10s #================================ Processors =================================== processors: diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index ba87306dc..2cfdd4c66 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -102,7 +102,7 @@ xpack.actions.preconfiguredAlertHistoryEsIndex: true # =================== System: Logging =================== # Set the value of this setting to off to suppress all logging output, or to debug to log everything. Defaults to 'info' -logging.root.level: debug +logging.root.level: error # Enables you to specify a file where Kibana stores log output. #logging.appenders.default: # type: file From f51e42a702ebe55b3a5439fa90cd0e8312bc4e30 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 16:20:39 +0300 Subject: [PATCH 089/146] widen search user privileges to accommodate reindex job --- infrastructure/elasticsearch/roles/search_user.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/infrastructure/elasticsearch/roles/search_user.json b/infrastructure/elasticsearch/roles/search_user.json index b7be198b5..da60cdeb8 100644 --- a/infrastructure/elasticsearch/roles/search_user.json +++ b/infrastructure/elasticsearch/roles/search_user.json @@ -1,8 +1,17 @@ { + "cluster": ["manage"], "indices": [ { - "names": ["ocrvs"], - "privileges": ["write", "create", "create_index", "delete", "delete_index", "read"] + "names": ["ocrvs", "ocrvs-*"], + "privileges": [ + "write", + "create", + "create_index", + "delete", + "delete_index", + "read", + "manage" + ] } ] } From 7681ad414467e7016caab8d232133675cfcfc066 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 16:21:01 +0300 Subject: [PATCH 090/146] upgrade elastalert to 2.19 2.4 onwards elastic8 is supported --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index fdd49d7f4..3af57d9e6 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -384,7 +384,7 @@ services: gelf-address: 'udp://127.0.0.1:12201' tag: 'setup-elasticsearch-users' elastalert: - image: jertel/elastalert2:2.3.0 + image: jertel/elastalert2:2.19.0 restart: unless-stopped environment: - ES_USERNAME=elastic From 96886c924279a455b370f65bde7147ad0b4e4697 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 17:05:09 +0300 Subject: [PATCH 091/146] use minimal access for search user --- infrastructure/elasticsearch/roles/search_user.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/infrastructure/elasticsearch/roles/search_user.json b/infrastructure/elasticsearch/roles/search_user.json index da60cdeb8..1c84a4173 100644 --- a/infrastructure/elasticsearch/roles/search_user.json +++ b/infrastructure/elasticsearch/roles/search_user.json @@ -1,5 +1,5 @@ { - "cluster": ["manage"], + "cluster": ["monitoring"], "indices": [ { "names": ["ocrvs", "ocrvs-*"], @@ -9,8 +9,7 @@ "create_index", "delete", "delete_index", - "read", - "manage" + "read" ] } ] From 5cd3377683ac4ef630875c6f4f5ea5669d063e36 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 17:26:26 +0300 Subject: [PATCH 092/146] update index creation error check match against v8 error message https://github.com/elastic/elasticsearch/blob/e64aab1b08f03a0af8a2845ff0cef226bde363af/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java#L186 --- infrastructure/elasticsearch/setup-helpers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index c6a25e874..38caab50a 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -253,7 +253,7 @@ function create_elastic_index { echo "${output}" - if [[ "${output: -3}" -eq 200 || $output == *"resource_already_exists"* ]]; then + if [[ "${output: -3}" -eq 200 || $output == *"already exists as alias"* ]]; then result=0 fi From 0cd875137b48db13c37113056d0293ae14f77f1d Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 17:36:25 +0300 Subject: [PATCH 093/146] add manage privilege back to search user --- infrastructure/elasticsearch/roles/search_user.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/infrastructure/elasticsearch/roles/search_user.json b/infrastructure/elasticsearch/roles/search_user.json index 1c84a4173..da60cdeb8 100644 --- a/infrastructure/elasticsearch/roles/search_user.json +++ b/infrastructure/elasticsearch/roles/search_user.json @@ -1,5 +1,5 @@ { - "cluster": ["monitoring"], + "cluster": ["manage"], "indices": [ { "names": ["ocrvs", "ocrvs-*"], @@ -9,7 +9,8 @@ "create_index", "delete", "delete_index", - "read" + "read", + "manage" ] } ] From 5fa77fafb2a4131f5a4ddc837e884dd1169a13e3 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 17 Jul 2024 08:42:07 +0300 Subject: [PATCH 094/146] add comment for error message check --- infrastructure/elasticsearch/setup-helpers.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index 38caab50a..5df149fbb 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -253,6 +253,8 @@ function create_elastic_index { echo "${output}" + # @TODO: Preferably check whether the index already exists before creating it. + # Error message might change in the future so we should not depend on it. if [[ "${output: -3}" -eq 200 || $output == *"already exists as alias"* ]]; then result=0 fi From af3942374b1dd5f74d9d6477b75483fc95a0b3e0 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 17 Jul 2024 13:39:05 +0300 Subject: [PATCH 095/146] fix cherry-pick inconsistencies Changes were initially ran against test country config --- infrastructure/deployment/deploy.sh | 1 - infrastructure/monitoring/elastalert/rules/alert.yaml | 6 +++--- infrastructure/monitoring/kibana/config.ndjson | 1 + infrastructure/monitoring/kibana/kibana.yml | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index e1e58fe10..5d8725915 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -331,7 +331,6 @@ export ROTATING_METRICBEAT_ELASTIC_PASSWORD=`generate_password` # Used by APM for writing data to ElasticSearch export ROTATING_APM_ELASTIC_PASSWORD=`generate_password` - # Download core compose files to /tmp/ for compose_file in ${COMPOSE_FILES_DOWNLOADED_FROM_CORE[@]}; do if [ ! -f $compose_file ]; then diff --git a/infrastructure/monitoring/elastalert/rules/alert.yaml b/infrastructure/monitoring/elastalert/rules/alert.yaml index 58a74d374..d5bfbcb50 100644 --- a/infrastructure/monitoring/elastalert/rules/alert.yaml +++ b/infrastructure/monitoring/elastalert/rules/alert.yaml @@ -30,9 +30,9 @@ filter: - term: rule.name.keyword: value: 'CPU under heavy load' - # - term: - # rule.name.keyword: - # value: 'Low on available disk space' + - term: + rule.name.keyword: + value: 'Low on available disk space' minimum_should_match: 1 alert: post2 diff --git a/infrastructure/monitoring/kibana/config.ndjson b/infrastructure/monitoring/kibana/config.ndjson index 6c26fe491..a8f47219d 100644 --- a/infrastructure/monitoring/kibana/config.ndjson +++ b/infrastructure/monitoring/kibana/config.ndjson @@ -5,5 +5,6 @@ {"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"query matched","params":{"documents":["{\"@timestamp\":\"2023-11-20T10:19:30.521Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":".es-query","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-20T09:12:19.237Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Successful SSH login","notifyWhen":"onActionGroupChange","params":{"esQuery":"{ \"query\": { \"bool\": { \"must\": [ \n { \"term\": { \"log.file.path\": \"/var/log/auth.log\" } },\n { \"term\": { \"event.outcome\": \"success\" }}\n ] } } }","index":["filebeat-*"],"size":100,"threshold":[1],"thresholdComparator":">=","timeField":"@timestamp","timeWindowSize":1,"timeWindowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:19.537Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"e79aaa90-8784-11ee-b9ba-89bbe73df7ff","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457367,232778],"type":"alert","updated_at":"2024-02-07T08:27:37.367Z","version":"WzQ5NTQ0MCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2022-06-20T06:16:33.414Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.084Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"CPU under heavy load","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":">","customMetric":{"aggregation":"avg","field":"","id":"alert-custom-metric","type":"custom"},"metric":"cpu","threshold":[70],"timeSize":1,"timeUnit":"m"}],"nodeType":"host","sourceId":"default"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:30.573Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f022bee0-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457362,232774],"type":"alert","updated_at":"2024-02-07T08:27:37.362Z","version":"WzQ5NTQzOCwxOV0="} +{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/mapper/cryptfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/mapper/cryptfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2023-06-15T07:57:35.954Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"],"indexOverride":"kibana-alert-history-services"}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.069Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:21.551Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f02b4a60-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457374,232780],"type":"alert","updated_at":"2024-02-07T08:27:37.374Z","version":"WzQ5NTQ0MSwxOV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":9,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 2cfdd4c66..a97e87780 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -63,6 +63,7 @@ xpack.actions.preconfiguredAlertHistoryEsIndex: true # Kibana can also authenticate to Elasticsearch via "service account tokens". # Service account tokens are Bearer style tokens that replace the traditional username/password based configuration. # Use this token instead of a username/password. +# elasticsearch.serviceAccountToken: "my_token" # Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of # the elasticsearch.requestTimeout setting. From fd134263b2b238ed5b9ed49c085addd1de352350 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 18 Jul 2024 10:55:47 +0300 Subject: [PATCH 096/146] set kibana_system password on setup-users.sh --- infrastructure/elasticsearch/setup-users.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 366090da1..30d2f5923 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -24,6 +24,7 @@ users_passwords=( [$SEARCH_ELASTIC_USERNAME]="${SEARCH_ELASTIC_PASSWORD:-}" [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" + [kibana_system]="${KIBANA_SYSTEM_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" ) From eba09adc3e24414b3a10b86a8afa0b6f5aedb5d3 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 25 Jul 2024 16:11:49 +0300 Subject: [PATCH 097/146] stop creating ocrvs index as a part of elastic setup --- infrastructure/elasticsearch/setup-helpers.sh | 34 ------------------- .../elasticsearch/setup-settings.sh | 3 -- 2 files changed, 37 deletions(-) diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index 5df149fbb..e55e0a4ae 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -231,37 +231,3 @@ function ensure_settings { return $result } - -function create_elastic_index { - local index_name=$1 - local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" - - local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' - "http://${elasticsearch_host}:9200/${index_name}" - '-X' 'PUT' - '-H' 'Content-Type: application/json' - ) - - if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then - args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) - fi - - local -i result=1 - local output - - output="$(curl "${args[@]}")" - - echo "${output}" - - # @TODO: Preferably check whether the index already exists before creating it. - # Error message might change in the future so we should not depend on it. - if [[ "${output: -3}" -eq 200 || $output == *"already exists as alias"* ]]; then - result=0 - fi - - if ((result)); then - echo -e "\n${output::-3}\n" - fi - - return $result -} diff --git a/infrastructure/elasticsearch/setup-settings.sh b/infrastructure/elasticsearch/setup-settings.sh index eea61f90a..260e13a31 100644 --- a/infrastructure/elasticsearch/setup-settings.sh +++ b/infrastructure/elasticsearch/setup-settings.sh @@ -19,8 +19,5 @@ echo "-------- $(date) --------" log 'Waiting for availability of Elasticsearch' wait_for_elasticsearch -log "Creating index for Elasticsearch. Index: ocrvs" -create_elastic_index "ocrvs" - log "Updating replicas for Elasticsearch" ensure_settings "{\"index\":{\"number_of_replicas\":0}}" From 4264ab1288fcc76b9dd5c0fec93f3a761ba62d41 Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 26 Jul 2024 10:12:43 +0300 Subject: [PATCH 098/146] stop creating ocrvs index before running migrations during cleanup --- infrastructure/run-migrations.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/infrastructure/run-migrations.sh b/infrastructure/run-migrations.sh index d68ee546b..fbe00d8b0 100755 --- a/infrastructure/run-migrations.sh +++ b/infrastructure/run-migrations.sh @@ -25,13 +25,5 @@ elasticsearch_host() { fi } -create_elastic_index () { - local index_name=$1 - echo "Creating ElasticSearch Index: ${index_name}" - docker run --rm --network=opencrvs_overlay_net appropriate/curl curl -XPUT "http://$(elasticsearch_host)/$index_name" -v -} - -create_elastic_index "ocrvs" - # run migration by restarting migration service docker service update --force --update-parallelism 1 --update-delay 30s opencrvs_migration From 709c8d81df1eb5bb61fd9167fb8d7a246acfca38 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 29 Jul 2024 13:03:57 +0300 Subject: [PATCH 099/146] Restart and remove existing elastalert indices --- .../elasticsearch/setup-elastalert-indices.sh | 29 +++++++++++++++++ infrastructure/elasticsearch/setup-helpers.sh | 31 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100755 infrastructure/elasticsearch/setup-elastalert-indices.sh diff --git a/infrastructure/elasticsearch/setup-elastalert-indices.sh b/infrastructure/elasticsearch/setup-elastalert-indices.sh new file mode 100755 index 000000000..e53e4ce4a --- /dev/null +++ b/infrastructure/elasticsearch/setup-elastalert-indices.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# 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. + +set -eu +set -o pipefail + +source "$(dirname "${BASH_SOURCE[0]}")/setup-helpers.sh" + +echo "-------- $(date) --------" + +log 'Waiting for availability of Elasticsearch' +wait_for_elasticsearch + + +log 'Scaling down Elastalert' +docker service scale opencrvs_elastalert=0 +log 'Deleting Elastalert indices' +delete_elastalert_indices +log 'Scaling up Elastalert' +docker service scale opencrvs_elastalert=1 + diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index e55e0a4ae..b058a2658 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -231,3 +231,34 @@ function ensure_settings { return $result } +# Upgrading from 7 to 8 requires deleting elastalert indices. https://elastalert2.readthedocs.io/en/latest/recipes/faq.html#does-elastalert-2-support-elasticsearch-8 +# Elastalert depends on kibana/beat indices, so we delete can elastalert indices during each deployment. +function delete_elastalert_indices { + # Opt for explicity over wildcard since we are deleting indices + local indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' + + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' + "http://${elasticsearch_host}:9200/${indices}" + '-X' 'DELETE' + ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + local output + + output="$(curl "${args[@]}")" + if [[ "${output: -3}" -eq 200 ]]; then + result=0 + fi + + if ((result)); then + echo -e "\n${output::-3}\n" + fi + + return $result +} \ No newline at end of file From 1db45683303240c88076855e890fe992f547c376 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 30 Jul 2024 07:47:26 +0300 Subject: [PATCH 100/146] move restart to deploy script --- infrastructure/deployment/deploy.sh | 9 +++++ .../elasticsearch/setup-elastalert-indices.sh | 35 +++++++++++++------ infrastructure/elasticsearch/setup-helpers.sh | 32 ----------------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 5d8725915..ba3eda77d 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -386,6 +386,15 @@ echo echo "Waiting 2 mins for mongo to deploy before working with data. Please note it can take up to 10 minutes for the entire stack to deploy in some scenarios." echo +echo 'Setting up elastalert indices' + +while true; do + if configured_ssh "/opt/opencrvs/infrastructure/elasticsearch/setup-elastalert-indices.sh"; then + break + fi + sleep 5 +done + echo "Setting up Kibana config & alerts" while true; do diff --git a/infrastructure/elasticsearch/setup-elastalert-indices.sh b/infrastructure/elasticsearch/setup-elastalert-indices.sh index e53e4ce4a..699ec03de 100755 --- a/infrastructure/elasticsearch/setup-elastalert-indices.sh +++ b/infrastructure/elasticsearch/setup-elastalert-indices.sh @@ -9,21 +9,36 @@ # # Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. -set -eu -set -o pipefail +# Upgrading from 7 to 8 requires deleting elastalert indices. https://elastalert2.readthedocs.io/en/latest/recipes/faq.html#does-elastalert-2-support-elasticsearch-8 -source "$(dirname "${BASH_SOURCE[0]}")/setup-helpers.sh" +set -e -echo "-------- $(date) --------" +docker_command="docker run --rm --network=opencrvs_overlay_net curlimages/curl" -log 'Waiting for availability of Elasticsearch' -wait_for_elasticsearch +echo 'Waiting for availability of Elasticsearch' +ping_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200") +if [ "$ping_status_code" -ne 200 ]; then + echo "Elasticsearch is not ready. API returned status code: $ping_status_code" + exit 1 +fi + + + +echo 'Scaling down Elastalert' -log 'Scaling down Elastalert' docker service scale opencrvs_elastalert=0 -log 'Deleting Elastalert indices' -delete_elastalert_indices -log 'Scaling up Elastalert' + +echo 'Deleting Elastalert indices' +indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' + +delete_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200/${indices}" -X DELETE) + +if [ "$delete_status_code" -ne 200 ]; then + echo "Could not delete indices. API returned status code: $delete_status_code" + exit 1 +fi + +echo 'Scaling up Elastalert' docker service scale opencrvs_elastalert=1 diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index b058a2658..1b3380f0f 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -230,35 +230,3 @@ function ensure_settings { return $result } - -# Upgrading from 7 to 8 requires deleting elastalert indices. https://elastalert2.readthedocs.io/en/latest/recipes/faq.html#does-elastalert-2-support-elasticsearch-8 -# Elastalert depends on kibana/beat indices, so we delete can elastalert indices during each deployment. -function delete_elastalert_indices { - # Opt for explicity over wildcard since we are deleting indices - local indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' - - local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" - - local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' - "http://${elasticsearch_host}:9200/${indices}" - '-X' 'DELETE' - ) - - if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then - args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) - fi - - local -i result=1 - local output - - output="$(curl "${args[@]}")" - if [[ "${output: -3}" -eq 200 ]]; then - result=0 - fi - - if ((result)); then - echo -e "\n${output::-3}\n" - fi - - return $result -} \ No newline at end of file From 969ff802c4f0fb7cdb80395a6eacea0f8f1a6bb6 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 18 Jul 2024 17:04:00 +0300 Subject: [PATCH 101/146] Include first commit to range, render commits to body --- .github/workflows/auto-pr-to-release.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 0be40b8fb..84e5e2634 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -120,7 +120,8 @@ jobs: echo "First commit: ${{ env.FIRST_COMMIT_SHA }}" echo "Latest commit: ${{ env.LATEST_COMMIT_SHA }}" - COMMIT_RANGE="${{ env.FIRST_COMMIT_SHA }}..${{ env.LATEST_COMMIT_SHA }}" + # NOTE: git cherry-pick range needs ~ to include the FIRST_COMMIT_SHA (https://stackoverflow.com/questions/1994463/how-to-cherry-pick-a-range-of-commits-and-merge-them-into-another-branch) + COMMIT_RANGE="${{ env.FIRST_COMMIT_SHA }}~..${{ env.LATEST_COMMIT_SHA }}" if [ "${{ env.FIRST_COMMIT_SHA }}" == "${{ env.LATEST_COMMIT_SHA }}" ]; then COMMIT_RANGE=${{ env.FIRST_COMMIT_SHA }} @@ -133,6 +134,9 @@ jobs: git cherry-pick --abort || true # If cherry-pick fails, create a placeholder commit echo "Cherry-pick failed. Creating placeholder commit." + + GIT_LOG_ONELINE_OUTPUT=$(git log --oneline --no-decorate $COMMIT_RANGE) + git reset --hard git commit --allow-empty -m "Placeholder commit for PR #${{ env.PR_ID }}" @@ -154,6 +158,12 @@ jobs: git reset --hard HEAD~1 # Remove placeholder commit git cherry-pick $COMMIT_RANGE \`\`\` + + + **Individual commits:** + \`\`\` + $GIT_LOG_ONELINE_OUTPUT + \`\`\` " } From de13f8c7289a250e4f4417b1c7976d3c37b8e66f Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 1 Aug 2024 10:55:19 +0600 Subject: [PATCH 102/146] fix: remove overshadowing rect --- .../certificates/source/Farajaland-birth-certificate-v2.svg | 4 ---- .../certificates/source/Farajaland-death-certificate-v2.svg | 4 ---- .../source/Farajaland-marriage-certificate-v2.svg | 4 ---- 3 files changed, 12 deletions(-) diff --git a/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg index 6d66223f4..7aa67b024 100644 --- a/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg @@ -47,7 +47,6 @@ REPUBLIC OF FARAJALAND / REPUBLIQUE DE FARAJALAND CERTIFICATE OF BIRTH / ACTE DE NAISSANCE - 1. Child’s full name /
Nom complet de l'enfant @@ -129,9 +128,6 @@ - - - diff --git a/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg index 5ed6a20cd..994b425a3 100644 --- a/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg @@ -14,7 +14,6 @@ Registrar / L'Officier de l'État Civil {{registrar.name}} I certify that this certificate is a true copy of the civil registry and is issued by the mandated authority in pursuance of civil registration law / Je certifie que le présent certificat est une copie conforme du registre d'état civil et qu'il est délivré par l'autorité mandatée conformément à la loi sur l'état civil. - 1. Deceased full name /
Nom complet du défunt @@ -114,9 +113,6 @@ REPUBLIC OF FARAJALAND / REPUBLIQUE DE FARAJALAND CERTIFICATE OF DEATH / ACTE DE DEATH - - - diff --git a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg index 3c0dca946..97b1a44f4 100644 --- a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg @@ -36,7 +36,6 @@ REPUBLIC OF FARAJALAND / REPUBLIQUE DE FARAJALAND CERTIFICATE OF MARRIAGE / ACTE DE MARRIAGE - 1. Groom’s full name /
Nom complet du marié @@ -115,9 +114,6 @@ - - - From 283f2d6409de84a26c8a8a5d90752c00c5454579 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 1 Aug 2024 11:00:19 +0600 Subject: [PATCH 103/146] chore: remove unused bg image --- .../certificates/source/Farajaland-marriage-certificate-v2.svg | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg index 97b1a44f4..e84fb604d 100644 --- a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg @@ -163,6 +163,5 @@ {{#ifCond printInAdvance '!==' true}}{{/ifCond}} - From 8b44980b792f173c2e47c555042fd73f70d27aee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:25:04 +0300 Subject: [PATCH 104/146] =?UTF-8?q?=F0=9F=8D=92=20Merge=20changes=20from?= =?UTF-8?q?=20PR=20#229=20to=20release-v1.6.0=20(#230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Riku Rouvila --- CHANGELOG.md | 4 ++++ infrastructure/monitoring/kibana/config.ndjson | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfa5bcef..ef8a0c7f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. - Remove `inputFieldWidth` from Number type form field +### Bug fixes + +- Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) + ## 1.5.0 ### Breaking changes diff --git a/infrastructure/monitoring/kibana/config.ndjson b/infrastructure/monitoring/kibana/config.ndjson index a8f47219d..d8ff821ed 100644 --- a/infrastructure/monitoring/kibana/config.ndjson +++ b/infrastructure/monitoring/kibana/config.ndjson @@ -1,10 +1,10 @@ -{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-17T12:01:46.420Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Available disk space in data partition","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":"<","customMetric":{"aggregation":"min","field":"system.filesystem.available","id":"alert-custom-metric","type":"custom"},"metric":"custom","threshold":[170000000000],"timeSize":1,"timeUnit":"h","warningComparator":"<","warningThreshold":[220000000000]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/vda\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/vda\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:29.567Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"14778650-8541-11ee-9002-2f37fdc4e5d5","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294424438,232754],"type":"alert","updated_at":"2024-02-07T08:27:04.438Z","version":"WzQ5NTQzMSwxOV0="} +{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2024-08-06T07:57:35.644Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-17T12:01:46.420Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-08-06T08:02:44.568Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.18"},"muteAll":false,"mutedInstanceIds":[],"name":"Available disk space in root file system","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">=","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">=","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.mount_point\":\"/hostfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.mount_point : \"/hostfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-08-06T08:01:56.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.18","id":"14778650-8541-11ee-9002-2f37fdc4e5d5","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1722931316554,643],"type":"alert","updated_at":"2024-08-06T08:01:56.554Z","version":"WzM5MywxXQ=="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2022-04-18T07:05:33.819Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-06-01T11:30:27.033Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-05T03:00:20.633Z","updatedBy":"opencrvs-admin"},"coreMigrationVersion":"7.17.0","id":"3b6722e0-e19e-11ec-ba8e-51649755648d","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707273006619,214975],"type":"alert","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MjAzNCwxOV0="} {"attributes":{"buildNum":46534,"defaultIndex":"metricbeat-*"},"coreMigrationVersion":"7.17.0","id":"7.17.0","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1707273006619,216009],"type":"config","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MjQyOCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"logs.threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-22T08:25:47.329Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"logs.alert.document.count","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-22T08:32:38.272Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in backup logs","notifyWhen":"onActionGroupChange","params":{"count":{"comparator":"more than or equals","value":1},"criteria":[{"comparator":"matches","field":"message","value":"error"},{"comparator":"equals","field":"log.file.path","value":"/var/log/opencrvs-backup.log"}],"timeSize":1,"timeUnit":"h"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:22.558Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"b166fcb0-8911-11ee-8111-2f3be9e93efc","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294436464,232766],"type":"alert","updated_at":"2024-02-07T08:27:16.464Z","version":"WzQ5NTQzNCwxOV0="} {"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"query matched","params":{"documents":["{\"@timestamp\":\"2023-11-20T10:19:30.521Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":".es-query","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-20T09:12:19.237Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Successful SSH login","notifyWhen":"onActionGroupChange","params":{"esQuery":"{ \"query\": { \"bool\": { \"must\": [ \n { \"term\": { \"log.file.path\": \"/var/log/auth.log\" } },\n { \"term\": { \"event.outcome\": \"success\" }}\n ] } } }","index":["filebeat-*"],"size":100,"threshold":[1],"thresholdComparator":">=","timeField":"@timestamp","timeWindowSize":1,"timeWindowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:19.537Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"e79aaa90-8784-11ee-b9ba-89bbe73df7ff","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457367,232778],"type":"alert","updated_at":"2024-02-07T08:27:37.367Z","version":"WzQ5NTQ0MCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2022-06-20T06:16:33.414Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.084Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"CPU under heavy load","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":">","customMetric":{"aggregation":"avg","field":"","id":"alert-custom-metric","type":"custom"},"metric":"cpu","threshold":[70],"timeSize":1,"timeUnit":"m"}],"nodeType":"host","sourceId":"default"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:30.573Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f022bee0-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457362,232774],"type":"alert","updated_at":"2024-02-07T08:27:37.362Z","version":"WzQ5NTQzOCwxOV0="} -{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/mapper/cryptfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/mapper/cryptfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} +{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space in data partition","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">=","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">=","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.mount_point\":\"/hostfs/data\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.mount_point : \"/hostfs/data\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2023-06-15T07:57:35.954Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"],"indexOverride":"kibana-alert-history-services"}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.069Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:21.551Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f02b4a60-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457374,232780],"type":"alert","updated_at":"2024-02-07T08:27:37.374Z","version":"WzQ5NTQ0MSwxOV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":9,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file From 27d8212e3ca9fad8b60cde782fc937c5ac46a326 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 14 Aug 2024 13:15:20 +0300 Subject: [PATCH 105/146] ignore unavailable indices upon delete --- infrastructure/elasticsearch/setup-elastalert-indices.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/elasticsearch/setup-elastalert-indices.sh b/infrastructure/elasticsearch/setup-elastalert-indices.sh index 699ec03de..d36fd8d10 100755 --- a/infrastructure/elasticsearch/setup-elastalert-indices.sh +++ b/infrastructure/elasticsearch/setup-elastalert-indices.sh @@ -32,7 +32,7 @@ docker service scale opencrvs_elastalert=0 echo 'Deleting Elastalert indices' indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' -delete_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200/${indices}" -X DELETE) +delete_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200/${indices}?ignore_unavailable=true" -X DELETE) if [ "$delete_status_code" -ne 200 ]; then echo "Could not delete indices. API returned status code: $delete_status_code" From e80f9cb0a738a303fa9d53792c15bae457e24cfd Mon Sep 17 00:00:00 2001 From: tareq89 Date: Mon, 19 Aug 2024 13:43:20 +0600 Subject: [PATCH 106/146] Serve client manifest and icons from country-config --- CHANGELOG.md | 1 + .../images/icons/icon-128x128.png | Bin 0 -> 4533 bytes .../images/icons/icon-144x144.png | Bin 0 -> 5309 bytes .../images/icons/icon-152x152.png | Bin 0 -> 5683 bytes .../images/icons/icon-196x196.png | Bin 0 -> 7618 bytes .../images/icons/icon-256x256.png | Bin 0 -> 10779 bytes .../images/icons/icon-512x512.png | Bin 0 -> 26617 bytes src/client-static/images/icons/icon-72x72.png | Bin 0 -> 2039 bytes src/client-static/images/logo-90x90.svg | 22 ++++++++ src/client-static/manifest.json | 47 ++++++++++++++++++ src/index.ts | 18 +++++++ 11 files changed, 88 insertions(+) create mode 100644 src/client-static/images/icons/icon-128x128.png create mode 100644 src/client-static/images/icons/icon-144x144.png create mode 100644 src/client-static/images/icons/icon-152x152.png create mode 100644 src/client-static/images/icons/icon-196x196.png create mode 100644 src/client-static/images/icons/icon-256x256.png create mode 100644 src/client-static/images/icons/icon-512x512.png create mode 100644 src/client-static/images/icons/icon-72x72.png create mode 100644 src/client-static/images/logo-90x90.svg create mode 100644 src/client-static/manifest.json diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8a0c7f0..d11c156ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. - Remove `inputFieldWidth` from Number type form field +- You can now configure the home screen application’s name and icons in your country configuration package as manifest.json and app icon files are moved from core to country config (check `src/client-static` folder) ### Bug fixes diff --git a/src/client-static/images/icons/icon-128x128.png b/src/client-static/images/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..fbf19dc54e94521e13f53057f6bb7d7b2cf7e1c1 GIT binary patch literal 4533 zcmZvgcQo6N*T+9e#7eE!-bHFv?Ny^O`Xy#-&xF!aiV&+tt#8p9^;9To&(>D8iS-)i(J%!~av%lz)4d zy5qp#0m7Q-XabeP-0J|qs;Q%uM@3PgBUrpT zS~eW@C7vy=vsawEl`T=Z;NKWw!@MkokT;R=CyD1QgJHCH{k@mJPqfkblCDJscg0>3 zab3h(D#Ys1HWrk}EM)f(d4oh z^S^mZD71*}-fVqW_4Lk>jUsB`)9C6W9}k`%&jR?>Fo1z7>#g|k?JSvQL+M`hfdt+n zoH}sp`bm$>$;j8yx&pz%JJJo_Gp}RKTj}U|0psC+k36zW(&Q-R?uZ7KDdKmgtDAa3 zb>687y=8ot_R9;+bm|j)#cv|9I?QNJ=KEmweRLpKn;Jv*sp7%fHJ({zkLDsfxU>y^ zwC(J`U@GAFr!MGy*BHTZ&~9zA&*QO#JtZY5ubIC0TEs?gNA5z;Z2xttaHj3VcBS9? z-#4bGnfM7p@)e)`Y>BBfm`RMVTk<5t;P~YGqm#bS>fnaKs~is%8GcuKNI=wLCQOjw zHa+!qXt*?aMu+(6rIFL5UcHlvLg(}- zolg~&Xi_C(3mhtEHT2TIT!PtywLb~}s|`3%wCr1_1;REb;Z959|DFc@V}kIHK;}d> zEhpP6kD1O`bgU^?Z<37Ou6w;ifh$>K*A9pcjwfw>5qK3~Gwo=v1=Bf^sQoD0`kWS? zp(flZ@t#)anqIW)t-kH&py9UZh^EmAzN{8kffdXMsygKE!A5qx**+~$yQJTp6-cMM zK{!>UkK{;l4ps4yQdR8{ja09AM)}10N*!C2jQC{Q4SkY8E|8GAa&6pF6*d=o8iGQB z9P-H-Iu2*^)jAA}>_5L0YQOkQbBC7(+pGLgEPp+oclEKsB&kLbUr?nvl4cR+gsgr6 zo5M22E6=r??Nb6wdIpTx#0)4`!`*~?gaVZMV6D^Au~Ec;@lg47$K?4pMe> zn?3g4(TXfv!t((Ib{WRh5nVnnwIJqJ5I**3{G7PgbDEXBe=}`fytAS#nxnvBa(%l> z^yw%qLuS)+c*e?`G+(O0uwmuk!Qbd_R<&vcB-<9~_7|Ydrw}blgaZEB$BjA^ zMR+wScefUC%zpv!7UWjRyT;R=_RFRhKGtd*WqD!7X-D_dFt>g6TM`b-dna)u{1xlr zXrJQlzzSyB$w-UfJ)*c@us9%kECU!e@jdo}Do{i zW#yttGH`2~OWf=`_rFSMY+qFdJD)ErW3F8MnSeC+kkU9^}s~RJDfM z&Ky@t7csZ{a%;@ikLFdxN&mR)F#|lZLBLBN)yqt4@@;n0j^fK;JgRlx3!3Y;B2GZ? zwwZ4%btUB9&?3;Mi!448eF*}fVE($&S^Sfg8=K);RGn*Gb|R-8!Ob2YKW_Ui$c!HDYZtHl(pMv8EePfIT0w&*JsiQSK~!=xBN%}s2*7+*M4b_O-l=7p=}!{e zHR*Szr6_aw;Wu1D6z>!jiBOh8HKdu`f^UESIE>;Y^n1BcQ%3ekHYrg-8_!e)C~1oZe6#=jEy|XF7Kn77; z%%F7U@#vLjK3^Hy2&iU3GYD2iuP*xRLcKYo{}Q0^VImtZiB{`G>ijntn0GQE;x?9Q z2=;kpN?F<iwXh!wF03%6gGvMCytJ=Z{{X-zUa+|9c@vXP+%Za0t$cd z+m$0~;ao{1LNxc367i7-Le5sFwDI@OoW$osoTVw$WT1&NC9%=DVrlK*Hr8tz97d<9 zz}YQXDB1aTNP}*%!~0ceL#M~c2{MA=I)9w3?#%^b)(#G58n_Q-A2qHtj5$*y(H5x- zguZEtBBfKH^>Q;TnB%_xagp7o*(Q>$OOPPMEl2nZ?z#pT)p&|CBKECUMh35T&pPw(Oyc|IKgwD?6br?gK@Iq$z8iup8bMd7lYQt{q=It*u(eNhd6rtq0 zmK(7;V&`k$9gEz$`vP0@H>HI}%&o5hY3jK@sxQwvyo8&6?p`FNcUx0>UfZ3(5@c*xc&g6CMjF>_MT=?AaA@#F-5hbO%&3cwhqkXHuoVvj*9M^3I9*~z%>>^ z<6e#!faZb>{Di6Eo;0?gvQ_0pci(aj)dE>1^H%s4!=sM zIzX3>Rk~f&=2t<%5+ZohR=N~YNwe1c*o0=>Uzp$O!tmZ8K{J*IcoIgqT9Nf@hD(1S zr8XFW1Z6HV)vmS4b(_-+`G2fhS{##bVr>JwDqm(p6`F>R>?J}yz28`7g2r`DvDjcJ zzI6=ZxtFZ6$i1trjeCwBhe4^cw zFoo(}%CZ;!tAwSpmPgCFhP@e56<6$q*NXqF;gB0vb)EYY2pJo)ZfoSP7p##msJDVz z_j`(1y$JVr_|MSn44snMa-+Ko(3mq%UWt#GnGwf6_evJ~Ih1DYmBg1R(aN^*#a1xh z?GI9r+$movjLzIzmXNd4QJH}0m4;sYp{u+dZ_`>96lfQbH%W6@?uc<${`{t)0ugVL zw{oL$nBDQGK!)5((fHiy!m*olt0rQcX}A1+7IUnirmi?Tx)jvvaO%58m`}-yD}w}K zZ6hpc7}bKFMg|m#yS;LqERdY#otRtwEh_sxhhm@Ww<*IPaW$J14Kwz9(~ThN*uEJ( zIV$-$3n5dJeIy0&J*B|t(fGE%9u65BqQO|vd-%g(svRv;RwSLvXGc@ANDP9pi9sox zkF$%2xrcoaqBaO8$@osXrZI0ia@Oxmtk)Pt{%k!f}X;6|A$fv*s z`w##9rkw7Gm`co7bXm&z&QiNazm1~8C(GKGx23os9{_TVPyB>%O~F{Y52wyfJO6N5 zl!fKJP_LsJRH7N?S^6gv*GXUwy>1GAf94a%GwM<{*)e2{k7Ub)S=+HNnuW@`4V$OF z$#?z%!F-4JSYJM6;eVW!3|4%``lU``+;vQypiomm?}pQv>U}P_lAehvS5eQ%>OOv0 zRrK=lr-O<*NB=bm?qVMj=)&_s^TVT) ziL=A}r9eR2DF8_J=Q&`AhO`P-XSiRuzI62|``L86|(c|7C&Tf5HVz$g`KGr3NK@LWp!31;SG(@JsKbOsgc2Yrye- z<1-lWDKbl6U;`c$#}E10fPf~GQUekU5HaojeYXM#w-YrlK86A~Q73Gc1%Rl;iH)Cw zfqq;~lact}<$5XWp&;OK|CC(`3y>sU)PK+hgv&gBRyj@!d`mHT9JCk)3QI0vlU6vJ R`^}Pd8OpTmBgcX`rSA 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 H*@pffTV~CP literal 0 HcmV?d00001 diff --git a/src/client-static/images/icons/icon-152x152.png b/src/client-static/images/icons/icon-152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..4f27e38bd31234780337ed8cdb5451c0991a26f1 GIT binary patch literal 5683 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 WRT{t!IUky`01Xvg~gkk(eQs5}QBLD#5mmmNR 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 literal 0 HcmV?d00001 diff --git a/src/client-static/images/icons/icon-512x512.png b/src/client-static/images/icons/icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..4625da3fc6cc7cd52968d54395add81a83764f51 GIT binary patch literal 26617 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�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 V;dDU2D_j5o002ovPDHLkV1ks#zIp%v literal 0 HcmV?d00001 diff --git a/src/client-static/images/logo-90x90.svg b/src/client-static/images/logo-90x90.svg new file mode 100644 index 000000000..7e24a831b --- /dev/null +++ b/src/client-static/images/logo-90x90.svg @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/src/client-static/manifest.json b/src/client-static/manifest.json new file mode 100644 index 000000000..195d48c73 --- /dev/null +++ b/src/client-static/manifest.json @@ -0,0 +1,47 @@ +{ + "name": "OpenCRVS", + "short_name": "OpenCRVS", + "theme_color": "#4c68c1", + "background_color": "#4c68c1", + "display": "standalone", + "scope": "/", + "start_url": "/", + "icons": [ + { + "src": "images/icons/icon-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "images/icons/icon-128x128.png", + "sizes": "128x128", + "type": "image/png" + }, + { + "src": "images/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "images/icons/icon-152x152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "images/icons/icon-196x196.png", + "sizes": "196x196", + "type": "image/png" + }, + { + "src": "images/icons/icon-256x256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "images/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "permissions": ["unlimitedStorage"] +} diff --git a/src/index.ts b/src/index.ts index c7ea2459b..8c4edee3c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ require('app-module-path').addPath(require('path').join(__dirname)) require('dotenv').config() import fetch from 'node-fetch' +import path from 'path' import * as Hapi from '@hapi/hapi' import * as Pino from 'hapi-pino' import * as JWT from 'hapi-auth-jwt2' @@ -529,6 +530,23 @@ export async function createServer() { } }) + server.route({ + method: 'GET', + path: '/static/{param*}', + handler: { + directory: { + path: path.join(__dirname, 'client-static'), + redirectToSlash: true, + index: false + } + }, + options: { + auth: false, + tags: ['api', 'static'], + description: 'Server static files for client' + } + }) + server.ext({ type: 'onRequest', method(request: Hapi.Request & { sentryScope?: any }, h) { From 23d1c5ed92076d24339c9a815753e03776993d76 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 21 Aug 2024 15:11:09 +0300 Subject: [PATCH 107/146] explicitly specify from which branch the release tag should be created from --- .github/workflows/publish-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 3ffcabf0c..25fbb111d 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -7,6 +7,7 @@ on: description: Branch to build from default: master required: true + release_version: description: Release tag. It will be prepended by your repository name required: true @@ -45,6 +46,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} tag_prefix: ${{ github.event.repository.name }}- custom_tag: ${{ env.TAG }} + release_branches: ${{ github.event.inputs.branch_name }} - name: Login to DockerHub uses: docker/login-action@v3 From d5b03bf0d363c1c2fae19dd90ec7ddf08f4a376a Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 21 Aug 2024 15:26:22 +0300 Subject: [PATCH 108/146] use release version instead of package.json version --- .github/workflows/publish-release.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 25fbb111d..4b12bc56c 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -9,7 +9,7 @@ on: required: true release_version: - description: Release tag. It will be prepended by your repository name + description: Release tag. It will be prepended by your repository name (e.g. 1.5.3 or 20240501) required: true jobs: @@ -23,12 +23,8 @@ jobs: - name: Install jq run: sudo apt-get install jq -y - - name: Read version from package.json - id: get_version - run: echo "::set-output name=version::$(jq -r '.version' package.json)" - - name: Set TAG environment variable - run: echo "TAG=v${{ steps.get_version.outputs.version }}" >> $GITHUB_ENV + run: echo "TAG=v${{ github.event.inputs.release_version }}" >> $GITHUB_ENV - uses: trstringer/manual-approval@v1 with: From 0db8684285f7de7b37d9354f3342d5837eb458d5 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 21 Aug 2024 16:26:15 +0300 Subject: [PATCH 109/146] add descriptive merge messages --- .github/workflows/publish-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 4b12bc56c..e472ac4ab 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -72,6 +72,7 @@ jobs: type: now from_branch: ${{ github.event.inputs.branch_name }} target_branch: master + message: Merge ${{ github.event.inputs.branch_name }} to master github_token: ${{ secrets.GITHUB_TOKEN }} - name: Merge master -> develop @@ -80,4 +81,5 @@ jobs: type: now from_branch: master target_branch: develop + message: Merge master back to develop github_token: ${{ secrets.GITHUB_TOKEN }} From 54f6922cf7e67b6ae64ac0f51c2ab8a55735fc44 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 21 Aug 2024 16:26:54 +0300 Subject: [PATCH 110/146] remove the v-prefix from versions --- .github/workflows/publish-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index e472ac4ab..d03dd8251 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -9,7 +9,7 @@ on: required: true release_version: - description: Release tag. It will be prepended by your repository name (e.g. 1.5.3 or 20240501) + description: Release tag. It will be prepended by your repository name (e.g. v1.5.3 or 20240501) required: true jobs: @@ -24,7 +24,7 @@ jobs: run: sudo apt-get install jq -y - name: Set TAG environment variable - run: echo "TAG=v${{ github.event.inputs.release_version }}" >> $GITHUB_ENV + run: echo "TAG=${{ github.event.inputs.release_version }}" >> $GITHUB_ENV - uses: trstringer/manual-approval@v1 with: From 68d6c4d388952cd86a52e524d77cf4c72f159aef Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 21 Aug 2024 16:57:37 +0300 Subject: [PATCH 111/146] update CHANGELOG --- CHANGELOG.md | 74 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8a0c7f0..188d86582 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,41 +11,75 @@ ### Bug fixes - Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) +- "Publish release" pipeline now correctly uses the "Branch to build from" value as the branch to be tagged. Previously it tried tagging "master". "Release tag" is also now used as the release version as is instead of it being read from `package.json`. -## 1.5.0 +## 1.5.0 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.1...v1.5.0) ### Breaking changes -- #### Update the certificate preview mechanism +- **Removed dependency on OpenHIM.** - In effort of minimizing JavaScript-bundle size, we have streamlined the way how review certificate -page renders certificates. In case the images in your certificates are previewing blurry, you need to update your SVG-certificates to print QR-codes and other images directly with `` instead of the more complicated `` -paradigm. This doesn't affect printed certificates as they are still created as previously. + The performance of OpenHIM added an unexpected burden of 200 m/s to every interaction. Cumulatively, this was negatively affecting user experience and therefore we decided to deprecate it. -- #### Application config items `DATE_OF_BIRTH_UNKNOWN` and `INFORMANT_SIGNATURE_REQUIRED` moved under `FEATURES` + Interested implementers are free to re-introduce OpenHIM should they wish to use it as an interoperability layer without affecting the performance of OpenCRVS now that our architecture no longer depends on it. - See https://github.com/opencrvs/opencrvs-farajaland/pull/1005 for details + The OpenHIM database is kept for backwards compatibility reasons and will be removed in v1.6. [OpenHIM](https://openhim.org/) is an Open Source middleware component designed for managing FHIR interoperability between disparate systems as part of the OpenHIE architectural specification. We had been using this component in a much more fundamental way to monitor microservice comms in a similar fashion to Amazon SQS. -### Infrastructure +- **Upgrade node version to 18** -- Allow using staging to both period restore of production backup and also for backing up its own data to a different location using `backup_server_remote_target_directory` and `backup_server_remote_source_directory` ansible variables. This use case is mostly meant for OpenCRVS team internal use. + This version enforces environment to have Node 18 installed (supported until April 2025) and removes support for Node 16 -- Automate SSH key exchange between application and backup server. For staging servers, automatically fetch production backup encryption key if periodic restore is enabled + - Use nvm to upgrade your local development environment to use node version `18.19.x.` + - Specified operating systems in js modules as `darwin, linux` + - Dev scripts and Vite run with an environment variable `NODE_OPTIONS=--dns-result-order=ipv4first` to resolve ipv4 addresses for `localhost` to support systems that resolves ipv6 addresses by default in Node versions >=17 -- Improved support for non-22 SSH port +- **Update the certificate preview mechanism** In effort of minimizing JavaScript-bundle size, we have streamlined the way how review certificate -page renders certificates. In case the images in your certificates are previewing blurry, you need to update your SVG-certificates to print QR-codes and other images directly with `` instead of the more complicated `` -paradigm. This doesn't affect printed certificates as they are still created as previously. +- **Generate default address according to logged-in user's location** We have dropped support for the 'agentDefault' prop which was used as initial value for SELECT_WITH_DYNAMIC_OPTIONS fields. If you have not made any changes to address generation, then this should not affect you. If you have, you can refer to this PR to see how agentDefault has been deprecated in an example country: [https://github.com/opencrvs/opencrvs-farajaland/pull/978](https://github.com/opencrvs/opencrvs-farajaland/pull/978) +- **Remove system admin UI items: Application, User roles** We have now moved to configuring these items away from the UI in favour of directly editing these from country configuration repository in code - specifically in application-config-default.ts. +- **Set Metabase default credentials.** These must be configured via countryconfig repository environment variables and secrets otherwise the dashboard service won't start +- **Check your Metabase map file.** For Metabase configuration, we renamed `farajaland-map.geojson` to `map.geojson` to not tie implementations into example country naming conventions. +- **Feature flags** In order to make application config settings more readable, we re-organised `src/api/application/application-config-default.ts` with a clear feature flag block like so. These are then used across the front and back end of the application to control configurable functionality. New feature flags DEATH_REGISTRATION allow you to optionally run off death registration if your country doesnt want to run its first pilot including death and PRINT_DECLARATION (see New Features) have been added. + `FEATURES: { + DEATH_REGISTRATION: true, + MARRIAGE_REGISTRATION: false, + ... +} ` +- **Improve rendering of addresses in review page where addresses match** When entering father's address details, some countries make use of a checkbox which says "Address is the same as the mothers. " which, when selected, makes the mother's address and fathers address the same. The checkbox has a programatic value of "Yes" or "No". As a result on the review page, the value "Yes" was displayed which didn't make grammatical sense as a response. We decided to use a custom label: "Same as mother's", which is what was asked on the form. This requires some code changes in the src/form/addresses/index.ts file to pull in the `hideInPreview` prop which will hide the value "Yes" on the review page and replace with a content managed label. Associated bug [#5086](https://github.com/opencrvs/opencrvs-core/issues/5086) -- Treat backup host identically to other hosts. To migrate: +### Infrastructure breaking changes - 1. Move all inventory files (qa.yml, production.yml...) from `infrastructure/server-setup` to `infrastructure/server-setup/inventory` - 2. Run environment creator for your backup server `yarn environment:init --environment=backup` +More improvements have been made to the infrastructure provisioning and Github environment creation scripts and documentation. The complexity is somewhat reduced. -### Other changes +- **We removed the example Wireguard VPN set up as it was confusing.** Our intention was to ensure that all implementers were aware that OpenCRVS should be installed behind a VPN and used Wireguard as an example. But the configuration requirements for Wireguard confused implementers who are not using it. Therefore we decided to remove Wireguard as an example. +- **We now have a "backup" Github environment and the backup server is automatically provisioned.** We moved the inventory file location to an explicit directory and removed parameters to scripts that can be automated. To migrate, move all inventory files (qa.yml, production.yml, staging.yml from `infrastructure/server-setup` to `infrastructure/server-setup/inventory` and configure `infrastructure/server-setup/inventory/backup.yml`. Run environment creator for your backup server `yarn environment:init --environment=backup` +- **You can configure the file path on the backup server where backups are stored.** We can also allow using staging to both periodically restore a production backup and also give it the capability if required to backup it's own data to a different location using `backup_server_remote_target_directory` and `backup_server_remote_source_directory` Ansible variables. This use case is mostly meant for OpenCRVS team internal use. +- **We now automate SSH key exchange between application and backup server.** For staging servers, automatically fetch production backup encryption key if periodic restore is enabled using `ansible_ssh_private_key_file` Ansible variables. Therefore documentation is simplified for a new server set-up. +- **In infrastructure Github workflows: SSH_PORT is new and required allowing you the ability to use a non-standard SSH port.** This Github Action environment variable must be added. +- **In infrastructure Github workflows: SSH_HOST** should be moved from being a Github Action environment secret to a Github Action environment variable before it is deprecated in 1.7.0 +- **No longer an assumption made that production server Docker replicas and Mongo replica-sets are necessary.** In our Docker Compose files, we had originally assumed that a production deployment would always be deployed on a cluster to enable load balancing. We applied a [Mongo replica set](https://github.com/opencrvs/opencrvs-countryconfig/blob/48cf278bab9d17e07b60b427294a26c8f35bcc1b/infrastructure/docker-compose.production-deploy.yml#L170C3-L201C19) by default on production and set [replicas: 2](https://github.com/opencrvs/opencrvs-countryconfig/blob/48cf278bab9d17e07b60b427294a26c8f35bcc1b/infrastructure/docker-compose.production-deploy.yml#L124) on each microservice. However after experience in multiple countries running small scale pilots, a production deployment usually starts off as 1 server node and then scales into a cluster over time in order to save costs and resources. Therefore these replicas are a waste of resources. So you will notice that this has been deleted. You can always manually add your desired replicas back into you Docker Compose configuration if you want. In Docker Compose files, search for REPLICAS and update accordingly as well as attending to the linked examples. -- Upgrade Node.js to 18 -- Remove dependency OpenHIM. The OpenHIM database is kept for backwards compatibility reasons and will be removed in v1.6 -- Change auth URLs to access them via gateway -- Add hearth URL to search service -- Include an endpoint for serving individual certificates in development mode -- Include compositionId in confirm registration payload -- Remove logrocket refrences +Follow the descriptions in the migration notes to re-provision all servers safely. + +### New features + +- Introduced rate limiting to routes that could potentially be bruteforced or extracted PII from. +- The login and client application loading experience has improved. A loading bar appears before the javaScript bundle has loaded and this transitions when fetching records. +- Development time logs are now much tidier and errors easier to point out. Production logging will still remain as is. +- Masked emails and phone numbers from notification logs. +- Support for landscape certificate templates. +- Allow defining maxLength attribute for number type fields. +- A new certificate handlebar for registration fees has been added `registrationFees` +- A new certificate handlebar for logged-in user details has been added `loggedInUser` +- Add support for image compression configuration. Two new properties to this form field are available: `DOCUMENT_UPLOADER_WITH_OPTION` + - `compressImagesToSizeMB` : An optional prop of number type to define a compressed size. Compression is ignored when the input file is already smaller or equal of the given value or a falsy given value. + - `maxSizeMB`: An optional validation prop to prevent input of a file bigger than a defined value. +- If a country doesnt wish to use Sentry for logging errors, the SENTRY_DSN variable is now optional and the LogRocket option has been deprecated due to lack of demand. +- Given that upon an upgrade between versions of OpenCRVS, that users cache is cleared, it is important to inform staff to submit any draft applications before the upgrade date. We introduced an "Email all users" feature so that National System Admins can send all staff messages. This feature can be used for any other all staff comms that are deemed required. + +

+ +- Included an endpoint for serving individual certificates in development mode. This improves the developer experience when configuring certificates. +- Removed logrocket refrences. - Enable gzip compression in client & login - Make SENTRY_DSN variable optional - Use docker compose v2 in github workflows From 5b19a8f5234a3e97cb80b837aafdbe9e993b7a7d Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 21 Aug 2024 17:35:46 +0300 Subject: [PATCH 112/146] revert 1.5.0 CHANGELOG changes --- CHANGELOG.md | 73 ++++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 188d86582..33d932f07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,73 +13,40 @@ - Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) - "Publish release" pipeline now correctly uses the "Branch to build from" value as the branch to be tagged. Previously it tried tagging "master". "Release tag" is also now used as the release version as is instead of it being read from `package.json`. -## 1.5.0 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.1...v1.5.0) +## 1.5.0 ### Breaking changes -- **Removed dependency on OpenHIM.** +- #### Update the certificate preview mechanism - The performance of OpenHIM added an unexpected burden of 200 m/s to every interaction. Cumulatively, this was negatively affecting user experience and therefore we decided to deprecate it. + In effort of minimizing JavaScript-bundle size, we have streamlined the way how review certificate -page renders certificates. In case the images in your certificates are previewing blurry, you need to update your SVG-certificates to print QR-codes and other images directly with `` instead of the more complicated `` -paradigm. This doesn't affect printed certificates as they are still created as previously. - Interested implementers are free to re-introduce OpenHIM should they wish to use it as an interoperability layer without affecting the performance of OpenCRVS now that our architecture no longer depends on it. +- #### Application config items `DATE_OF_BIRTH_UNKNOWN` and `INFORMANT_SIGNATURE_REQUIRED` moved under `FEATURES` - The OpenHIM database is kept for backwards compatibility reasons and will be removed in v1.6. [OpenHIM](https://openhim.org/) is an Open Source middleware component designed for managing FHIR interoperability between disparate systems as part of the OpenHIE architectural specification. We had been using this component in a much more fundamental way to monitor microservice comms in a similar fashion to Amazon SQS. + See https://github.com/opencrvs/opencrvs-farajaland/pull/1005 for details -- **Upgrade node version to 18** +### Infrastructure - This version enforces environment to have Node 18 installed (supported until April 2025) and removes support for Node 16 +- Allow using staging to both period restore of production backup and also for backing up its own data to a different location using `backup_server_remote_target_directory` and `backup_server_remote_source_directory` ansible variables. This use case is mostly meant for OpenCRVS team internal use. - - Use nvm to upgrade your local development environment to use node version `18.19.x.` - - Specified operating systems in js modules as `darwin, linux` - - Dev scripts and Vite run with an environment variable `NODE_OPTIONS=--dns-result-order=ipv4first` to resolve ipv4 addresses for `localhost` to support systems that resolves ipv6 addresses by default in Node versions >=17 +- Automate SSH key exchange between application and backup server. For staging servers, automatically fetch production backup encryption key if periodic restore is enabled -- **Update the certificate preview mechanism** In effort of minimizing JavaScript-bundle size, we have streamlined the way how review certificate -page renders certificates. In case the images in your certificates are previewing blurry, you need to update your SVG-certificates to print QR-codes and other images directly with `` instead of the more complicated `` -paradigm. This doesn't affect printed certificates as they are still created as previously. -- **Generate default address according to logged-in user's location** We have dropped support for the 'agentDefault' prop which was used as initial value for SELECT_WITH_DYNAMIC_OPTIONS fields. If you have not made any changes to address generation, then this should not affect you. If you have, you can refer to this PR to see how agentDefault has been deprecated in an example country: [https://github.com/opencrvs/opencrvs-farajaland/pull/978](https://github.com/opencrvs/opencrvs-farajaland/pull/978) -- **Remove system admin UI items: Application, User roles** We have now moved to configuring these items away from the UI in favour of directly editing these from country configuration repository in code - specifically in application-config-default.ts. -- **Set Metabase default credentials.** These must be configured via countryconfig repository environment variables and secrets otherwise the dashboard service won't start -- **Check your Metabase map file.** For Metabase configuration, we renamed `farajaland-map.geojson` to `map.geojson` to not tie implementations into example country naming conventions. -- **Feature flags** In order to make application config settings more readable, we re-organised `src/api/application/application-config-default.ts` with a clear feature flag block like so. These are then used across the front and back end of the application to control configurable functionality. New feature flags DEATH_REGISTRATION allow you to optionally run off death registration if your country doesnt want to run its first pilot including death and PRINT_DECLARATION (see New Features) have been added. - `FEATURES: { - DEATH_REGISTRATION: true, - MARRIAGE_REGISTRATION: false, - ... -} ` -- **Improve rendering of addresses in review page where addresses match** When entering father's address details, some countries make use of a checkbox which says "Address is the same as the mothers. " which, when selected, makes the mother's address and fathers address the same. The checkbox has a programatic value of "Yes" or "No". As a result on the review page, the value "Yes" was displayed which didn't make grammatical sense as a response. We decided to use a custom label: "Same as mother's", which is what was asked on the form. This requires some code changes in the src/form/addresses/index.ts file to pull in the `hideInPreview` prop which will hide the value "Yes" on the review page and replace with a content managed label. Associated bug [#5086](https://github.com/opencrvs/opencrvs-core/issues/5086) +- Improved support for non-22 SSH port -### Infrastructure breaking changes +- Treat backup host identically to other hosts. To migrate: -More improvements have been made to the infrastructure provisioning and Github environment creation scripts and documentation. The complexity is somewhat reduced. + 1. Move all inventory files (qa.yml, production.yml...) from `infrastructure/server-setup` to `infrastructure/server-setup/inventory` + 2. Run environment creator for your backup server `yarn environment:init --environment=backup` -- **We removed the example Wireguard VPN set up as it was confusing.** Our intention was to ensure that all implementers were aware that OpenCRVS should be installed behind a VPN and used Wireguard as an example. But the configuration requirements for Wireguard confused implementers who are not using it. Therefore we decided to remove Wireguard as an example. -- **We now have a "backup" Github environment and the backup server is automatically provisioned.** We moved the inventory file location to an explicit directory and removed parameters to scripts that can be automated. To migrate, move all inventory files (qa.yml, production.yml, staging.yml from `infrastructure/server-setup` to `infrastructure/server-setup/inventory` and configure `infrastructure/server-setup/inventory/backup.yml`. Run environment creator for your backup server `yarn environment:init --environment=backup` -- **You can configure the file path on the backup server where backups are stored.** We can also allow using staging to both periodically restore a production backup and also give it the capability if required to backup it's own data to a different location using `backup_server_remote_target_directory` and `backup_server_remote_source_directory` Ansible variables. This use case is mostly meant for OpenCRVS team internal use. -- **We now automate SSH key exchange between application and backup server.** For staging servers, automatically fetch production backup encryption key if periodic restore is enabled using `ansible_ssh_private_key_file` Ansible variables. Therefore documentation is simplified for a new server set-up. -- **In infrastructure Github workflows: SSH_PORT is new and required allowing you the ability to use a non-standard SSH port.** This Github Action environment variable must be added. -- **In infrastructure Github workflows: SSH_HOST** should be moved from being a Github Action environment secret to a Github Action environment variable before it is deprecated in 1.7.0 -- **No longer an assumption made that production server Docker replicas and Mongo replica-sets are necessary.** In our Docker Compose files, we had originally assumed that a production deployment would always be deployed on a cluster to enable load balancing. We applied a [Mongo replica set](https://github.com/opencrvs/opencrvs-countryconfig/blob/48cf278bab9d17e07b60b427294a26c8f35bcc1b/infrastructure/docker-compose.production-deploy.yml#L170C3-L201C19) by default on production and set [replicas: 2](https://github.com/opencrvs/opencrvs-countryconfig/blob/48cf278bab9d17e07b60b427294a26c8f35bcc1b/infrastructure/docker-compose.production-deploy.yml#L124) on each microservice. However after experience in multiple countries running small scale pilots, a production deployment usually starts off as 1 server node and then scales into a cluster over time in order to save costs and resources. Therefore these replicas are a waste of resources. So you will notice that this has been deleted. You can always manually add your desired replicas back into you Docker Compose configuration if you want. In Docker Compose files, search for REPLICAS and update accordingly as well as attending to the linked examples. +### Other changes -Follow the descriptions in the migration notes to re-provision all servers safely. - -### New features - -- Introduced rate limiting to routes that could potentially be bruteforced or extracted PII from. -- The login and client application loading experience has improved. A loading bar appears before the javaScript bundle has loaded and this transitions when fetching records. -- Development time logs are now much tidier and errors easier to point out. Production logging will still remain as is. -- Masked emails and phone numbers from notification logs. -- Support for landscape certificate templates. -- Allow defining maxLength attribute for number type fields. -- A new certificate handlebar for registration fees has been added `registrationFees` -- A new certificate handlebar for logged-in user details has been added `loggedInUser` -- Add support for image compression configuration. Two new properties to this form field are available: `DOCUMENT_UPLOADER_WITH_OPTION` - - `compressImagesToSizeMB` : An optional prop of number type to define a compressed size. Compression is ignored when the input file is already smaller or equal of the given value or a falsy given value. - - `maxSizeMB`: An optional validation prop to prevent input of a file bigger than a defined value. -- If a country doesnt wish to use Sentry for logging errors, the SENTRY_DSN variable is now optional and the LogRocket option has been deprecated due to lack of demand. -- Given that upon an upgrade between versions of OpenCRVS, that users cache is cleared, it is important to inform staff to submit any draft applications before the upgrade date. We introduced an "Email all users" feature so that National System Admins can send all staff messages. This feature can be used for any other all staff comms that are deemed required. - -
- -- Included an endpoint for serving individual certificates in development mode. This improves the developer experience when configuring certificates. -- Removed logrocket refrences. +- Upgrade Node.js to 18 +- Remove dependency OpenHIM. The OpenHIM database is kept for backwards compatibility reasons and will be removed in v1.6 +- Change auth URLs to access them via gateway +- Add hearth URL to search service +- Include an endpoint for serving individual certificates in development mode +- Include compositionId in confirm registration payload +- Remove logrocket refrences - Enable gzip compression in client & login - Make SENTRY_DSN variable optional - Use docker compose v2 in github workflows From e586522214d2c5cce02b4f336f47ab13aadc7936 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:59:41 +0300 Subject: [PATCH 113/146] =?UTF-8?q?=F0=9F=8D=92=20Merge=20changes=20from?= =?UTF-8?q?=20PR=20#247=20to=20release-v1.6.0=20(#249)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Riku Rouvila --- .github/workflows/get-secret-from-environment.yml | 11 +++++++++++ CHANGELOG.md | 3 +-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/get-secret-from-environment.yml b/.github/workflows/get-secret-from-environment.yml index 79fe4c73a..d495fe5b1 100644 --- a/.github/workflows/get-secret-from-environment.yml +++ b/.github/workflows/get-secret-from-environment.yml @@ -43,6 +43,17 @@ jobs: exit 1 fi + - name: Verify GitHub token validity + id: verify-token + run: | + RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${{ secrets.gh_token }}" \ + "https://api.github.com/user") + if [ "$RESPONSE" -ne 200 ]; then + echo "Invalid or expired GitHub token." + exit 1 + fi + echo "GitHub token is valid." + - name: Check if environment exists id: check-env run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8a0c7f0..2d47dbb24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,7 @@ ### Bug fixes - Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) - -## 1.5.0 +- Environment creator script now requires countries to provide a Github token with no expiry date. This is to reduce effort in keeping the token up to date. ### Breaking changes From c07ec921625b66ff69df3d066c40562a8323211f Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Thu, 22 Aug 2024 16:53:42 +0600 Subject: [PATCH 114/146] chore: protect individual certificate endpoint with token --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index c7ea2459b..42e040371 100644 --- a/src/index.ts +++ b/src/index.ts @@ -243,7 +243,6 @@ export async function createServer() { path: '/certificates/{event}.svg', handler: certificateHandler, options: { - auth: false, tags: ['api', 'certificates'], description: 'Returns only one certificate metadata' } From 389dec82bc662e6d24e399c80277b9d5c47d4d6b Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Thu, 22 Aug 2024 17:18:27 +0600 Subject: [PATCH 115/146] chore: add event cert route in production --- src/index.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index 42e040371..e4f10a4d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -237,17 +237,15 @@ export async function createServer() { server.auth.default('jwt') - if (process.env.NODE_ENV !== 'production') { - server.route({ - method: 'GET', - path: '/certificates/{event}.svg', - handler: certificateHandler, - options: { - tags: ['api', 'certificates'], - description: 'Returns only one certificate metadata' - } - }) - } + server.route({ + method: 'GET', + path: '/certificates/{event}.svg', + handler: certificateHandler, + options: { + tags: ['api', 'certificates'], + description: 'Returns only one certificate metadata' + } + }) // add ping route by default for health check server.route({ method: 'GET', From b52bec42330da9cf1cbd4b1ac278b72689a136c2 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Thu, 22 Aug 2024 17:22:17 +0600 Subject: [PATCH 116/146] chore: clean up redundant certificate route --- src/data-seeding/certificates/handler.ts | 36 +++--------------------- src/index.ts | 10 ------- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/src/data-seeding/certificates/handler.ts b/src/data-seeding/certificates/handler.ts index 57ee2ed35..5c24517fa 100644 --- a/src/data-seeding/certificates/handler.ts +++ b/src/data-seeding/certificates/handler.ts @@ -13,36 +13,8 @@ import { Request, ResponseToolkit } from '@hapi/hapi' import { readFileSync } from 'fs' export async function certificateHandler(request: Request, h: ResponseToolkit) { - if (request.params.event) { - const res = readFileSync( - `./src/data-seeding/certificates/source/Farajaland-${request.params.event}-certificate-v2.svg` - ).toString() - return h.response(res).code(200) - } - - const Certificates = [ - { - event: 'birth', - fileName: 'Farajaland-birth-certificate-v2.svg', - svgCode: readFileSync( - './src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg' - ).toString() - }, - { - event: 'death', - fileName: 'Farajaland-death-certificate-v2.svg', - svgCode: readFileSync( - './src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg' - ).toString() - }, - { - event: 'marriage', - fileName: 'Farajaland-marriage-certificate-v2.svg', - svgCode: readFileSync( - './src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg' - ).toString() - } - ] - const res = JSON.stringify(Certificates) - return h.response(res) + const res = readFileSync( + `./src/data-seeding/certificates/source/Farajaland-${request.params.event}-certificate-v2.svg` + ).toString() + return h.response(res).code(200) } diff --git a/src/index.ts b/src/index.ts index e4f10a4d2..aa2078cfb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -486,16 +486,6 @@ export async function createServer() { } }) - server.route({ - method: 'GET', - path: '/certificates', - handler: certificateHandler, - options: { - tags: ['api', 'certificates'], - description: 'Returns certificate metadata' - } - }) - server.route({ method: 'GET', path: '/roles', From f12ae7664759b947a30ca8e74c7f279d7b44bbb6 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Tue, 27 Aug 2024 18:46:12 +0600 Subject: [PATCH 117/146] docs: update changelog(7464) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d47dbb24..a28863ba8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Bug fixes +- Protect individual certificate endpoint with token - Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) - Environment creator script now requires countries to provide a Github token with no expiry date. This is to reduce effort in keeping the token up to date. From fb2a58988c9009eb7e4992cd0e05ec48e263975f Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Wed, 28 Aug 2024 18:13:43 +0600 Subject: [PATCH 118/146] chore: relocate certificate folder from data-seeding --- src/{data-seeding => api}/certificates/handler.ts | 2 +- .../certificates/source/Farajaland-birth-certificate-v2.svg | 0 .../certificates/source/Farajaland-death-certificate-v2.svg | 0 .../certificates/source/Farajaland-marriage-certificate-v2.svg | 0 .../certificates/source/test/UsingAddressHandlebars.svg | 0 .../certificates/source/test/UsingSignatureHandlebars.svg | 0 src/index.ts | 2 +- 7 files changed, 2 insertions(+), 2 deletions(-) rename src/{data-seeding => api}/certificates/handler.ts (87%) rename src/{data-seeding => api}/certificates/source/Farajaland-birth-certificate-v2.svg (100%) rename src/{data-seeding => api}/certificates/source/Farajaland-death-certificate-v2.svg (100%) rename src/{data-seeding => api}/certificates/source/Farajaland-marriage-certificate-v2.svg (100%) rename src/{data-seeding => api}/certificates/source/test/UsingAddressHandlebars.svg (100%) rename src/{data-seeding => api}/certificates/source/test/UsingSignatureHandlebars.svg (100%) diff --git a/src/data-seeding/certificates/handler.ts b/src/api/certificates/handler.ts similarity index 87% rename from src/data-seeding/certificates/handler.ts rename to src/api/certificates/handler.ts index 5c24517fa..658e04eb6 100644 --- a/src/data-seeding/certificates/handler.ts +++ b/src/api/certificates/handler.ts @@ -14,7 +14,7 @@ import { readFileSync } from 'fs' export async function certificateHandler(request: Request, h: ResponseToolkit) { const res = readFileSync( - `./src/data-seeding/certificates/source/Farajaland-${request.params.event}-certificate-v2.svg` + `./src/api/certificates/source/Farajaland-${request.params.event}-certificate-v2.svg` ).toString() return h.response(res).code(200) } diff --git a/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg b/src/api/certificates/source/Farajaland-birth-certificate-v2.svg similarity index 100% rename from src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg rename to src/api/certificates/source/Farajaland-birth-certificate-v2.svg diff --git a/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg b/src/api/certificates/source/Farajaland-death-certificate-v2.svg similarity index 100% rename from src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg rename to src/api/certificates/source/Farajaland-death-certificate-v2.svg diff --git a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg b/src/api/certificates/source/Farajaland-marriage-certificate-v2.svg similarity index 100% rename from src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg rename to src/api/certificates/source/Farajaland-marriage-certificate-v2.svg diff --git a/src/data-seeding/certificates/source/test/UsingAddressHandlebars.svg b/src/api/certificates/source/test/UsingAddressHandlebars.svg similarity index 100% rename from src/data-seeding/certificates/source/test/UsingAddressHandlebars.svg rename to src/api/certificates/source/test/UsingAddressHandlebars.svg diff --git a/src/data-seeding/certificates/source/test/UsingSignatureHandlebars.svg b/src/api/certificates/source/test/UsingSignatureHandlebars.svg similarity index 100% rename from src/data-seeding/certificates/source/test/UsingSignatureHandlebars.svg rename to src/api/certificates/source/test/UsingSignatureHandlebars.svg diff --git a/src/index.ts b/src/index.ts index aa2078cfb..c593851a5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,7 +48,7 @@ import { ErrorContext } from 'hapi-auth-jwt2' import { mapGeojsonHandler } from '@countryconfig/api/dashboards/handler' import { formHandler } from '@countryconfig/form' import { locationsHandler } from './data-seeding/locations/handler' -import { certificateHandler } from './data-seeding/certificates/handler' +import { certificateHandler } from './api/certificates/handler' import { rolesHandler } from './data-seeding/roles/handler' import { usersHandler } from './data-seeding/employees/handler' import { applicationConfigHandler } from './api/application/handler' From 57c72b5a3c4cab44db0a606ca3616626c88281aa Mon Sep 17 00:00:00 2001 From: jamil314 Date: Mon, 2 Sep 2024 21:01:09 +0600 Subject: [PATCH 119/146] fix: groom & bride signature not showing up in pdf --- .../certificates/source/Farajaland-marriage-certificate-v2.svg | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/api/certificates/source/Farajaland-marriage-certificate-v2.svg b/src/api/certificates/source/Farajaland-marriage-certificate-v2.svg index e84fb604d..7bd68e87b 100644 --- a/src/api/certificates/source/Farajaland-marriage-certificate-v2.svg +++ b/src/api/certificates/source/Farajaland-marriage-certificate-v2.svg @@ -100,10 +100,8 @@ 9. Signatures of bride and groom / Signatures des mariés - Groom / Marié - Bride / Mariée From b5599b3bb5a13c4be2de400ffd83732e209f603b Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 5 Sep 2024 11:11:20 +0300 Subject: [PATCH 120/146] fix: mutate logstash host to object before sending it to elastic --- infrastructure/monitoring/logstash/logstash.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infrastructure/monitoring/logstash/logstash.conf b/infrastructure/monitoring/logstash/logstash.conf index f71eb84da..dfc866217 100644 --- a/infrastructure/monitoring/logstash/logstash.conf +++ b/infrastructure/monitoring/logstash/logstash.conf @@ -9,7 +9,10 @@ filter { # Docker's GELF driver want's to write into a field named # container_id, but Kibana and other tools read from container.id by default rename => {"container_id" => "container.id"} + # As of V8, Elasticsearch JSON logs now comply with ECS. [host] becomes object + rename => {"[host]" => "[host][name]"} } + truncate { length_bytes => 12 fields => ["container.id"] From edb44845aec18647d906b8aa6fff7080d6fe49f9 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 5 Sep 2024 12:05:57 +0300 Subject: [PATCH 121/146] add ecs-logstash to index config --- infrastructure/monitoring/kibana/config.ndjson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/monitoring/kibana/config.ndjson b/infrastructure/monitoring/kibana/config.ndjson index d8ff821ed..f79787241 100644 --- a/infrastructure/monitoring/kibana/config.ndjson +++ b/infrastructure/monitoring/kibana/config.ndjson @@ -2,7 +2,7 @@ {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2022-04-18T07:05:33.819Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-06-01T11:30:27.033Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-05T03:00:20.633Z","updatedBy":"opencrvs-admin"},"coreMigrationVersion":"7.17.0","id":"3b6722e0-e19e-11ec-ba8e-51649755648d","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707273006619,214975],"type":"alert","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MjAzNCwxOV0="} {"attributes":{"buildNum":46534,"defaultIndex":"metricbeat-*"},"coreMigrationVersion":"7.17.0","id":"7.17.0","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1707273006619,216009],"type":"config","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MjQyOCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"logs.threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-22T08:25:47.329Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"logs.alert.document.count","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-22T08:32:38.272Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in backup logs","notifyWhen":"onActionGroupChange","params":{"count":{"comparator":"more than or equals","value":1},"criteria":[{"comparator":"matches","field":"message","value":"error"},{"comparator":"equals","field":"log.file.path","value":"/var/log/opencrvs-backup.log"}],"timeSize":1,"timeUnit":"h"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:22.558Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"b166fcb0-8911-11ee-8111-2f3be9e93efc","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294436464,232766],"type":"alert","updated_at":"2024-02-07T08:27:16.464Z","version":"WzQ5NTQzNCwxOV0="} -{"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} +{"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*,ecs-logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"query matched","params":{"documents":["{\"@timestamp\":\"2023-11-20T10:19:30.521Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":".es-query","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-20T09:12:19.237Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Successful SSH login","notifyWhen":"onActionGroupChange","params":{"esQuery":"{ \"query\": { \"bool\": { \"must\": [ \n { \"term\": { \"log.file.path\": \"/var/log/auth.log\" } },\n { \"term\": { \"event.outcome\": \"success\" }}\n ] } } }","index":["filebeat-*"],"size":100,"threshold":[1],"thresholdComparator":">=","timeField":"@timestamp","timeWindowSize":1,"timeWindowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:19.537Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"e79aaa90-8784-11ee-b9ba-89bbe73df7ff","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457367,232778],"type":"alert","updated_at":"2024-02-07T08:27:37.367Z","version":"WzQ5NTQ0MCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2022-06-20T06:16:33.414Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.084Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"CPU under heavy load","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":">","customMetric":{"aggregation":"avg","field":"","id":"alert-custom-metric","type":"custom"},"metric":"cpu","threshold":[70],"timeSize":1,"timeUnit":"m"}],"nodeType":"host","sourceId":"default"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:30.573Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f022bee0-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457362,232774],"type":"alert","updated_at":"2024-02-07T08:27:37.362Z","version":"WzQ5NTQzOCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space in data partition","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">=","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">=","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.mount_point\":\"/hostfs/data\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.mount_point : \"/hostfs/data\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} From fa997d49fc2f07dfadc899f8c9f66a917ec0a947 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Wed, 11 Sep 2024 13:33:59 +0600 Subject: [PATCH 122/146] chore: add allowedFileFormat properties for signature fields --- src/form/common/common-optional-fields.ts | 1 + src/form/marriage/required-sections.ts | 4 ++++ src/form/types/types.ts | 7 ++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/form/common/common-optional-fields.ts b/src/form/common/common-optional-fields.ts index 3be7a5e25..3d78ff4e2 100644 --- a/src/form/common/common-optional-fields.ts +++ b/src/form/common/common-optional-fields.ts @@ -209,6 +209,7 @@ export const informantsSignature = { }, validator: [], type: 'SIGNATURE', + allowedFileFormats: ['image/png', 'image/svg'], mapping: { mutation: { operation: 'fieldValueSectionExchangeTransformer', diff --git a/src/form/marriage/required-sections.ts b/src/form/marriage/required-sections.ts index dc57c50a5..358b1f58c 100644 --- a/src/form/marriage/required-sections.ts +++ b/src/form/marriage/required-sections.ts @@ -123,6 +123,7 @@ const signatureFields = [ required: true, validator: [], type: 'SIGNATURE', + allowedFileFormats: ['image/png'], mapping: { mutation: { operation: 'fieldValueSectionExchangeTransformer', @@ -144,6 +145,7 @@ const signatureFields = [ required: true, validator: [], type: 'SIGNATURE', + allowedFileFormats: ['image/png'], mapping: { mutation: { operation: 'fieldValueSectionExchangeTransformer', @@ -165,6 +167,7 @@ const signatureFields = [ required: true, validator: [], type: 'SIGNATURE', + allowedFileFormats: ['image/png'], mapping: { mutation: { operation: 'fieldValueSectionExchangeTransformer', @@ -186,6 +189,7 @@ const signatureFields = [ required: true, validator: [], type: 'SIGNATURE', + allowedFileFormats: ['image/png'], mapping: { mutation: { operation: 'fieldValueSectionExchangeTransformer', diff --git a/src/form/types/types.ts b/src/form/types/types.ts index 28a95b547..04d315e4d 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -486,7 +486,12 @@ export interface IHeading3Field extends IFormFieldBase { export interface ISignatureFormField extends IFormFieldBase { type: typeof SIGNATURE maxSizeMb?: number - allowedFileFormats?: ('png' | 'jpg' | 'jpeg' | 'svg')[] + allowedFileFormats?: ( + | 'image/png' + | 'image/jpg' + | 'image/jpeg' + | 'image/svg' + )[] } export type IFormField = From 4e1a8df7c44c06139c99bc9f7811df112a655a8e Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Thu, 12 Sep 2024 13:47:22 +0600 Subject: [PATCH 123/146] docs: update changelog for updating allowed file format for signatures --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66ab92ea4..e94f6857d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,42 @@ - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. - Remove `inputFieldWidth` from Number type form field -- You can now configure the home screen application’s name and icons in your country configuration package as manifest.json and app icon files are moved from core to country config (check `src/client-static` folder) +- Application config file is renamed to `application-config.ts` +- Allow configuring the default search criteria for record search which can be done by adding or modifying a property named `SEARCH_DEFAULT_CRITERIA` in `application-config.ts` + Value of `SEARCH_DEFAULT_CRITERIA` can be one of the following + 1. 'TRACKING_ID', + 2. 'REGISTRATION_NUMBER', + 3. 'NATIONAL_ID', + 4. 'NAME', + 5. 'PHONE_NUMBER', + 6. 'EMAIL' +- Updated `allowedFileFormats` in signature fields to use MIME types (`image/png`, `image/jpg`, `image/jpeg`, `image/svg`) instead of simple file extensions. If you are already using the `allowedFileFormats` field in your implementation, please ensure to update the format accordingly. -### Bug fixes +### New features + +- Certificate handlebar for registration fees `registrationFees` [#6817](https://github.com/opencrvs/opencrvs-core/issues/6817) +- Logged in user details handlebar `loggedInUser` [#6529](https://github.com/opencrvs/opencrvs-core/issues/6529) +- Supporting document fields can now be made required +- If there is only one option in the document uploader select, then it stays hidden and only the upload button is showed with the only option being selected by default + +* **ElasticSearch reindexing** + +Allows reindexing ElasticSearch via a new search-service endpoint `reindex`. We're replacing the original `ocrvs` index with timestamped ones. This is done automatically when upgrading and migrating, but this is an important architectural change that should be noted. More details in [#7033](https://github.com/opencrvs/opencrvs-core/pull/7033). + +- Introduce a new certificate handlebar "preview" which can be used to conditionally render some svg element when previewing the certificate e.g. background image similar to security paper + +### New content keys requiring translation + +``` +INSERT CSV ROWS IN ENGLISH ONLY +``` + +## Bug fixes + +- Github pipeline dedicated for reading secrets and variables from other environments now checks if GH_TOKEN is still valid before attempting other operations +- Remove unnecessary UI dividers that add in various sections of the declaration forms(e.g the Death, Birth and Marriage forms) [#244](https://github.com/opencrvs/opencrvs-countryconfig/pull/244) -- Protect individual certificate endpoint with token -- Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) -- Environment creator script now requires countries to provide a Github token with no expiry date. This is to reduce effort in keeping the token up to date. +## 1.5.0 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.1...v1.5.0) ### Breaking changes From 2231e6e84c92b00c39e33b9fe7aef1e5e5ce2bb6 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Mon, 2 Sep 2024 15:08:04 +0600 Subject: [PATCH 124/146] feat: optionCondition to filter document select options --- src/form/types/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/form/types/types.ts b/src/form/types/types.ts index 28a95b547..b77296a1c 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -413,6 +413,7 @@ export interface IImageUploaderWithOptionsFormField extends IFormFieldBase { export interface IDocumentUploaderWithOptionsFormField extends IFormFieldBase { type: typeof DOCUMENT_UPLOADER_WITH_OPTION options: ISelectOption[] + optionCondition?: string hideOnEmptyOption?: boolean compressImagesToSizeMB?: number maxSizeMB?: number From 298e4ef631e1bc6a5cbbd72e941c2b56c10faa71 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Mon, 2 Sep 2024 15:24:03 +0600 Subject: [PATCH 125/146] docs: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66ab92ea4..67edce70e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,12 @@ - Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) - Environment creator script now requires countries to provide a Github token with no expiry date. This is to reduce effort in keeping the token up to date. +### New features + +- The select options in DOCUMENT_UPLOADER_WITH_OPTION field can now be hidden using the new `optionCondition` property. It works similarly to the same property available in SELECT_WITH_OPTIONS field + +## 1.5.0 + ### Breaking changes - #### Update the certificate preview mechanism From c5391c334c671a3a7698d0ed6b3815d1e27348c9 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Thu, 12 Sep 2024 15:40:32 +0600 Subject: [PATCH 126/146] chore: amend changelog --- CHANGELOG.md | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e94f6857d..8966cafe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,42 +7,20 @@ - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. - Remove `inputFieldWidth` from Number type form field -- Application config file is renamed to `application-config.ts` -- Allow configuring the default search criteria for record search which can be done by adding or modifying a property named `SEARCH_DEFAULT_CRITERIA` in `application-config.ts` - Value of `SEARCH_DEFAULT_CRITERIA` can be one of the following - 1. 'TRACKING_ID', - 2. 'REGISTRATION_NUMBER', - 3. 'NATIONAL_ID', - 4. 'NAME', - 5. 'PHONE_NUMBER', - 6. 'EMAIL' +- You can now configure the home screen application’s name and icons in your country configuration package as manifest.json and app icon files are moved from core to country config (check `src/client-static` folder) - Updated `allowedFileFormats` in signature fields to use MIME types (`image/png`, `image/jpg`, `image/jpeg`, `image/svg`) instead of simple file extensions. If you are already using the `allowedFileFormats` field in your implementation, please ensure to update the format accordingly. -### New features - -- Certificate handlebar for registration fees `registrationFees` [#6817](https://github.com/opencrvs/opencrvs-core/issues/6817) -- Logged in user details handlebar `loggedInUser` [#6529](https://github.com/opencrvs/opencrvs-core/issues/6529) -- Supporting document fields can now be made required -- If there is only one option in the document uploader select, then it stays hidden and only the upload button is showed with the only option being selected by default - -* **ElasticSearch reindexing** - -Allows reindexing ElasticSearch via a new search-service endpoint `reindex`. We're replacing the original `ocrvs` index with timestamped ones. This is done automatically when upgrading and migrating, but this is an important architectural change that should be noted. More details in [#7033](https://github.com/opencrvs/opencrvs-core/pull/7033). - -- Introduce a new certificate handlebar "preview" which can be used to conditionally render some svg element when previewing the certificate e.g. background image similar to security paper - -### New content keys requiring translation +### Bug fixes -``` -INSERT CSV ROWS IN ENGLISH ONLY -``` +- Protect individual certificate endpoint with token +- Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) +- Environment creator script now requires countries to provide a Github token with no expiry date. This is to reduce effort in keeping the token up to date. -## Bug fixes +### New features -- Github pipeline dedicated for reading secrets and variables from other environments now checks if GH_TOKEN is still valid before attempting other operations -- Remove unnecessary UI dividers that add in various sections of the declaration forms(e.g the Death, Birth and Marriage forms) [#244](https://github.com/opencrvs/opencrvs-countryconfig/pull/244) +- The select options in DOCUMENT_UPLOADER_WITH_OPTION field can now be hidden using the new `optionCondition` property. It works similarly to the same property available in SELECT_WITH_OPTIONS field -## 1.5.0 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.1...v1.5.0) +## 1.5.0 ### Breaking changes From 6afef7626e8112337b871c77f5739230d9df97a9 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Thu, 11 Jul 2024 13:16:40 +0600 Subject: [PATCH 127/146] chore: rename application config file --- .../{application-config-default.ts => application-config.ts} | 2 +- src/api/application/handler.ts | 2 +- src/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/api/application/{application-config-default.ts => application-config.ts} (97%) diff --git a/src/api/application/application-config-default.ts b/src/api/application/application-config.ts similarity index 97% rename from src/api/application/application-config-default.ts rename to src/api/application/application-config.ts index 9b2db665a..d775b9135 100644 --- a/src/api/application/application-config-default.ts +++ b/src/api/application/application-config.ts @@ -1,6 +1,6 @@ import { countryLogo } from '@countryconfig/api/application/country-logo' -export const defaultApplicationConfig = { +export const applicationConfig = { APPLICATION_NAME: 'Farajaland CRS', BIRTH: { REGISTRATION_TARGET: 30, diff --git a/src/api/application/handler.ts b/src/api/application/handler.ts index 68e655f47..a7b028d30 100644 --- a/src/api/application/handler.ts +++ b/src/api/application/handler.ts @@ -10,7 +10,7 @@ */ import { Request, ResponseToolkit } from '@hapi/hapi' -import { defaultApplicationConfig as applicationConfig } from './application-config-default' +import { applicationConfig } from './application-config' export async function applicationConfigHandler(_: Request, h: ResponseToolkit) { const res = JSON.stringify(applicationConfig) diff --git a/src/index.ts b/src/index.ts index 43615a200..90b416f45 100644 --- a/src/index.ts +++ b/src/index.ts @@ -55,7 +55,7 @@ import { usersHandler } from './data-seeding/employees/handler' import { applicationConfigHandler } from './api/application/handler' import { validatorsHandler } from './form/common/custom-validation-conditionals/validators-handler' import { conditionalsHandler } from './form/common/custom-validation-conditionals/conditionals-handler' -import { COUNTRY_WIDE_CRUDE_DEATH_RATE } from './api/application/application-config-default' +import { COUNTRY_WIDE_CRUDE_DEATH_RATE } from './api/application/application-config' import { handlebarsHandler } from './form/common/certificate/handlebars/handler' import { trackingIDHandler } from './api/tracking-id/handler' import { dashboardQueriesHandler } from './api/dashboards/handler' From a8da778da8d78ac71224b1f3123057849eafa165 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Thu, 11 Jul 2024 19:11:41 +0600 Subject: [PATCH 128/146] chore!: remove unused config messages --- src/translations/client.csv | 111 ------------------------------------ 1 file changed, 111 deletions(-) diff --git a/src/translations/client.csv b/src/translations/client.csv index 525186ad2..13e418047 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -156,134 +156,23 @@ changeEmail.validation.msg,Phone number validation message,Must be a valid email changePhone.validation.msg,Phone number validation message,Must be a valid 10 digit number that starts with 0,Doit être un numéro valide à {num} chiffres qui commence par {start}. config.advanced.search,This is used for the advanced search,Advanced Search,Recherche avancée config.advanced.search.instruction,This is used for the advanced search,Select the options to build an advanced search. A minimum of two search parameters is required.,Sélectionnez les options pour construire une recherche avancée. Un minimum de deux paramètres de recherche est requis. -config.application.applicationNameChangeNotification,Message for application name change notification,Name of application updated,Nom de l'application mise à jour -config.application.applicationNameLabel,Application name config label,Name of application,Nom de l'application -config.application.backgroundImageChangeNotification,Message for background image change notification,Background image updated,Mise à jour de l'image de fond -config.application.backgroundImageError,Error message for background image change,Unable to change image. Please try again.,Impossible de modifier l'image. Veuillez réessayer. -config.application.backgroundImageFileLimitError,Error message for large Background file,Background image file must be less than 2mb,Le fichier de l'image d'arrière-plan doit être inférieur à 2 Mo -config.application.birthDelayedDialogTitle,Delayed dialog title for brith,Delayed registration time period for birth registration,Délai d'enregistrement retardé pour l'enregistrement des naissances -config.application.birthDelayedFeeChangeNotification,Message for application birth delayed fee change notification,Birth delayed fee updated,Mise à jour de la pénalité de déclaration tardive des naissances -config.application.birthLateFeeChangeNotification,Message for application birth late fee change notification,Birth late fee updated,Mise à jour de la pénalité de déclaration tardive des naissances -config.application.birthLateRegTargetChangeNotification,Message for application birth late registration target change notification,Birth late registration target days updated,Mise à jour des jours cibles d'enregistrement tardif des naissances -config.application.birthLegallySpecifiedDialogTitle,Legally specified dialog title for brith,Legally specified time period for birth registration,Délai légal pour déclaration des naissances -config.application.birthOnTimeFeeChangeNotification,Message for application birth on time fee change notification,Birth on time fee updated,Mise à jour des frais de naissance à temps -config.application.birthRegTargetChangeNotification,Message for application birth registration target change notification,Birth registration target days updated,Mise à jour des jours cibles pour l'enregistrement des naissances config.application.birthTabTitle,The title for birth tab,Birth,Naissance config.application.birthTabTitleExport,The title for birth tab for VSExport,Births,Naissances -config.application.colourTabText,The title for colour tab text,Hex code,Code hexadécimal -config.application.colourTabTitle,The title for colour tab,Colour,Couleur -config.application.configChangeError,Error message for application config change,Unable to make change. Please try again,Impossible d'effectuer la modification. Veuillez réessayer -config.application.currencyChangeMessage,Message for application currency change modal,Select your currency for your CRVS system,Selectionnez la devise -config.application.currencyChangeNotification,Message for application currency change notification,Currency updated,Devise mise à jour -config.application.currencyLabel,Currency config label,Currency,Devise -config.application.deathDelayedFeeChangeNotification,Message for application death delayed fee change notification,Death delayed fee updated,Mise à jour de la pénalité de retard déclaration du décès -config.application.deathLegallySpecifiedDialogTitle,Legally specified dialog title for death,Legally specified time period for death registration,Délais légal de déclaration du décès -config.application.deathOnTimeFeeChangeNotification,Message for application death on time fee change notification,Death on time fee updated,Mise à jour des frais de déclaration de décès dans le délais legal -config.application.deathRegTargetChangeNotification,Message for application death registration target change notification,Death registration target days updated,Mise à jour des jours cibles de déclaration des décès config.application.deathTabTitle,The title for death tab,Death,Décès config.application.deathTabTitleExport,The title for death tab for VSExport,Deaths,Décès -config.application.delayedFeeDialogTitle,Delayed fee dialog title,Registration fees for delayed registrations,Frais pour les declarations tardives -config.application.delayedRegistrationLabel,Delayed registration config label,Delayed registration,Enregistrement retardé -config.application.delayedRegistrationValue,Delayed registration config value,After {lateTime} days,Après {lateTime} jours config.application.emptystate,Vital Statistics Export Empty State Text,The previous month's vital statistics data (based on vital event registrations occurring within that month) will become available for you to export as of the 1st of every month. Large CSV files cannot be opened in Excel and should therefore be opened in a statistical program such as {posit}.,Les données statistiques vitales du mois précédent (basées sur les enregistrements d'événements vitaux survenus au cours de ce mois) seront disponibles pour l'exportation à partir du 1er de chaque mois. Les grands fichiers CSV ne peuvent pas être ouverts dans Excel et doivent donc être ouverts dans un programme statistique tel que {posit}. -config.application.eventTargetInputLabel,The label for event target label,days,jours -config.application.example,Label for Example,Example,Exemple config.application.export,Download Export CSV,Export,Export -config.application.generalTabTitle,The title for general tab,General,Général -config.application.govermentLogoLabel,Government logo config label,Goverment logo,Logo du gouvernement -config.application.govtLogoChangeError,Error message for country logo change,Unable to change logo. Please try again.,Impossible de modifier le logo. Veuillez réessayer. -config.application.govtLogoChangeMessage,Message for government logo change modal,Upload a logo to be used on the login and declaration review screens,Téléchargez le logo du gouvernement qui sera utilisé sur le login et la décalcomanie du formulaire. Notez que le logo du certificat est téléchargé dans le cadre du modèle de certificat. -config.application.govtLogoChangeNotification,Message for government logo change notification,Government logo updated,Mise à jour du logo du gouvernement -config.application.govtLogoFileLimitError,Error message for large country logo file,Logo image file must be less than 2mb,Le fichier image du logo doit être inférieur à 2 Mo -config.application.imageTabTitle,The title for image tab,Image,Image -config.application.invalidExample,Label for Invalid example,Invalid,Invalide -config.application.lateFeeDialogTitle,Date fee dialog title,Registration fees for late registrations,Frais d'inscription pour les inscriptions tardives -config.application.lateRegistrationLabel,Late registration config label,Late registration,Déclaration tardive -config.application.lateRegistrationValue,Late registration config value,Between {onTime} days and {lateTime} days,Entre {onTime} jours et {lateTime} jours -config.application.legallySpecifiedLabel,Legally specified config label,Legally specified,Mention légale -config.application.legallySpecifiedValue,Legally specified config value,Within {onTime} days,Dans (ontime) jours -config.application.loginBackgroundLabel,Login Background config label,Login Background,Historique de la connexion -config.application.loginImageText,Login Image config label,Upload an image and set how you would like it to display in the background,Téléchargez une image et définissez comment vous souhaitez qu'elle s'affiche en arrière-plan. -config.application.marriageDelayedFeeChangeNotification,Message for application marriage delayed fee change notification,Marriage delayed fee updated,Mise à jour de la pénalité de retard déclaration du marriage -config.application.marriageLegallySpecifiedDialogTitle,Legally specified dialog title for marriage,Legally specified time period for marriage registration,Délais légal de déclaration du marriage -config.application.marriageOnTimeFeeChangeNotification,Message for application marriage on time fee change notification,Marriage on time fee updated,Mise à jour des frais de déclaration de marriage dans le délais legal -config.application.marriageRegTargetChangeNotification,Message for application marriage registration target change notification,Marriage registration target days updated,Mise à jour des jours cibles de déclaration des marriages -config.application.marriageTabTitle,The title for marriage tab,Marriage,Mariage -config.application.nameChangeMessage,Message for application name change modal,Choose a name for your CRVS system,Choisissez un nom pour votre système CRVS -config.application.nidPatternChangeError,Error message for invalid regular expression for NID number,Invalid regular expression for a National ID,Expression régulière invalide pour un identifiant national -config.application.nidPatternChangeMessage,Unique Identification Number (UIN) config message,Set the regex pattern for your national ID. For guidance please refer to www.regex101.com,Expression régulière invalide pour un identifiant national -config.application.nidPatternChangeNotification,Message for NID Pattern change modal,Unique Identification Number (UIN) regex pattern updated,Mise à jour du modèle regex pour le numéro d'identification unique (UIN) -config.application.nidPatternTitle,Unique Identification Number (UIN) config title,Unique Identification Number (UIN) e.g. National ID,"Numéro d'identification unique (UIN), par exemple la carte d'identité nationale." -config.application.onTimeFeeDialogTitle,On time fee dialog title,Registration fees within legally specified time,Droits d'inscription dans le délai légal -config.application.pattern,Label for Pattern,Pattern,Modèle -config.application.phoneNumberChangeError,Error message for invalid regular expression for phone number number,Invalid regular expression for a phone number,Expression régulière invalide pour un numéro de téléphone -config.application.phoneNumberChangeMessage,phone number config config message,Set the regex pattern for your country phone number. For guidance please refer to www.regex101.com,"Définissez le modèle regex pour le numéro de téléphone de votre pays. Pour obtenir des conseils, veuillez consulter le site www.regex101.com" -config.application.phoneNumberChangeNotification,Message for phone number Pattern change modal,Phone regex pattern updated,Mise à jour du modèle de regex téléphonique -config.application.phoneNumberExampleLabel,,example: {example},exemple: {example} -config.application.phoneNumberLabel,Phone number config label,Phone number,Numéro de téléphone -config.application.phoneNumberPatternLabel,,pattern: {pattern},motif: {pattern} -config.application.phoneNumberPatternTitle,Phone number config title,Phone number regex,Regex du numéro de téléphone -config.application.registrationFeesGroupTitle,The title for registration fee group,Registration fees,Droits d'inscription -config.application.registrationTimePeriodsGroupTitle,The title for registration time periods group,Registration time periods,Périodes d'enregistrement -config.application.settings,Link Text for Config Application Settings,Application,Application -config.application.testNumber,Label for test number,Test number,Numéro de test -config.application.updatingeMessage,Message for application config updated modal,Updating...,Mise à jour en cours -config.application.validExample,Label for valid example,Valid,Valable config.application.vitalStatistics,Vital Statistics Export,"Month-{month}-Farajaland-{event, select, birth{birth} death{death} other{birth}}-event-statistics.csv {fileSize}","Mois-{month}-Farajaland-{event, select, birth{birth} death{death} other{birth}}-événement-statistiques.csv {fileSize}" config.application.vsExportDownloadFailed,Vital Statistics Export Empty State Text,Sorry! Something went wrong,Désolé ! Quelque chose s'est mal passé config.application.vsexport,VS Export tab,Vital statistics,Statistiques vitales -config.application.withinLegallySpecifiedTimeLabel,Within legally specified time config label,Within legally specified time,Dans les délais prévus par la loi -config.birthDefaultTempDesc,Label for default birth certificate template,Default birth certificate template,Modèle d'acte de naissance par défaut -config.birthTemplate,Label for birth certificate template,Birth certificate,Certificat de naissance -config.birthUpdatedTempDesc,,Updated {birthLongDate},Mise à jour de {birthLongDate} config.certTemplate,Label for certificate templates,Certificate Template,Modèle de certificat -config.certificate.allowPrinting,To allow printing in advanced of issuance,Allow printing in advanced of issuance,Permettre l'impression à l'avance de l'émission -config.certificate.allowPrintingNotification,Message for allowing printing notification,Allow printing in advance of issuance updated,Permettre l'impression avant la mise à jour de l'émission -config.certificate.certificateUpdated,Certificate template change message on success,{eventName} certificate has been updated,Le certificat {eventName} a été mis à jour. -config.certificate.certificateUploading,Certificate template message when uploading SVG,Uploading and validating {eventName} certificate.,Téléchargement et validation du certificat {eventName}. -config.certificate.certificateValidationError,Certificate template error message on failed,Unable to read SVG. Please check,Impossible de lire le SVG. Veuillez vérifier config.certificate.options,Show options,Options,Options -config.certificate.printDescription,Allowing printing,Records printed off in advance of collections will be added to the ready to issue work-queue,Les documents imprimés avant les collectes seront ajoutés à la liste des documents prêts à être délivrés -config.certificate.template,Template for certificates,Template,Gabarit -config.certificate.uploadCertificateDialogCancel,Cancel new certificate template upload button,Cancel,Annuler -config.certificate.uploadCertificateDialogConfirm,Confirm new certificate template upload button,Upload,Télécharger -config.certificate.uploadCertificateDialogDescription,The description for the dialog when upload new certificate template,This will replace the current certificate template. We recommend downloading the existing certificate template as a reference.,Ceci remplacera le modèle de certificat actuel. Nous vous recommandons de télécharger le modèle de certificat existant comme référence. -config.certificate.uploadCertificateDialogTitle,Upload certificate template modal title,Upload new certificate?,Télécharger un nouveau certificat ? -config.certificateConfiguration,Link Text for Config Declaration Settings,Certificate configuration,Configuration du certificat -config.deathDefaultTempDesc,Label for default death certificate template,Default death certificate template,Modèle de certificat de décès par défaut -config.deathTemplate,Label for death certificate template,Death certificate,Acte de mariage -config.deathUpdatedTempDesc,,Updated {deathLongDate},Mise à jour de {deathLongDate} -config.downloadTemplate,Download action in certificate config action menu,Download,Télécharger config.emailAllUsers.modal.supportingCopy,Label for send email all users confirmation supporting copy,User will receive emails over the next 24 hours,L'utilisateur recevra des courriels au cours des prochaines 24 heures config.emailAllUsers.modal.title,Label for send email all users confirmation title,Send email to all users?,Envoyer un e-mail à tous les utilisateurs ? 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.eventUpdatedTempDesc,Label for updated birth certificate template,"Updated {lastModified, date, ::dd MMMM yyyy}","Mis à jour {lastModified, date, ::dd MMMM yyyy}" -config.form.settings.time,,Time input,Saisie de l'heure -config.form.tools.input.customSelectWithDynamicOptions,,Custom select with dynamic options,Sélection personnalisée avec options dynamiques -config.informantNotification.declarationSMS,Title for informant declarationSMS notification,Declaration sent for review,Déclaration envoyée pour examen -config.informantNotification.inProgressSMS,Title for informant inProgressSMS notification,Notification sent to Office,Notification envoyée au bureau -config.informantNotification.registrationSMS,Title for informant registrationSMS notification,Declaration registered,Déclaration enregistrée -config.informantNotification.rejectionSMS,Title for informant rejectionSMS notification,Declaration rejected,Déclaration rejetée -config.informantNotification.subtitle,Subtile for informant sms notification,Select the notifications to send to the informant to keep them informed of the progress to their declaration. Your system is configured to send {communicationType}.,Sélectionnez les notifications à envoyer à l'informateur pour le tenir informé de l'avancement de sa déclaration. Votre système est configuré pour envoyer {communicationType} -config.informantNotification.success,Notification for informant update success,Informant notifications updated,Mise à jour des notifications des informateurs -config.informantNotification.title,The title for Informant notifications,Informant notifications,Notifications d'informateurs -config.integrations,,Integrations,Intégrations -config.listDetails,Details for certificates templates list,To learn how to edit an SVG and upload a certificate to suite your country requirements please refer to this detailed guide. ,"Pour savoir comment modifier un SVG et télécharger un certificat en fonction des exigences de votre pays, veuillez consulter ce guide détaillé." -config.listDetailsQsn,Details question for certificates templates list,How to configure a certificate?,Comment configurer un certificat ? -config.listTitle,Title for certificates templates list,Certification,La certification -config.marriageDefaultTempDesc,Label for default marriage certificate template,Default marriage certificate template,Modèle de certificat de mariage par défaut -config.marriageTemplate,Label for marriage certificate template,Marriage certificate,Certificat de mariage -config.previewTemplate,,Preview,Prévisualiser -config.printTemplate,Print action in certificate config action menu,Print,Imprimer -config.uploadTemplate,Upload action in certificate config action menu,Upload,Télécharger config.userRoles.language,Language name,"{language, select, en {English} fr {French} other {{language}}}","{language, select, en {Anglais} fr {Français} other {{language}}}" -config.userRoles.role,ListViewSimplified header for role,ROLE,RÔLE 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} -config.userRoles.subtitle,Subtile for informant sms notification,Map user roles to each system role so that specific permissions and privileges are correctly assigned. To learn more about the different system roles see ... {link},"Associez les rôles d'utilisateur à chaque rôle système afin que les autorisations et les privilèges spécifiques soient correctement attribués. Pour en savoir plus sur les différents rôles système, voir ... {link}" -config.userRoles.systemRoleSuccessMsg,Label for System role updated success message,System role updated successfully,Rôle système mis à jour avec succès -config.userRoles.systemRoles,ListViewSimplified header for system roles,SYSTEM ROLES,RÔLES SYSTÈME -config.userRoles.title,The title for user roles,User roles,Rôles des utilisateurs 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. From 78b1edf4742a96c0eac059a59672dd5b01c4d4c4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 26 Aug 2024 07:55:25 +0000 Subject: [PATCH 129/146] Placeholder commit for PR #244 From 72fa314b14407ecb15d35613c66a0f0a66c8ec1e Mon Sep 17 00:00:00 2001 From: Siyasanga Date: Tue, 6 Aug 2024 15:12:35 +0200 Subject: [PATCH 130/146] Remove extra divider on Birth form's mother section * Also update the precedingFieldId of the defaultAddressConfiguration (#7404)[https://github.com/opencrvs/opencrvs-core/issues/7404] --- src/form/addresses/index.ts | 3 ++- src/form/birth/index.ts | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index 97bee6497..9cf67a073 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -93,7 +93,8 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ }, { // MOTHER ADDRESS FIELDS - precedingFieldId: 'birth.mother.mother-view-group.mother-nid-seperator', + precedingFieldId: + 'birth.mother.mother-view-group.motherBirthRegistrationNumber', configurations: [ { config: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, diff --git a/src/form/birth/index.ts b/src/form/birth/index.ts index 8fd20b3ae..adb5c009a 100644 --- a/src/form/birth/index.ts +++ b/src/form/birth/index.ts @@ -353,8 +353,6 @@ export const birthForm: ISerializedForm = { hideIfNidIntegrationEnabled.concat(detailsExist), true ), - // preceding field of address fields - divider('mother-nid-seperator', detailsExist), // ADDRESS FIELDS WILL RENDER HERE divider('mother-address-seperator', detailsExist), getMaritalStatus(certificateHandlebars.motherMaritalStatus, [ From c29fc9cf4e3f821c654b0b01771a759cfcc8408f Mon Sep 17 00:00:00 2001 From: Siyasanga Date: Wed, 7 Aug 2024 10:30:46 +0200 Subject: [PATCH 131/146] Remove extra divider on Birth form's father section * Also update the precedingFieldId of the defaultAddressConfiguration (#7404)[https://github.com/opencrvs/opencrvs-core/issues/7404] --- src/form/addresses/index.ts | 3 ++- src/form/birth/index.ts | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index 9cf67a073..36fb0bcdf 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -118,7 +118,8 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ }, { // FATHER ADDRESS FIELDS - precedingFieldId: 'birth.father.father-view-group.father-nid-seperator', + precedingFieldId: + 'birth.father.father-view-group.fatherBirthRegistrationNumber', configurations: [ { config: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, diff --git a/src/form/birth/index.ts b/src/form/birth/index.ts index adb5c009a..2123080cc 100644 --- a/src/form/birth/index.ts +++ b/src/form/birth/index.ts @@ -440,8 +440,6 @@ export const birthForm: ISerializedForm = { hideIfNidIntegrationEnabled.concat(detailsExist), true ), - // preceding field of address fields - divider('father-nid-seperator', detailsExist), // ADDRESS FIELDS WILL RENDER HERE divider('father-address-seperator', detailsExist), getMaritalStatus(certificateHandlebars.fatherMaritalStatus, [ From 4a0feb6e9de7365db78946ed6fbf59799fdd346b Mon Sep 17 00:00:00 2001 From: Siyasanga Date: Wed, 7 Aug 2024 10:46:32 +0200 Subject: [PATCH 132/146] Remove extra divider on Death form's Spouse section * Also update the precedingFieldId of the defaultAddressConfiguration (#7404)[https://github.com/opencrvs/opencrvs-core/issues/7404] --- src/form/addresses/index.ts | 3 ++- src/form/death/index.ts | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index 36fb0bcdf..b569e6bc1 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -268,7 +268,8 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ },*/ { // SPOUSE ADDRESS FIELDS - precedingFieldId: 'death.spouse.spouse-view-group.spouse-nid-seperator', + precedingFieldId: + 'death.spouse.spouse-view-group.spouseBirthRegistrationNumber', configurations: [ { config: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, diff --git a/src/form/death/index.ts b/src/form/death/index.ts index 66181d0b2..9883423cf 100644 --- a/src/form/death/index.ts +++ b/src/form/death/index.ts @@ -364,8 +364,6 @@ export const deathForm = { ), getIDType('death', 'spouse', detailsExist, true), ...getIDNumberFields('spouse', detailsExist, true), - // preceding field of address fields - divider('spouse-nid-seperator', detailsExist), // ADDRESS FIELDS WILL RENDER HERE divider('spouse-address-separator') ], From 2c8215d82153b3c69b82751c9d5d8027f5f3401b Mon Sep 17 00:00:00 2001 From: Siyasanga Date: Wed, 7 Aug 2024 11:02:47 +0200 Subject: [PATCH 133/146] Rm extra divider on Marriage form's placeOfMarriageTitle * Also update the precedingFieldId of the defaultAddressConfiguration (#7404)[https://github.com/opencrvs/opencrvs-core/issues/7404] --- src/form/addresses/index.ts | 2 +- src/form/marriage/index.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index b569e6bc1..a28396f9d 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -304,7 +304,7 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ { // PLACE OF MARRIAGE ADDRESS FIELDS precedingFieldId: - 'marriage.marriageEvent.marriage-event-details.place-of-marriage-seperator', + 'marriage.marriageEvent.marriage-event-details.placeOfMarriageTitle', configurations: [{ config: EventLocationAddressCases.PLACE_OF_MARRIAGE }] }, { diff --git a/src/form/marriage/index.ts b/src/form/marriage/index.ts index 3b77628e5..1892e2dff 100644 --- a/src/form/marriage/index.ts +++ b/src/form/marriage/index.ts @@ -264,8 +264,7 @@ export const marriageForm: ISerializedForm = { fields: [ getMarriageDate, // Required field getTypeOfMarriage, - placeOfMarriageSubsection, - divider('place-of-marriage-seperator') + placeOfMarriageSubsection // PLACE OF MARRIAGE FIELDS WILL RENDER HERE ] } From 89abe56029d057cb8a703ad2f54485c9038c34b6 Mon Sep 17 00:00:00 2001 From: Siyasanga Date: Mon, 19 Aug 2024 13:55:02 +0200 Subject: [PATCH 134/146] Record the removing of dividers in CHANGELOG.md We missed adding an entry into the CHANGELOG.md to document the changes we made and why we had to make changes https://github.com/opencrvs/opencrvs-core/issues/7404 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8966cafe5..ed63b9209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Remove `inputFieldWidth` from Number type form field - You can now configure the home screen application’s name and icons in your country configuration package as manifest.json and app icon files are moved from core to country config (check `src/client-static` folder) - Updated `allowedFileFormats` in signature fields to use MIME types (`image/png`, `image/jpg`, `image/jpeg`, `image/svg`) instead of simple file extensions. If you are already using the `allowedFileFormats` field in your implementation, please ensure to update the format accordingly. +- Remove unnecessary UI dividers that add in various sections of the declaration forms(e.g the Death, Birth and Marriage forms) [#244](https://github.com/opencrvs/opencrvs-countryconfig/pull/244) ### Bug fixes From a7f67066e2ec157b18452e2ef6c1bf6bc3b6dedf Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Wed, 18 Sep 2024 15:13:05 +0600 Subject: [PATCH 135/146] chore: add route to serve record notification --- src/api/application/application-config.ts | 24 +++++++++++++++++++++++ src/api/record-notification/handler.ts | 9 +++++++++ src/index.ts | 11 +++++++++++ 3 files changed, 44 insertions(+) create mode 100644 src/api/record-notification/handler.ts diff --git a/src/api/application/application-config.ts b/src/api/application/application-config.ts index d775b9135..62a957e05 100644 --- a/src/api/application/application-config.ts +++ b/src/api/application/application-config.ts @@ -56,3 +56,27 @@ export const applicationConfig = { } export const COUNTRY_WIDE_CRUDE_DEATH_RATE = 10 + +export const notificationForRecord = { + birth: { + 'sent-notification': true, + 'sent-notification-for-review': true, + 'sent-for-approval': true, + registered: true, + 'sent-for-updates': true + }, + death: { + 'sent-notification': true, + 'sent-notification-for-review': true, + 'sent-for-approval': true, + registered: true, + 'sent-for-updates': true + }, + marriage: { + 'sent-notification': null, + 'sent-notification-for-review': null, + 'sent-for-approval': null, + registered: null, + 'sent-for-updates': null + } +} diff --git a/src/api/record-notification/handler.ts b/src/api/record-notification/handler.ts new file mode 100644 index 000000000..a10916e07 --- /dev/null +++ b/src/api/record-notification/handler.ts @@ -0,0 +1,9 @@ +import * as Hapi from '@hapi/hapi' +import { notificationForRecord } from '../application/application-config' + +export function recordNotificationHandler( + request: Hapi.Request, + h: Hapi.ResponseToolkit +) { + return h.response(notificationForRecord) +} diff --git a/src/index.ts b/src/index.ts index 90b416f45..c8ddb8f32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,6 +61,7 @@ import { trackingIDHandler } from './api/tracking-id/handler' import { dashboardQueriesHandler } from './api/dashboards/handler' import { fontsHandler } from './api/fonts/handler' import { certificateConfigurationHandler } from './api/certificate-configuration/handler' +import { recordNotificationHandler } from './api/record-notification/handler' export interface ITokenPayload { sub: string @@ -534,6 +535,16 @@ export async function createServer() { } }) + server.route({ + method: 'GET', + path: '/record-notification', + handler: recordNotificationHandler, + options: { + tags: ['api'], + description: 'Checks for enabled notification for record' + } + }) + server.ext({ type: 'onRequest', method(request: Hapi.Request & { sentryScope?: any }, h) { From c134a3a560f5d1d24aa12b07bc13cdfb18ad0e0d Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Wed, 18 Sep 2024 17:44:19 +0600 Subject: [PATCH 136/146] chore: improve record notification types --- src/api/application/application-config.ts | 27 ++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/api/application/application-config.ts b/src/api/application/application-config.ts index 62a957e05..e7a8fcfad 100644 --- a/src/api/application/application-config.ts +++ b/src/api/application/application-config.ts @@ -57,26 +57,33 @@ export const applicationConfig = { export const COUNTRY_WIDE_CRUDE_DEATH_RATE = 10 -export const notificationForRecord = { - birth: { +type EventNotificationFlags = { + 'sent-notification': boolean + 'sent-notification-for-review': boolean + 'sent-for-approval': boolean + registered: boolean + 'sent-for-updates': boolean +} + +type NotificationFlags = { + BIRTH?: EventNotificationFlags + DEATH?: EventNotificationFlags + MARRIAGE?: EventNotificationFlags +} + +export const notificationForRecord: NotificationFlags = { + BIRTH: { 'sent-notification': true, 'sent-notification-for-review': true, 'sent-for-approval': true, registered: true, 'sent-for-updates': true }, - death: { + DEATH: { 'sent-notification': true, 'sent-notification-for-review': true, 'sent-for-approval': true, registered: true, 'sent-for-updates': true - }, - marriage: { - 'sent-notification': null, - 'sent-notification-for-review': null, - 'sent-for-approval': null, - registered: null, - 'sent-for-updates': null } } From 86d507d31610327d943b063f292e3ee6cdad7559 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Wed, 18 Sep 2024 17:58:06 +0600 Subject: [PATCH 137/146] docs: update changelog for addtion of notification flag --- CHANGELOG.md | 10 ++++++++++ src/api/application/application-config.ts | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8966cafe5..693d2f0ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ ### Breaking changes +- Addition of the check of informant notification being enabled in the `application-config.ts` file. Maintain the type of `NotificationFlags` for proper setup of the notification. For an event, the notification flags can be the following - + +``` + 'sent-notification', + 'sent-notification-for-review', + 'sent-for-approval', + 'registered', + 'sent-for-updates' +``` + - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. - Remove `inputFieldWidth` from Number type form field diff --git a/src/api/application/application-config.ts b/src/api/application/application-config.ts index e7a8fcfad..9ac529c54 100644 --- a/src/api/application/application-config.ts +++ b/src/api/application/application-config.ts @@ -58,11 +58,11 @@ export const applicationConfig = { export const COUNTRY_WIDE_CRUDE_DEATH_RATE = 10 type EventNotificationFlags = { - 'sent-notification': boolean - 'sent-notification-for-review': boolean - 'sent-for-approval': boolean - registered: boolean - 'sent-for-updates': boolean + 'sent-notification'?: boolean + 'sent-notification-for-review'?: boolean + 'sent-for-approval'?: boolean + registered?: boolean + 'sent-for-updates'?: boolean } type NotificationFlags = { From b27279ea0caff7af7a2dba9196c3cd0c13efe5e2 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Wed, 18 Sep 2024 18:40:01 +0600 Subject: [PATCH 138/146] docs: amend changelog for notification flag --- CHANGELOG.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 693d2f0ff..7332ce1ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,24 @@ ### Breaking changes -- Addition of the check of informant notification being enabled in the `application-config.ts` file. Maintain the type of `NotificationFlags` for proper setup of the notification. For an event, the notification flags can be the following - - -``` - 'sent-notification', - 'sent-notification-for-review', - 'sent-for-approval', - 'registered', - 'sent-for-updates' -``` +### Added Notification Flags and API Endpoint + +- **Notification Flags**: The configuration of various notifications is now controlled from `countryconfig` instead of being handled in the UI, as notification settings are not something that should be changed on the fly. To simplify this process, we have moved the settings to the `application-config.ts` file. From now on, the notifications can be managed in the `notificationForRecord` object defined in the mentioned file. Any changes will take effect after a new deployment. + + **_Country implementors must define the `notificationForRecord` object in the `application-config.ts` file to enable the notifications they want. Not doing so will keep notifications disabled by default._** + +- **Feature**: Added notification flags for `BIRTH`, `DEATH`, and `MARRIAGE` events, including: + + - `sent-notification` + - `sent-notification-for-review` + - `sent-for-approval` + - `registered` + - `sent-for-updates` + +- **API**: Added `/record-notification` endpoint to check enabled notifications for records. + + - The API returns the `notificationForRecord` object for `BIRTH` and `DEATH` events, listing their respective flags. + - Route configuration includes description and tags for API documentation. - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. From f3b1665814e9a1f1b90a3d2846cbd2c8a48e0dd6 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 19 Sep 2024 11:16:54 +0600 Subject: [PATCH 139/146] docs: rearrange notification flags CHANGELOG entry --- CHANGELOG.md | 68 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7332ce1ec..166ca83ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,24 +4,35 @@ ### Breaking changes -### Added Notification Flags and API Endpoint - -- **Notification Flags**: The configuration of various notifications is now controlled from `countryconfig` instead of being handled in the UI, as notification settings are not something that should be changed on the fly. To simplify this process, we have moved the settings to the `application-config.ts` file. From now on, the notifications can be managed in the `notificationForRecord` object defined in the mentioned file. Any changes will take effect after a new deployment. +- **Notification Flags** The configuration of various notifications is now controlled from `countryconfig` instead of being handled in the UI, as notification settings are not something that should be changed on the fly. To simplify this process, we have moved the settings to the `application-config.ts` file. From now on, the notifications can be managed in the `notificationForRecord` object defined in the mentioned file. Any changes will take effect after a new deployment. **_Country implementors must define the `notificationForRecord` object in the `application-config.ts` file to enable the notifications they want. Not doing so will keep notifications disabled by default._** -- **Feature**: Added notification flags for `BIRTH`, `DEATH`, and `MARRIAGE` events, including: - - - `sent-notification` - - `sent-notification-for-review` - - `sent-for-approval` - - `registered` - - `sent-for-updates` - -- **API**: Added `/record-notification` endpoint to check enabled notifications for records. - - - The API returns the `notificationForRecord` object for `BIRTH` and `DEATH` events, listing their respective flags. - - Route configuration includes description and tags for API documentation. +- **Gateways searchEvents API updated** `operationHistories` only returns `operationType` & `operatedOn` due to the other fields being unused in OpenCRVS +- **Config changes to review/preview and signatures** Core used to provide review/preview section by default which are now removed and need to be provided from countryconfig. The signature field definitions (e.g. informant signature, bride signature etc.) were hard coded in core which also have now been removed. The signatures can now be added through the review/preview sections defined in countryconfig just like any other field. You can use the following section definition as the default which is without any additional fields. We highly recommend checking out our reference country repository which has the signature fields in it's review/preview sections + +``` +{ + id: 'preview', + viewType: 'preview', + name: { + defaultMessage: 'Preview', + description: 'Form section name for Preview', + id: 'register.form.section.preview.name' + }, + title: { + defaultMessage: 'Preview', + description: 'Form section title for Preview', + id: 'register.form.section.preview.title' + }, + groups: [ + { + id: 'preview-view-group', + fields: [] + } + ] +} +``` - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. @@ -39,7 +50,32 @@ - The select options in DOCUMENT_UPLOADER_WITH_OPTION field can now be hidden using the new `optionCondition` property. It works similarly to the same property available in SELECT_WITH_OPTIONS field -## 1.5.0 +* **ElasticSearch reindexing** Allows reindexing ElasticSearch via a new search-service endpoint `reindex`. We're replacing the original `ocrvs` index with timestamped ones. This is done automatically when upgrading and migrating, but this is an important architectural change that should be noted. More details in [#7033](https://github.com/opencrvs/opencrvs-core/pull/7033). + +- Introduce a new certificate handlebar "preview" which can be used to conditionally render some svg element when previewing the certificate e.g. background image similar to security paper + +- **Notification flags**: Added notification flags for `BIRTH`, `DEATH`, and `MARRIAGE` events, including: + + - `sent-notification` + - `sent-notification-for-review` + - `sent-for-approval` + - `registered` + - `sent-for-updates` + +- **`/record-notification` API**: Endpoint to check enabled notifications for records. The API returns the `notificationForRecord` object for `BIRTH` and `DEATH` events, listing their respective flags. Route configuration includes description and tags for API documentation. + +### New content keys requiring translation + +``` +INSERT CSV ROWS IN ENGLISH ONLY +``` + +## Bug fixes + +- Github pipeline dedicated for reading secrets and variables from other environments now checks if GH_TOKEN is still valid before attempting other operations +- Remove unnecessary UI dividers that add in various sections of the declaration forms(e.g the Death, Birth and Marriage forms) [#244](https://github.com/opencrvs/opencrvs-countryconfig/pull/244) + +## 1.5.0 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.1...v1.5.0) ### Breaking changes From de05cbc4305503f7abbca70627cd920fac8060d1 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 6 Aug 2024 12:31:49 +0600 Subject: [PATCH 140/146] fix: add output for reset job (#159) * fix: add output for reset job * fix: add id to the reset data step --- .github/workflows/clear-environment.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/clear-environment.yml b/.github/workflows/clear-environment.yml index 25f360477..fea82bf7a 100644 --- a/.github/workflows/clear-environment.yml +++ b/.github/workflows/clear-environment.yml @@ -22,6 +22,8 @@ jobs: name: 'Reset data' environment: ${{ github.event.inputs.environment }} runs-on: ubuntu-22.04 + outputs: + outcome: ${{ steps.reset-data.outcome }} timeout-minutes: 60 steps: - name: Clone country config resource package @@ -45,6 +47,7 @@ jobs: known_hosts: ${{ env.KNOWN_HOSTS }} - name: Reset data + id: reset-data env: HOST: ${{ vars.DOMAIN }} ENV: ${{ vars.ENVIRONMENT_TYPE }} From f5e8689b15307707f9da151ea09057d6b744fd9d Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Thu, 19 Sep 2024 14:57:17 +0600 Subject: [PATCH 141/146] docs: update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b05f92dc6..3d85f7eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) - "Publish release" pipeline now correctly uses the "Branch to build from" value as the branch to be tagged. Previously it tried tagging "master". "Release tag" is also now used as the release version as is instead of it being read from `package.json`. - Environment creator script now requires countries to provide a Github token with no expiry date. This is to reduce effort in keeping the token up to date. +- Added the missing outputs for the clear environment workflow which was causing the seed data workflow to not run even if the reset option was checked when deploying ### New features From fa19a0872f1c4a89872bcc0b7d96c2113781892d Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Thu, 26 Sep 2024 11:35:13 +0600 Subject: [PATCH 142/146] chore: add search placeholder copy --- src/translations/client.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/src/translations/client.csv b/src/translations/client.csv index 525186ad2..d200f3789 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -1920,6 +1920,7 @@ search.locationNotFound,Label for location not found,Location Not Found,Emplacem search.noDeclarations,The text when there is no declaration to review,No declarations to review,Aucune déclaration à examiner search.noResultFor,The no result text,No results for ”{param}”,Aucun résultat pour “{param}” search.noResults,Text to display if the search return no results for the current filters,No result to display,Aucun résultat à afficher +search.placeholder,Placeholder text of search input,Name of query,Nom de la requête search.removeBbookmarkAdvancedSearchModalBody,Modal body for remove bookmark advacnced search,This advanced search bookmark will be removed from the side bar shortcut,Ce signet de recherche avancée sera supprimé du raccourci de la barre latérale search.removeBookmarkAdvancedSearchModalTitle,Modal title for remove bookmark advacnced search,Remove search query?,Supprimer la requête de recherche ? search.results,Results label at the top of the data table component,Results,Résultats From e8bd3e3d4218cf565b6b5758feabc3204a4bfa1d Mon Sep 17 00:00:00 2001 From: jamil314 Date: Fri, 27 Sep 2024 13:17:15 +0600 Subject: [PATCH 143/146] fix: remove extra divider in birth declaration form --- src/form/addresses/index.ts | 2 +- src/form/birth/index.ts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index a28396f9d..6576f0015 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -62,7 +62,7 @@ export const defaultAddressConfiguration: IAddressConfiguration[] = [ { // INFORMANT ADDRESS FIELDS precedingFieldId: - 'birth.informant.informant-view-group.informant-nid-seperator', + 'birth.informant.informant-view-group.informantBirthRegistrationNumber', configurations: [ { config: AddressSubsections.PRIMARY_ADDRESS_SUBSECTION, diff --git a/src/form/birth/index.ts b/src/form/birth/index.ts index 2123080cc..3cbd0093c 100644 --- a/src/form/birth/index.ts +++ b/src/form/birth/index.ts @@ -274,13 +274,6 @@ export const birthForm: ISerializedForm = { hideIfNidIntegrationEnabled.concat(hideIfInformantMotherOrFather), true ), - // preceding field of address fields - divider('informant-nid-seperator', [ - { - action: 'hide', - expression: informantNotMotherOrFather - } - ]), // ADDRESS FIELDS WILL RENDER HERE divider('informant-address-seperator', [ { From ad3ba0221aa6493348d343d0e8e27e5006cfff5b Mon Sep 17 00:00:00 2001 From: Ollie OpenCRVS Date: Mon, 7 Oct 2024 15:51:37 +0300 Subject: [PATCH 144/146] =?UTF-8?q?=F0=9F=8D=92=20Merge=20changes=20from?= =?UTF-8?q?=20PR=20#293=20to=20release-v1.6.0=20(#295)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Riku Rouvila --- .github/workflows/auto-pr-to-release.yml | 166 ++++-- CHANGELOG.md | 6 + .../monitoring/elastalert/rules/alert.yaml | 4 +- package.json | 14 +- ...ify-elastalert-kibana-alerts-match.test.ts | 54 ++ yarn.lock | 517 +++++++++++++++++- 6 files changed, 707 insertions(+), 54 deletions(-) create mode 100644 tests/verify-elastalert-kibana-alerts-match.test.ts diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 84e5e2634..cdc8eceee 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -18,36 +18,122 @@ on: description: 'PR number to process' required: true default: '' + dry_run: + description: 'Dry run' + required: false + default: false + type: boolean jobs: - create-pr: + resolve-releases: if: ${{ github.event_name == 'pull_request' && github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-latest - + outputs: + matrix: ${{ steps.resolve-applicable-versions.outputs.matrix }} steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Get PR details from workflow dispatch if: ${{ github.event_name == 'workflow_dispatch' }} id: get_pr_details_dispatch run: | PR_NUMBER=${{ github.event.inputs.pr_number }} - PR_DATA=$(gh pr view $PR_NUMBER --json number,headRefName,baseRefName,mergedBy,mergeCommit,author,milestone,title --jq '{number: .number, headRefName: .headRefName, baseRefName: .baseRefName, merger: .mergedBy.login, author: .author.login, milestone: .milestone.title, title: .title}') - echo "PR_ID=$(echo $PR_DATA | jq -r '.number')" >> $GITHUB_ENV - echo "PR_AUTHOR=$(echo $PR_DATA | jq -r '.author')" >> $GITHUB_ENV - echo "PR_MERGER=$(echo $PR_DATA | jq -r '.merger')" >> $GITHUB_ENV - echo "MILESTONE=$(echo $PR_DATA | jq -r '.milestone')" >> $GITHUB_ENV - echo "BASE_BRANCH=$(echo $PR_DATA | jq -r '.baseRefName')" >> $GITHUB_ENV - echo "HEAD_BRANCH=$(echo $PR_DATA | jq -r '.headRefName')" >> $GITHUB_ENV - echo "PR_TITLE=$(echo $PR_DATA | jq -r '.title')" >> $GITHUB_ENV + PR_DATA=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/opencrvs/opencrvs-countryconfig/pulls/$PR_NUMBER) + echo "MILESTONE=$(printf '%s' $PR_DATA | jq -r '.milestone.title')" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - LATEST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid') - FIRST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[0].oid') - echo "LATEST_COMMIT_SHA=${LATEST_COMMIT_SHA}" >> $GITHUB_ENV - echo "FIRST_COMMIT_SHA=${FIRST_COMMIT_SHA}" >> $GITHUB_ENV + - name: Get PR details from event + if: ${{ github.event_name == 'pull_request' }} + id: get_pr_details_event + run: | + echo "MILESTONE=${{ github.event.pull_request.milestone.title }}" >> $GITHUB_ENV env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + + - name: Check for milestone and if release branch exists + continue-on-error: true + id: resolve-applicable-versions + run: | + if [ -z "${{ env.MILESTONE }}" ] || [ "${{ env.MILESTONE }}" = "null" ]; then + echo "No milestone set. Exiting." + exit 1 + fi + + filter_versions() { + local input_version=$1 + + # List remote branches, extract versions, and sort them semantically + versions=$(git ls-remote --heads origin 'release-*' | awk -F'release-' '{print $2}' | sort -V) + + # Filter out versions less than the input version + filtered_versions=$(echo "$versions" | awk -v input="$input_version" ' + function compare_versions(v1, v2) { + split(v1, a, /[.v]/); + split(v2, b, /[.v]/); + for (i = 2; i <= 4; i++) { + if (a[i] < b[i]) return -1; + if (a[i] > b[i]) return 1; + } + return 0; + } + { + if (compare_versions($0, input) >= 0) { + print $0 + } + }') + + # Keep only the highest patch version for each minor version + echo "$filtered_versions" | awk -F. ' + { + minor = $1 "." $2; + patches[minor] = $0; + } + END { + for (minor in patches) { + print patches[minor]; + } + }' | sort -V + } + + versions=$(filter_versions "${{ env.MILESTONE }}") + json_array=$(echo "$versions" | jq -R -s -c 'split("\n") | map(select(. != ""))') + echo "matrix=$json_array" >> $GITHUB_OUTPUT + + create-pr: + needs: resolve-releases + runs-on: ubuntu-22.04 + if: ${{ always() && needs.resolve-releases.result == 'success' }} + + strategy: + fail-fast: false + matrix: + version: ${{fromJson(needs.resolve-releases.outputs.matrix)}} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get PR details from workflow dispatch + if: ${{ github.event_name == 'workflow_dispatch' }} + id: get_pr_details_dispatch + run: | + PR_NUMBER=${{ github.event.inputs.pr_number }} + PR_DATA=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/opencrvs/opencrvs-countryconfig/pulls/$PR_NUMBER) + # printf escapes the newlines in the JSON, so we can use jq to parse output such as: + # "body": "![image](https://github.com/user-attachments/assets/8eee5bcf-7692-490f-a19f-576623e09961)\r\n", + echo "PR_ID=$(printf '%s' $PR_DATA | jq -r '.number')" >> $GITHUB_ENV + echo "PR_AUTHOR=$(printf '%s' $PR_DATA | jq -r '.user.login')" >> $GITHUB_ENV + echo "PR_MERGER=$(printf '%s' $PR_DATA | jq -r '.merged_by.login')" >> $GITHUB_ENV + echo "BASE_BRANCH=$(printf '%s' $PR_DATA | jq -r '.base.ref')" >> $GITHUB_ENV + echo "HEAD_BRANCH=$(printf '%s' $PR_DATA | jq -r '.head.ref')" >> $GITHUB_ENV + echo "PR_TITLE=$(printf '%s' $PR_DATA | jq -r '.title')" >> $GITHUB_ENV + echo "BASE_SHA=$(printf '%s' $PR_DATA | jq -r '.base.sha')" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - name: Get PR details from event if: ${{ github.event_name == 'pull_request' }} @@ -56,7 +142,6 @@ jobs: PR_NUMBER=${{ github.event.pull_request.number }} echo "PR_ID=${{ github.event.pull_request.number }}" >> $GITHUB_ENV echo "PR_AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV - echo "MILESTONE=${{ github.event.pull_request.milestone.title }}" >> $GITHUB_ENV echo "BASE_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV echo "HEAD_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV echo "PR_TITLE=${{ github.event.pull_request.title }}" >> $GITHUB_ENV @@ -70,40 +155,24 @@ jobs: MERGED_BY_LOGIN=$(echo "$PR_DETAILS" | jq -r '.mergedBy.login') echo "PR_MERGER=$MERGED_BY_LOGIN" >> $GITHUB_ENV env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check for milestone and if release branch exists - continue-on-error: true - id: check_release_branch - run: | - if [ -z "${{ env.MILESTONE }}" ]; then - echo "No milestone set. Exiting." - exit 1 - fi - - RELEASE_BRANCH="release-${{ env.MILESTONE }}" - - # Check if the release branch exists - if git ls-remote --heads origin $RELEASE_BRANCH | grep -q "refs/heads/$RELEASE_BRANCH"; then - echo "RELEASE_BRANCH=${RELEASE_BRANCH}" >> $GITHUB_ENV - else - echo "Release branch $RELEASE_BRANCH does not exist. Exiting." - exit 1 - fi + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - name: Create and push the new branch for the PR - if: ${{ steps.check_release_branch.outcome == 'success' }} + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} run: | SEMANTIC_PR_TITLE="${{ env.PR_TITLE }}" + RELEASE_BRANCH="release-${{ matrix.version }}" + MILESTONE="${{ matrix.version }}" # Check for semantic prefix if [[ $SEMANTIC_PR_TITLE =~ ^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert|wip|merge)\: ]]; then SEMANTIC_PR_TITLE="${BASH_REMATCH[1]}(${MILESTONE}): ${SEMANTIC_PR_TITLE#*: }" else - SEMANTIC_PR_TITLE="🍒 Merge changes from PR #${{ env.PR_ID }} to ${{ env.RELEASE_BRANCH }}" + SEMANTIC_PR_TITLE="🍒 Merge changes from PR #${{ env.PR_ID }} to $RELEASE_BRANCH" fi - PR_BODY="Automated PR to merge changes from develop to ${{ env.RELEASE_BRANCH }}" + PR_BODY="Automated PR to merge changes from develop to $RELEASE_BRANCH" # Configure git git config user.name "github-actions" @@ -111,11 +180,11 @@ jobs: git config advice.mergeConflict false # Fetch and checkout the release branch - git fetch --all - git checkout ${{ env.RELEASE_BRANCH }} + git fetch --all --unshallow + git checkout $RELEASE_BRANCH # Create a new branch for the PR - NEW_BRANCH="auto-pr-${{ env.RELEASE_BRANCH }}-${{ env.PR_ID }}-$RANDOM" + NEW_BRANCH="auto-pr-$RELEASE_BRANCH-${{ env.PR_ID }}-$RANDOM" git checkout -b $NEW_BRANCH echo "First commit: ${{ env.FIRST_COMMIT_SHA }}" @@ -167,6 +236,15 @@ jobs: " } + if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then + echo "This is a dry run." + echo "Would have pushed the new branch $NEW_BRANCH" + echo "PR title: $SEMANTIC_PR_TITLE" + echo "PR body:" + echo "$PR_BODY" + exit 0 + fi + # Push the new branch git push origin $NEW_BRANCH @@ -180,6 +258,4 @@ jobs: AUTHOR=${{ env.PR_MERGER }} fi fi - gh pr create --title "$SEMANTIC_PR_TITLE" --body "$PR_BODY" --head "$NEW_BRANCH" --base "${{ env.RELEASE_BRANCH }}" --assignee "$AUTHOR" --reviewer "$AUTHOR" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + gh pr create --title "$SEMANTIC_PR_TITLE" --body "$PR_BODY" --head "$NEW_BRANCH" --base "$RELEASE_BRANCH" --assignee "$AUTHOR" --reviewer "$AUTHOR" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b5a2afc5..ba833a9c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,12 @@ INSERT CSV ROWS IN ENGLISH ONLY - Github pipeline dedicated for reading secrets and variables from other environments now checks if GH_TOKEN is still valid before attempting other operations - Remove unnecessary UI dividers that add in various sections of the declaration forms(e.g the Death, Birth and Marriage forms) [#244](https://github.com/opencrvs/opencrvs-countryconfig/pull/244) +## 1.5.2 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.5.1...v1.5.2) + +## Bug fixes + +- Broken email alerts from low disk space are now fixed [293](https://github.com/opencrvs/opencrvs-countryconfig/pull/293) + ## 1.5.0 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.4.1...v1.5.0) ### Breaking changes diff --git a/infrastructure/monitoring/elastalert/rules/alert.yaml b/infrastructure/monitoring/elastalert/rules/alert.yaml index d5bfbcb50..a7064c695 100644 --- a/infrastructure/monitoring/elastalert/rules/alert.yaml +++ b/infrastructure/monitoring/elastalert/rules/alert.yaml @@ -26,13 +26,13 @@ filter: should: - term: rule.name.keyword: - value: 'Available disk space in data partition' + value: 'Available disk space in root file system' - term: rule.name.keyword: value: 'CPU under heavy load' - term: rule.name.keyword: - value: 'Low on available disk space' + value: 'Low on available disk space in data partition' minimum_should_match: 1 alert: post2 diff --git a/package.json b/package.json index 45f8ea317..6de9bf306 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "scripts": { "dev": "yarn start", "precommit": "lint-staged", - "test": "echo 'no tests, yet'", + "test": "vitest", "test:compilation": "tsc --noEmit", "lint": "eslint -c .eslintrc.js", "start": "cross-env NODE_ENV=development NODE_OPTIONS=--dns-result-order=ipv4first nodemon --exec ts-node -r tsconfig-paths/register src/index.ts", @@ -34,8 +34,8 @@ "@graphql-codegen/add": "^3.1.1", "@graphql-codegen/cli": "^3.3.1", "@graphql-codegen/introspection": "^3.0.1", - "@graphql-codegen/typescript-operations": "^3.0.4", "@graphql-codegen/typescript": "^3.0.4", + "@graphql-codegen/typescript-operations": "^3.0.4", "@inquirer/editor": "^1.2.13", "@octokit/core": "4.2.1", "@types/google-libphonenumber": "^7.4.23", @@ -49,11 +49,12 @@ "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "cypress-xpath": "^2.0.1", + "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "eslint": "^8.43.0", "husky": "1.0.0-rc.13", "inquirer": "^9.2.12", + "js-yaml": "^4.1.0", "kleur": "^4.1.5", "libsodium-wrappers": "^0.7.13", "lint-staged": "^7.1.0", @@ -61,7 +62,8 @@ "nodemon": "^2.0.22", "pino-pretty": "^11.0.0", "prettier": "^2.8.8", - "react-intl": "^6.4.3" + "react-intl": "^6.4.3", + "vitest": "^2.1.2" }, "dependencies": { "@faker-js/faker": "^6.0.0-alpha.5", @@ -76,8 +78,8 @@ "@types/hapi__hapi": "^20.0.0", "@types/jwt-decode": "^2.2.1", "@types/lodash": "^4.14.117", - "@types/node-fetch": "^2.6.2", "@types/node": "^10.12.5", + "@types/node-fetch": "^2.6.2", "@types/nodemailer": "^6.4.14", "app-module-path": "^2.2.0", "chalk": "^2.4.1", @@ -89,8 +91,8 @@ "dotenv": "^16.4.5", "esbuild": "^0.18.9", "google-libphonenumber": "^3.2.32", - "graphql-tag": "^2.12.6", "graphql": "^16.3.0", + "graphql-tag": "^2.12.6", "handlebars": "^4.7.7", "hapi-auth-jwt2": "10.4.0", "hapi-pino": "^9.0.0", diff --git a/tests/verify-elastalert-kibana-alerts-match.test.ts b/tests/verify-elastalert-kibana-alerts-match.test.ts new file mode 100644 index 000000000..4da5e82c5 --- /dev/null +++ b/tests/verify-elastalert-kibana-alerts-match.test.ts @@ -0,0 +1,54 @@ +import { readdirSync, readFileSync } from 'fs' +import yaml from 'js-yaml' +import { join } from 'path' +import { expect, it } from 'vitest' + +function findAllValuesByKey(obj: unknown, key: string): any[] { + const result: any[] = [] + + const recurse = (item: unknown) => { + if (Array.isArray(item)) { + for (const element of item) { + recurse(element) + } + } else if (typeof item === 'object' && item !== null) { + for (const k in item) { + if (k === key) { + result.push(item[k]) + } + recurse(item[k]) + } + } + } + + recurse(obj) + return result +} + +it('all tests defined in Kibana config are also defined in Elastalert config', () => { + const allAlertNames = readFileSync( + join(__dirname, '../infrastructure/monitoring/kibana', 'config.ndjson'), + 'utf8' + ) + .split('\n') + .map((str) => JSON.parse(str)) + .filter((item) => item.type === 'alert') + .map((item) => item.attributes.name) + .sort() + .filter((value, index, self) => self.indexOf(value) === index) + + const ruleNameFilters = readdirSync( + join(__dirname, '../infrastructure/monitoring/elastalert/rules') + ) + .map((file) => + join(__dirname, '../infrastructure/monitoring/elastalert/rules', file) + ) + .map((file) => readFileSync(file, 'utf8')) + .map((file) => yaml.load(file)) + .flatMap((rule) => findAllValuesByKey(rule, 'rule.name.keyword')) + .map((x) => x.value) + .sort() + .filter((value, index, self) => self.indexOf(value) === index) + + expect(ruleNameFilters).toEqual(allAlertNames) +}) diff --git a/yarn.lock b/yarn.lock index 584e11b1d..c5123f94d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -602,116 +602,231 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + "@esbuild/android-arm64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.9.tgz#9cad38830ea5fa714c196bb283c0958f49b64bfb" integrity sha512-G1rIBpSgjv0DEFmBYjljL85l4asf1dtQYwjoD02A5YG85JV3dsQSJL94vsEMWYMWkNd46hcvz3suURuY4dr+9g== +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + "@esbuild/android-arm@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.9.tgz#f0cfe4e981f901d290b32f3b3d322d867dc8b42f" integrity sha512-v1cr0l0RZOzIgLtTe8M1cRFFP0ICRdymPPa8HCPUpgZ+XasQrd5Mxyp9KlDqXLLyGmnZpzhufKEThLIihQL53A== +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + "@esbuild/android-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.9.tgz#d047bbe7f3e046650e46394132ca8553e4e4d4a6" integrity sha512-rPgcISGfoP7/Yk8+0eUf9R/KLCYGgqtojz/Uvj26wp7/EclwxoaOMArBnDChfuWF5YLdS16dDfqb4qwXS087lw== +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + "@esbuild/darwin-arm64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.9.tgz#22e2f880ef99f51e8f52a238170fa24d68f035e9" integrity sha512-vw9kWBT2EvDhLAVkI5c2KWFh+GMwgXrzR1QnIpZazA+tIacaelNLMMSTHEJisOeQqiMQhv8goTODFm9liS7wpw== +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + "@esbuild/darwin-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.9.tgz#e42d4b0a5315a6eab3591baa0d3be4fccfca0fb9" integrity sha512-tDbKKMUeS0PckRtIxdF3+NgkE19kTyLFmUQ0umgXDnBvcWC3/DqhZyu4P4Af3zBzOfWH5DAAmGW1hgy53Z706w== +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + "@esbuild/freebsd-arm64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.9.tgz#3ec3bb5a70edf0b890f11b8077c69ed297e0467e" integrity sha512-Anyk3qeTKJUcxiLE8VQ6y6frVuqFc71M5TEc2EzvXchoy6oWn5eZK+MpZBVnENVMSDA4wOjDKiFsPtVhnrhHHA== +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + "@esbuild/freebsd-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.9.tgz#87a33b75e4be4c7b3d2cf15aef0cd1e1b0d24043" integrity sha512-BsOYio/4p/6RWG+sDQXVYet8qQ0bB91rfO0YNk5s0HlqE9vEth3Yi1jFNi4v7bUA4vQDWWoybpA/9NTz1sM88A== +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + "@esbuild/linux-arm64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.9.tgz#0628d1559b6936aad8fa9b3df4eca6404d3a0669" integrity sha512-2fJtf4KKR301FrhRNY1KIgVid2nUrZV6fzx39E+JgT3jAw2NsZYUiphR31CyH4MloyoEwgQTnskwaQH+nT4bHA== +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + "@esbuild/linux-arm@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.9.tgz#4608fb5635e22a764310b2ff0a7bfafd2a29018b" integrity sha512-YotJBEt9swVrEBRBIXQzI03A4kDQSWk+mbGTTBreIRvWWWTXXqhNYZgqiwnEvtyQi9aqSipEzkRzAGNqs54EXw== +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + "@esbuild/linux-ia32@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.9.tgz#10f6d7001553c35f3b4cdda1cd0fa92b7bf14909" integrity sha512-pTTBAGi2lrduXo4vATnqCtFi9zRbyXOlcV+euznW5EoFyjAIR+JCQgFDeFCMo343E2EI2MgV7ZQctO8IWcsdsA== +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + "@esbuild/linux-loong64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.9.tgz#8d6283269b9e6afa9a4f5ad546e6a005395f873f" integrity sha512-hmsjvhwHrsCKPthXhhNjLE+QON8uQCE9P/OBktaYOD8UDfmz9+txm04uXhnkRH0fDEqStsDEedbX+8KPg1CwyA== +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + "@esbuild/linux-mips64el@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.9.tgz#cc734a77fa17118060dc45e9c5500626b5ba72e7" integrity sha512-Ymv4j25ie7mVEVlcThnOlRVvqDSsj22MJBH31QGMsyA0dUwReqCg9yNqRM2Dh8QHDRO2UrMhGmiL6BaTdBWlQw== +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + "@esbuild/linux-ppc64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.9.tgz#2f5ede177ba030070c17466c372d438690758297" integrity sha512-y2viEHwLpNfWP1eLa+vV+DWIbw/pQyv1Vf6qxSGJeBQmmu9T2hOagMiCr6zhDo89l+MUAXiShdKmqlKI6HdCkw== +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + "@esbuild/linux-riscv64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.9.tgz#b28f9ee395754ca65a261d0f724e52e8af4a1529" integrity sha512-na8WG8Z7z1EIUcJFuXKOawJEsq8luOur7LHK/ophO0+RSE8A9yxCsKYhaN9IxlR1UciAuHjo/7d5yiflABwUmA== +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + "@esbuild/linux-s390x@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.9.tgz#0f57a44ce133048218963b04075bb2956e4b2c3f" integrity sha512-XsnaI89KstE0jG4cMdzuJ8SKcKAod26had7U/4SzvuMrci0/XyEQXB1jikn6MB7LPGrd5rcLeYp3F7psUxhkWw== +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + "@esbuild/linux-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.9.tgz#6607e056fba189b2ac0f295492ce16344f995a64" integrity sha512-odEbmjtm3tLPtY43FRWOG+CLN7d4ooQpGjYVFVti5rLXLym26dORxnlbekNPXuQRuQKNMPczNNWE1jOc8yAyJQ== +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + "@esbuild/netbsd-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.9.tgz#0e67492dd5197bc6227234563465a66789ac5dde" integrity sha512-j/GgOjKNUPd54isC/RBYlS6CREbulnMWAJEIKTnPM0QnY0pEGfMHkFh73bsmZdovp/97zRty0NdePRk4dTP/cw== +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + "@esbuild/openbsd-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.9.tgz#03121aad11bbe3ef5e801f751cb4cef483efa147" integrity sha512-DN0Z9RGU/hlaMWSG9GaDLvlu0718u1HDGiF19wJ35fUznf9yJYgXDwZ5/cRQXUewHXJB0pD/VyQfRLDP3M4maw== +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + "@esbuild/sunos-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.9.tgz#9442369e234d7eb53d06fb385ad3a745af365790" integrity sha512-W/eHabLCXdki/8H3jmfE/ClDuh3bQQKpYfQHGQ7lQync9W72ZdVr2y1iWfEVTE7ZK/DQROo3GyfTkx5HPBZxmQ== +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + "@esbuild/win32-arm64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.9.tgz#6a233d4d0bafffad51253022952d97f22b0ad91a" integrity sha512-84FMz3Sh1hwGk/oWy6XGIW2bGVcsqvHLjjtbwd982XoTHOvQSthhrMef0J+4ShE1ZE7VeUXHIt2Mfer+myedYw== +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + "@esbuild/win32-ia32@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.9.tgz#f060d7609d70986324d486c71454d5be0a96f5e9" integrity sha512-/RsFTk0P13Nb+ixBVZfPdlLWKsP+he3ZLxOO/1eCsZZ2U7c/JxB053U7kURsyhhUPwiGzGVaAQAeyhGtYe8ehw== +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + "@esbuild/win32-x64@0.18.9": version "0.18.9" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.9.tgz#05686cd64032f1f46569dc19670c937381db2c86" integrity sha512-S+oBiO8UE1hmDJZlZJ6HZEdBBrxCGovwN66P9rle4DWVktM5fsMouYhpbtUf4WQLEy0HvcE2ZOQ2gIq8v0BkBw== +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1722,6 +1837,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -1885,6 +2005,86 @@ resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== +"@rollup/rollup-android-arm-eabi@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz#1661ff5ea9beb362795304cb916049aba7ac9c54" + integrity sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA== + +"@rollup/rollup-android-arm64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz#2ffaa91f1b55a0082b8a722525741aadcbd3971e" + integrity sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA== + +"@rollup/rollup-darwin-arm64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz#627007221b24b8cc3063703eee0b9177edf49c1f" + integrity sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA== + +"@rollup/rollup-darwin-x64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz#0605506142b9e796c370d59c5984ae95b9758724" + integrity sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz#62dfd196d4b10c0c2db833897164d2d319ee0cbb" + integrity sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA== + +"@rollup/rollup-linux-arm-musleabihf@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz#53ce72aeb982f1f34b58b380baafaf6a240fddb3" + integrity sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw== + +"@rollup/rollup-linux-arm64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz#1632990f62a75c74f43e4b14ab3597d7ed416496" + integrity sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA== + +"@rollup/rollup-linux-arm64-musl@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz#8c03a996efb41e257b414b2e0560b7a21f2d9065" + integrity sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw== + +"@rollup/rollup-linux-powerpc64le-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz#5b98729628d5bcc8f7f37b58b04d6845f85c7b5d" + integrity sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw== + +"@rollup/rollup-linux-riscv64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz#48e42e41f4cabf3573cfefcb448599c512e22983" + integrity sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg== + +"@rollup/rollup-linux-s390x-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz#e0b4f9a966872cb7d3e21b9e412a4b7efd7f0b58" + integrity sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g== + +"@rollup/rollup-linux-x64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz#78144741993100f47bd3da72fce215e077ae036b" + integrity sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A== + +"@rollup/rollup-linux-x64-musl@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz#d9fe32971883cd1bd858336bd33a1c3ca6146127" + integrity sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ== + +"@rollup/rollup-win32-arm64-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz#71fa3ea369316db703a909c790743972e98afae5" + integrity sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ== + +"@rollup/rollup-win32-ia32-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz#653f5989a60658e17d7576a3996deb3902e342e2" + integrity sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ== + +"@rollup/rollup-win32-x64-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz#0574d7e87b44ee8511d08cc7f914bcb802b70818" + integrity sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.1" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" @@ -2011,6 +2211,11 @@ dependencies: "@types/node" "*" +"@types/estree@1.0.6", "@types/estree@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/fhir@^0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/fhir/-/fhir-0.0.30.tgz#a0aec3b2d7dd2a7584474dac446ede5f9a4d7a13" @@ -2337,6 +2542,65 @@ "@typescript-eslint/types" "5.60.1" eslint-visitor-keys "^3.3.0" +"@vitest/expect@2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.2.tgz#e92fa284b8472548f72cacfe896020c64af6bf78" + integrity sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg== + dependencies: + "@vitest/spy" "2.1.2" + "@vitest/utils" "2.1.2" + chai "^5.1.1" + tinyrainbow "^1.2.0" + +"@vitest/mocker@2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.2.tgz#08853a9d8d12afba284aebdf9b5ea26ddae5f20a" + integrity sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA== + dependencies: + "@vitest/spy" "^2.1.0-beta.1" + estree-walker "^3.0.3" + magic-string "^0.30.11" + +"@vitest/pretty-format@2.1.2", "@vitest/pretty-format@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.2.tgz#42882ea18c4cd40428e34f74bbac706a82465193" + integrity sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA== + dependencies: + tinyrainbow "^1.2.0" + +"@vitest/runner@2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.2.tgz#14da1f5eac43fbd9a37d7cd72de102e8f785d727" + integrity sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw== + dependencies: + "@vitest/utils" "2.1.2" + pathe "^1.1.2" + +"@vitest/snapshot@2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.2.tgz#e20bd794b33fdcd4bfe69138baac7bb890c4d51f" + integrity sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA== + dependencies: + "@vitest/pretty-format" "2.1.2" + magic-string "^0.30.11" + pathe "^1.1.2" + +"@vitest/spy@2.1.2", "@vitest/spy@^2.1.0-beta.1": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.2.tgz#bccdeca597c8fc3777302889e8c98cec9264df44" + integrity sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A== + dependencies: + tinyspy "^3.0.0" + +"@vitest/utils@2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.2.tgz#222ac35ba02493173e40581256eb7a62520fcdba" + integrity sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ== + dependencies: + "@vitest/pretty-format" "2.1.2" + loupe "^3.1.1" + tinyrainbow "^1.2.0" + "@whatwg-node/events@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" @@ -2562,6 +2826,11 @@ asn1js@^3.0.1, asn1js@^3.0.5: pvutils "^1.1.3" tslib "^2.4.0" +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -2760,6 +3029,11 @@ busboy@^1.6.0: dependencies: streamsearch "^1.1.0" +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2835,6 +3109,17 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" +chai@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.1.tgz#f035d9792a22b481ead1c65908d14bb62ec1c82c" + integrity sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + chalk@*: version "5.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.1.2.tgz#d957f370038b75ac572471e83be4c5ca9f8e8c45" @@ -2928,6 +3213,11 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== + chokidar@^3.5.2: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -3311,6 +3601,13 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.6: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + 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" @@ -3326,6 +3623,11 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3515,6 +3817,35 @@ esbuild@^0.18.9: "@esbuild/win32-ia32" "0.18.9" "@esbuild/win32-x64" "0.18.9" +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -3656,6 +3987,13 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -3993,6 +4331,11 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4013,6 +4356,11 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" @@ -5144,6 +5492,13 @@ loose-envify@^1.0.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +loupe@^3.1.0, loupe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.1.tgz#71d038d59007d890e3247c5db97c1ec5a92edc54" + integrity sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw== + dependencies: + get-func-name "^2.0.1" + lower-case-first@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-2.0.2.tgz#64c2324a2250bf7c37c5901e76a5b5309301160b" @@ -5178,6 +5533,13 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== +magic-string@^0.30.11: + version "0.30.11" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.11.tgz#301a6f93b3e8c2cb13ac1a7a673492c0dfd12954" + integrity sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -5303,7 +5665,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -5323,6 +5685,11 @@ nan@^2.18.0, nan@^2.19.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0" integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw== +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -5778,11 +6145,26 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -5870,6 +6252,15 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcss@^8.4.43: + version "8.4.47" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" + integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.0" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -6180,6 +6571,31 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +rollup@^4.20.0: + version "4.24.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.0.tgz#c14a3576f20622ea6a5c9cad7caca5e6e9555d05" + integrity sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.24.0" + "@rollup/rollup-android-arm64" "4.24.0" + "@rollup/rollup-darwin-arm64" "4.24.0" + "@rollup/rollup-darwin-x64" "4.24.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.24.0" + "@rollup/rollup-linux-arm-musleabihf" "4.24.0" + "@rollup/rollup-linux-arm64-gnu" "4.24.0" + "@rollup/rollup-linux-arm64-musl" "4.24.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.24.0" + "@rollup/rollup-linux-riscv64-gnu" "4.24.0" + "@rollup/rollup-linux-s390x-gnu" "4.24.0" + "@rollup/rollup-linux-x64-gnu" "4.24.0" + "@rollup/rollup-linux-x64-musl" "4.24.0" + "@rollup/rollup-win32-arm64-msvc" "4.24.0" + "@rollup/rollup-win32-ia32-msvc" "4.24.0" + "@rollup/rollup-win32-x64-msvc" "4.24.0" + fsevents "~2.3.2" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -6373,6 +6789,11 @@ short-uid@^0.1.0: resolved "https://registry.yarnpkg.com/short-uid/-/short-uid-0.1.0.tgz#069d94d044fc498afb0ea7bb22c5e634979434c8" integrity sha512-FcrUzrOPh7gZcJ9piR15qs3bTSOyZWzpPFeKYK37A4Mr20wKnwskWFdYRj+mk8AWSO1wsflM+HgyeNTbgBecPg== +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -6485,6 +6906,11 @@ sonic-boom@^4.0.1: dependencies: atomic-sleep "^1.0.0" +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -6572,6 +6998,11 @@ ssh2@^1.14.0: cpu-features "~0.0.9" nan "^2.18.0" +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + staged-git-files@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.1.tgz#37c2218ef0d6d26178b1310719309a16a59f8f7b" @@ -6585,6 +7016,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +std-env@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== + stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" @@ -6764,6 +7200,31 @@ through@^2.3.6, through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.0.tgz#ed60cfce19c17799d4a241e06b31b0ec2bee69e6" + integrity sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg== + +tinypool@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.1.tgz#c64233c4fac4304e109a64340178760116dbe1fe" + integrity sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA== + +tinyrainbow@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" + integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== + +tinyspy@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== + title-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" @@ -7054,6 +7515,52 @@ value-or-promise@^1.0.12: resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== +vite-node@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.2.tgz#f5491a2b399959c9e2f3c4b70cb0cbaecf9be6d2" + integrity sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ== + dependencies: + cac "^6.7.14" + debug "^4.3.6" + pathe "^1.1.2" + vite "^5.0.0" + +vite@^5.0.0: + version "5.4.8" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.8.tgz#af548ce1c211b2785478d3ba3e8da51e39a287e8" + integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.2.tgz#f285fdde876749fddc0cb4d9748ae224443c1694" + integrity sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A== + dependencies: + "@vitest/expect" "2.1.2" + "@vitest/mocker" "2.1.2" + "@vitest/pretty-format" "^2.1.2" + "@vitest/runner" "2.1.2" + "@vitest/snapshot" "2.1.2" + "@vitest/spy" "2.1.2" + "@vitest/utils" "2.1.2" + chai "^5.1.1" + debug "^4.3.6" + magic-string "^0.30.11" + pathe "^1.1.2" + std-env "^3.7.0" + tinybench "^2.9.0" + tinyexec "^0.3.0" + tinypool "^1.0.0" + tinyrainbow "^1.2.0" + vite "^5.0.0" + vite-node "2.1.2" + why-is-node-running "^2.3.0" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -7114,6 +7621,14 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + word-wrap@^1.2.3: version "1.2.4" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" From 7bc24561845419c2f47662a6b252fee47c7efe38 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 28 Oct 2024 18:01:23 +0600 Subject: [PATCH 145/146] fix: update template transformers for fields `informantType`, `otherInformantType` --- src/utils/mapping/field-mapping-utils.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/utils/mapping/field-mapping-utils.ts b/src/utils/mapping/field-mapping-utils.ts index a4a4451ae..25f9200e6 100644 --- a/src/utils/mapping/field-mapping-utils.ts +++ b/src/utils/mapping/field-mapping-utils.ts @@ -135,11 +135,17 @@ export function getFieldMapping( }, template: { fieldName: certificateHandlebar, - operation: 'selectTransformer' + operation: 'fieldValueTransformer', + parameters: ['relationship'] } } case 'otherInformantType': return { + template: { + fieldName: certificateHandlebar, + operation: 'fieldValueTransformer', + parameters: ['otherRelationship'] + }, mutation: { operation: 'fieldValueSectionExchangeTransformer', parameters: ['registration', 'otherInformantType'] From 2f03a54000335feb980dd33f5b410b002441b490 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 28 Oct 2024 18:33:14 +0600 Subject: [PATCH 146/146] chore: update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba833a9c7..fca42f0c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ INSERT CSV ROWS IN ENGLISH ONLY - Github pipeline dedicated for reading secrets and variables from other environments now checks if GH_TOKEN is still valid before attempting other operations - Remove unnecessary UI dividers that add in various sections of the declaration forms(e.g the Death, Birth and Marriage forms) [#244](https://github.com/opencrvs/opencrvs-countryconfig/pull/244) +- Update template transformer for fields `informantType` and `otherInformantType` that fixes the bug of unavailability of these template fields [#5952](https://github.com/opencrvs/opencrvs-countryconfig/pull/5952) ## 1.5.2 (https://github.com/opencrvs/opencrvs-countryconfig/compare/v1.5.1...v1.5.2)