@opencrvs/mosip 1.7.0-alpha.2 → 1.7.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/events.d.ts CHANGED
@@ -1,4 +1,30 @@
1
1
  import type * as Hapi from "@hapi/hapi";
2
+ interface VerificationStatus {
3
+ father: boolean;
4
+ mother: boolean;
5
+ informant: boolean;
6
+ }
7
+ /**
8
+ * Replaces event registration handler in country config
9
+ */
2
10
  export declare const mosipRegistrationHandler: ({ url }: {
3
11
  url: string;
4
12
  }) => (request: Hapi.Request, h: Hapi.ResponseToolkit) => Promise<Hapi.ResponseObject>;
13
+ /**
14
+ * Replaces `/events/{event}/actions/sent-notification-for-review` handler in country config
15
+ */
16
+ export declare const mosipRegistrationForReviewHandler: ({ url }: {
17
+ url: string;
18
+ }) => (request: Hapi.Request, h: Hapi.ResponseToolkit) => Promise<Hapi.ResponseObject>;
19
+ /**
20
+ * Replaces `/events/{event}/actions/sent-for-approval` handler in country config
21
+ * Currently the same as `/events/{event}/actions/sent-notification-for-review`
22
+ */
23
+ export declare const mosipRegistrationForApprovalHandler: ({ url }: {
24
+ url: string;
25
+ }) => (request: Hapi.Request, h: Hapi.ResponseToolkit) => Promise<Hapi.ResponseObject>;
26
+ export declare const verify: ({ url, request, }: {
27
+ url: string;
28
+ request: Hapi.Request;
29
+ }) => Promise<Partial<VerificationStatus>>;
30
+ export {};
package/build/events.js CHANGED
@@ -3,19 +3,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.mosipRegistrationHandler = void 0;
6
+ exports.verify = exports.mosipRegistrationForApprovalHandler = exports.mosipRegistrationForReviewHandler = exports.mosipRegistrationHandler = void 0;
7
7
  const node_fetch_1 = __importDefault(require("node-fetch"));
