@medplum/core 2.0.2 → 2.0.4
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/README.md +7 -3
- package/dist/cjs/client.d.ts +16 -3
- package/dist/cjs/index.cjs +785 -438
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +4 -3
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/cjs/outcomes.d.ts +1 -0
- package/dist/cjs/{searchparams.d.ts → search/details.d.ts} +0 -0
- package/dist/cjs/{match.d.ts → search/match.d.ts} +0 -0
- package/dist/cjs/search/parse.d.ts +17 -0
- package/dist/cjs/{search.d.ts → search/search.d.ts} +0 -0
- package/dist/cjs/types.d.ts +30 -7
- package/dist/esm/base-schema.json.mjs +7 -0
- package/dist/esm/base-schema.json.mjs.map +1 -1
- package/dist/esm/client.d.ts +16 -3
- package/dist/esm/client.mjs +114 -26
- package/dist/esm/client.mjs.map +1 -1
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.min.mjs +1 -1
- package/dist/esm/index.mjs +6 -5
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/outcomes.d.ts +1 -0
- package/dist/esm/outcomes.mjs +6 -4
- package/dist/esm/outcomes.mjs.map +1 -1
- package/dist/esm/{searchparams.d.ts → search/details.d.ts} +0 -0
- package/dist/esm/{searchparams.mjs → search/details.mjs} +9 -11
- package/dist/esm/search/details.mjs.map +1 -0
- package/dist/esm/{match.d.ts → search/match.d.ts} +0 -0
- package/dist/esm/{match.mjs → search/match.mjs} +7 -7
- package/dist/esm/search/match.mjs.map +1 -0
- package/dist/esm/search/parse.d.ts +17 -0
- package/dist/esm/search/parse.mjs +218 -0
- package/dist/esm/search/parse.mjs.map +1 -0
- package/dist/esm/{search.d.ts → search/search.d.ts} +0 -0
- package/dist/esm/{search.mjs → search/search.mjs} +0 -3
- package/dist/esm/search/search.mjs.map +1 -0
- package/dist/esm/types.d.ts +30 -7
- package/dist/esm/types.mjs +63 -25
- package/dist/esm/types.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/esm/match.mjs.map +0 -1
- package/dist/esm/search.mjs.map +0 -1
- package/dist/esm/searchparams.mjs.map +0 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -1174,6 +1174,211 @@
|
|
|
1174
1174
|
return decodePayload(payload);
|
|
1175
1175
|
}
|
|
1176
1176
|
|
|
1177
|
+
const OK_ID = 'ok';
|
|
1178
|
+
const CREATED_ID = 'created';
|
|
1179
|
+
const GONE_ID = 'gone';
|
|
1180
|
+
const NOT_MODIFIED_ID = 'not-modified';
|
|
1181
|
+
const NOT_FOUND_ID = 'not-found';
|
|
1182
|
+
const UNAUTHORIZED_ID = 'unauthorized';
|
|
1183
|
+
const FORBIDDEN_ID = 'forbidden';
|
|
1184
|
+
const TOO_MANY_REQUESTS_ID = 'too-many-requests';
|
|
1185
|
+
const allOk = {
|
|
1186
|
+
resourceType: 'OperationOutcome',
|
|
1187
|
+
id: OK_ID,
|
|
1188
|
+
issue: [
|
|
1189
|
+
{
|
|
1190
|
+
severity: 'information',
|
|
1191
|
+
code: 'informational',
|
|
1192
|
+
details: {
|
|
1193
|
+
text: 'All OK',
|
|
1194
|
+
},
|
|
1195
|
+
},
|
|
1196
|
+
],
|
|
1197
|
+
};
|
|
1198
|
+
const created = {
|
|
1199
|
+
resourceType: 'OperationOutcome',
|
|
1200
|
+
id: CREATED_ID,
|
|
1201
|
+
issue: [
|
|
1202
|
+
{
|
|
1203
|
+
severity: 'information',
|
|
1204
|
+
code: 'informational',
|
|
1205
|
+
details: {
|
|
1206
|
+
text: 'Created',
|
|
1207
|
+
},
|
|
1208
|
+
},
|
|
1209
|
+
],
|
|
1210
|
+
};
|
|
1211
|
+
const notModified = {
|
|
1212
|
+
resourceType: 'OperationOutcome',
|
|
1213
|
+
id: NOT_MODIFIED_ID,
|
|
1214
|
+
issue: [
|
|
1215
|
+
{
|
|
1216
|
+
severity: 'information',
|
|
1217
|
+
code: 'informational',
|
|
1218
|
+
details: {
|
|
1219
|
+
text: 'Not Modified',
|
|
1220
|
+
},
|
|
1221
|
+
},
|
|
1222
|
+
],
|
|
1223
|
+
};
|
|
1224
|
+
const notFound = {
|
|
1225
|
+
resourceType: 'OperationOutcome',
|
|
1226
|
+
id: NOT_FOUND_ID,
|
|
1227
|
+
issue: [
|
|
1228
|
+
{
|
|
1229
|
+
severity: 'error',
|
|
1230
|
+
code: 'not-found',
|
|
1231
|
+
details: {
|
|
1232
|
+
text: 'Not found',
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
],
|
|
1236
|
+
};
|
|
1237
|
+
const unauthorized = {
|
|
1238
|
+
resourceType: 'OperationOutcome',
|
|
1239
|
+
id: UNAUTHORIZED_ID,
|
|
1240
|
+
issue: [
|
|
1241
|
+
{
|
|
1242
|
+
severity: 'error',
|
|
1243
|
+
code: 'login',
|
|
1244
|
+
details: {
|
|
1245
|
+
text: 'Unauthorized',
|
|
1246
|
+
},
|
|
1247
|
+
},
|
|
1248
|
+
],
|
|
1249
|
+
};
|
|
1250
|
+
const forbidden = {
|
|
1251
|
+
resourceType: 'OperationOutcome',
|
|
1252
|
+
id: FORBIDDEN_ID,
|
|
1253
|
+
issue: [
|
|
1254
|
+
{
|
|
1255
|
+
severity: 'error',
|
|
1256
|
+
code: 'forbidden',
|
|
1257
|
+
details: {
|
|
1258
|
+
text: 'Forbidden',
|
|
1259
|
+
},
|
|
1260
|
+
},
|
|
1261
|
+
],
|
|
1262
|
+
};
|
|
1263
|
+
const gone = {
|
|
1264
|
+
resourceType: 'OperationOutcome',
|
|
1265
|
+
id: GONE_ID,
|
|
1266
|
+
issue: [
|
|
1267
|
+
{
|
|
1268
|
+
severity: 'error',
|
|
1269
|
+
code: 'deleted',
|
|
1270
|
+
details: {
|
|
1271
|
+
text: 'Gone',
|
|
1272
|
+
},
|
|
1273
|
+
},
|
|
1274
|
+
],
|
|
1275
|
+
};
|
|
1276
|
+
const tooManyRequests = {
|
|
1277
|
+
resourceType: 'OperationOutcome',
|
|
1278
|
+
id: TOO_MANY_REQUESTS_ID,
|
|
1279
|
+
issue: [
|
|
1280
|
+
{
|
|
1281
|
+
severity: 'error',
|
|
1282
|
+
code: 'throttled',
|
|
1283
|
+
details: {
|
|
1284
|
+
text: 'Too Many Requests',
|
|
1285
|
+
},
|
|
1286
|
+
},
|
|
1287
|
+
],
|
|
1288
|
+
};
|
|
1289
|
+
function badRequest(details, expression) {
|
|
1290
|
+
return {
|
|
1291
|
+
resourceType: 'OperationOutcome',
|
|
1292
|
+
issue: [
|
|
1293
|
+
{
|
|
1294
|
+
severity: 'error',
|
|
1295
|
+
code: 'invalid',
|
|
1296
|
+
details: {
|
|
1297
|
+
text: details,
|
|
1298
|
+
},
|
|
1299
|
+
expression: expression ? [expression] : undefined,
|
|
1300
|
+
},
|
|
1301
|
+
],
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
function isOperationOutcome(value) {
|
|
1305
|
+
return typeof value === 'object' && value !== null && value.resourceType === 'OperationOutcome';
|
|
1306
|
+
}
|
|
1307
|
+
function isOk(outcome) {
|
|
1308
|
+
return outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID;
|
|
1309
|
+
}
|
|
1310
|
+
function isNotFound(outcome) {
|
|
1311
|
+
return outcome.id === NOT_FOUND_ID;
|
|
1312
|
+
}
|
|
1313
|
+
function isGone(outcome) {
|
|
1314
|
+
return outcome.id === GONE_ID;
|
|
1315
|
+
}
|
|
1316
|
+
function getStatus(outcome) {
|
|
1317
|
+
if (outcome.id === OK_ID) {
|
|
1318
|
+
return 200;
|
|
1319
|
+
}
|
|
1320
|
+
else if (outcome.id === CREATED_ID) {
|
|
1321
|
+
return 201;
|
|
1322
|
+
}
|
|
1323
|
+
else if (outcome.id === NOT_MODIFIED_ID) {
|
|
1324
|
+
return 304;
|
|
1325
|
+
}
|
|
1326
|
+
else if (outcome.id === UNAUTHORIZED_ID) {
|
|
1327
|
+
return 401;
|
|
1328
|
+
}
|
|
1329
|
+
else if (outcome.id === FORBIDDEN_ID) {
|
|
1330
|
+
return 403;
|
|
1331
|
+
}
|
|
1332
|
+
else if (outcome.id === NOT_FOUND_ID) {
|
|
1333
|
+
return 404;
|
|
1334
|
+
}
|
|
1335
|
+
else if (outcome.id === GONE_ID) {
|
|
1336
|
+
return 410;
|
|
1337
|
+
}
|
|
1338
|
+
else if (outcome.id === TOO_MANY_REQUESTS_ID) {
|
|
1339
|
+
return 429;
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
return 400;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Asserts that the operation completed successfully and that the resource is defined.
|
|
1347
|
+
* @param outcome The operation outcome.
|
|
1348
|
+
* @param resource The resource that may or may not have been returned.
|
|
1349
|
+
*/
|
|
1350
|
+
function assertOk(outcome, resource) {
|
|
1351
|
+
if (!isOk(outcome) || resource === undefined) {
|
|
1352
|
+
throw new OperationOutcomeError(outcome);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
class OperationOutcomeError extends Error {
|
|
1356
|
+
constructor(outcome) {
|
|
1357
|
+
super(outcome?.issue?.[0].details?.text);
|
|
1358
|
+
this.outcome = outcome;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Normalizes an error object into a displayable error string.
|
|
1363
|
+
* @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.
|
|
1364
|
+
* @returns A display string for the error.
|
|
1365
|
+
*/
|
|
1366
|
+
function normalizeErrorString(error) {
|
|
1367
|
+
if (!error) {
|
|
1368
|
+
return 'Unknown error';
|
|
1369
|
+
}
|
|
1370
|
+
if (typeof error === 'string') {
|
|
1371
|
+
return error;
|
|
1372
|
+
}
|
|
1373
|
+
if (error instanceof Error) {
|
|
1374
|
+
return error.message;
|
|
1375
|
+
}
|
|
1376
|
+
if (isOperationOutcome(error)) {
|
|
1377
|
+
return error.issue?.[0]?.details?.text ?? 'Unknown error';
|
|
1378
|
+
}
|
|
1379
|
+
return JSON.stringify(error);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1177
1382
|
var _ReadablePromise_suspender, _ReadablePromise_status, _ReadablePromise_response, _ReadablePromise_error, _a;
|
|
1178
1383
|
/**
|
|
1179
1384
|
* The ReadablePromise class wraps a request promise suitable for React Suspense.
|
|
@@ -5736,6 +5941,13 @@
|
|
|
5736
5941
|
code: "string"
|
|
5737
5942
|
}
|
|
5738
5943
|
]
|
|
5944
|
+
},
|
|
5945
|
+
useSubject: {
|
|
5946
|
+
type: [
|
|
5947
|
+
{
|
|
5948
|
+
code: "boolean"
|
|
5949
|
+
}
|
|
5950
|
+
]
|
|
5739
5951
|
}
|
|
5740
5952
|
}
|
|
5741
5953
|
}
|
|
@@ -5811,23 +6023,6 @@
|
|
|
5811
6023
|
PropertyType["url"] = "url";
|
|
5812
6024
|
PropertyType["uuid"] = "uuid";
|
|
5813
6025
|
})(exports.PropertyType || (exports.PropertyType = {}));
|
|
5814
|
-
/**
|
|
5815
|
-
* Creates a new empty IndexedStructureDefinition.
|
|
5816
|
-
* @returns The empty IndexedStructureDefinition.
|
|
5817
|
-
* @deprecated Use globalSchema
|
|
5818
|
-
*/
|
|
5819
|
-
function createSchema() {
|
|
5820
|
-
return { types: {} };
|
|
5821
|
-
}
|
|
5822
|
-
function createTypeSchema(typeName, structureDefinition, elementDefinition) {
|
|
5823
|
-
return {
|
|
5824
|
-
structureDefinition,
|
|
5825
|
-
elementDefinition,
|
|
5826
|
-
display: typeName,
|
|
5827
|
-
description: elementDefinition.definition,
|
|
5828
|
-
properties: {},
|
|
5829
|
-
};
|
|
5830
|
-
}
|
|
5831
6026
|
/**
|
|
5832
6027
|
* Indexes a bundle of StructureDefinitions for faster lookup.
|
|
5833
6028
|
* @param bundle A FHIR bundle StructureDefinition resources.
|
|
@@ -5875,8 +6070,16 @@
|
|
|
5875
6070
|
}
|
|
5876
6071
|
const parts = path.split('.');
|
|
5877
6072
|
const typeName = buildTypeName(parts);
|
|
5878
|
-
globalSchema.types[typeName]
|
|
5879
|
-
|
|
6073
|
+
let typeSchema = globalSchema.types[typeName];
|
|
6074
|
+
if (!typeSchema) {
|
|
6075
|
+
globalSchema.types[typeName] = typeSchema = {};
|
|
6076
|
+
}
|
|
6077
|
+
typeSchema.parentType = typeSchema.parentType || buildTypeName(parts.slice(0, parts.length - 1));
|
|
6078
|
+
typeSchema.display = typeSchema.display || typeName;
|
|
6079
|
+
typeSchema.structureDefinition = typeSchema.structureDefinition || structureDefinition;
|
|
6080
|
+
typeSchema.elementDefinition = typeSchema.elementDefinition || elementDefinition;
|
|
6081
|
+
typeSchema.description = typeSchema.description || elementDefinition.definition;
|
|
6082
|
+
typeSchema.properties = typeSchema.properties || {};
|
|
5880
6083
|
}
|
|
5881
6084
|
/**
|
|
5882
6085
|
* Indexes PropertySchema from an ElementDefinition.
|
|
@@ -5991,15 +6194,54 @@
|
|
|
5991
6194
|
}
|
|
5992
6195
|
return components.map(capitalize).join('');
|
|
5993
6196
|
}
|
|
6197
|
+
/**
|
|
6198
|
+
* Returns true if the type schema is a DomainResource.
|
|
6199
|
+
* @param typeSchema The type schema to check.
|
|
6200
|
+
* @returns True if the type schema is a DomainResource.
|
|
6201
|
+
*/
|
|
6202
|
+
function isResourceType(typeSchema) {
|
|
6203
|
+
return typeSchema.structureDefinition?.baseDefinition === 'http://hl7.org/fhir/StructureDefinition/DomainResource';
|
|
6204
|
+
}
|
|
6205
|
+
/**
|
|
6206
|
+
* Returns an array of all resource types.
|
|
6207
|
+
* Note that this is based on globalSchema, and will only return resource types that are currently in memory.
|
|
6208
|
+
* @returns An array of all resource types.
|
|
6209
|
+
*/
|
|
6210
|
+
function getResourceTypes() {
|
|
6211
|
+
const result = [];
|
|
6212
|
+
for (const [resourceType, typeSchema] of Object.entries(globalSchema.types)) {
|
|
6213
|
+
if (isResourceType(typeSchema)) {
|
|
6214
|
+
result.push(resourceType);
|
|
6215
|
+
}
|
|
6216
|
+
}
|
|
6217
|
+
return result;
|
|
6218
|
+
}
|
|
6219
|
+
/**
|
|
6220
|
+
* Returns the type schema for the resource type.
|
|
6221
|
+
* @param resourceType The resource type.
|
|
6222
|
+
* @returns The type schema for the resource type.
|
|
6223
|
+
*/
|
|
6224
|
+
function getResourceTypeSchema(resourceType) {
|
|
6225
|
+
return globalSchema.types[resourceType];
|
|
6226
|
+
}
|
|
6227
|
+
/**
|
|
6228
|
+
* Returns the search parameters for the resource type indexed by search code.
|
|
6229
|
+
* @param resourceType The resource type.
|
|
6230
|
+
* @returns The search parameters for the resource type indexed by search code.
|
|
6231
|
+
*/
|
|
6232
|
+
function getSearchParameters(resourceType) {
|
|
6233
|
+
return globalSchema.types[resourceType].searchParams;
|
|
6234
|
+
}
|
|
6235
|
+
/**
|
|
6236
|
+
* Returns a human friendly display name for a FHIR element definition path.
|
|
6237
|
+
* @param path The FHIR element definition path.
|
|
6238
|
+
* @returns The best guess of the display name.
|
|
6239
|
+
*/
|
|
5994
6240
|
function getPropertyDisplayName(path) {
|
|
5995
6241
|
// Get the property name, which is the remainder after the last period
|
|
5996
6242
|
// For example, for path "Patient.birthDate"
|
|
5997
6243
|
// the property name is "birthDate"
|
|
5998
6244
|
const propertyName = path.replaceAll('[x]', '').split('.').pop();
|
|
5999
|
-
// Special case for ID
|
|
6000
|
-
if (propertyName === 'id') {
|
|
6001
|
-
return 'ID';
|
|
6002
|
-
}
|
|
6003
6245
|
// Split by capital letters
|
|
6004
6246
|
// Capitalize the first letter of each word
|
|
6005
6247
|
// Join together with spaces in between
|
|
@@ -6008,11 +6250,19 @@
|
|
|
6008
6250
|
// the display name is "Birth Date".
|
|
6009
6251
|
return propertyName
|
|
6010
6252
|
.split(/(?=[A-Z])/)
|
|
6011
|
-
.map(
|
|
6253
|
+
.map(capitalizeDisplayWord)
|
|
6012
6254
|
.join(' ')
|
|
6013
6255
|
.replace('_', ' ')
|
|
6014
6256
|
.replace(/\s+/g, ' ');
|
|
6015
6257
|
}
|
|
6258
|
+
const capitalizedWords = new Set(['ID', 'PKCE', 'JWKS', 'URI', 'URL']);
|
|
6259
|
+
function capitalizeDisplayWord(word) {
|
|
6260
|
+
const upper = word.toUpperCase();
|
|
6261
|
+
if (capitalizedWords.has(upper)) {
|
|
6262
|
+
return upper;
|
|
6263
|
+
}
|
|
6264
|
+
return upper.charAt(0) + word.slice(1);
|
|
6265
|
+
}
|
|
6016
6266
|
/**
|
|
6017
6267
|
* Returns an element definition by type and property name.
|
|
6018
6268
|
* Handles content references.
|
|
@@ -6046,18 +6296,19 @@
|
|
|
6046
6296
|
|
|
6047
6297
|
// PKCE auth based on:
|
|
6048
6298
|
// https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
|
|
6049
|
-
var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_fhirBaseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_autoBatchTime, _MedplumClient_autoBatchQueue, _MedplumClient_clientId, _MedplumClient_clientSecret, _MedplumClient_autoBatchTimerId, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_request, _MedplumClient_executeAutoBatch, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
|
|
6050
|
-
const MEDPLUM_VERSION = "2.0.
|
|
6299
|
+
var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_fhirBaseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_autoBatchTime, _MedplumClient_autoBatchQueue, _MedplumClient_clientId, _MedplumClient_clientSecret, _MedplumClient_autoBatchTimerId, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_cacheResource, _MedplumClient_deleteCacheEntry, _MedplumClient_request, _MedplumClient_fetchWithRetry, _MedplumClient_executeAutoBatch, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
|
|
6300
|
+
const MEDPLUM_VERSION = "2.0.4-c4747b4e";
|
|
6051
6301
|
const DEFAULT_BASE_URL = 'https://api.medplum.com/';
|
|
6052
6302
|
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
|
|
6053
6303
|
const DEFAULT_CACHE_TIME = 60000; // 60 seconds
|
|
6054
6304
|
const JSON_CONTENT_TYPE = 'application/json';
|
|
6055
6305
|
const FHIR_CONTENT_TYPE = 'application/fhir+json';
|
|
6056
6306
|
const PATCH_CONTENT_TYPE = 'application/json-patch+json';
|
|
6307
|
+
const system = { resourceType: 'Device', id: 'system', deviceName: [{ name: 'System' }] };
|
|
6057
6308
|
/**
|
|
6058
6309
|
* The MedplumClient class provides a client for the Medplum FHIR server.
|
|
6059
6310
|
*
|
|
6060
|
-
* The client can be used in the browser, in a
|
|
6311
|
+
* The client can be used in the browser, in a Node.js application, or in a Medplum Bot.
|
|
6061
6312
|
*
|
|
6062
6313
|
* The client provides helpful methods for common operations such as:
|
|
6063
6314
|
* 1) Authenticating
|
|
@@ -6465,16 +6716,29 @@
|
|
|
6465
6716
|
* @param clientId The external client ID.
|
|
6466
6717
|
* @param redirectUri The external identity provider redirect URI.
|
|
6467
6718
|
* @param baseLogin The Medplum login request.
|
|
6719
|
+
* @category Authentication
|
|
6468
6720
|
*/
|
|
6469
6721
|
async signInWithExternalAuth(authorizeUrl, clientId, redirectUri, baseLogin) {
|
|
6470
6722
|
const loginRequest = await this.ensureCodeChallenge(baseLogin);
|
|
6723
|
+
window.location.assign(this.getExternalAuthRedirectUri(authorizeUrl, clientId, redirectUri, loginRequest));
|
|
6724
|
+
}
|
|
6725
|
+
/**
|
|
6726
|
+
* Builds the external identity provider redirect URI.
|
|
6727
|
+
* @param authorizeUrl The external authorization URL.
|
|
6728
|
+
* @param clientId The external client ID.
|
|
6729
|
+
* @param redirectUri The external identity provider redirect URI.
|
|
6730
|
+
* @param loginRequest The Medplum login request.
|
|
6731
|
+
* @returns The external identity provider redirect URI.
|
|
6732
|
+
* @category Authentication
|
|
6733
|
+
*/
|
|
6734
|
+
getExternalAuthRedirectUri(authorizeUrl, clientId, redirectUri, loginRequest) {
|
|
6471
6735
|
const url = new URL(authorizeUrl);
|
|
6472
6736
|
url.searchParams.set('response_type', 'code');
|
|
6473
6737
|
url.searchParams.set('client_id', clientId);
|
|
6474
6738
|
url.searchParams.set('redirect_uri', redirectUri);
|
|
6475
6739
|
url.searchParams.set('scope', 'openid profile email');
|
|
6476
6740
|
url.searchParams.set('state', JSON.stringify(loginRequest));
|
|
6477
|
-
|
|
6741
|
+
return url.toString();
|
|
6478
6742
|
}
|
|
6479
6743
|
/**
|
|
6480
6744
|
* Builds a FHIR URL from a collection of URL path components.
|
|
@@ -6549,7 +6813,23 @@
|
|
|
6549
6813
|
* @returns Promise to the search result bundle.
|
|
6550
6814
|
*/
|
|
6551
6815
|
search(resourceType, query, options = {}) {
|
|
6552
|
-
|
|
6816
|
+
const url = this.fhirSearchUrl(resourceType, query);
|
|
6817
|
+
const cacheKey = url.toString() + '-search';
|
|
6818
|
+
const cached = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_getCacheEntry).call(this, cacheKey, options);
|
|
6819
|
+
if (cached) {
|
|
6820
|
+
return cached.value;
|
|
6821
|
+
}
|
|
6822
|
+
const promise = new ReadablePromise((async () => {
|
|
6823
|
+
const bundle = await this.get(url, options);
|
|
6824
|
+
if (bundle.entry) {
|
|
6825
|
+
for (const entry of bundle.entry) {
|
|
6826
|
+
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_cacheResource).call(this, entry.resource);
|
|
6827
|
+
}
|
|
6828
|
+
}
|
|
6829
|
+
return bundle;
|
|
6830
|
+
})());
|
|
6831
|
+
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, cacheKey, promise);
|
|
6832
|
+
return promise;
|
|
6553
6833
|
}
|
|
6554
6834
|
/**
|
|
6555
6835
|
* Sends a FHIR search request for a single resource.
|
|
@@ -6658,6 +6938,9 @@
|
|
|
6658
6938
|
if (!refString) {
|
|
6659
6939
|
return undefined;
|
|
6660
6940
|
}
|
|
6941
|
+
if (refString === 'system') {
|
|
6942
|
+
return system;
|
|
6943
|
+
}
|
|
6661
6944
|
const [resourceType, id] = refString.split('/');
|
|
6662
6945
|
if (!resourceType || !id) {
|
|
6663
6946
|
return undefined;
|
|
@@ -6710,6 +6993,9 @@
|
|
|
6710
6993
|
if (!refString) {
|
|
6711
6994
|
return new ReadablePromise(Promise.reject(new Error('Missing reference')));
|
|
6712
6995
|
}
|
|
6996
|
+
if (refString === 'system') {
|
|
6997
|
+
return new ReadablePromise(Promise.resolve(system));
|
|
6998
|
+
}
|
|
6713
6999
|
const [resourceType, id] = refString.split('/');
|
|
6714
7000
|
if (!resourceType || !id) {
|
|
6715
7001
|
return new ReadablePromise(Promise.reject(new Error('Invalid reference')));
|
|
@@ -6734,11 +7020,17 @@
|
|
|
6734
7020
|
* @param resourceType The FHIR resource type.
|
|
6735
7021
|
* @returns Promise to a schema with the requested resource type.
|
|
6736
7022
|
*/
|
|
6737
|
-
|
|
7023
|
+
requestSchema(resourceType) {
|
|
6738
7024
|
if (resourceType in globalSchema.types) {
|
|
6739
|
-
return globalSchema;
|
|
7025
|
+
return Promise.resolve(globalSchema);
|
|
6740
7026
|
}
|
|
6741
|
-
const
|
|
7027
|
+
const cacheKey = resourceType + '-requestSchema';
|
|
7028
|
+
const cached = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_getCacheEntry).call(this, cacheKey, undefined);
|
|
7029
|
+
if (cached) {
|
|
7030
|
+
return cached.value;
|
|
7031
|
+
}
|
|
7032
|
+
const promise = new ReadablePromise((async () => {
|
|
7033
|
+
const query = `{
|
|
6742
7034
|
StructureDefinitionList(name: "${resourceType}") {
|
|
6743
7035
|
name,
|
|
6744
7036
|
description,
|
|
@@ -6767,14 +7059,17 @@
|
|
|
6767
7059
|
target
|
|
6768
7060
|
}
|
|
6769
7061
|
}`.replace(/\s+/g, ' ');
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
7062
|
+
const response = (await this.graphql(query));
|
|
7063
|
+
for (const structureDefinition of response.data.StructureDefinitionList) {
|
|
7064
|
+
indexStructureDefinition(structureDefinition);
|
|
7065
|
+
}
|
|
7066
|
+
for (const searchParameter of response.data.SearchParameterList) {
|
|
7067
|
+
indexSearchParameter(searchParameter);
|
|
7068
|
+
}
|
|
7069
|
+
return globalSchema;
|
|
7070
|
+
})());
|
|
7071
|
+
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, cacheKey, promise);
|
|
7072
|
+
return promise;
|
|
6778
7073
|
}
|
|
6779
7074
|
/**
|
|
6780
7075
|
* Reads resource history by resource type and ID.
|
|
@@ -7075,10 +7370,15 @@
|
|
|
7075
7370
|
throw new Error('Missing id');
|
|
7076
7371
|
}
|
|
7077
7372
|
this.invalidateSearches(resource.resourceType);
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7373
|
+
let result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource);
|
|
7374
|
+
if (!result) {
|
|
7375
|
+
// On 304 not modified, result will be undefined
|
|
7376
|
+
// Return the user input instead
|
|
7377
|
+
// return result ?? resource;
|
|
7378
|
+
result = resource;
|
|
7379
|
+
}
|
|
7380
|
+
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_cacheResource).call(this, result);
|
|
7381
|
+
return result;
|
|
7082
7382
|
}
|
|
7083
7383
|
/**
|
|
7084
7384
|
* Updates a FHIR resource using JSONPatch operations.
|
|
@@ -7125,6 +7425,7 @@
|
|
|
7125
7425
|
* @returns The result of the delete operation.
|
|
7126
7426
|
*/
|
|
7127
7427
|
deleteResource(resourceType, id) {
|
|
7428
|
+
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_deleteCacheEntry).call(this, this.fhirUrl(resourceType, id).toString());
|
|
7128
7429
|
this.invalidateSearches(resourceType);
|
|
7129
7430
|
return this.delete(this.fhirUrl(resourceType, id));
|
|
7130
7431
|
}
|
|
@@ -7388,6 +7689,7 @@
|
|
|
7388
7689
|
/**
|
|
7389
7690
|
* Starts a new PKCE flow.
|
|
7390
7691
|
* These PKCE values are stateful, and must survive redirects and page refreshes.
|
|
7692
|
+
* @category Authentication
|
|
7391
7693
|
*/
|
|
7392
7694
|
async startPkce() {
|
|
7393
7695
|
const pkceState = getRandomString();
|
|
@@ -7403,6 +7705,7 @@
|
|
|
7403
7705
|
* Processes an OAuth authorization code.
|
|
7404
7706
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
|
7405
7707
|
* @param code The authorization code received by URL parameter.
|
|
7708
|
+
* @category Authentication
|
|
7406
7709
|
*/
|
|
7407
7710
|
processCode(code) {
|
|
7408
7711
|
const formBody = new URLSearchParams();
|
|
@@ -7410,9 +7713,11 @@
|
|
|
7410
7713
|
formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
|
|
7411
7714
|
formBody.set('code', code);
|
|
7412
7715
|
formBody.set('redirect_uri', getWindowOrigin());
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7716
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
7717
|
+
const codeVerifier = sessionStorage.getItem('codeVerifier');
|
|
7718
|
+
if (codeVerifier) {
|
|
7719
|
+
formBody.set('code_verifier', codeVerifier);
|
|
7720
|
+
}
|
|
7416
7721
|
}
|
|
7417
7722
|
return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody);
|
|
7418
7723
|
}
|
|
@@ -7464,6 +7769,14 @@
|
|
|
7464
7769
|
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
|
|
7465
7770
|
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(key, { requestTime: Date.now(), value });
|
|
7466
7771
|
}
|
|
7772
|
+
}, _MedplumClient_cacheResource = function _MedplumClient_cacheResource(resource) {
|
|
7773
|
+
if (resource?.id) {
|
|
7774
|
+
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, this.fhirUrl(resource.resourceType, resource.id).toString(), new ReadablePromise(Promise.resolve(resource)));
|
|
7775
|
+
}
|
|
7776
|
+
}, _MedplumClient_deleteCacheEntry = function _MedplumClient_deleteCacheEntry(key) {
|
|
7777
|
+
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
|
|
7778
|
+
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(key);
|
|
7779
|
+
}
|
|
7467
7780
|
}, _MedplumClient_request =
|
|
7468
7781
|
/**
|
|
7469
7782
|
* Makes an HTTP request.
|
|
@@ -7481,7 +7794,7 @@
|
|
|
7481
7794
|
}
|
|
7482
7795
|
options.method = method;
|
|
7483
7796
|
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addFetchOptionsDefaults).call(this, options);
|
|
7484
|
-
const response = await __classPrivateFieldGet(this,
|
|
7797
|
+
const response = await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchWithRetry).call(this, url, options);
|
|
7485
7798
|
if (response.status === 401) {
|
|
7486
7799
|
// Refresh and try again
|
|
7487
7800
|
return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_handleUnauthenticated).call(this, method, url, options);
|
|
@@ -7490,11 +7803,30 @@
|
|
|
7490
7803
|
// No content or change
|
|
7491
7804
|
return undefined;
|
|
7492
7805
|
}
|
|
7493
|
-
|
|
7806
|
+
let obj = undefined;
|
|
7807
|
+
try {
|
|
7808
|
+
obj = await response.json();
|
|
7809
|
+
}
|
|
7810
|
+
catch (err) {
|
|
7811
|
+
console.error('Error parsing response', response.status, err);
|
|
7812
|
+
throw err;
|
|
7813
|
+
}
|
|
7494
7814
|
if (response.status >= 400) {
|
|
7495
7815
|
throw obj;
|
|
7496
7816
|
}
|
|
7497
7817
|
return obj;
|
|
7818
|
+
}, _MedplumClient_fetchWithRetry = async function _MedplumClient_fetchWithRetry(url, options) {
|
|
7819
|
+
const maxRetries = 3;
|
|
7820
|
+
const retryDelay = 200;
|
|
7821
|
+
let response = undefined;
|
|
7822
|
+
for (let retry = 0; retry < maxRetries; retry++) {
|
|
7823
|
+
response = (await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options));
|
|
7824
|
+
if (response.status < 500) {
|
|
7825
|
+
return response;
|
|
7826
|
+
}
|
|
7827
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
7828
|
+
}
|
|
7829
|
+
return response;
|
|
7498
7830
|
}, _MedplumClient_executeAutoBatch =
|
|
7499
7831
|
/**
|
|
7500
7832
|
* Executes a batch of requests that were automatically batched together.
|
|
@@ -7530,7 +7862,12 @@
|
|
|
7530
7862
|
for (let i = 0; i < entries.length; i++) {
|
|
7531
7863
|
const entry = entries[i];
|
|
7532
7864
|
const responseEntry = response.entry?.[i];
|
|
7533
|
-
|
|
7865
|
+
if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {
|
|
7866
|
+
entry.reject(responseEntry.response.outcome);
|
|
7867
|
+
}
|
|
7868
|
+
else {
|
|
7869
|
+
entry.resolve(responseEntry?.resource);
|
|
7870
|
+
}
|
|
7534
7871
|
}
|
|
7535
7872
|
}, _MedplumClient_addFetchOptionsDefaults = function _MedplumClient_addFetchOptionsDefaults(options) {
|
|
7536
7873
|
if (!options.headers) {
|
|
@@ -10619,6 +10956,160 @@
|
|
|
10619
10956
|
}
|
|
10620
10957
|
}
|
|
10621
10958
|
|
|
10959
|
+
exports.SearchParameterType = void 0;
|
|
10960
|
+
(function (SearchParameterType) {
|
|
10961
|
+
SearchParameterType["BOOLEAN"] = "BOOLEAN";
|
|
10962
|
+
SearchParameterType["NUMBER"] = "NUMBER";
|
|
10963
|
+
SearchParameterType["QUANTITY"] = "QUANTITY";
|
|
10964
|
+
SearchParameterType["TEXT"] = "TEXT";
|
|
10965
|
+
SearchParameterType["REFERENCE"] = "REFERENCE";
|
|
10966
|
+
SearchParameterType["DATE"] = "DATE";
|
|
10967
|
+
SearchParameterType["DATETIME"] = "DATETIME";
|
|
10968
|
+
SearchParameterType["PERIOD"] = "PERIOD";
|
|
10969
|
+
})(exports.SearchParameterType || (exports.SearchParameterType = {}));
|
|
10970
|
+
/**
|
|
10971
|
+
* Returns the type details of a SearchParameter.
|
|
10972
|
+
*
|
|
10973
|
+
* The SearchParameter resource has a "type" parameter, but that is missing some critical information.
|
|
10974
|
+
*
|
|
10975
|
+
* For example:
|
|
10976
|
+
* 1) The "date" type includes "date", "datetime", and "period".
|
|
10977
|
+
* 2) The "token" type includes enums and booleans.
|
|
10978
|
+
* 3) Arrays/multiple values are not reflected at all.
|
|
10979
|
+
*
|
|
10980
|
+
* @param resourceType The root resource type.
|
|
10981
|
+
* @param searchParam The search parameter.
|
|
10982
|
+
* @returns The search parameter type details.
|
|
10983
|
+
*/
|
|
10984
|
+
function getSearchParameterDetails(resourceType, searchParam) {
|
|
10985
|
+
let result = globalSchema.types[resourceType]?.searchParamsDetails?.[searchParam.code];
|
|
10986
|
+
if (!result) {
|
|
10987
|
+
result = buildSearchParamterDetails(resourceType, searchParam);
|
|
10988
|
+
}
|
|
10989
|
+
return result;
|
|
10990
|
+
}
|
|
10991
|
+
function setSearchParamterDetails(resourceType, code, details) {
|
|
10992
|
+
const typeSchema = globalSchema.types[resourceType];
|
|
10993
|
+
if (!typeSchema.searchParamsDetails) {
|
|
10994
|
+
typeSchema.searchParamsDetails = {};
|
|
10995
|
+
}
|
|
10996
|
+
typeSchema.searchParamsDetails[code] = details;
|
|
10997
|
+
}
|
|
10998
|
+
function buildSearchParamterDetails(resourceType, searchParam) {
|
|
10999
|
+
const code = searchParam.code;
|
|
11000
|
+
const columnName = convertCodeToColumnName(code);
|
|
11001
|
+
const expression = getExpressionForResourceType(resourceType, searchParam.expression)?.split('.');
|
|
11002
|
+
if (!expression) {
|
|
11003
|
+
// This happens on compound types
|
|
11004
|
+
// In the future, explore returning multiple column definitions
|
|
11005
|
+
return { columnName, type: exports.SearchParameterType.TEXT };
|
|
11006
|
+
}
|
|
11007
|
+
const defaultType = getSearchParameterType(searchParam);
|
|
11008
|
+
let baseType = resourceType;
|
|
11009
|
+
let elementDefinition = undefined;
|
|
11010
|
+
let propertyType = undefined;
|
|
11011
|
+
let array = false;
|
|
11012
|
+
for (let i = 1; i < expression.length; i++) {
|
|
11013
|
+
const propertyName = expression[i];
|
|
11014
|
+
elementDefinition = getElementDefinition(baseType, propertyName);
|
|
11015
|
+
if (!elementDefinition) {
|
|
11016
|
+
throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);
|
|
11017
|
+
}
|
|
11018
|
+
if (elementDefinition.max === '*') {
|
|
11019
|
+
array = true;
|
|
11020
|
+
}
|
|
11021
|
+
propertyType = elementDefinition.type?.[0].code;
|
|
11022
|
+
if (!propertyType) {
|
|
11023
|
+
// This happens when one of parent properties uses contentReference
|
|
11024
|
+
// In the future, explore following the reference
|
|
11025
|
+
return { columnName, type: defaultType, array };
|
|
11026
|
+
}
|
|
11027
|
+
if (i < expression.length - 1) {
|
|
11028
|
+
if (isBackboneElement(propertyType)) {
|
|
11029
|
+
baseType = buildTypeName(elementDefinition.path?.split('.'));
|
|
11030
|
+
}
|
|
11031
|
+
else {
|
|
11032
|
+
baseType = propertyType;
|
|
11033
|
+
}
|
|
11034
|
+
}
|
|
11035
|
+
}
|
|
11036
|
+
const type = getSearchParameterType(searchParam, propertyType);
|
|
11037
|
+
const result = { columnName, type, elementDefinition, array };
|
|
11038
|
+
setSearchParamterDetails(resourceType, code, result);
|
|
11039
|
+
return result;
|
|
11040
|
+
}
|
|
11041
|
+
function isBackboneElement(propertyType) {
|
|
11042
|
+
return propertyType === 'Element' || propertyType === 'BackboneElement';
|
|
11043
|
+
}
|
|
11044
|
+
/**
|
|
11045
|
+
* Converts a hyphen-delimited code to camelCase string.
|
|
11046
|
+
* @param code The search parameter code.
|
|
11047
|
+
* @returns The SQL column name.
|
|
11048
|
+
*/
|
|
11049
|
+
function convertCodeToColumnName(code) {
|
|
11050
|
+
return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');
|
|
11051
|
+
}
|
|
11052
|
+
function getSearchParameterType(searchParam, propertyType) {
|
|
11053
|
+
let type = exports.SearchParameterType.TEXT;
|
|
11054
|
+
switch (searchParam.type) {
|
|
11055
|
+
case 'date':
|
|
11056
|
+
if (propertyType === exports.PropertyType.dateTime || propertyType === exports.PropertyType.instant) {
|
|
11057
|
+
type = exports.SearchParameterType.DATETIME;
|
|
11058
|
+
}
|
|
11059
|
+
else {
|
|
11060
|
+
type = exports.SearchParameterType.DATE;
|
|
11061
|
+
}
|
|
11062
|
+
break;
|
|
11063
|
+
case 'number':
|
|
11064
|
+
type = exports.SearchParameterType.NUMBER;
|
|
11065
|
+
break;
|
|
11066
|
+
case 'quantity':
|
|
11067
|
+
type = exports.SearchParameterType.QUANTITY;
|
|
11068
|
+
break;
|
|
11069
|
+
case 'reference':
|
|
11070
|
+
type = exports.SearchParameterType.REFERENCE;
|
|
11071
|
+
break;
|
|
11072
|
+
case 'token':
|
|
11073
|
+
if (propertyType === 'boolean') {
|
|
11074
|
+
type = exports.SearchParameterType.BOOLEAN;
|
|
11075
|
+
}
|
|
11076
|
+
break;
|
|
11077
|
+
}
|
|
11078
|
+
return type;
|
|
11079
|
+
}
|
|
11080
|
+
function getExpressionForResourceType(resourceType, expression) {
|
|
11081
|
+
const expressions = expression.split(' | ');
|
|
11082
|
+
for (const e of expressions) {
|
|
11083
|
+
if (isIgnoredExpression(e)) {
|
|
11084
|
+
continue;
|
|
11085
|
+
}
|
|
11086
|
+
const simplified = simplifyExpression(e);
|
|
11087
|
+
if (simplified.startsWith(resourceType + '.')) {
|
|
11088
|
+
return simplified;
|
|
11089
|
+
}
|
|
11090
|
+
}
|
|
11091
|
+
return undefined;
|
|
11092
|
+
}
|
|
11093
|
+
function isIgnoredExpression(input) {
|
|
11094
|
+
return input.includes(' as Period') || input.includes(' as SampledDate');
|
|
11095
|
+
}
|
|
11096
|
+
function simplifyExpression(input) {
|
|
11097
|
+
let result = input.trim();
|
|
11098
|
+
if (result.startsWith('(') && result.endsWith(')')) {
|
|
11099
|
+
result = result.substring(1, result.length - 1);
|
|
11100
|
+
}
|
|
11101
|
+
if (result.includes('[0]')) {
|
|
11102
|
+
result = result.replaceAll('[0]', '');
|
|
11103
|
+
}
|
|
11104
|
+
const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.where('];
|
|
11105
|
+
for (const stopString of stopStrings) {
|
|
11106
|
+
if (result.includes(stopString)) {
|
|
11107
|
+
result = result.substring(0, result.indexOf(stopString));
|
|
11108
|
+
}
|
|
11109
|
+
}
|
|
11110
|
+
return result;
|
|
11111
|
+
}
|
|
11112
|
+
|
|
10622
11113
|
const DEFAULT_SEARCH_COUNT = 20;
|
|
10623
11114
|
/**
|
|
10624
11115
|
* Search operators.
|
|
@@ -10818,216 +11309,57 @@
|
|
|
10818
11309
|
return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
|
|
10819
11310
|
}
|
|
10820
11311
|
function formatSortRules(sortRules) {
|
|
10821
|
-
if (!sortRules || sortRules.length === 0) {
|
|
10822
|
-
return '';
|
|
10823
|
-
}
|
|
10824
11312
|
return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
|
|
10825
11313
|
}
|
|
10826
11314
|
|
|
10827
|
-
exports.SearchParameterType = void 0;
|
|
10828
|
-
(function (SearchParameterType) {
|
|
10829
|
-
SearchParameterType["BOOLEAN"] = "BOOLEAN";
|
|
10830
|
-
SearchParameterType["NUMBER"] = "NUMBER";
|
|
10831
|
-
SearchParameterType["QUANTITY"] = "QUANTITY";
|
|
10832
|
-
SearchParameterType["TEXT"] = "TEXT";
|
|
10833
|
-
SearchParameterType["REFERENCE"] = "REFERENCE";
|
|
10834
|
-
SearchParameterType["DATE"] = "DATE";
|
|
10835
|
-
SearchParameterType["DATETIME"] = "DATETIME";
|
|
10836
|
-
SearchParameterType["PERIOD"] = "PERIOD";
|
|
10837
|
-
})(exports.SearchParameterType || (exports.SearchParameterType = {}));
|
|
10838
11315
|
/**
|
|
10839
|
-
*
|
|
10840
|
-
*
|
|
10841
|
-
* The
|
|
10842
|
-
*
|
|
10843
|
-
* For example:
|
|
10844
|
-
* 1) The "date" type includes "date", "datetime", and "period".
|
|
10845
|
-
* 2) The "token" type includes enums and booleans.
|
|
10846
|
-
* 3) Arrays/multiple values are not reflected at all.
|
|
10847
|
-
*
|
|
10848
|
-
* @param resourceType The root resource type.
|
|
10849
|
-
* @param searchParam The search parameter.
|
|
10850
|
-
* @returns The search parameter type details.
|
|
11316
|
+
* Determines if the resource matches the search request.
|
|
11317
|
+
* @param resource The resource that was created or updated.
|
|
11318
|
+
* @param searchRequest The subscription criteria as a search request.
|
|
11319
|
+
* @returns True if the resource satisfies the search request.
|
|
10851
11320
|
*/
|
|
10852
|
-
function
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
result = buildSearchParamterDetails(resourceType, searchParam);
|
|
11321
|
+
function matchesSearchRequest(resource, searchRequest) {
|
|
11322
|
+
if (searchRequest.resourceType !== resource.resourceType) {
|
|
11323
|
+
return false;
|
|
10856
11324
|
}
|
|
10857
|
-
|
|
11325
|
+
if (searchRequest.filters) {
|
|
11326
|
+
for (const filter of searchRequest.filters) {
|
|
11327
|
+
if (!matchesSearchFilter(resource, searchRequest, filter)) {
|
|
11328
|
+
return false;
|
|
11329
|
+
}
|
|
11330
|
+
}
|
|
11331
|
+
}
|
|
11332
|
+
return true;
|
|
10858
11333
|
}
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
11334
|
+
/**
|
|
11335
|
+
* Determines if the resource matches the search filter.
|
|
11336
|
+
* @param resource The resource that was created or updated.
|
|
11337
|
+
* @param filter One of the filters of a subscription criteria.
|
|
11338
|
+
* @returns True if the resource satisfies the search filter.
|
|
11339
|
+
*/
|
|
11340
|
+
function matchesSearchFilter(resource, searchRequest, filter) {
|
|
11341
|
+
const searchParam = globalSchema.types[searchRequest.resourceType]?.searchParams?.[filter.code];
|
|
11342
|
+
switch (searchParam?.type) {
|
|
11343
|
+
case 'reference':
|
|
11344
|
+
return matchesReferenceFilter(resource, filter, searchParam);
|
|
11345
|
+
case 'string':
|
|
11346
|
+
return matchesStringFilter(resource, filter, searchParam);
|
|
11347
|
+
case 'token':
|
|
11348
|
+
return matchesTokenFilter(resource, filter, searchParam);
|
|
11349
|
+
case 'date':
|
|
11350
|
+
return matchesDateFilter(resource, filter, searchParam);
|
|
10863
11351
|
}
|
|
10864
|
-
|
|
11352
|
+
// Unknown search parameter or search parameter type
|
|
11353
|
+
// Default fail the check
|
|
11354
|
+
return false;
|
|
10865
11355
|
}
|
|
10866
|
-
function
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10873
|
-
if (!expression) {
|
|
10874
|
-
// This happens on compound types
|
|
10875
|
-
// In the future, explore returning multiple column definitions
|
|
10876
|
-
return { columnName, type: exports.SearchParameterType.TEXT };
|
|
10877
|
-
}
|
|
10878
|
-
const defaultType = getSearchParameterType(searchParam);
|
|
10879
|
-
let baseType = resourceType;
|
|
10880
|
-
let elementDefinition = undefined;
|
|
10881
|
-
let propertyType = undefined;
|
|
10882
|
-
let array = false;
|
|
10883
|
-
for (let i = 1; i < expression.length; i++) {
|
|
10884
|
-
const propertyName = expression[i];
|
|
10885
|
-
elementDefinition =
|
|
10886
|
-
globalSchema.types[baseType]?.properties?.[propertyName] ??
|
|
10887
|
-
globalSchema.types[baseType]?.properties?.[propertyName + '[x]'];
|
|
10888
|
-
if (!elementDefinition) {
|
|
10889
|
-
throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);
|
|
10890
|
-
}
|
|
10891
|
-
if (elementDefinition.max === '*') {
|
|
10892
|
-
array = true;
|
|
10893
|
-
}
|
|
10894
|
-
propertyType = elementDefinition.type?.[0].code;
|
|
10895
|
-
if (!propertyType) {
|
|
10896
|
-
// This happens when one of parent properties uses contentReference
|
|
10897
|
-
// In the future, explore following the reference
|
|
10898
|
-
return { columnName, type: defaultType, array };
|
|
10899
|
-
}
|
|
10900
|
-
if (i < expression.length - 1) {
|
|
10901
|
-
if (propertyType === 'Element' || propertyType === 'BackboneElement') {
|
|
10902
|
-
baseType = baseType + capitalize(propertyName);
|
|
10903
|
-
}
|
|
10904
|
-
else {
|
|
10905
|
-
baseType = propertyType;
|
|
10906
|
-
}
|
|
10907
|
-
}
|
|
10908
|
-
}
|
|
10909
|
-
const type = getSearchParameterType(searchParam, propertyType);
|
|
10910
|
-
const result = { columnName, type, elementDefinition, array };
|
|
10911
|
-
setSearchParamterDetails(resourceType, code, result);
|
|
10912
|
-
return result;
|
|
10913
|
-
}
|
|
10914
|
-
/**
|
|
10915
|
-
* Converts a hyphen-delimited code to camelCase string.
|
|
10916
|
-
* @param code The search parameter code.
|
|
10917
|
-
* @returns The SQL column name.
|
|
10918
|
-
*/
|
|
10919
|
-
function convertCodeToColumnName(code) {
|
|
10920
|
-
return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');
|
|
10921
|
-
}
|
|
10922
|
-
function getSearchParameterType(searchParam, propertyType) {
|
|
10923
|
-
let type = exports.SearchParameterType.TEXT;
|
|
10924
|
-
switch (searchParam.type) {
|
|
10925
|
-
case 'date':
|
|
10926
|
-
if (propertyType === exports.PropertyType.dateTime || propertyType === exports.PropertyType.instant) {
|
|
10927
|
-
type = exports.SearchParameterType.DATETIME;
|
|
10928
|
-
}
|
|
10929
|
-
else {
|
|
10930
|
-
type = exports.SearchParameterType.DATE;
|
|
10931
|
-
}
|
|
10932
|
-
break;
|
|
10933
|
-
case 'number':
|
|
10934
|
-
type = exports.SearchParameterType.NUMBER;
|
|
10935
|
-
break;
|
|
10936
|
-
case 'quantity':
|
|
10937
|
-
type = exports.SearchParameterType.QUANTITY;
|
|
10938
|
-
break;
|
|
10939
|
-
case 'reference':
|
|
10940
|
-
type = exports.SearchParameterType.REFERENCE;
|
|
10941
|
-
break;
|
|
10942
|
-
case 'token':
|
|
10943
|
-
if (propertyType === 'boolean') {
|
|
10944
|
-
type = exports.SearchParameterType.BOOLEAN;
|
|
10945
|
-
}
|
|
10946
|
-
break;
|
|
10947
|
-
}
|
|
10948
|
-
return type;
|
|
10949
|
-
}
|
|
10950
|
-
function getExpressionForResourceType(resourceType, expression) {
|
|
10951
|
-
const expressions = expression.split(' | ');
|
|
10952
|
-
for (const e of expressions) {
|
|
10953
|
-
if (isIgnoredExpression(e)) {
|
|
10954
|
-
continue;
|
|
10955
|
-
}
|
|
10956
|
-
const simplified = simplifyExpression(e);
|
|
10957
|
-
if (simplified.startsWith(resourceType + '.')) {
|
|
10958
|
-
return simplified;
|
|
10959
|
-
}
|
|
10960
|
-
}
|
|
10961
|
-
return undefined;
|
|
10962
|
-
}
|
|
10963
|
-
function isIgnoredExpression(input) {
|
|
10964
|
-
return input.includes(' as Period') || input.includes(' as SampledDate');
|
|
10965
|
-
}
|
|
10966
|
-
function simplifyExpression(input) {
|
|
10967
|
-
let result = input.trim();
|
|
10968
|
-
if (result.startsWith('(') && result.endsWith(')')) {
|
|
10969
|
-
result = result.substring(1, result.length - 1);
|
|
10970
|
-
}
|
|
10971
|
-
if (result.includes('[0]')) {
|
|
10972
|
-
result = result.replaceAll('[0]', '');
|
|
10973
|
-
}
|
|
10974
|
-
const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.where('];
|
|
10975
|
-
for (const stopString of stopStrings) {
|
|
10976
|
-
if (result.includes(stopString)) {
|
|
10977
|
-
result = result.substring(0, result.indexOf(stopString));
|
|
10978
|
-
}
|
|
10979
|
-
}
|
|
10980
|
-
return result;
|
|
10981
|
-
}
|
|
10982
|
-
|
|
10983
|
-
/**
|
|
10984
|
-
* Determines if the resource matches the search request.
|
|
10985
|
-
* @param resource The resource that was created or updated.
|
|
10986
|
-
* @param searchRequest The subscription criteria as a search request.
|
|
10987
|
-
* @returns True if the resource satisfies the search request.
|
|
10988
|
-
*/
|
|
10989
|
-
function matchesSearchRequest(resource, searchRequest) {
|
|
10990
|
-
if (searchRequest.resourceType !== resource.resourceType) {
|
|
10991
|
-
return false;
|
|
10992
|
-
}
|
|
10993
|
-
if (searchRequest.filters) {
|
|
10994
|
-
for (const filter of searchRequest.filters) {
|
|
10995
|
-
if (!matchesSearchFilter(resource, searchRequest, filter)) {
|
|
10996
|
-
return false;
|
|
10997
|
-
}
|
|
10998
|
-
}
|
|
10999
|
-
}
|
|
11000
|
-
return true;
|
|
11001
|
-
}
|
|
11002
|
-
/**
|
|
11003
|
-
* Determines if the resource matches the search filter.
|
|
11004
|
-
* @param resource The resource that was created or updated.
|
|
11005
|
-
* @param filter One of the filters of a subscription criteria.
|
|
11006
|
-
* @returns True if the resource satisfies the search filter.
|
|
11007
|
-
*/
|
|
11008
|
-
function matchesSearchFilter(resource, searchRequest, filter) {
|
|
11009
|
-
const searchParam = globalSchema.types[searchRequest.resourceType]?.searchParams?.[filter.code];
|
|
11010
|
-
switch (searchParam?.type) {
|
|
11011
|
-
case 'reference':
|
|
11012
|
-
return matchesReferenceFilter(resource, filter, searchParam);
|
|
11013
|
-
case 'string':
|
|
11014
|
-
return matchesStringFilter(resource, filter, searchParam);
|
|
11015
|
-
case 'token':
|
|
11016
|
-
return matchesTokenFilter(resource, filter, searchParam);
|
|
11017
|
-
case 'date':
|
|
11018
|
-
return matchesDateFilter(resource, filter, searchParam);
|
|
11019
|
-
}
|
|
11020
|
-
// Unknown search parameter or search parameter type
|
|
11021
|
-
// Default fail the check
|
|
11022
|
-
return false;
|
|
11023
|
-
}
|
|
11024
|
-
function matchesReferenceFilter(resource, filter, searchParam) {
|
|
11025
|
-
const values = evalFhirPath(searchParam.expression, resource);
|
|
11026
|
-
const negated = isNegated(filter.operator);
|
|
11027
|
-
if (filter.value === '' && values.length === 0) {
|
|
11028
|
-
// If the filter operator is "equals", then the filter matches.
|
|
11029
|
-
// If the filter operator is "not equals", then the filter does not match.
|
|
11030
|
-
return filter.operator === exports.Operator.EQUALS;
|
|
11356
|
+
function matchesReferenceFilter(resource, filter, searchParam) {
|
|
11357
|
+
const values = evalFhirPath(searchParam.expression, resource);
|
|
11358
|
+
const negated = isNegated(filter.operator);
|
|
11359
|
+
if (filter.value === '' && values.length === 0) {
|
|
11360
|
+
// If the filter operator is "equals", then the filter matches.
|
|
11361
|
+
// If the filter operator is "not equals", then the filter does not match.
|
|
11362
|
+
return filter.operator === exports.Operator.EQUALS;
|
|
11031
11363
|
}
|
|
11032
11364
|
// Normalize the values array into reference strings
|
|
11033
11365
|
const references = values.map((value) => (typeof value === 'string' ? value : value.reference));
|
|
@@ -11129,207 +11461,216 @@
|
|
|
11129
11461
|
return operator === exports.Operator.NOT_EQUALS || operator === exports.Operator.NOT;
|
|
11130
11462
|
}
|
|
11131
11463
|
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
const created = {
|
|
11154
|
-
resourceType: 'OperationOutcome',
|
|
11155
|
-
id: CREATED_ID,
|
|
11156
|
-
issue: [
|
|
11157
|
-
{
|
|
11158
|
-
severity: 'information',
|
|
11159
|
-
code: 'informational',
|
|
11160
|
-
details: {
|
|
11161
|
-
text: 'Created',
|
|
11162
|
-
},
|
|
11163
|
-
},
|
|
11164
|
-
],
|
|
11165
|
-
};
|
|
11166
|
-
const notModified = {
|
|
11167
|
-
resourceType: 'OperationOutcome',
|
|
11168
|
-
id: NOT_MODIFIED_ID,
|
|
11169
|
-
issue: [
|
|
11170
|
-
{
|
|
11171
|
-
severity: 'information',
|
|
11172
|
-
code: 'informational',
|
|
11173
|
-
details: {
|
|
11174
|
-
text: 'Not Modified',
|
|
11175
|
-
},
|
|
11176
|
-
},
|
|
11177
|
-
],
|
|
11178
|
-
};
|
|
11179
|
-
const notFound = {
|
|
11180
|
-
resourceType: 'OperationOutcome',
|
|
11181
|
-
id: NOT_FOUND_ID,
|
|
11182
|
-
issue: [
|
|
11183
|
-
{
|
|
11184
|
-
severity: 'error',
|
|
11185
|
-
code: 'not-found',
|
|
11186
|
-
details: {
|
|
11187
|
-
text: 'Not found',
|
|
11188
|
-
},
|
|
11189
|
-
},
|
|
11190
|
-
],
|
|
11191
|
-
};
|
|
11192
|
-
const unauthorized = {
|
|
11193
|
-
resourceType: 'OperationOutcome',
|
|
11194
|
-
id: UNAUTHORIZED_ID,
|
|
11195
|
-
issue: [
|
|
11196
|
-
{
|
|
11197
|
-
severity: 'error',
|
|
11198
|
-
code: 'login',
|
|
11199
|
-
details: {
|
|
11200
|
-
text: 'Unauthorized',
|
|
11201
|
-
},
|
|
11202
|
-
},
|
|
11203
|
-
],
|
|
11204
|
-
};
|
|
11205
|
-
const forbidden = {
|
|
11206
|
-
resourceType: 'OperationOutcome',
|
|
11207
|
-
id: FORBIDDEN_ID,
|
|
11208
|
-
issue: [
|
|
11209
|
-
{
|
|
11210
|
-
severity: 'error',
|
|
11211
|
-
code: 'forbidden',
|
|
11212
|
-
details: {
|
|
11213
|
-
text: 'Forbidden',
|
|
11214
|
-
},
|
|
11215
|
-
},
|
|
11216
|
-
],
|
|
11217
|
-
};
|
|
11218
|
-
const gone = {
|
|
11219
|
-
resourceType: 'OperationOutcome',
|
|
11220
|
-
id: GONE_ID,
|
|
11221
|
-
issue: [
|
|
11222
|
-
{
|
|
11223
|
-
severity: 'error',
|
|
11224
|
-
code: 'deleted',
|
|
11225
|
-
details: {
|
|
11226
|
-
text: 'Gone',
|
|
11227
|
-
},
|
|
11228
|
-
},
|
|
11229
|
-
],
|
|
11464
|
+
var _SearchParser_instances, _SearchParser_parseKeyValue, _SearchParser_parseSortRule, _SearchParser_parseParameter, _SearchParser_parsePrefixType, _SearchParser_parseModifierType, _SearchParser_parseReference, _SearchParser_parseQuantity, _SearchParser_parseUnknownParameter;
|
|
11465
|
+
/**
|
|
11466
|
+
* Parses a FHIR search query.
|
|
11467
|
+
* See: https://www.hl7.org/fhir/search.html
|
|
11468
|
+
*/
|
|
11469
|
+
/**
|
|
11470
|
+
* For the ordered parameter types of number, date, and quantity,
|
|
11471
|
+
* a prefix to the parameter value may be used to control the nature
|
|
11472
|
+
* of the matching.
|
|
11473
|
+
* See: https://www.hl7.org/fhir/search.html#prefix
|
|
11474
|
+
*/
|
|
11475
|
+
const prefixMap = {
|
|
11476
|
+
eq: exports.Operator.EQUALS,
|
|
11477
|
+
ne: exports.Operator.NOT_EQUALS,
|
|
11478
|
+
lt: exports.Operator.LESS_THAN,
|
|
11479
|
+
le: exports.Operator.LESS_THAN_OR_EQUALS,
|
|
11480
|
+
gt: exports.Operator.GREATER_THAN,
|
|
11481
|
+
ge: exports.Operator.GREATER_THAN_OR_EQUALS,
|
|
11482
|
+
sa: exports.Operator.STARTS_AFTER,
|
|
11483
|
+
eb: exports.Operator.ENDS_BEFORE,
|
|
11484
|
+
ap: exports.Operator.APPROXIMATELY,
|
|
11230
11485
|
};
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11486
|
+
/**
|
|
11487
|
+
* Parameter names may specify a modifier as a suffix.
|
|
11488
|
+
* The modifiers are separated from the parameter name by a colon.
|
|
11489
|
+
* See: https://www.hl7.org/fhir/search.html#modifiers
|
|
11490
|
+
*/
|
|
11491
|
+
const modifierMap = {
|
|
11492
|
+
contains: exports.Operator.CONTAINS,
|
|
11493
|
+
exact: exports.Operator.EXACT,
|
|
11494
|
+
above: exports.Operator.ABOVE,
|
|
11495
|
+
below: exports.Operator.BELOW,
|
|
11496
|
+
text: exports.Operator.TEXT,
|
|
11497
|
+
not: exports.Operator.NOT,
|
|
11498
|
+
in: exports.Operator.IN,
|
|
11499
|
+
'not-in': exports.Operator.NOT_IN,
|
|
11500
|
+
'of-type': exports.Operator.OF_TYPE,
|
|
11243
11501
|
};
|
|
11244
|
-
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
text: details,
|
|
11253
|
-
},
|
|
11254
|
-
expression: expression ? [expression] : undefined,
|
|
11255
|
-
},
|
|
11256
|
-
],
|
|
11257
|
-
};
|
|
11258
|
-
}
|
|
11259
|
-
function isOk(outcome) {
|
|
11260
|
-
return outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID;
|
|
11261
|
-
}
|
|
11262
|
-
function isNotFound(outcome) {
|
|
11263
|
-
return outcome.id === NOT_FOUND_ID;
|
|
11264
|
-
}
|
|
11265
|
-
function isGone(outcome) {
|
|
11266
|
-
return outcome.id === GONE_ID;
|
|
11502
|
+
/**
|
|
11503
|
+
* Parses a search URL into a search request.
|
|
11504
|
+
* @param resourceType The FHIR resource type.
|
|
11505
|
+
* @param query The collection of query string parameters.
|
|
11506
|
+
* @returns A parsed SearchRequest.
|
|
11507
|
+
*/
|
|
11508
|
+
function parseSearchRequest(resourceType, query) {
|
|
11509
|
+
return new SearchParser(resourceType, query);
|
|
11267
11510
|
}
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11511
|
+
/**
|
|
11512
|
+
* Parses a search URL into a search request.
|
|
11513
|
+
* @param url The search URL.
|
|
11514
|
+
* @returns A parsed SearchRequest.
|
|
11515
|
+
*/
|
|
11516
|
+
function parseSearchUrl(url) {
|
|
11517
|
+
const resourceType = url.pathname.split('/').pop();
|
|
11518
|
+
return new SearchParser(resourceType, Object.fromEntries(url.searchParams.entries()));
|
|
11519
|
+
}
|
|
11520
|
+
class SearchParser {
|
|
11521
|
+
constructor(resourceType, query) {
|
|
11522
|
+
_SearchParser_instances.add(this);
|
|
11523
|
+
this.resourceType = resourceType;
|
|
11524
|
+
this.filters = [];
|
|
11525
|
+
this.sortRules = [];
|
|
11526
|
+
for (const [key, value] of Object.entries(query)) {
|
|
11527
|
+
if (Array.isArray(value)) {
|
|
11528
|
+
value.forEach((element) => __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, element));
|
|
11529
|
+
}
|
|
11530
|
+
else {
|
|
11531
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, value ?? '');
|
|
11532
|
+
}
|
|
11533
|
+
}
|
|
11271
11534
|
}
|
|
11272
|
-
|
|
11273
|
-
|
|
11535
|
+
}
|
|
11536
|
+
_SearchParser_instances = new WeakSet(), _SearchParser_parseKeyValue = function _SearchParser_parseKeyValue(key, value) {
|
|
11537
|
+
let code;
|
|
11538
|
+
let modifier;
|
|
11539
|
+
const colonIndex = key.indexOf(':');
|
|
11540
|
+
if (colonIndex >= 0) {
|
|
11541
|
+
code = key.substring(0, colonIndex);
|
|
11542
|
+
modifier = key.substring(colonIndex + 1);
|
|
11274
11543
|
}
|
|
11275
|
-
else
|
|
11276
|
-
|
|
11544
|
+
else {
|
|
11545
|
+
code = key;
|
|
11546
|
+
modifier = '';
|
|
11277
11547
|
}
|
|
11278
|
-
|
|
11279
|
-
|
|
11548
|
+
switch (code) {
|
|
11549
|
+
case '_sort':
|
|
11550
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseSortRule).call(this, value);
|
|
11551
|
+
break;
|
|
11552
|
+
case '_count':
|
|
11553
|
+
this.count = parseInt(value);
|
|
11554
|
+
break;
|
|
11555
|
+
case '_offset':
|
|
11556
|
+
this.offset = parseInt(value);
|
|
11557
|
+
break;
|
|
11558
|
+
case '_total':
|
|
11559
|
+
this.total = value;
|
|
11560
|
+
break;
|
|
11561
|
+
case '_summary':
|
|
11562
|
+
this.total = 'estimate';
|
|
11563
|
+
this.count = 0;
|
|
11564
|
+
break;
|
|
11565
|
+
case '_revinclude':
|
|
11566
|
+
this.revInclude = value;
|
|
11567
|
+
break;
|
|
11568
|
+
default: {
|
|
11569
|
+
const param = globalSchema.types[this.resourceType]?.searchParams?.[code];
|
|
11570
|
+
if (param) {
|
|
11571
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseParameter).call(this, param, modifier, value);
|
|
11572
|
+
}
|
|
11573
|
+
else {
|
|
11574
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseUnknownParameter).call(this, code, modifier, value);
|
|
11575
|
+
}
|
|
11576
|
+
}
|
|
11280
11577
|
}
|
|
11281
|
-
|
|
11282
|
-
|
|
11578
|
+
}, _SearchParser_parseSortRule = function _SearchParser_parseSortRule(value) {
|
|
11579
|
+
for (const field of value.split(',')) {
|
|
11580
|
+
let code;
|
|
11581
|
+
let descending = false;
|
|
11582
|
+
if (field.startsWith('-')) {
|
|
11583
|
+
code = field.substring(1);
|
|
11584
|
+
descending = true;
|
|
11585
|
+
}
|
|
11586
|
+
else {
|
|
11587
|
+
code = field;
|
|
11588
|
+
}
|
|
11589
|
+
this.sortRules.push({ code, descending });
|
|
11283
11590
|
}
|
|
11284
|
-
|
|
11285
|
-
|
|
11591
|
+
}, _SearchParser_parseParameter = function _SearchParser_parseParameter(searchParam, modifier, value) {
|
|
11592
|
+
if (modifier === 'missing') {
|
|
11593
|
+
this.filters.push({
|
|
11594
|
+
code: searchParam.code,
|
|
11595
|
+
operator: exports.Operator.MISSING,
|
|
11596
|
+
value,
|
|
11597
|
+
});
|
|
11598
|
+
return;
|
|
11286
11599
|
}
|
|
11287
|
-
|
|
11288
|
-
|
|
11600
|
+
switch (searchParam.type) {
|
|
11601
|
+
case 'number':
|
|
11602
|
+
case 'date':
|
|
11603
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parsePrefixType).call(this, searchParam, value);
|
|
11604
|
+
break;
|
|
11605
|
+
case 'string':
|
|
11606
|
+
case 'token':
|
|
11607
|
+
case 'uri':
|
|
11608
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseModifierType).call(this, searchParam, modifier, value);
|
|
11609
|
+
break;
|
|
11610
|
+
case 'reference':
|
|
11611
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseReference).call(this, searchParam, value);
|
|
11612
|
+
break;
|
|
11613
|
+
case 'quantity':
|
|
11614
|
+
__classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseQuantity).call(this, searchParam, value);
|
|
11615
|
+
break;
|
|
11289
11616
|
}
|
|
11290
|
-
|
|
11291
|
-
|
|
11617
|
+
}, _SearchParser_parsePrefixType = function _SearchParser_parsePrefixType(param, input) {
|
|
11618
|
+
const { operator, value } = parsePrefix(input);
|
|
11619
|
+
this.filters.push({
|
|
11620
|
+
code: param.code,
|
|
11621
|
+
operator,
|
|
11622
|
+
value,
|
|
11623
|
+
});
|
|
11624
|
+
}, _SearchParser_parseModifierType = function _SearchParser_parseModifierType(param, modifier, value) {
|
|
11625
|
+
this.filters.push({
|
|
11626
|
+
code: param.code,
|
|
11627
|
+
operator: parseModifier(modifier),
|
|
11628
|
+
value,
|
|
11629
|
+
});
|
|
11630
|
+
}, _SearchParser_parseReference = function _SearchParser_parseReference(param, value) {
|
|
11631
|
+
this.filters.push({
|
|
11632
|
+
code: param.code,
|
|
11633
|
+
operator: exports.Operator.EQUALS,
|
|
11634
|
+
value: value,
|
|
11635
|
+
});
|
|
11636
|
+
}, _SearchParser_parseQuantity = function _SearchParser_parseQuantity(param, input) {
|
|
11637
|
+
const [prefixNumber, unitSystem, unitCode] = input.split('|');
|
|
11638
|
+
const { operator, value } = parsePrefix(prefixNumber);
|
|
11639
|
+
this.filters.push({
|
|
11640
|
+
code: param.code,
|
|
11641
|
+
operator,
|
|
11642
|
+
value,
|
|
11643
|
+
unitSystem,
|
|
11644
|
+
unitCode,
|
|
11645
|
+
});
|
|
11646
|
+
}, _SearchParser_parseUnknownParameter = function _SearchParser_parseUnknownParameter(code, modifier, value) {
|
|
11647
|
+
let operator = exports.Operator.EQUALS;
|
|
11648
|
+
if (modifier) {
|
|
11649
|
+
operator = modifier;
|
|
11292
11650
|
}
|
|
11293
11651
|
else {
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
* @param resource The resource that may or may not have been returned.
|
|
11301
|
-
*/
|
|
11302
|
-
function assertOk(outcome, resource) {
|
|
11303
|
-
if (!isOk(outcome) || resource === undefined) {
|
|
11304
|
-
throw new OperationOutcomeError(outcome);
|
|
11652
|
+
for (const prefix of Object.keys(prefixMap)) {
|
|
11653
|
+
if (value.match(new RegExp('^' + prefix + '\\d'))) {
|
|
11654
|
+
operator = prefix;
|
|
11655
|
+
value = value.substring(prefix.length);
|
|
11656
|
+
}
|
|
11657
|
+
}
|
|
11305
11658
|
}
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11659
|
+
this.filters.push({
|
|
11660
|
+
code,
|
|
11661
|
+
operator,
|
|
11662
|
+
value,
|
|
11663
|
+
});
|
|
11664
|
+
};
|
|
11665
|
+
function parsePrefix(input) {
|
|
11666
|
+
const prefix = input.substring(0, 2);
|
|
11667
|
+
if (prefix in prefixMap) {
|
|
11668
|
+
return { operator: prefixMap[prefix], value: input.substring(2) };
|
|
11311
11669
|
}
|
|
11670
|
+
return { operator: exports.Operator.EQUALS, value: input };
|
|
11312
11671
|
}
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
* @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.
|
|
11316
|
-
* @returns A display string for the error.
|
|
11317
|
-
*/
|
|
11318
|
-
function normalizeErrorString(error) {
|
|
11319
|
-
if (!error) {
|
|
11320
|
-
return 'Unknown error';
|
|
11321
|
-
}
|
|
11322
|
-
if (typeof error === 'string') {
|
|
11323
|
-
return error;
|
|
11324
|
-
}
|
|
11325
|
-
if (error instanceof Error) {
|
|
11326
|
-
return error.message;
|
|
11327
|
-
}
|
|
11328
|
-
if (typeof error === 'object' && 'resourceType' in error) {
|
|
11329
|
-
const outcome = error;
|
|
11330
|
-
return outcome.issue?.[0]?.details?.text ?? 'Unknown error';
|
|
11331
|
-
}
|
|
11332
|
-
return JSON.stringify(error);
|
|
11672
|
+
function parseModifier(modifier) {
|
|
11673
|
+
return modifierMap[modifier] || exports.Operator.EQUALS;
|
|
11333
11674
|
}
|
|
11334
11675
|
|
|
11335
11676
|
exports.AndAtom = AndAtom;
|
|
@@ -11377,7 +11718,6 @@
|
|
|
11377
11718
|
exports.calculateAgeString = calculateAgeString;
|
|
11378
11719
|
exports.capitalize = capitalize;
|
|
11379
11720
|
exports.createReference = createReference;
|
|
11380
|
-
exports.createSchema = createSchema;
|
|
11381
11721
|
exports.created = created;
|
|
11382
11722
|
exports.deepClone = deepClone;
|
|
11383
11723
|
exports.deepEquals = deepEquals$1;
|
|
@@ -11421,7 +11761,10 @@
|
|
|
11421
11761
|
exports.getPropertyDisplayName = getPropertyDisplayName;
|
|
11422
11762
|
exports.getQuestionnaireAnswers = getQuestionnaireAnswers;
|
|
11423
11763
|
exports.getReferenceString = getReferenceString;
|
|
11764
|
+
exports.getResourceTypeSchema = getResourceTypeSchema;
|
|
11765
|
+
exports.getResourceTypes = getResourceTypes;
|
|
11424
11766
|
exports.getSearchParameterDetails = getSearchParameterDetails;
|
|
11767
|
+
exports.getSearchParameters = getSearchParameters;
|
|
11425
11768
|
exports.getStatus = getStatus;
|
|
11426
11769
|
exports.getTypedPropertyValue = getTypedPropertyValue;
|
|
11427
11770
|
exports.globalSchema = globalSchema;
|
|
@@ -11437,10 +11780,12 @@
|
|
|
11437
11780
|
exports.isNotFound = isNotFound;
|
|
11438
11781
|
exports.isObject = isObject$1;
|
|
11439
11782
|
exports.isOk = isOk;
|
|
11783
|
+
exports.isOperationOutcome = isOperationOutcome;
|
|
11440
11784
|
exports.isPeriod = isPeriod;
|
|
11441
11785
|
exports.isProfileResource = isProfileResource;
|
|
11442
11786
|
exports.isQuantity = isQuantity;
|
|
11443
11787
|
exports.isQuantityEquivalent = isQuantityEquivalent;
|
|
11788
|
+
exports.isResourceType = isResourceType;
|
|
11444
11789
|
exports.isStringArray = isStringArray;
|
|
11445
11790
|
exports.isUUID = isUUID;
|
|
11446
11791
|
exports.isValidDate = isValidDate;
|
|
@@ -11452,6 +11797,8 @@
|
|
|
11452
11797
|
exports.parseFhirPath = parseFhirPath;
|
|
11453
11798
|
exports.parseJWTPayload = parseJWTPayload;
|
|
11454
11799
|
exports.parseSearchDefinition = parseSearchDefinition;
|
|
11800
|
+
exports.parseSearchRequest = parseSearchRequest;
|
|
11801
|
+
exports.parseSearchUrl = parseSearchUrl;
|
|
11455
11802
|
exports.preciseEquals = preciseEquals;
|
|
11456
11803
|
exports.preciseGreaterThan = preciseGreaterThan;
|
|
11457
11804
|
exports.preciseGreaterThanOrEquals = preciseGreaterThanOrEquals;
|