oro-sdk 3.4.0 → 3.6.1
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/dist/client.d.ts +2 -1
- package/dist/helpers/patient-registration.d.ts +2 -1
- package/dist/oro-sdk.cjs.development.js +117 -92
- package/dist/oro-sdk.cjs.development.js.map +1 -1
- package/dist/oro-sdk.cjs.production.min.js +1 -1
- package/dist/oro-sdk.cjs.production.min.js.map +1 -1
- package/dist/oro-sdk.esm.js +117 -92
- package/dist/oro-sdk.esm.js.map +1 -1
- package/package.json +58 -58
- package/src/client.ts +38 -35
- package/src/helpers/patient-registration.ts +70 -51
package/package.json
CHANGED
@@ -1,61 +1,61 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
},
|
13
|
-
"scripts": {
|
14
|
-
"link:watch": "npm link && tsdx watch",
|
15
|
-
"link:watch:apis": "npm link oro-sdk-apis && npm link && tsdx watch",
|
16
|
-
"start": "tsdx watch",
|
17
|
-
"build": "tsdx build",
|
18
|
-
"test": "tsdx test",
|
19
|
-
"lint": "tsdx lint",
|
20
|
-
"prepare": "tsdx build",
|
21
|
-
"size": "size-limit",
|
22
|
-
"analyze": "size-limit --why",
|
23
|
-
"package": "tsdx build && npm publish",
|
24
|
-
"pretty": "prettier --config ../../.prettierrc.yaml --write './src/**/*.{ts,js,json,md}' && prettier --write './*.md'"
|
25
|
-
},
|
26
|
-
"name": "oro-sdk",
|
27
|
-
"author": "Antoine Jaouën <antoine@orohealth.me>",
|
28
|
-
"module": "dist/oro-sdk.esm.js",
|
29
|
-
"description": "This package is intended to be run in browser only. It contains everything needed to interact with backend services, especially the vault",
|
30
|
-
"size-limit": [
|
31
|
-
{
|
32
|
-
"path": "dist/oro-sdk.cjs.production.min.js",
|
33
|
-
"limit": "10 KB"
|
2
|
+
"version": "3.6.1",
|
3
|
+
"main": "dist/index.js",
|
4
|
+
"typings": "dist/index.d.ts",
|
5
|
+
"files": [
|
6
|
+
"dist",
|
7
|
+
"src"
|
8
|
+
],
|
9
|
+
"engines": {
|
10
|
+
"node": ">=10",
|
11
|
+
"npm": ">=6.14.13"
|
34
12
|
},
|
35
|
-
{
|
36
|
-
|
37
|
-
|
13
|
+
"scripts": {
|
14
|
+
"link:watch": "npm link && tsdx watch",
|
15
|
+
"link:watch:apis": "npm link oro-sdk-apis && npm link && tsdx watch",
|
16
|
+
"start": "tsdx watch",
|
17
|
+
"build": "tsdx build",
|
18
|
+
"test": "tsdx test",
|
19
|
+
"lint": "tsdx lint",
|
20
|
+
"prepare": "tsdx build",
|
21
|
+
"size": "size-limit",
|
22
|
+
"analyze": "size-limit --why",
|
23
|
+
"package": "tsdx build && npm publish",
|
24
|
+
"pretty": "prettier --config ../../.prettierrc.yaml --write './src/**/*.{ts,js,json,md}' && prettier --write './*.md'"
|
25
|
+
},
|
26
|
+
"name": "oro-sdk",
|
27
|
+
"author": "Antoine Jaouën <antoine@orohealth.me>",
|
28
|
+
"module": "dist/oro-sdk.esm.js",
|
29
|
+
"description": "This package is intended to be run in browser only. It contains everything needed to interact with backend services, especially the vault",
|
30
|
+
"size-limit": [
|
31
|
+
{
|
32
|
+
"path": "dist/oro-sdk.cjs.production.min.js",
|
33
|
+
"limit": "10 KB"
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"path": "dist/oro-sdk.esm.js",
|
37
|
+
"limit": "10 KB"
|
38
|
+
}
|
39
|
+
],
|
40
|
+
"devDependencies": {
|
41
|
+
"@size-limit/preset-small-lib": "^4.10.2",
|
42
|
+
"@types/jest": "^27.4.1",
|
43
|
+
"@types/uuid": "^8.3.0",
|
44
|
+
"prettier": "^2.5.1",
|
45
|
+
"prettier-plugin-svelte": "^2.3.0",
|
46
|
+
"size-limit": "^4.10.2",
|
47
|
+
"tsdx": "^0.14.1",
|
48
|
+
"tslib": "^2.2.0",
|
49
|
+
"typescript": "^4.2.4"
|
50
|
+
},
|
51
|
+
"dependencies": {
|
52
|
+
"axios": "^0.21.4",
|
53
|
+
"axios-auth-refresh": "^3.2.1",
|
54
|
+
"form-data": "^4.0.0",
|
55
|
+
"formdata-node": "^4.3.1",
|
56
|
+
"idb-keyval": "^5.0.6",
|
57
|
+
"oro-sdk-apis": "1.41.0",
|
58
|
+
"oro-toolbox": "0.0.6",
|
59
|
+
"uuid": "^8.3.2"
|
38
60
|
}
|
39
|
-
|
40
|
-
"devDependencies": {
|
41
|
-
"@size-limit/preset-small-lib": "^4.10.2",
|
42
|
-
"@types/jest": "^27.4.1",
|
43
|
-
"@types/uuid": "^8.3.0",
|
44
|
-
"prettier": "^2.5.1",
|
45
|
-
"prettier-plugin-svelte": "^2.3.0",
|
46
|
-
"size-limit": "^4.10.2",
|
47
|
-
"tsdx": "^0.14.1",
|
48
|
-
"tslib": "^2.2.0",
|
49
|
-
"typescript": "^4.2.4"
|
50
|
-
},
|
51
|
-
"dependencies": {
|
52
|
-
"axios": "^0.21.4",
|
53
|
-
"axios-auth-refresh": "^3.2.1",
|
54
|
-
"form-data": "^4.0.0",
|
55
|
-
"formdata-node": "^4.3.1",
|
56
|
-
"idb-keyval": "^5.0.6",
|
57
|
-
"oro-sdk-apis": "1.40.0",
|
58
|
-
"oro-toolbox": "0.0.6",
|
59
|
-
"uuid": "^8.3.2"
|
60
|
-
}
|
61
|
-
}
|
61
|
+
}
|
package/src/client.ts
CHANGED
@@ -83,7 +83,7 @@ export class OroClient {
|
|
83
83
|
public workflowClient: WorkflowService,
|
84
84
|
public diagnosisClient: DiagnosisService,
|
85
85
|
private authenticationCallback?: (err: Error) => void
|
86
|
-
) {
|
86
|
+
) {}
|
87
87
|
|
88
88
|
/**
|
89
89
|
* clears the vaultIndex and cached metadata grants
|
@@ -280,6 +280,7 @@ export class OroClient {
|
|
280
280
|
* @param consult
|
281
281
|
* @param workflow
|
282
282
|
* @param recoveryQA
|
283
|
+
* @param indexSearch create search index for the consultation if true
|
283
284
|
* @returns
|
284
285
|
*/
|
285
286
|
public async registerPatient(
|
@@ -289,10 +290,11 @@ export class OroClient {
|
|
289
290
|
recoveryQA?: {
|
290
291
|
recoverySecurityQuestions: string[]
|
291
292
|
recoverySecurityAnswers: string[]
|
292
|
-
}
|
293
|
+
},
|
294
|
+
indexSearch: boolean = true
|
293
295
|
): Promise<RegisterPatientOutput> {
|
294
296
|
if (!this.rsa) throw IncompleteAuthentication
|
295
|
-
return registerPatient(patientUuid, consult, workflow, this, this.toolbox.uuid(), recoveryQA)
|
297
|
+
return registerPatient(patientUuid, consult, workflow, this, this.toolbox.uuid(), recoveryQA, indexSearch)
|
296
298
|
}
|
297
299
|
|
298
300
|
/**
|
@@ -391,18 +393,18 @@ export class OroClient {
|
|
391
393
|
}))
|
392
394
|
.map(
|
393
395
|
(e: IndexConsultLockbox) =>
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
396
|
+
({
|
397
|
+
uuid: e.uuid,
|
398
|
+
timestamp: e.timestamp,
|
399
|
+
uniqueHash: e.uniqueHash,
|
400
|
+
encryptedIndexEntry: CryptoRSA.jsonWithPubEncryptToBase64(
|
401
|
+
{
|
402
|
+
consultationId: e.consultationId,
|
403
|
+
grant: e.grant,
|
404
|
+
},
|
405
|
+
rsaPub
|
406
|
+
),
|
407
|
+
} as EncryptedIndexEntry)
|
406
408
|
)
|
407
409
|
break
|
408
410
|
//// DEPRECATED : REMOVE ME : BEGIN ///////////////////////////////////////////
|
@@ -424,18 +426,18 @@ export class OroClient {
|
|
424
426
|
)
|
425
427
|
.map(
|
426
428
|
(e: IndexConsultLockbox) =>
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
429
|
+
({
|
430
|
+
uuid: e.uuid,
|
431
|
+
timestamp: e.timestamp,
|
432
|
+
uniqueHash: e.uniqueHash,
|
433
|
+
encryptedIndexEntry: CryptoRSA.jsonWithPubEncryptToBase64(
|
434
|
+
{
|
435
|
+
consultationId: e.consultationId,
|
436
|
+
grant: e.grant,
|
437
|
+
},
|
438
|
+
rsaPub
|
439
|
+
),
|
440
|
+
} as EncryptedIndexEntry)
|
439
441
|
)
|
440
442
|
break
|
441
443
|
//// DEPRECATED : REMOVE ME : END ///////////////////////////////////////////
|
@@ -785,14 +787,15 @@ export class OroClient {
|
|
785
787
|
|
786
788
|
// if there is a filter to apply, then the grant can be retrieved from the vault index, otherwise, all grants are fetched
|
787
789
|
// Note: will work only if the filter being applied is exclusively a consult id
|
788
|
-
const grantsByConsultLockbox = filter
|
789
|
-
await this.vaultClient
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
790
|
+
const grantsByConsultLockbox = filter
|
791
|
+
? await this.vaultClient
|
792
|
+
.vaultIndexGet([IndexKey.ConsultationLockbox], [filter.consultationId])
|
793
|
+
.then((res) => res[IndexKey.ConsultationLockbox])
|
794
|
+
.catch((e) => {
|
795
|
+
console.error(e)
|
796
|
+
return []
|
797
|
+
})
|
798
|
+
: undefined
|
796
799
|
const decryptedConsults = decryptConsultLockboxGrants(grantsByConsultLockbox ?? [], this.rsa)
|
797
800
|
if (decryptedConsults.length > 0) {
|
798
801
|
console.info('[sdk:index] Grants found in user`s constant time secure index')
|
@@ -27,7 +27,7 @@ import {
|
|
27
27
|
identificationToPersonalInformations,
|
28
28
|
OroClient,
|
29
29
|
RegisterPatientOutput,
|
30
|
-
toActualObject
|
30
|
+
toActualObject,
|
31
31
|
} from '..'
|
32
32
|
|
33
33
|
const MAX_RETRIES = 15
|
@@ -50,6 +50,7 @@ const MAX_RETRIES = 15
|
|
50
50
|
* @param oroClient
|
51
51
|
* @param masterKey
|
52
52
|
* @param recoveryQA
|
53
|
+
* @param indexSearch create search index for the consultation if true
|
53
54
|
* @returns the successful registration
|
54
55
|
*/
|
55
56
|
export async function registerPatient(
|
@@ -61,7 +62,8 @@ export async function registerPatient(
|
|
61
62
|
recoveryQA?: {
|
62
63
|
recoverySecurityQuestions: string[]
|
63
64
|
recoverySecurityAnswers: string[]
|
64
|
-
}
|
65
|
+
},
|
66
|
+
indexSearch: boolean = true
|
65
67
|
): Promise<RegisterPatientOutput> {
|
66
68
|
let consult: Consult | undefined = undefined
|
67
69
|
let lockboxUuid: Uuid | undefined = undefined
|
@@ -95,8 +97,7 @@ export async function registerPatient(
|
|
95
97
|
// Creating lockbox
|
96
98
|
if (!lockboxUuid) lockboxUuid = await getOrCreatePatientLockbox(oroClient)
|
97
99
|
|
98
|
-
if (!identity)
|
99
|
-
identity = await oroClient.guardClient.identityGet(patientUuid)
|
100
|
+
if (!identity) identity = await oroClient.guardClient.identityGet(patientUuid)
|
100
101
|
|
101
102
|
await oroClient.grantLockbox(practitionerAdmin, lockboxUuid).catch((err) => {
|
102
103
|
console.error(`Error while granting lockbox to practitioner admin ${practitionerAdmin}`, err)
|
@@ -131,14 +132,16 @@ export async function registerPatient(
|
|
131
132
|
// the index will identify in which lockbox a consultation resides
|
132
133
|
let consultIndexPromises = practitioners.map(async (practitioner) => {
|
133
134
|
return oroClient.vaultIndexAdd(consultIndex, practitioner.uuid).catch((err) => {
|
134
|
-
console.error(
|
135
|
+
console.error(
|
136
|
+
`[SDK: registration] Error while adding to the practitioner's index ${practitioner.uuid}`,
|
137
|
+
err
|
138
|
+
)
|
135
139
|
// Acceptable to continue as the index can be rebuilt, but we should still retry until the last retry remains
|
136
140
|
if (retry <= 1) return
|
137
141
|
else errorsThrown.push(err)
|
138
142
|
})
|
139
143
|
})
|
140
144
|
|
141
|
-
|
142
145
|
await storeImageAliases(consult.uuid, lockboxUuid, workflow, oroClient).catch((err) => {
|
143
146
|
console.error('[SDK: registration] Some errors happened during image upload', err)
|
144
147
|
// Acceptable to continue as images can be requested during the consultation, but we should still retry until the last retry remains
|
@@ -146,13 +149,19 @@ export async function registerPatient(
|
|
146
149
|
else errorsThrown.push(err)
|
147
150
|
})
|
148
151
|
|
149
|
-
await storePatientData(
|
152
|
+
await storePatientData(
|
153
|
+
consult.uuid,
|
154
|
+
consultRequest.isoLanguageRequired,
|
155
|
+
lockboxUuid,
|
156
|
+
workflow,
|
157
|
+
oroClient
|
158
|
+
).catch((err) => {
|
150
159
|
console.error('[SDK: registration] Some errors happened during patient data upload', err)
|
151
160
|
errorsThrown.push(err)
|
152
161
|
})
|
153
162
|
|
154
163
|
if (masterKey && !identity?.recoveryMasterKey) {
|
155
|
-
// generate and store recovery payload and updates the identity
|
164
|
+
// generate and store recovery payload and updates the identity
|
156
165
|
identity = await oroClient.updateMasterKey(patientUuid, masterKey, lockboxUuid).catch((err) => {
|
157
166
|
console.error(`[SDK: registration] Error while updating master key`, err)
|
158
167
|
/// it's acceptable to continue registration (return old identity)
|
@@ -166,7 +175,7 @@ export async function registerPatient(
|
|
166
175
|
}
|
167
176
|
|
168
177
|
if (recoveryQA && !identity?.recoverySecurityQuestions)
|
169
|
-
// Patient security question recovery threshold is 2 answers and updates the identity
|
178
|
+
// Patient security question recovery threshold is 2 answers and updates the identity
|
170
179
|
identity = await oroClient
|
171
180
|
.updateSecurityQuestions(
|
172
181
|
patientUuid,
|
@@ -184,15 +193,19 @@ export async function registerPatient(
|
|
184
193
|
|
185
194
|
await Promise.all([...grantPromises, ...consultIndexPromises])
|
186
195
|
|
187
|
-
// TODO: uncomment when search is deployed in prod
|
188
|
-
// await buildConsultSearchIndex(consult, workflow, oroClient).catch((err) => {
|
189
|
-
// console.error('[SDK: registration] personal information not found or another error occured during search indexing', err)
|
190
|
-
// if(retry <= 1) return // this statement is to avoid failing the registration due to the failure in search indexing the consult, this practically implements a soft retry
|
191
|
-
// errorsThrown.push(err)
|
192
|
-
// })
|
193
196
|
|
194
|
-
if
|
195
|
-
|
197
|
+
if(indexSearch) {
|
198
|
+
await buildConsultSearchIndex(consult, workflow, oroClient).catch((err) => {
|
199
|
+
console.error(
|
200
|
+
'[SDK: registration] personal information not found or another error occured during search indexing',
|
201
|
+
err
|
202
|
+
)
|
203
|
+
if (retry <= 1) return // this statement is to avoid failing the registration due to the failure in search indexing the consult, this practically implements a soft retry
|
204
|
+
errorsThrown.push(err)
|
205
|
+
})
|
206
|
+
}
|
207
|
+
|
208
|
+
if (errorsThrown.length > 0) throw errorsThrown
|
196
209
|
|
197
210
|
// Deem the consultation as ready
|
198
211
|
await oroClient.consultClient.updateConsultByUUID(consult.uuid, {
|
@@ -417,14 +430,14 @@ export async function extractAndStorePersonalWorkflowData(
|
|
417
430
|
* @param workflow
|
418
431
|
*/
|
419
432
|
export async function extractPersonalInfoFromWorkflowData(workflow: WorkflowData): Promise<{
|
420
|
-
personalInfoPopulatedWfData: PopulatedWorkflowData
|
421
|
-
childPersonalInfoPopulatedWfData: PopulatedWorkflowData
|
422
|
-
otherPersonalInfoPopulatedWfData: PopulatedWorkflowData
|
433
|
+
personalInfoPopulatedWfData: PopulatedWorkflowData
|
434
|
+
childPersonalInfoPopulatedWfData: PopulatedWorkflowData
|
435
|
+
otherPersonalInfoPopulatedWfData: PopulatedWorkflowData
|
423
436
|
}> {
|
424
437
|
return Promise.all([
|
425
438
|
getWorkflowDataByCategory(workflow, MetadataCategory.Personal),
|
426
439
|
getWorkflowDataByCategory(workflow, MetadataCategory.ChildPersonal),
|
427
|
-
getWorkflowDataByCategory(workflow, MetadataCategory.OtherPersonal)
|
440
|
+
getWorkflowDataByCategory(workflow, MetadataCategory.OtherPersonal),
|
428
441
|
]).then(([personalInfoPopulatedWfData, childPersonalInfoPopulatedWfData, otherPersonalInfoPopulatedWfData]) => {
|
429
442
|
return {
|
430
443
|
personalInfoPopulatedWfData,
|
@@ -442,17 +455,14 @@ export async function extractPersonalInfoFromWorkflowData(workflow: WorkflowData
|
|
442
455
|
*/
|
443
456
|
export async function buildConsultSearchIndex(consult: Consult, workflow: WorkflowData, oroClient: OroClient) {
|
444
457
|
let terms: Terms = [
|
445
|
-
<Term>
|
458
|
+
<Term>{
|
446
459
|
kind: 'consult-shortid',
|
447
|
-
value: consult.shortId
|
448
|
-
}
|
460
|
+
value: consult.shortId,
|
461
|
+
},
|
449
462
|
]
|
450
463
|
|
451
|
-
const {
|
452
|
-
|
453
|
-
childPersonalInfoPopulatedWfData,
|
454
|
-
otherPersonalInfoPopulatedWfData
|
455
|
-
} = await extractPersonalInfoFromWorkflowData(workflow)
|
464
|
+
const { personalInfoPopulatedWfData, childPersonalInfoPopulatedWfData, otherPersonalInfoPopulatedWfData } =
|
465
|
+
await extractPersonalInfoFromWorkflowData(workflow)
|
456
466
|
|
457
467
|
const personalInfo = identificationToPersonalInformations(
|
458
468
|
toActualObject(personalInfoPopulatedWfData),
|
@@ -467,32 +477,41 @@ export async function buildConsultSearchIndex(consult: Consult, workflow: Workfl
|
|
467
477
|
MetadataCategory.OtherPersonal
|
468
478
|
)
|
469
479
|
|
470
|
-
terms.push(
|
471
|
-
|
472
|
-
value: personalInfo.firstname,
|
473
|
-
}, <Term> {
|
474
|
-
kind: 'last-name',
|
475
|
-
value: personalInfo.name
|
476
|
-
})
|
477
|
-
|
478
|
-
if(childPersonalInfo.firstname && childPersonalInfo.name) {
|
479
|
-
terms.push(<Term>{
|
480
|
+
terms.push(
|
481
|
+
<Term>{
|
480
482
|
kind: 'first-name',
|
481
|
-
value:
|
482
|
-
},
|
483
|
+
value: personalInfo.firstname,
|
484
|
+
},
|
485
|
+
<Term>{
|
483
486
|
kind: 'last-name',
|
484
|
-
value:
|
485
|
-
}
|
487
|
+
value: personalInfo.name,
|
488
|
+
}
|
489
|
+
)
|
490
|
+
|
491
|
+
if (childPersonalInfo.firstname && childPersonalInfo.name) {
|
492
|
+
terms.push(
|
493
|
+
<Term>{
|
494
|
+
kind: 'first-name',
|
495
|
+
value: childPersonalInfo.firstname,
|
496
|
+
},
|
497
|
+
<Term>{
|
498
|
+
kind: 'last-name',
|
499
|
+
value: childPersonalInfo.name,
|
500
|
+
}
|
501
|
+
)
|
486
502
|
}
|
487
503
|
|
488
|
-
if(otherPersonalInfo.firstname && otherPersonalInfo.name) {
|
489
|
-
terms.push(
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
504
|
+
if (otherPersonalInfo.firstname && otherPersonalInfo.name) {
|
505
|
+
terms.push(
|
506
|
+
<Term>{
|
507
|
+
kind: 'first-name',
|
508
|
+
value: otherPersonalInfo.firstname,
|
509
|
+
},
|
510
|
+
<Term>{
|
511
|
+
kind: 'last-name',
|
512
|
+
value: otherPersonalInfo.name,
|
513
|
+
}
|
514
|
+
)
|
496
515
|
}
|
497
516
|
|
498
517
|
await oroClient.searchClient.index(consult.uuid, terms)
|