8
+ /**
9
+ * Replaces event registration handler in country config
10
+ */
8
11
  const mosipRegistrationHandler = ({ url }) => (async (request, h) => {
9
- const OPENCRVS_MOSIP_GATEWAY_URL = new URL("./webhooks/opencrvs", url);
10
- const response = await (0, node_fetch_1.default)(OPENCRVS_MOSIP_GATEWAY_URL, {
12
+ const MOSIP_API_REGISTRATION_EVENT_URL = new URL("./events/registration", url);
13
+ const response = await (0, node_fetch_1.default)(MOSIP_API_REGISTRATION_EVENT_URL, {
11
14
  method: "POST",
12
15
  body: JSON.stringify(request.payload),
13
- headers: request.headers,
16
+ headers: {
17
+ ...request.headers,
18
+ "content-type": "application/json",
19
+ },
14
20
  });
15
21
  if (!response.ok) {
16
22
  return h
17
23
  .response({
18
- error: "OpenCRVS-MOSIP gateway did not return a 200",
24
+ error: "OpenCRVS-MOSIP event registration route did not return a 200",
19
25
  response: await response.text(),
20
26
  })
21
27
  .code(500);
@@ -23,3 +29,44 @@ const mosipRegistrationHandler = ({ url }) => (async (request, h) => {
23
29
  return h.response({ success: true }).code(200);
24
30
  });
25
31
  exports.mosipRegistrationHandler = mosipRegistrationHandler;
32
+ /**
33
+ * Replaces `/events/{event}/actions/sent-notification-for-review` handler in country config
34
+ */
35
+ const mosipRegistrationForReviewHandler = ({ url }) => (async (request, h) => {
36
+ // Corresponds to `packages/mosip-api` /events/review -route
37
+ let response;
38
+ try {
39
+ response = await (0, exports.verify)({ url, request });
40
+ }
41
+ catch (error) {
42
+ return h
43
+ .response({
44
+ error: "OpenCRVS-MOSIP event review route did not return a 200",
45
+ response: error instanceof Error ? error.message : "Unknown error",
46
+ })
47
+ .code(500);
48
+ }
49
+ return h.response(response).code(200);
50
+ });
51
+ exports.mosipRegistrationForReviewHandler = mosipRegistrationForReviewHandler;
52
+ /**
53
+ * Replaces `/events/{event}/actions/sent-for-approval` handler in country config
54
+ * Currently the same as `/events/{event}/actions/sent-notification-for-review`
55
+ */
56
+ exports.mosipRegistrationForApprovalHandler = exports.mosipRegistrationForReviewHandler;
57
+ const verify = async ({ url, request, }) => {
58
+ const MOSIP_API_REVIEW_EVENT_URL = new URL("./events/review", url);
59
+ const response = await (0, node_fetch_1.default)(MOSIP_API_REVIEW_EVENT_URL, {
60
+ method: "POST",
61
+ body: JSON.stringify(request.payload),
62
+ headers: {
63
+ ...request.headers,
64
+ "content-type": "application/json",
65
+ },
66
+ });
67
+ if (!response.ok) {
68
+ throw new Error(await response.text());
69
+ }
70
+ return (await response.json());
71
+ };
72
+ exports.verify = verify;
package/build/forms.d.ts CHANGED
@@ -1,6 +1,46 @@
1
1
  /**
2
- * E-Signet popup button form definition
2
+ * E-Signet popup button and hidden field form definitions
3
+ * @example
4
+ * ```
5
+ * [
6
+ * // ...other fields
7
+ * ...esignet({ url: "https://opencrvs-mosip-gateway.farajaland.opencrvs.org" })
8
+ * ]
9
+ * ```
3
10
  */
11
+ /**
12
+ *
13
+ * @description E-Signet REDIRECT button form definition. Calls E-Signet /authorize (this field may not be supported in the latest release of OpenCRVS yet)
14
+ *
15
+ */
16
+ export declare const esignet: (esignetAuthUrl: string, openIdProviderClientId: string, openIdProviderClaims: string | undefined, fieldName: string, callbackFieldName: string) => {
17
+ name: string;
18
+ validator: never[];
19
+ icon: {
20
+ desktop: string;
21
+ mobile: string;
22
+ };
23
+ type: string;
24
+ custom: boolean;
25
+ label: {
26
+ id: string;
27
+ defaultMessage: string;
28
+ };
29
+ hideInPreview: boolean;
30
+ conditionals: {
31
+ action: string;
32
+ expression: string;
33
+ }[];
34
+ options: {
35
+ url: string;
36
+ callback: {
37
+ params: {
38
+ state: string;
39
+ };
40
+ trigger: string;
41
+ };
42
+ };
43
+ };
4
44
  export declare const popupButton: ({ url, }: {
5
45
  url: string;
6
46
  }) => {
@@ -8,24 +48,173 @@ export declare const popupButton: ({ url, }: {
8
48
  type: string;
9
49
  url: string;
10
50
  };
11
- export declare const hidden: () => {
51
+ /**
52
+ *
53
+ * @description esignet callback button form definition. Calls https://mosip-api.{{hostname}}/esignet/get-oidp-user-info
54
+ *
55
+ */
56
+ export declare const esignetCallback: ({ fieldName, mosipAPIUserInfoUrl, openIdProviderClientId, }: {
57
+ fieldName: string;
58
+ mosipAPIUserInfoUrl: string;
59
+ openIdProviderClientId: string;
60
+ }) => {
61
+ name: string;
62
+ type: string;
63
+ custom: boolean;
64
+ label: {
65
+ id: string;
66
+ defaultMessage: string;
67
+ };
68
+ validator: never[];
69
+ hideInPreview: boolean;
70
+ options: {
71
+ url: string;
72
+ headers: {
73
+ "Content-type": string;
74
+ };
75
+ body: {
76
+ clientId: string;
77
+ redirectUri: string;
78
+ };
79
+ method: string;
80
+ };
81
+ };
82
+ export declare const esignetHidden: () => {
12
83
  name: string;
13
84
  type: string;
14
85
  };
15
86
  /**
16
- * E-Signet popup button and hidden field form definitions
17
- * @example
18
- * ```
19
- * [
20
- * // ...other fields
21
- * ...esignet({ url: "https://opencrvs-mosip-gateway.farajaland.opencrvs.org" })
22
- * ]
23
- * ```
87
+ *
88
+ * @description QR reader type definition (this field may not be supported in the latest release of OpenCRVS yet)
89
+ *
24
90
  */
25
- export declare const esignet: ({ url, }: {
26
- /** URL to OpenCRVS-MOSIP gateway (e.g. https://opencrvs-mosip-gateway.farajaland.opencrvs.org) */
27
- url: string;
28
- }) => {
91
+ export declare const idReader: (event: string, sectionId: string, conditionals: any[], readers: any[]) => {
92
+ name: string;
93
+ customQuestionMappingId: string;
94
+ custom: boolean;
95
+ required: boolean;
96
+ type: string;
97
+ label: {
98
+ id: string;
99
+ defaultMessage: string;
100
+ };
101
+ hideInPreview: boolean;
102
+ initialValue: string;
103
+ validator: never[];
104
+ conditionals: any[];
105
+ dividerLabel: {
106
+ id: string;
107
+ defaultMessage: string;
108
+ };
109
+ manualInputInstructionLabel: {
110
+ id: string;
111
+ defaultMessage: string;
112
+ };
113
+ mapping: {
114
+ mutation: {
115
+ operation: string;
116
+ };
117
+ };
118
+ readers: any[];
119
+ };
120
+ type MessageDescriptor = {
121
+ id: string;
122
+ defaultMessage: string;
123
+ description?: string;
124
+ };
125
+ interface QRValidation {
126
+ rule: Record<string, unknown>;
127
+ errorMessage: MessageDescriptor;
128
+ }
129
+ interface QRConfig {
130
+ validation: QRValidation;
131
+ }
132
+ export declare const qr: ({ validation }: QRConfig) => {
133
+ type: string;
134
+ validation: QRValidation;
135
+ };
136
+ interface ESignetConfig {
137
+ esignetAuthUrl: string;
138
+ openIdProviderClientId: string;
139
+ openIdProviderClaims: string | undefined;
140
+ fieldName: string;
141
+ callback: {
142
+ fieldName: string;
143
+ mosipAPIUserInfoUrl: string;
144
+ };
145
+ }
146
+ export declare const verified: (event: string, sectionId: string, mapping: any) => {
147
+ name: string;
148
+ fieldId: string;
149
+ customQuestionMappingId: string;
150
+ type: string;
151
+ custom: boolean;
152
+ label: {
153
+ id: string;
154
+ defaultMessage: string;
155
+ };
156
+ initialValue: {
157
+ dependsOn: string[];
158
+ expression: string;
159
+ };
160
+ validator: never[];
161
+ mapping: any;
162
+ };
163
+ export declare const idVerificationBanner: (event: string, sectionId: string, status: "verified" | "failed" | "authenticated") => {
164
+ name: string;
165
+ type: string;
166
+ fieldId: string;
167
+ hideInPreview: boolean;
168
+ custom: boolean;
169
+ bannerType: "verified" | "failed" | "authenticated";
170
+ idFieldName: string;
171
+ label: {
172
+ id: string;
173
+ defaultMessage: string;
174
+ };
175
+ validator: never[];
176
+ conditionals: {
177
+ action: string;
178
+ expression: string;
179
+ }[];
180
+ };
181
+ export declare const getInitialValueFromIDReader: (fieldNameInReader: string) => {
182
+ dependsOn: string[];
183
+ expression: string;
184
+ };
185
+ export declare const idReaderFields: (event: "birth" | "death", section: "informant" | "mother" | "father", qrConfig: QRConfig, esignetConfig: ESignetConfig | undefined, verifiedCustomFieldMapping: any, conditionals?: any[]) => any[];
186
+ export declare const idVerificationFields: (event: string, sectionId: string, mapping: any) => ({
187
+ name: string;
188
+ fieldId: string;
189
+ customQuestionMappingId: string;
190
+ type: string;
191
+ custom: boolean;
192
+ label: {
193
+ id: string;
194
+ defaultMessage: string;
195
+ };
196
+ initialValue: {
197
+ dependsOn: string[];
198
+ expression: string;
199
+ };
200
+ validator: never[];
201
+ mapping: any;
202
+ } | {
29
203
  name: string;
30
204
  type: string;
31
- }[];
205
+ fieldId: string;
206
+ hideInPreview: boolean;
207
+ custom: boolean;
208
+ bannerType: "verified" | "failed" | "authenticated";
209
+ idFieldName: string;
210
+ label: {
211
+ id: string;
212
+ defaultMessage: string;
213
+ };
214
+ validator: never[];
215
+ conditionals: {
216
+ action: string;
217
+ expression: string;
218
+ }[];
219
+ })[];
220
+ export {};
package/build/forms.js CHANGED
@@ -1,11 +1,63 @@
1
1
  "use strict";
2
- // @TODO: we could start building these definitions to core tbh
3
- // @TODO: add renovate or something to listen for core updates and create a PRs, then we get an error message if we have broken this
2
+ // @TODO: Yet to be implemented! Placeholders for NPM publish.
4
3
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.esignet = exports.hidden = exports.popupButton = void 0;
4
+ exports.idVerificationFields = exports.idReaderFields = exports.getInitialValueFromIDReader = exports.idVerificationBanner = exports.verified = exports.qr = exports.idReader = exports.esignetHidden = exports.esignetCallback = exports.popupButton = exports.esignet = void 0;
6
5
  /**
7
- * E-Signet popup button form definition
6
+ * E-Signet popup button and hidden field form definitions
7
+ * @example
8
+ * ```
9
+ * [
10
+ * // ...other fields
11
+ * ...esignet({ url: "https://opencrvs-mosip-gateway.farajaland.opencrvs.org" })
12
+ * ]
13
+ * ```
14
+ */
15
+ /**
16
+ *
17
+ * @description E-Signet REDIRECT button form definition. Calls E-Signet /authorize (this field may not be supported in the latest release of OpenCRVS yet)
18
+ *
8
19
  */
20
+ const esignet = (esignetAuthUrl, openIdProviderClientId, openIdProviderClaims = "name,family_name,given_name,middle_name,birthdate,address", fieldName, callbackFieldName) => {
21
+ const url = new URL(esignetAuthUrl);
22
+ url.searchParams.append("client_id", openIdProviderClientId || "mock-client_id");
23
+ url.searchParams.append("response_type", "code");
24
+ url.searchParams.append("scope", "openid profile");
25
+ url.searchParams.append("acr_values", "mosip:idp:acr:static-code");
26
+ url.searchParams.append("claims", openIdProviderClaims);
27
+ url.searchParams.append("state", "fetch-on-mount");
28
+ url.searchParams.append("redirect_uri", "${window.location.href}");
29
+ return {
30
+ name: fieldName,
31
+ validator: [],
32
+ icon: {
33
+ desktop: "Globe",
34
+ mobile: "Fingerprint",
35
+ },
36
+ type: "LINK_BUTTON",
37
+ custom: true,
38
+ label: {
39
+ id: "views.idReader.label.eSignet",
40
+ defaultMessage: "E-signet",
41
+ },
42
+ hideInPreview: true,
43
+ conditionals: [
44
+ {
45
+ action: "disable",
46
+ expression: "!!$form.redirectCallbackFetch",
47
+ },
48
+ ],
49
+ options: {
50
+ url: url.toString(),
51
+ callback: {
52
+ params: {
53
+ state: "fetch-on-mount",
54
+ },
55
+ trigger: callbackFieldName,
56
+ },
57
+ },
58
+ };
59
+ };
60
+ exports.esignet = esignet;
9
61
  const popupButton = ({
10
62
  /** URL to OpenCRVS-MOSIP gateway (e.g. https://opencrvs-mosip-gateway.farajaland.opencrvs.org) */
11
63
  url, }) => {
@@ -16,22 +68,168 @@ url, }) => {
16
68
  };
17
69
  };
18
70
  exports.popupButton = popupButton;
19
- const hidden = () => {
71
+ /**
72
+ *
73
+ * @description esignet callback button form definition. Calls https://mosip-api.{{hostname}}/esignet/get-oidp-user-info
74
+ *
75
+ */
76
+ const esignetCallback = ({ fieldName, mosipAPIUserInfoUrl, openIdProviderClientId, }) => ({
77
+ name: fieldName,
78
+ type: "HTTP",
79
+ custom: true,
80
+ label: {
81
+ id: "form.field.label.empty",
82
+ defaultMessage: "",
83
+ },
84
+ validator: [],
85
+ hideInPreview: true,
86
+ options: {
87
+ url: mosipAPIUserInfoUrl,
88
+ headers: {
89
+ "Content-type": "application/json",
90
+ },
91
+ body: {
92
+ clientId: openIdProviderClientId,
93
+ redirectUri: "${window.location.href}",
94
+ },
95
+ method: "POST",
96
+ },
97
+ });
98
+ exports.esignetCallback = esignetCallback;
99
+ const esignetHidden = () => {
20
100
  return {
21
101
  name: "INFORMANT_PSUT_TOKEN",
22
102
  type: "HIDDEN",
23
103
  };
24
104
  };
25
- exports.hidden = hidden;
105
+ exports.esignetHidden = esignetHidden;
26
106
  /**
27
- * E-Signet popup button and hidden field form definitions
28
- * @example
29
- * ```
30
- * [
31
- * // ...other fields
32
- * ...esignet({ url: "https://opencrvs-mosip-gateway.farajaland.opencrvs.org" })
33
- * ]
34
- * ```
107
+ *
108
+ * @description QR reader type definition (this field may not be supported in the latest release of OpenCRVS yet)
109
+ *
35
110
  */
36
- const esignet = ({ url, }) => [(0, exports.popupButton)({ url }), (0, exports.hidden)()];
37
- exports.esignet = esignet;
111
+ const idReader = (event, sectionId, conditionals, readers) => {
112
+ const fieldName = "idReader";
113
+ const fieldId = `${event}.${sectionId}.${sectionId}-view-group.${fieldName}`;
114
+ return {
115
+ name: fieldName,
116
+ customQuestionMappingId: fieldId,
117
+ custom: true,
118
+ required: false,
119
+ type: "ID_READER",
120
+ label: {
121
+ id: "form.field.label.idReader",
122
+ defaultMessage: "ID verification",
123
+ },
124
+ hideInPreview: true,
125
+ initialValue: "",
126
+ validator: [],
127
+ conditionals,
128
+ dividerLabel: {
129
+ id: "views.idReader.label.or",
130
+ defaultMessage: "Or",
131
+ },
132
+ manualInputInstructionLabel: {
133
+ id: "views.idReader.label.manualInput",
134
+ defaultMessage: "Complete fields below",
135
+ },
136
+ mapping: {
137
+ mutation: {
138
+ operation: "ignoreFieldTransformer",
139
+ },
140
+ },
141
+ readers,
142
+ };
143
+ };
144
+ exports.idReader = idReader;
145
+ const qr = ({ validation }) => ({
146
+ type: "QR",
147
+ validation,
148
+ });
149
+ exports.qr = qr;
150
+ const verified = (event, sectionId, mapping) => {
151
+ const fieldName = "verified";
152
+ const fieldId = `${event}.${sectionId}.${sectionId}-view-group.${fieldName}`;
153
+ return {
154
+ name: "verified",
155
+ fieldId,
156
+ customQuestionMappingId: fieldId,
157
+ type: "HIDDEN",
158
+ custom: true,
159
+ label: {
160
+ id: "form.field.label.empty",
161
+ defaultMessage: "",
162
+ },
163
+ initialValue: {
164
+ dependsOn: ["idReader"],
165
+ expression: 'Boolean($form?.idReader)? "pending":""',
166
+ },
167
+ validator: [],
168
+ mapping,
169
+ };
170
+ };
171
+ exports.verified = verified;
172
+ function capitalize(str) {
173
+ return str.charAt(0).toUpperCase() + str.slice(1);
174
+ }
175
+ const idVerificationBanner = (event, sectionId, status) => {
176
+ const fieldName = "verified";
177
+ const fieldId = `${event}.${sectionId}.${sectionId}-view-group.${fieldName}`;
178
+ return {
179
+ name: `id${capitalize(status)}.`,
180
+ type: "ID_VERIFICATION_BANNER",
181
+ fieldId,
182
+ hideInPreview: true,
183
+ custom: true,
184
+ bannerType: status,
185
+ idFieldName: "idReader",
186
+ label: {
187
+ id: "form.field.label.empty",
188
+ defaultMessage: "",
189
+ },
190
+ validator: [],
191
+ conditionals: [
192
+ {
193
+ action: "hide",
194
+ expression: `$form?.verified !== "${status}"`,
195
+ },
196
+ ],
197
+ };
198
+ };
199
+ exports.idVerificationBanner = idVerificationBanner;
200
+ const getInitialValueFromIDReader = (fieldNameInReader) => ({
201
+ dependsOn: ["idReader", "esignetCallback"],
202
+ expression: `$form?.idReader?.${fieldNameInReader} || $form?.esignetCallback?.data?.${fieldNameInReader} || ""`,
203
+ });
204
+ exports.getInitialValueFromIDReader = getInitialValueFromIDReader;
205
+ const idReaderFields = (event, section, qrConfig, esignetConfig, verifiedCustomFieldMapping, conditionals = []) => {
206
+ const readers = [(0, exports.qr)(qrConfig)];
207
+ const fields = [
208
+ (0, exports.idReader)(event, section, conditionals.concat({
209
+ action: "hide",
210
+ expression: "$form?.verified === 'verified' || $form?.verified === 'authenticated' || $form?.verified === 'failed'",
211
+ }), readers),
212
+ ];
213
+ if (esignetConfig) {
214
+ readers.push((0, exports.esignet)(esignetConfig.esignetAuthUrl, esignetConfig.openIdProviderClientId, esignetConfig.openIdProviderClaims, esignetConfig.fieldName, esignetConfig.callback.fieldName));
215
+ fields.push((0, exports.esignetCallback)({
216
+ fieldName: esignetConfig.callback.fieldName,
217
+ mosipAPIUserInfoUrl: esignetConfig.callback.mosipAPIUserInfoUrl,
218
+ openIdProviderClientId: esignetConfig.openIdProviderClientId,
219
+ }));
220
+ }
221
+ return [
222
+ ...fields,
223
+ ...(0, exports.idVerificationFields)(event, section, verifiedCustomFieldMapping),
224
+ ];
225
+ };
226
+ exports.idReaderFields = idReaderFields;
227
+ const idVerificationFields = (event, sectionId, mapping) => {
228
+ return [
229
+ (0, exports.verified)(event, sectionId, mapping),
230
+ (0, exports.idVerificationBanner)(event, sectionId, "verified"),
231
+ (0, exports.idVerificationBanner)(event, sectionId, "failed"),
232
+ (0, exports.idVerificationBanner)(event, sectionId, "authenticated"),
233
+ ];
234
+ };
235
+ exports.idVerificationFields = idVerificationFields;
package/package.json CHANGED
@@ -1,26 +1,25 @@
1
1
  {
2
2
  "name": "@opencrvs/mosip",
3
- "version": "1.7.0-alpha.2",
3
+ "version": "1.7.0-alpha.21",
4
4
  "license": "MPL-2.0",
5
5
  "main": "./build/index.js",
6
6
  "exports": {
7
7
  ".": "./build/index.js",
8
- "./form-definitions": "./build/form-definitions.js",
8
+ "./forms": "./build/forms.js",
9
9
  "./events": "./build/events.js"
10
10
  },
11
11
  "typings": "build/index.d.ts",
12
12
  "scripts": {
13
13
  "build": "tsc"
14
14
  },
15
- "dependencies": {
16
- "@hapi/hapi": "^20.0.1"
17
- },
15
+ "dependencies": {},
18
16
  "devDependencies": {
19
17
  "@types/hapi__hapi": "^20.0.0",
20
18
  "@types/node-fetch": "^2.6.11",
19
+ "node-fetch": "2.6.7",
21
20
  "typescript": "^5.6.3"
22
21
  },
23
22
  "peerDependencies": {
24
- "node-fetch": "^3.3.2"
23
+ "node-fetch": "2.6.7"
25
24
  }
26
25
  }
package/src/events.ts CHANGED
@@ -1,20 +1,35 @@
1
1
  import type * as Hapi from "@hapi/hapi";
2
2
  import fetch from "node-fetch";
3
3
 
4
+ interface VerificationStatus {
5
+ father: boolean;
6
+ mother: boolean;
7
+ informant: boolean;
8
+ }
9
+
10
+ /**
11
+ * Replaces event registration handler in country config
12
+ */
4
13
  export const mosipRegistrationHandler = ({ url }: { url: string }) =>
5
14
  (async (request: Hapi.Request, h: Hapi.ResponseToolkit) => {
6
- const OPENCRVS_MOSIP_GATEWAY_URL = new URL("./webhooks/opencrvs", url);
15
+ const MOSIP_API_REGISTRATION_EVENT_URL = new URL(
16
+ "./events/registration",
17
+ url,
18
+ );
7
19
 
8
- const response = await fetch(OPENCRVS_MOSIP_GATEWAY_URL, {
20
+ const response = await fetch(MOSIP_API_REGISTRATION_EVENT_URL, {
9
21
  method: "POST",
10
22
  body: JSON.stringify(request.payload),
11
- headers: request.headers,
23
+ headers: {
24
+ ...request.headers,
25
+ "content-type": "application/json",
26
+ },
12
27
  });
13
28
 
14
29
  if (!response.ok) {
15
30
  return h
16
31
  .response({
17
- error: "OpenCRVS-MOSIP gateway did not return a 200",
32
+ error: "OpenCRVS-MOSIP event registration route did not return a 200",
18
33
  response: await response.text(),
19
34
  })
20
35
  .code(500);
@@ -22,3 +37,56 @@ export const mosipRegistrationHandler = ({ url }: { url: string }) =>
22
37
 
23
38
  return h.response({ success: true }).code(200);
24
39
  }) satisfies Hapi.ServerRoute["handler"];
40
+
41
+ /**
42
+ * Replaces `/events/{event}/actions/sent-notification-for-review` handler in country config
43
+ */
44
+ export const mosipRegistrationForReviewHandler = ({ url }: { url: string }) =>
45
+ (async (request: Hapi.Request, h: Hapi.ResponseToolkit) => {
46
+ // Corresponds to `packages/mosip-api` /events/review -route
47
+ let response;
48
+ try {
49
+ response = await verify({ url, request });
50
+ } catch (error) {
51
+ return h
52
+ .response({
53
+ error: "OpenCRVS-MOSIP event review route did not return a 200",
54
+ response: error instanceof Error ? error.message : "Unknown error",
55
+ })
56
+ .code(500);
57
+ }
58
+
59
+ return h.response(response).code(200);
60
+ }) satisfies Hapi.ServerRoute["handler"];
61
+
62
+ /**
63
+ * Replaces `/events/{event}/actions/sent-for-approval` handler in country config
64
+ * Currently the same as `/events/{event}/actions/sent-notification-for-review`
65
+ */
66
+ export const mosipRegistrationForApprovalHandler =
67
+ mosipRegistrationForReviewHandler;
68
+
69
+ export const verify = async ({
70
+ url,
71
+ request,
72
+ }: {
73
+ url: string;
74
+ request: Hapi.Request;
75
+ }): Promise<Partial<VerificationStatus>> => {
76
+ const MOSIP_API_REVIEW_EVENT_URL = new URL("./events/review", url);
77
+
78
+ const response = await fetch(MOSIP_API_REVIEW_EVENT_URL, {
79
+ method: "POST",
80
+ body: JSON.stringify(request.payload),
81
+ headers: {
82
+ ...request.headers,
83
+ "content-type": "application/json",
84
+ },
85
+ });
86
+
87
+ if (!response.ok) {
88
+ throw new Error(await response.text());
89
+ }
90
+
91
+ return (await response.json()) satisfies Partial<VerificationStatus>;
92
+ };
package/src/forms.ts CHANGED
@@ -1,9 +1,73 @@
1
- // @TODO: we could start building these definitions to core tbh
2
- // @TODO: add renovate or something to listen for core updates and create a PRs, then we get an error message if we have broken this
1
+ // @TODO: Yet to be implemented! Placeholders for NPM publish.
3
2
 
4
3
  /**
5
- * E-Signet popup button form definition
4
+ * E-Signet popup button and hidden field form definitions
5
+ * @example
6
+ * ```
7
+ * [
8
+ * // ...other fields
9
+ * ...esignet({ url: "https://opencrvs-mosip-gateway.farajaland.opencrvs.org" })
10
+ * ]
11
+ * ```
6
12
  */
13
+
14
+ /**
15
+ *
16
+ * @description E-Signet REDIRECT button form definition. Calls E-Signet /authorize (this field may not be supported in the latest release of OpenCRVS yet)
17
+ *
18
+ */
19
+ export const esignet = (
20
+ esignetAuthUrl: string,
21
+ openIdProviderClientId: string,
22
+ openIdProviderClaims: string = "name,family_name,given_name,middle_name,birthdate,address",
23
+ fieldName: string,
24
+ callbackFieldName: string,
25
+ ) => {
26
+ const url = new URL(esignetAuthUrl);
27
+
28
+ url.searchParams.append(
29
+ "client_id",
30
+ openIdProviderClientId || "mock-client_id",
31
+ );
32
+ url.searchParams.append("response_type", "code");
33
+ url.searchParams.append("scope", "openid profile");
34
+ url.searchParams.append("acr_values", "mosip:idp:acr:static-code");
35
+ url.searchParams.append("claims", openIdProviderClaims);
36
+ url.searchParams.append("state", "fetch-on-mount");
37
+ url.searchParams.append("redirect_uri", "${window.location.href}");
38
+
39
+ return {
40
+ name: fieldName,
41
+ validator: [],
42
+ icon: {
43
+ desktop: "Globe",
44
+ mobile: "Fingerprint",
45
+ },
46
+ type: "LINK_BUTTON",
47
+ custom: true,
48
+ label: {
49
+ id: "views.idReader.label.eSignet",
50
+ defaultMessage: "E-signet",
51
+ },
52
+ hideInPreview: true,
53
+ conditionals: [
54
+ {
55
+ action: "disable",
56
+ expression: "!!$form.redirectCallbackFetch",
57
+ },
58
+ ],
59
+ options: {
60
+ url: url.toString(),
61
+ callback: {
62
+ params: {
63
+ state: "fetch-on-mount",
64
+ },
65
+ trigger: callbackFieldName,
66
+ },
67
+ },
68
+ };
69
+ };
70
+
7
71
  export const popupButton = ({
8
72
  /** URL to OpenCRVS-MOSIP gateway (e.g. https://opencrvs-mosip-gateway.farajaland.opencrvs.org) */
9
73
  url,
@@ -17,7 +81,45 @@ export const popupButton = ({
17
81
  };
18
82
  };
19
83
 
20
- export const hidden = () => {
84
+ /**
85
+ *
86
+ * @description esignet callback button form definition. Calls https://mosip-api.{{hostname}}/esignet/get-oidp-user-info
87
+ *
88
+ */
89
+
90
+ export const esignetCallback = ({
91
+ fieldName,
92
+ mosipAPIUserInfoUrl,
93
+ openIdProviderClientId,
94
+ }: {
95
+ fieldName: string;
96
+ mosipAPIUserInfoUrl: string;
97
+ openIdProviderClientId: string;
98
+ }) => ({
99
+ name: fieldName,
100
+ type: "HTTP",
101
+ custom: true,
102
+ label: {
103
+ id: "form.field.label.empty",
104
+ defaultMessage: "",
105
+ },
106
+ validator: [],
107
+ hideInPreview: true,
108
+ options: {
109
+ url: mosipAPIUserInfoUrl,
110
+ headers: {
111
+ "Content-type": "application/json",
112
+ },
113
+ body: {
114
+ clientId: openIdProviderClientId,
115
+ redirectUri: "${window.location.href}",
116
+ },
117
+
118
+ method: "POST",
119
+ },
120
+ });
121
+
122
+ export const esignetHidden = () => {
21
123
  return {
22
124
  name: "INFORMANT_PSUT_TOKEN",
23
125
  type: "HIDDEN",
@@ -25,18 +127,194 @@ export const hidden = () => {
25
127
  };
26
128
 
27
129
  /**
28
- * E-Signet popup button and hidden field form definitions
29
- * @example
30
- * ```
31
- * [
32
- * // ...other fields
33
- * ...esignet({ url: "https://opencrvs-mosip-gateway.farajaland.opencrvs.org" })
34
- * ]
35
- * ```
130
+ *
131
+ * @description QR reader type definition (this field may not be supported in the latest release of OpenCRVS yet)
132
+ *
36
133
  */
37
- export const esignet = ({
38
- url,
39
- }: {
40
- /** URL to OpenCRVS-MOSIP gateway (e.g. https://opencrvs-mosip-gateway.farajaland.opencrvs.org) */
41
- url: string;
42
- }) => [popupButton({ url }), hidden()];
134
+
135
+ export const idReader = (
136
+ event: string,
137
+ sectionId: string,
138
+ conditionals: any[],
139
+ readers: any[],
140
+ ) => {
141
+ const fieldName: string = "idReader";
142
+ const fieldId: string = `${event}.${sectionId}.${sectionId}-view-group.${fieldName}`;
143
+ return {
144
+ name: fieldName,
145
+ customQuestionMappingId: fieldId,
146
+ custom: true,
147
+ required: false,
148
+ type: "ID_READER",
149
+ label: {
150
+ id: "form.field.label.idReader",
151
+ defaultMessage: "ID verification",
152
+ },
153
+ hideInPreview: true,
154
+ initialValue: "",
155
+ validator: [],
156
+ conditionals,
157
+ dividerLabel: {
158
+ id: "views.idReader.label.or",
159
+ defaultMessage: "Or",
160
+ },
161
+ manualInputInstructionLabel: {
162
+ id: "views.idReader.label.manualInput",
163
+ defaultMessage: "Complete fields below",
164
+ },
165
+ mapping: {
166
+ mutation: {
167
+ operation: "ignoreFieldTransformer",
168
+ },
169
+ },
170
+ readers,
171
+ };
172
+ };
173
+
174
+ type MessageDescriptor = {
175
+ id: string;
176
+ defaultMessage: string;
177
+ description?: string;
178
+ };
179
+
180
+ interface QRValidation {
181
+ rule: Record<string, unknown>;
182
+ errorMessage: MessageDescriptor;
183
+ }
184
+
185
+ interface QRConfig {
186
+ validation: QRValidation;
187
+ }
188
+
189
+ export const qr = ({ validation }: QRConfig) => ({
190
+ type: "QR",
191
+ validation,
192
+ });
193
+
194
+ interface ESignetConfig {
195
+ esignetAuthUrl: string;
196
+ openIdProviderClientId: string;
197
+ openIdProviderClaims: string | undefined;
198
+ fieldName: string;
199
+ callback: {
200
+ fieldName: string;
201
+ mosipAPIUserInfoUrl: string;
202
+ };
203
+ }
204
+
205
+ export const verified = (event: string, sectionId: string, mapping: any) => {
206
+ const fieldName = "verified";
207
+ const fieldId = `${event}.${sectionId}.${sectionId}-view-group.${fieldName}`;
208
+ return {
209
+ name: "verified",
210
+ fieldId,
211
+ customQuestionMappingId: fieldId,
212
+ type: "HIDDEN",
213
+ custom: true,
214
+ label: {
215
+ id: "form.field.label.empty",
216
+ defaultMessage: "",
217
+ },
218
+ initialValue: {
219
+ dependsOn: ["idReader"],
220
+ expression: 'Boolean($form?.idReader)? "pending":""',
221
+ },
222
+ validator: [],
223
+ mapping,
224
+ };
225
+ };
226
+
227
+ function capitalize(str: string) {
228
+ return str.charAt(0).toUpperCase() + str.slice(1);
229
+ }
230
+
231
+ export const idVerificationBanner = (
232
+ event: string,
233
+ sectionId: string,
234
+ status: "verified" | "failed" | "authenticated",
235
+ ) => {
236
+ const fieldName = "verified";
237
+ const fieldId = `${event}.${sectionId}.${sectionId}-view-group.${fieldName}`;
238
+ return {
239
+ name: `id${capitalize(status)}.`,
240
+ type: "ID_VERIFICATION_BANNER",
241
+ fieldId,
242
+ hideInPreview: true,
243
+ custom: true,
244
+ bannerType: status,
245
+ idFieldName: "idReader",
246
+ label: {
247
+ id: "form.field.label.empty",
248
+ defaultMessage: "",
249
+ },
250
+ validator: [],
251
+ conditionals: [
252
+ {
253
+ action: "hide",
254
+ expression: `$form?.verified !== "${status}"`,
255
+ },
256
+ ],
257
+ };
258
+ };
259
+
260
+ export const getInitialValueFromIDReader = (fieldNameInReader: string) => ({
261
+ dependsOn: ["idReader", "esignetCallback"],
262
+ expression: `$form?.idReader?.${fieldNameInReader} || $form?.esignetCallback?.data?.${fieldNameInReader} || ""`,
263
+ });
264
+
265
+ export const idReaderFields = (
266
+ event: "birth" | "death",
267
+ section: "informant" | "mother" | "father",
268
+ qrConfig: QRConfig,
269
+ esignetConfig: ESignetConfig | undefined,
270
+ verifiedCustomFieldMapping: any,
271
+ conditionals: any[] = [],
272
+ ) => {
273
+ const readers: any[] = [qr(qrConfig)];
274
+ const fields: any[] = [
275
+ idReader(
276
+ event,
277
+ section,
278
+ conditionals.concat({
279
+ action: "hide",
280
+ expression:
281
+ "$form?.verified === 'verified' || $form?.verified === 'authenticated' || $form?.verified === 'failed'",
282
+ }),
283
+ readers,
284
+ ),
285
+ ];
286
+ if (esignetConfig) {
287
+ readers.push(
288
+ esignet(
289
+ esignetConfig.esignetAuthUrl,
290
+ esignetConfig.openIdProviderClientId,
291
+ esignetConfig.openIdProviderClaims,
292
+ esignetConfig.fieldName,
293
+ esignetConfig.callback.fieldName,
294
+ ),
295
+ );
296
+ fields.push(
297
+ esignetCallback({
298
+ fieldName: esignetConfig.callback.fieldName,
299
+ mosipAPIUserInfoUrl: esignetConfig.callback.mosipAPIUserInfoUrl,
300
+ openIdProviderClientId: esignetConfig.openIdProviderClientId,
301
+ }),
302
+ );
303
+ }
304
+ return [
305
+ ...fields,
306
+ ...idVerificationFields(event, section, verifiedCustomFieldMapping),
307
+ ];
308
+ };
309
+ export const idVerificationFields = (
310
+ event: string,
311
+ sectionId: string,
312
+ mapping: any,
313
+ ) => {
314
+ return [
315
+ verified(event, sectionId, mapping),
316
+ idVerificationBanner(event, sectionId, "verified"),
317
+ idVerificationBanner(event, sectionId, "failed"),
318
+ idVerificationBanner(event, sectionId, "authenticated"),
319
+ ];
320
+ };
package/tsconfig.json CHANGED
@@ -8,4 +8,4 @@
8
8
  "declaration": true,
9
9
  "outDir": "./build"
10
10
  }
11
- }
11
+ }