oro-sdk 2.1.4-dev1
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/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/client.d.ts +464 -0
- package/dist/helpers/client.d.ts +23 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/patient-registration.d.ts +16 -0
- package/dist/helpers/vault-grants.d.ts +20 -0
- package/dist/helpers/workflow.d.ts +23 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +8 -0
- package/dist/models/client.d.ts +28 -0
- package/dist/models/consult.d.ts +102 -0
- package/dist/models/diagnosis.d.ts +122 -0
- package/dist/models/error.d.ts +26 -0
- package/dist/models/guard.d.ts +119 -0
- package/dist/models/index.d.ts +9 -0
- package/dist/models/practice.d.ts +353 -0
- package/dist/models/shared.d.ts +8 -0
- package/dist/models/vault.d.ts +124 -0
- package/dist/models/workflow.d.ts +106 -0
- package/dist/oro-sdk.cjs.development.js +7685 -0
- package/dist/oro-sdk.cjs.development.js.map +1 -0
- package/dist/oro-sdk.cjs.production.min.js +2 -0
- package/dist/oro-sdk.cjs.production.min.js.map +1 -0
- package/dist/oro-sdk.esm.js +7692 -0
- package/dist/oro-sdk.esm.js.map +1 -0
- package/dist/sdk-revision/client.d.ts +21 -0
- package/dist/sdk-revision/index.d.ts +1 -0
- package/dist/services/api.d.ts +11 -0
- package/dist/services/axios.d.ts +14 -0
- package/dist/services/consult.d.ts +54 -0
- package/dist/services/diagnosis.d.ts +44 -0
- package/dist/services/external/clinia.d.ts +82 -0
- package/dist/services/external/index.d.ts +1 -0
- package/dist/services/guard.d.ts +92 -0
- package/dist/services/index.d.ts +10 -0
- package/dist/services/practice.d.ts +100 -0
- package/dist/services/teller.d.ts +9 -0
- package/dist/services/vault.d.ts +54 -0
- package/dist/services/workflow.d.ts +21 -0
- package/package.json +63 -0
- package/src/client.ts +1843 -0
- package/src/helpers/client.ts +199 -0
- package/src/helpers/index.ts +4 -0
- package/src/helpers/patient-registration.ts +490 -0
- package/src/helpers/vault-grants.ts +51 -0
- package/src/helpers/workflow.ts +261 -0
- package/src/index.ts +61 -0
- package/src/models/client.ts +33 -0
- package/src/models/consult.ts +110 -0
- package/src/models/diagnosis.ts +141 -0
- package/src/models/error.ts +13 -0
- package/src/models/guard.ts +136 -0
- package/src/models/index.ts +9 -0
- package/src/models/practice.ts +411 -0
- package/src/models/shared.ts +6 -0
- package/src/models/vault.ts +158 -0
- package/src/models/workflow.ts +142 -0
- package/src/sdk-revision/client.ts +62 -0
- package/src/sdk-revision/index.ts +1 -0
- package/src/services/api.ts +77 -0
- package/src/services/axios.ts +91 -0
- package/src/services/consult.ts +265 -0
- package/src/services/diagnosis.ts +144 -0
- package/src/services/external/clinia.ts +133 -0
- package/src/services/external/index.ts +1 -0
- package/src/services/guard.ts +228 -0
- package/src/services/index.ts +10 -0
- package/src/services/practice.ts +537 -0
- package/src/services/teller.ts +39 -0
- package/src/services/vault.ts +178 -0
- package/src/services/workflow.ts +36 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
import {
|
2
|
+
PopulatedWorkflowData,
|
3
|
+
MetadataCategory,
|
4
|
+
SelectedAnswersData,
|
5
|
+
} from '../models'
|
6
|
+
import { PersonalInformations } from '../models/client'
|
7
|
+
|
8
|
+
const personalMetaToPrefix = {
|
9
|
+
[MetadataCategory.Personal]: 'you',
|
10
|
+
[MetadataCategory.ChildPersonal]: 'child',
|
11
|
+
[MetadataCategory.OtherPersonal]: 'other',
|
12
|
+
}
|
13
|
+
|
14
|
+
/**
|
15
|
+
* This function extract PersonalInformations from data input object coming from workflow
|
16
|
+
* @param data extracted from WorkflowData
|
17
|
+
* @returns PersonalInformations of a patient
|
18
|
+
*/
|
19
|
+
export function identificationToPersonalInformations(
|
20
|
+
data: any,
|
21
|
+
category:
|
22
|
+
| MetadataCategory.Personal
|
23
|
+
| MetadataCategory.ChildPersonal
|
24
|
+
| MetadataCategory.OtherPersonal
|
25
|
+
): PersonalInformations {
|
26
|
+
const prefix = personalMetaToPrefix[category]
|
27
|
+
|
28
|
+
return {
|
29
|
+
birthday: data[`${prefix}Birthday`],
|
30
|
+
firstname: data[`${prefix}Firstname`],
|
31
|
+
gender: data[`${prefix}Gender`],
|
32
|
+
name: data[`${prefix}Name`],
|
33
|
+
phone: data[`${prefix}Phone`],
|
34
|
+
zip: data[`${prefix}Zip`],
|
35
|
+
hid: data[`${prefix}HID`] ?? data[`${prefix}ID`], // This is done for backward compatibility (historically youID was used)
|
36
|
+
pharmacy: data[`${prefix}Pharmacy`],
|
37
|
+
address: data[`${prefix}Address`],
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
export function toActualObject(data: PopulatedWorkflowData) {
|
42
|
+
const ret: any = {}
|
43
|
+
|
44
|
+
Object.entries(data.fields).forEach(([key, field]) => {
|
45
|
+
ret[key] = field.displayedAnswer ? field.displayedAnswer : field.answer
|
46
|
+
})
|
47
|
+
|
48
|
+
return ret
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* This function update a PopulatedWorkflowData with PersonalInformations
|
53
|
+
* @param infos the personal informations
|
54
|
+
* @param data the PopulatedWorkflowData
|
55
|
+
* @returns an updated PopulatedWorkflowData
|
56
|
+
*/
|
57
|
+
export function updatePersonalIntoPopulatedWorkflowData(
|
58
|
+
infos: PersonalInformations,
|
59
|
+
data: PopulatedWorkflowData,
|
60
|
+
category:
|
61
|
+
| MetadataCategory.Personal
|
62
|
+
| MetadataCategory.ChildPersonal
|
63
|
+
| MetadataCategory.OtherPersonal
|
64
|
+
) {
|
65
|
+
const prefix = personalMetaToPrefix[category]
|
66
|
+
|
67
|
+
const ret = JSON.parse(JSON.stringify(data)) // deep copy PopulatedWorkflowData
|
68
|
+
|
69
|
+
if (infos.birthday && ret.fields[`${prefix}Birthday`])
|
70
|
+
ret.fields[`${prefix}Birthday`].answer = infos.birthday
|
71
|
+
if (infos.firstname && ret.fields[`${prefix}Firstname`])
|
72
|
+
ret.fields[`${prefix}Firstname`].answer = infos.firstname
|
73
|
+
if (infos.gender && ret.fields[`${prefix}Gender`])
|
74
|
+
ret.fields[`${prefix}Gender`].answer = infos.gender
|
75
|
+
if (infos.name && ret.fields[`${prefix}Name`])
|
76
|
+
ret.fields[`${prefix}Name`].answer = infos.name
|
77
|
+
if (infos.phone && ret.fields[`${prefix}Phone`])
|
78
|
+
ret.fields[`${prefix}Phone`].answer = infos.phone
|
79
|
+
if (infos.zip && ret.fields[`${prefix}Zip`])
|
80
|
+
ret.fields[`${prefix}Zip`].answer = infos.zip
|
81
|
+
if (infos.hid) {
|
82
|
+
if (ret.fields[`${prefix}HID`]) {
|
83
|
+
ret.fields[`${prefix}HID`].answer = infos.hid
|
84
|
+
} else if (ret.fields[`${prefix}ID`]) {
|
85
|
+
// This is done for backward compatibility (historically youID was used)
|
86
|
+
ret.fields[`${prefix}ID`].answer = infos.hid
|
87
|
+
} else {
|
88
|
+
// If does not exist create it
|
89
|
+
ret.fields[`${prefix}HID`] = { kind: 'text', answer: infos.hid }
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
return ret
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* This function extract an ISO 3166-1 alpha-2 country and subdivision code from data input object coming from workflow
|
98
|
+
* @param answers answers from the WorkflowData
|
99
|
+
* @returns an ISO 3166 alpha-2 code or undefined
|
100
|
+
*/
|
101
|
+
export function extractISOLocalityForConsult(
|
102
|
+
answers?: SelectedAnswersData
|
103
|
+
): string | undefined {
|
104
|
+
if (!answers) {
|
105
|
+
return undefined
|
106
|
+
}
|
107
|
+
|
108
|
+
const arrAnswersWithLocality = answers
|
109
|
+
.flatMap((currentAnswerPage) => {
|
110
|
+
const arrCountryFields = Object.keys(currentAnswerPage)
|
111
|
+
.filter(
|
112
|
+
(workflowFieldName) =>
|
113
|
+
workflowFieldName.indexOf('Country') !== -1
|
114
|
+
)
|
115
|
+
.flat()
|
116
|
+
const arrProvinceFields = Object.keys(currentAnswerPage)
|
117
|
+
.filter(
|
118
|
+
(workflowFieldName) =>
|
119
|
+
workflowFieldName.indexOf('Province') !== -1
|
120
|
+
)
|
121
|
+
.flat()
|
122
|
+
const arrConsultLocalFields = Object.keys(currentAnswerPage)
|
123
|
+
.filter(
|
124
|
+
(workflowFieldName) =>
|
125
|
+
workflowFieldName.indexOf('Locality') !== -1
|
126
|
+
)
|
127
|
+
.flat()
|
128
|
+
//returning the actual selected values, skipping if their IDs are more complex than a string
|
129
|
+
return [
|
130
|
+
...arrCountryFields.map(
|
131
|
+
(currentFieldName) =>
|
132
|
+
(typeof currentAnswerPage[currentFieldName] === 'string'
|
133
|
+
? currentAnswerPage[currentFieldName]
|
134
|
+
: undefined) as string
|
135
|
+
),
|
136
|
+
...arrProvinceFields.map(
|
137
|
+
(currentFieldName) =>
|
138
|
+
(typeof currentAnswerPage[currentFieldName] === 'string'
|
139
|
+
? currentAnswerPage[currentFieldName]
|
140
|
+
: undefined) as string
|
141
|
+
),
|
142
|
+
...arrConsultLocalFields.map(
|
143
|
+
(currentFieldName) =>
|
144
|
+
(typeof currentAnswerPage[currentFieldName] === 'string'
|
145
|
+
? currentAnswerPage[currentFieldName]
|
146
|
+
: undefined) as string
|
147
|
+
),
|
148
|
+
]
|
149
|
+
})
|
150
|
+
.filter((item) => item !== undefined)
|
151
|
+
|
152
|
+
const arrSelectedLocality = arrAnswersWithLocality.filter(
|
153
|
+
(currentSelectedLocality) =>
|
154
|
+
currentSelectedLocality.startsWith('isoLocalityConsult')
|
155
|
+
)
|
156
|
+
if (!arrSelectedLocality || arrSelectedLocality.length === 0) {
|
157
|
+
console.log('no locality found in ' + arrSelectedLocality)
|
158
|
+
return undefined
|
159
|
+
}
|
160
|
+
//to allow enforcing of an order, we will allow the following pattern in the isoLocalityConsult field name
|
161
|
+
// isoLocalityConsult-QC-CA and isoLocalityConsult_1-QC-CA
|
162
|
+
// or generally: isoLocalityConsult-<isoValue> or isoLocalityConsult_<priority>-<isoValue>
|
163
|
+
const allowedLocalityPatterns = /isoLocalityConsult(?:_(?<indexPriority>\d*))?-(?<isoValue>[a-zA-Z0-9]{2}-[a-zA-Z0-9]{1,3})/
|
164
|
+
const finalLocality = arrSelectedLocality.reduce<string | undefined>(
|
165
|
+
(finalLocality, currentSelectedLocality) => {
|
166
|
+
const extractedSelected = allowedLocalityPatterns.exec(
|
167
|
+
currentSelectedLocality
|
168
|
+
)
|
169
|
+
const [, indexSelectedPriority, isoSelectedValue] =
|
170
|
+
extractedSelected ?? []
|
171
|
+
if (!finalLocality) {
|
172
|
+
return isoSelectedValue
|
173
|
+
}
|
174
|
+
|
175
|
+
const extractedFinal = allowedLocalityPatterns.exec(finalLocality)
|
176
|
+
const [, indexFinalPriority, isoFinalValue] = extractedFinal ?? []
|
177
|
+
//we only keep the old value if there's priority used
|
178
|
+
// and the new value is of lower priority
|
179
|
+
if (
|
180
|
+
!indexSelectedPriority ||
|
181
|
+
(indexFinalPriority &&
|
182
|
+
indexFinalPriority > indexSelectedPriority)
|
183
|
+
) {
|
184
|
+
return isoFinalValue
|
185
|
+
}
|
186
|
+
|
187
|
+
return isoSelectedValue
|
188
|
+
},
|
189
|
+
undefined
|
190
|
+
)
|
191
|
+
|
192
|
+
console.log('Picking locality ' + finalLocality)
|
193
|
+
return finalLocality
|
194
|
+
}
|
195
|
+
|
196
|
+
const sessionPrivateKeyPrefix = 'sess-pkey'
|
197
|
+
export function sessionStorePrivateKeyName(id: string): string {
|
198
|
+
return sessionPrivateKeyPrefix + id
|
199
|
+
}
|
@@ -0,0 +1,490 @@
|
|
1
|
+
import {
|
2
|
+
Consult,
|
3
|
+
ConsultationImageMeta,
|
4
|
+
getImagesFromIndexDb,
|
5
|
+
getWorkflowDataByCategory,
|
6
|
+
filterTriggeredAnsweredWithKind,
|
7
|
+
MedicalStatus,
|
8
|
+
OroClient,
|
9
|
+
PersonalMeta,
|
10
|
+
Practitioner,
|
11
|
+
RegisterPatientOutput,
|
12
|
+
} from '..'
|
13
|
+
import {
|
14
|
+
ConsultationMeta,
|
15
|
+
ConsultRequest,
|
16
|
+
DocumentType,
|
17
|
+
IndexKey,
|
18
|
+
MedicalMeta,
|
19
|
+
MetadataCategory,
|
20
|
+
PreferenceMeta,
|
21
|
+
RawConsultationMeta,
|
22
|
+
Uuid,
|
23
|
+
VaultIndex,
|
24
|
+
WorkflowData,
|
25
|
+
} from '../models'
|
26
|
+
|
27
|
+
const MAX_RETRIES = 15
|
28
|
+
|
29
|
+
export async function registerPatient(
|
30
|
+
patientUuid: Uuid,
|
31
|
+
consultRequest: ConsultRequest,
|
32
|
+
workflow: WorkflowData,
|
33
|
+
oroClient: OroClient,
|
34
|
+
masterKey?: Uuid,
|
35
|
+
recoveryQA?: {
|
36
|
+
recoverySecurityQuestions: string[]
|
37
|
+
recoverySecurityAnswers: string[]
|
38
|
+
}
|
39
|
+
): Promise<RegisterPatientOutput> {
|
40
|
+
let consult: Consult | undefined = undefined
|
41
|
+
let lockboxUuid: Uuid | undefined = undefined
|
42
|
+
let practitionerAdmin: Uuid | undefined = undefined
|
43
|
+
let retry = MAX_RETRIES
|
44
|
+
let isExistingPatient = false
|
45
|
+
|
46
|
+
for (; retry > 0; retry--) {
|
47
|
+
try {
|
48
|
+
// Wait a bit each retry (we also want the first one to wait)
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
50
|
+
|
51
|
+
// Retrieving practitioners
|
52
|
+
if (!practitionerAdmin)
|
53
|
+
practitionerAdmin = (
|
54
|
+
await oroClient.practiceClient.practiceGetFromUuid(
|
55
|
+
consultRequest.uuidPractice
|
56
|
+
)
|
57
|
+
).uuidAdmin
|
58
|
+
|
59
|
+
let practitioners: Practitioner[] = await oroClient.practiceClient
|
60
|
+
.practiceGetPractitioners(consultRequest.uuidPractice)
|
61
|
+
.catch((err) => {
|
62
|
+
console.log(`Error retrieving practitioners`, err)
|
63
|
+
return []
|
64
|
+
})
|
65
|
+
|
66
|
+
// Creating consult
|
67
|
+
if (!consult) {
|
68
|
+
consult = await getOrCreatePatientConsultationUuid(
|
69
|
+
consultRequest,
|
70
|
+
oroClient
|
71
|
+
)
|
72
|
+
}
|
73
|
+
|
74
|
+
// Creating lockbox
|
75
|
+
if (!lockboxUuid)
|
76
|
+
lockboxUuid = await getOrCreatePatientLockbox(oroClient)
|
77
|
+
|
78
|
+
isExistingPatient = await patientConsultExists(
|
79
|
+
consult,
|
80
|
+
lockboxUuid,
|
81
|
+
oroClient
|
82
|
+
).catch((err) => {
|
83
|
+
console.error(`Error when retrieving existing consults ${err}`)
|
84
|
+
return false
|
85
|
+
})
|
86
|
+
await oroClient
|
87
|
+
.grantLockbox(practitionerAdmin, lockboxUuid)
|
88
|
+
.catch((err) => {
|
89
|
+
console.error(
|
90
|
+
`Error while granting lockbox to practitioner admin ${practitionerAdmin}`,
|
91
|
+
err
|
92
|
+
)
|
93
|
+
// if we cannot grant to the admin, then the registration will fail
|
94
|
+
throw err
|
95
|
+
})
|
96
|
+
|
97
|
+
// Patient Grant to practice
|
98
|
+
let grantPromises = practitioners
|
99
|
+
.filter(
|
100
|
+
(practitioner) => practitioner.uuid !== practitionerAdmin
|
101
|
+
)
|
102
|
+
.map(async (practitioner) => {
|
103
|
+
return oroClient
|
104
|
+
.grantLockbox(practitioner.uuid, lockboxUuid!)
|
105
|
+
.catch((err) => {
|
106
|
+
console.error(
|
107
|
+
`Error while granting lockbox to practitioner`,
|
108
|
+
err
|
109
|
+
)
|
110
|
+
// Acceptable to continue as admin has already been granted, but we should still try to retry
|
111
|
+
if (retry <= 1) return
|
112
|
+
else throw err
|
113
|
+
})
|
114
|
+
})
|
115
|
+
|
116
|
+
await storePatientData(
|
117
|
+
consult.uuid,
|
118
|
+
consultRequest.isoLanguageRequired,
|
119
|
+
lockboxUuid,
|
120
|
+
workflow,
|
121
|
+
oroClient
|
122
|
+
)
|
123
|
+
|
124
|
+
// the index will identify in which lockbox a consultation live
|
125
|
+
let consultIndex: VaultIndex = {
|
126
|
+
[IndexKey.ConsultationLockbox]: [
|
127
|
+
{
|
128
|
+
grant: {
|
129
|
+
lockboxUuid,
|
130
|
+
lockboxOwnerUuid: patientUuid,
|
131
|
+
},
|
132
|
+
consultationId: consult.uuid,
|
133
|
+
},
|
134
|
+
],
|
135
|
+
}
|
136
|
+
|
137
|
+
let consultIndexPromises = practitioners.map(async (practitioner) => {
|
138
|
+
return oroClient
|
139
|
+
.vaultIndexAdd(consultIndex, practitioner.uuid)
|
140
|
+
.catch((err) => {
|
141
|
+
console.error(
|
142
|
+
`Error while adding to the practitioner's index ${practitioner.uuid}`,
|
143
|
+
err
|
144
|
+
)
|
145
|
+
/// it's acceptable to continue registration
|
146
|
+
return
|
147
|
+
})
|
148
|
+
})
|
149
|
+
|
150
|
+
|
151
|
+
//DEPRECATED: REMOVE ME : BEGIN /////////////////////////////////////////
|
152
|
+
|
153
|
+
let deprecatedConsultIndex: VaultIndex = {
|
154
|
+
[IndexKey.Consultation]: [
|
155
|
+
{
|
156
|
+
grant: {
|
157
|
+
lockboxUuid,
|
158
|
+
lockboxOwnerUuid: patientUuid,
|
159
|
+
},
|
160
|
+
consultationId: consult.uuid,
|
161
|
+
},
|
162
|
+
],
|
163
|
+
}
|
164
|
+
|
165
|
+
let deprecatedConsultIndexPromises = practitioners.map(async (practitioner) => {
|
166
|
+
return oroClient
|
167
|
+
.vaultIndexAdd(deprecatedConsultIndex, practitioner.uuid)
|
168
|
+
.catch((err) => {
|
169
|
+
console.error(
|
170
|
+
`Error while adding to the practitioner's index ${practitioner.uuid}`,
|
171
|
+
err
|
172
|
+
)
|
173
|
+
/// it's acceptable to continue registration
|
174
|
+
return
|
175
|
+
})
|
176
|
+
})
|
177
|
+
|
178
|
+
//DEPRECATED: REMOVE ME : END /////////////////////////////////////////
|
179
|
+
|
180
|
+
|
181
|
+
if (masterKey && !isExistingPatient) {
|
182
|
+
// generate and store recovery payload
|
183
|
+
await oroClient
|
184
|
+
.updateMasterKey(patientUuid, masterKey, lockboxUuid)
|
185
|
+
.catch((err) => {
|
186
|
+
console.error(`Error while updating master key`, err)
|
187
|
+
/// it's acceptable to continue registration
|
188
|
+
return
|
189
|
+
})
|
190
|
+
} else {
|
191
|
+
// we did not set the master key so we do not return it
|
192
|
+
masterKey = undefined
|
193
|
+
}
|
194
|
+
|
195
|
+
if (recoveryQA && !isExistingPatient)
|
196
|
+
// Patient security question recovery threshold is 2 answers
|
197
|
+
await oroClient
|
198
|
+
.updateSecurityQuestions(
|
199
|
+
patientUuid,
|
200
|
+
recoveryQA.recoverySecurityQuestions,
|
201
|
+
recoveryQA.recoverySecurityAnswers,
|
202
|
+
2
|
203
|
+
)
|
204
|
+
.catch((err) => {
|
205
|
+
console.error(
|
206
|
+
`Error while updating security questions`,
|
207
|
+
err
|
208
|
+
)
|
209
|
+
/// it's acceptable to continue registration
|
210
|
+
return
|
211
|
+
})
|
212
|
+
|
213
|
+
await Promise.all([...grantPromises, ...consultIndexPromises, ...deprecatedConsultIndexPromises])
|
214
|
+
|
215
|
+
// Deem the consultation as ready
|
216
|
+
await oroClient.consultClient.updateConsultByUUID(consult.uuid, {
|
217
|
+
statusMedical: MedicalStatus.New,
|
218
|
+
})
|
219
|
+
|
220
|
+
// if we got through the complete flow, the registration succeeded
|
221
|
+
break
|
222
|
+
} catch (err) {
|
223
|
+
console.error(
|
224
|
+
`Error occured during registration, retrying... Retries remaining: ${retry}`
|
225
|
+
)
|
226
|
+
continue
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
if (retry <= 0) {
|
231
|
+
console.error('registration failed: MAX_RETRIES reached')
|
232
|
+
throw 'RegistrationFailed'
|
233
|
+
}
|
234
|
+
|
235
|
+
console.log('Successfully Registered')
|
236
|
+
await oroClient.cleanIndex()
|
237
|
+
return {
|
238
|
+
masterKey,
|
239
|
+
consultationId: consult!.uuid,
|
240
|
+
lockboxUuid: lockboxUuid!,
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
/**
|
245
|
+
* Returns whether the consultation creation is for an existing patient (ignores data from the current consultation)
|
246
|
+
*
|
247
|
+
* @param currentConsultation
|
248
|
+
*/
|
249
|
+
async function patientConsultExists(
|
250
|
+
currentConsultation: Consult,
|
251
|
+
lockboxUuid: Uuid,
|
252
|
+
oroClient: OroClient
|
253
|
+
): Promise<boolean> {
|
254
|
+
let publicMetadata: ConsultationMeta = {
|
255
|
+
category: MetadataCategory.Consultation,
|
256
|
+
documentType: DocumentType.PopulatedWorkflowData,
|
257
|
+
}
|
258
|
+
let manifest = await oroClient.vaultClient.lockboxMetadataGet(
|
259
|
+
lockboxUuid,
|
260
|
+
['consultationId'],
|
261
|
+
[],
|
262
|
+
publicMetadata
|
263
|
+
)
|
264
|
+
return (
|
265
|
+
manifest[0].findIndex(
|
266
|
+
(v: { consultationId: Uuid }) =>
|
267
|
+
v.consultationId != null &&
|
268
|
+
v.consultationId !== currentConsultation.uuid
|
269
|
+
) !== -1
|
270
|
+
)
|
271
|
+
}
|
272
|
+
|
273
|
+
/**
|
274
|
+
* Creates a consultation if one has not been created and fails to be retrieved by the payment intent
|
275
|
+
* @param consult
|
276
|
+
* @param oroClient
|
277
|
+
* @returns the consult Uuid
|
278
|
+
*/
|
279
|
+
async function getOrCreatePatientConsultationUuid(
|
280
|
+
consult: ConsultRequest,
|
281
|
+
oroClient: OroClient
|
282
|
+
): Promise<Consult> {
|
283
|
+
let payment = await oroClient.practiceClient.practiceGetPayment(
|
284
|
+
consult.uuidPractice,
|
285
|
+
consult.idStripeInvoiceOrPaymentIntent
|
286
|
+
)
|
287
|
+
if (payment && payment.uuidConsult) {
|
288
|
+
return oroClient.consultClient
|
289
|
+
.getConsultByUUID(payment.uuidConsult)
|
290
|
+
.catch((err) => {
|
291
|
+
console.error('Error while retrieving consult', err)
|
292
|
+
throw err
|
293
|
+
})
|
294
|
+
} else {
|
295
|
+
return await oroClient.consultClient
|
296
|
+
.consultCreate(consult)
|
297
|
+
.catch((err) => {
|
298
|
+
console.error('Error while creating consult', err)
|
299
|
+
throw err
|
300
|
+
})
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
/**
|
305
|
+
* Creates a new lockbox for the patient if they do not have any, otherwise, use the first (and only one)
|
306
|
+
* @param oroClient
|
307
|
+
* @returns the lockbox Uuid
|
308
|
+
*/
|
309
|
+
async function getOrCreatePatientLockbox(oroClient: OroClient): Promise<Uuid> {
|
310
|
+
let grants = await oroClient.getGrants(undefined, true)
|
311
|
+
if (grants.length > 0) {
|
312
|
+
console.log(
|
313
|
+
'The grant has already been created, skipping lockbox create step'
|
314
|
+
)
|
315
|
+
return grants[0].lockboxUuid!
|
316
|
+
} else
|
317
|
+
return (
|
318
|
+
await oroClient.vaultClient.lockboxCreate().catch((err) => {
|
319
|
+
console.error('Error while creating lockbox', err)
|
320
|
+
throw err
|
321
|
+
})
|
322
|
+
).lockboxUuid
|
323
|
+
}
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Store all patient related information into his/her lockbox
|
327
|
+
* @param consultationId The consultation id
|
328
|
+
* @param isoLanguage the prefered language of communication (ISO 639-3 https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes)
|
329
|
+
* @param lockboxUuid the lockbox uuid to store data in
|
330
|
+
* @param workflow the workflow used to extract informations
|
331
|
+
* @param oroClient an oroClient instance
|
332
|
+
* @returns
|
333
|
+
*/
|
334
|
+
async function storePatientData(
|
335
|
+
consultationId: Uuid,
|
336
|
+
isoLanguage: string,
|
337
|
+
lockboxUuid: Uuid,
|
338
|
+
workflow: WorkflowData,
|
339
|
+
oroClient: OroClient
|
340
|
+
): Promise<(Uuid | void)[]> {
|
341
|
+
// Create and store registration data
|
342
|
+
return Promise.all([
|
343
|
+
storeImageAliases(
|
344
|
+
consultationId,
|
345
|
+
lockboxUuid,
|
346
|
+
workflow,
|
347
|
+
oroClient
|
348
|
+
).catch((err) => {
|
349
|
+
console.error('[SDK] Some errors happened during image upload', err)
|
350
|
+
}),
|
351
|
+
// Storing Raw data first
|
352
|
+
oroClient.getOrInsertJsonData<RawConsultationMeta>(
|
353
|
+
lockboxUuid,
|
354
|
+
workflow,
|
355
|
+
{
|
356
|
+
category: MetadataCategory.Raw,
|
357
|
+
contentType: 'application/json',
|
358
|
+
consultationId,
|
359
|
+
},
|
360
|
+
{}
|
361
|
+
),
|
362
|
+
getWorkflowDataByCategory(workflow, MetadataCategory.Consultation).then(
|
363
|
+
(data) =>
|
364
|
+
oroClient.getOrInsertJsonData<ConsultationMeta>(
|
365
|
+
lockboxUuid,
|
366
|
+
data,
|
367
|
+
{
|
368
|
+
category: MetadataCategory.Consultation,
|
369
|
+
documentType: DocumentType.PopulatedWorkflowData,
|
370
|
+
consultationId, // TODO: deprecated. Will finally only be in privateMetadata
|
371
|
+
},
|
372
|
+
{ consultationId }
|
373
|
+
)
|
374
|
+
),
|
375
|
+
getWorkflowDataByCategory(workflow, MetadataCategory.Medical).then(
|
376
|
+
(data) =>
|
377
|
+
oroClient.getOrInsertJsonData<MedicalMeta>(
|
378
|
+
lockboxUuid,
|
379
|
+
data,
|
380
|
+
{
|
381
|
+
category: MetadataCategory.Medical,
|
382
|
+
documentType: DocumentType.PopulatedWorkflowData,
|
383
|
+
consultationIds: [consultationId!],
|
384
|
+
},
|
385
|
+
{}
|
386
|
+
)
|
387
|
+
),
|
388
|
+
extractAndStorePersonalWorkflowData(
|
389
|
+
workflow,
|
390
|
+
lockboxUuid,
|
391
|
+
consultationId,
|
392
|
+
MetadataCategory.Personal,
|
393
|
+
oroClient
|
394
|
+
),
|
395
|
+
extractAndStorePersonalWorkflowData(
|
396
|
+
workflow,
|
397
|
+
lockboxUuid,
|
398
|
+
consultationId,
|
399
|
+
MetadataCategory.ChildPersonal,
|
400
|
+
oroClient
|
401
|
+
),
|
402
|
+
extractAndStorePersonalWorkflowData(
|
403
|
+
workflow,
|
404
|
+
lockboxUuid,
|
405
|
+
consultationId,
|
406
|
+
MetadataCategory.OtherPersonal,
|
407
|
+
oroClient
|
408
|
+
),
|
409
|
+
oroClient.getOrInsertJsonData<PreferenceMeta>(
|
410
|
+
lockboxUuid,
|
411
|
+
{ isoLanguage },
|
412
|
+
{
|
413
|
+
category: MetadataCategory.Preference,
|
414
|
+
contentType: 'application/json',
|
415
|
+
},
|
416
|
+
{}
|
417
|
+
),
|
418
|
+
]).then((dataUuids) => dataUuids.flat())
|
419
|
+
}
|
420
|
+
|
421
|
+
async function storeImageAliases(
|
422
|
+
consultationId: Uuid,
|
423
|
+
lockboxUuid: Uuid,
|
424
|
+
workflow: WorkflowData,
|
425
|
+
oroClient: OroClient
|
426
|
+
): Promise<(Uuid | void)[]> {
|
427
|
+
const images = await getImagesFromIndexDb(
|
428
|
+
(await filterTriggeredAnsweredWithKind(workflow, 'images-alias')).flat()
|
429
|
+
)
|
430
|
+
|
431
|
+
const nonNullImages = images.filter((img) => !!img)
|
432
|
+
|
433
|
+
if (images.length !== nonNullImages.length) {
|
434
|
+
console.error(
|
435
|
+
'[SDK] Some images have not been found, they have been skipped.'
|
436
|
+
)
|
437
|
+
}
|
438
|
+
|
439
|
+
let promises = nonNullImages.map((image) => {
|
440
|
+
return oroClient.getOrInsertJsonData<ConsultationImageMeta>(
|
441
|
+
lockboxUuid,
|
442
|
+
image,
|
443
|
+
{
|
444
|
+
category: MetadataCategory.Consultation,
|
445
|
+
documentType: DocumentType.ImageAlias,
|
446
|
+
consultationId,
|
447
|
+
idbId: image.idbId as string,
|
448
|
+
},
|
449
|
+
{}
|
450
|
+
)
|
451
|
+
})
|
452
|
+
return Promise.all(promises)
|
453
|
+
}
|
454
|
+
|
455
|
+
/**
|
456
|
+
* Extracts the workflow MetadataCategory for Personal, ChildPersonal and OtherPersonal
|
457
|
+
* then stores it in the vault
|
458
|
+
*
|
459
|
+
* @param workflow
|
460
|
+
* @param lockboxUuid
|
461
|
+
* @param category
|
462
|
+
* @returns The data uuid
|
463
|
+
*/
|
464
|
+
export async function extractAndStorePersonalWorkflowData(
|
465
|
+
workflow: WorkflowData,
|
466
|
+
lockboxUuid: Uuid,
|
467
|
+
consultationId: Uuid,
|
468
|
+
category:
|
469
|
+
| MetadataCategory.Personal
|
470
|
+
| MetadataCategory.ChildPersonal
|
471
|
+
| MetadataCategory.OtherPersonal,
|
472
|
+
oroClient: OroClient
|
473
|
+
): Promise<Uuid | void> {
|
474
|
+
return getWorkflowDataByCategory(
|
475
|
+
workflow,
|
476
|
+
(category as unknown) as MetadataCategory
|
477
|
+
).then((data) => {
|
478
|
+
if (Object.keys(data.fields).length === 0) return
|
479
|
+
return oroClient.getOrInsertJsonData<PersonalMeta>(
|
480
|
+
lockboxUuid,
|
481
|
+
data,
|
482
|
+
{
|
483
|
+
category,
|
484
|
+
documentType: DocumentType.PopulatedWorkflowData,
|
485
|
+
consultationIds: [consultationId],
|
486
|
+
},
|
487
|
+
{}
|
488
|
+
)
|
489
|
+
})
|
490
|
+
}
|