@medplum/mock 0.9.28 → 0.9.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,4 +1,42 @@
1
- import { createReference, getReferenceString, evalFhirPath, Operator, MedplumClient, badRequest, isOk, allOk, notFound, parseSearchDefinition, getStatus } from '@medplum/core';
1
+ import { createReference, getReferenceString, deepClone, badRequest, notFound, evalFhirPath, Operator, MedplumClient, allOk, isOk, parseSearchDefinition, getStatus } from '@medplum/core';
2
+
3
+ /******************************************************************************
4
+ Copyright (c) Microsoft Corporation.
5
+
6
+ Permission to use, copy, modify, and/or distribute this software for any
7
+ purpose with or without fee is hereby granted.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
16
+ ***************************************************************************** */
17
+
18
+ function __awaiter(thisArg, _arguments, P, generator) {
19
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
20
+ return new (P || (P = Promise))(function (resolve, reject) {
21
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
22
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
23
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
24
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
25
+ });
26
+ }
27
+
28
+ function __classPrivateFieldGet(receiver, state, kind, f) {
29
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
30
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
31
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
32
+ }
33
+
34
+ function __classPrivateFieldSet(receiver, state, value, kind, f) {
35
+ if (kind === "m") throw new TypeError("Private method is not writable");
36
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
37
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
38
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
39
+ }
2
40
 
3
41
  const ExampleAccessPolicy = {
4
42
  resourceType: 'AccessPolicy',
@@ -7095,34 +7133,6 @@ const ExampleWorkflowRequestGroup = {
7095
7133
  ],
7096
7134
  };
7097
7135
 
7098
- /******************************************************************************
7099
- Copyright (c) Microsoft Corporation.
7100
-
7101
- Permission to use, copy, modify, and/or distribute this software for any
7102
- purpose with or without fee is hereby granted.
7103
-
7104
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
7105
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
7106
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
7107
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
7108
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
7109
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
7110
- PERFORMANCE OF THIS SOFTWARE.
7111
- ***************************************************************************** */
7112
-
7113
- function __classPrivateFieldGet(receiver, state, kind, f) {
7114
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
7115
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
7116
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
7117
- }
7118
-
7119
- function __classPrivateFieldSet(receiver, state, value, kind, f) {
7120
- if (kind === "m") throw new TypeError("Private method is not writable");
7121
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
7122
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
7123
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7124
- }
7125
-
7126
7136
  var _MemoryRepository_resources, _MemoryRepository_history;
7127
7137
  class MemoryRepository {
7128
7138
  constructor() {
@@ -7133,7 +7143,7 @@ class MemoryRepository {
7133
7143
  }
7134
7144
  createResource(resource) {
7135
7145
  var _a, _b;
7136
- const result = JSON.parse(JSON.stringify(resource));
7146
+ const result = deepClone(resource);
7137
7147
  if (!result.id) {
7138
7148
  result.id = this.generateId();
7139
7149
  }
@@ -7158,7 +7168,7 @@ class MemoryRepository {
7158
7168
  }
7159
7169
  __classPrivateFieldGet(this, _MemoryRepository_resources, "f")[resourceType][id] = result;
7160
7170
  __classPrivateFieldGet(this, _MemoryRepository_history, "f")[resourceType][id].push(result);
7161
- return result;
7171
+ return deepClone(result);
7162
7172
  }
7163
7173
  updateResource(resource) {
7164
7174
  const result = JSON.parse(JSON.stringify(resource));
@@ -7172,23 +7182,51 @@ class MemoryRepository {
7172
7182
  }
7173
7183
  return this.createResource(result);
7174
7184
  }
7185
+ patchResource(resourceType, id, patch) {
7186
+ var _a, _b;
7187
+ return __awaiter(this, void 0, void 0, function* () {
7188
+ const resource = this.readResource(resourceType, id);
7189
+ let patchResult;
7190
+ try {
7191
+ const { applyPatch } = yield import('fast-json-patch');
7192
+ patchResult = applyPatch(resource, patch, true);
7193
+ }
7194
+ catch (err) {
7195
+ const patchError = err;
7196
+ const message = ((_b = (_a = patchError.message) === null || _a === void 0 ? void 0 : _a.split('\n')) === null || _b === void 0 ? void 0 : _b[0]) || 'JSONPatch error';
7197
+ throw badRequest(message);
7198
+ }
7199
+ const patchedResource = patchResult.newDocument;
7200
+ return this.updateResource(patchedResource);
7201
+ });
7202
+ }
7175
7203
  readResource(resourceType, id) {
7176
7204
  var _a, _b;
7177
- return (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_resources, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id];
7205
+ const resource = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_resources, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id];
7206
+ if (!resource) {
7207
+ throw notFound;
7208
+ }
7209
+ return deepClone(resource);
7178
7210
  }
7179
7211
  readHistory(resourceType, id) {
7180
7212
  var _a, _b, _c;
7213
+ this.readResource(resourceType, id);
7181
7214
  return {
7182
7215
  resourceType: 'Bundle',
7183
7216
  type: 'history',
7184
7217
  entry: ((_c = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_history, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id]) !== null && _c !== void 0 ? _c : [])
7185
- .sort((version1, version2) => { var _a, _b, _c; return -((_b = (_a = version1.meta) === null || _a === void 0 ? void 0 : _a.lastUpdated) === null || _b === void 0 ? void 0 : _b.localeCompare((_c = version2.meta) === null || _c === void 0 ? void 0 : _c.lastUpdated)); })
7186
- .map((version) => ({ resource: version })),
7218
+ .reverse()
7219
+ .map((version) => ({ resource: deepClone(version) })),
7187
7220
  };
7188
7221
  }
7189
7222
  readVersion(resourceType, id, versionId) {
7190
7223
  var _a, _b, _c;
7191
- return (_c = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_history, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id]) === null || _c === void 0 ? void 0 : _c.find((v) => { var _a; return ((_a = v.meta) === null || _a === void 0 ? void 0 : _a.versionId) === versionId; });
7224
+ this.readResource(resourceType, id);
7225
+ const version = (_c = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_history, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id]) === null || _c === void 0 ? void 0 : _c.find((v) => { var _a; return ((_a = v.meta) === null || _a === void 0 ? void 0 : _a.versionId) === versionId; });
7226
+ if (!version) {
7227
+ throw notFound;
7228
+ }
7229
+ return deepClone(version);
7192
7230
  }
7193
7231
  search(searchRequest) {
7194
7232
  var _a;
@@ -7198,7 +7236,7 @@ class MemoryRepository {
7198
7236
  return {
7199
7237
  resourceType: 'Bundle',
7200
7238
  type: 'searchset',
7201
- entry: result.map((resource) => ({ resource })),
7239
+ entry: result.map((resource) => ({ resource: deepClone(resource) })),
7202
7240
  total: result.length,
7203
7241
  };
7204
7242
  }
@@ -7257,9 +7295,21 @@ function matchesSearchFilter(resource, filter) {
7257
7295
  function matchesSearchFilterValue(resource, filter, filterValue) {
7258
7296
  const expression = filter.code.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
7259
7297
  const values = evalFhirPath(expression, resource);
7260
- const result = filterValue === '' ||
7261
- values.some((value) => JSON.stringify(value).toLowerCase().includes(filterValue.toLowerCase()));
7262
- return filter.operator === Operator.NOT_EQUALS ? !result : result;
7298
+ switch (filter.operator) {
7299
+ case Operator.GREATER_THAN:
7300
+ return values.some((value) => value.toString() > filterValue);
7301
+ case Operator.GREATER_THAN_OR_EQUALS:
7302
+ return values.some((value) => value.toString() >= filterValue);
7303
+ case Operator.LESS_THAN:
7304
+ return values.some((value) => value.toString() < filterValue);
7305
+ case Operator.LESS_THAN_OR_EQUALS:
7306
+ return values.some((value) => value.toString() <= filterValue);
7307
+ default: {
7308
+ const result = filterValue === '' ||
7309
+ values.some((value) => JSON.stringify(value).toLowerCase().includes(filterValue.toLowerCase()));
7310
+ return filter.operator === Operator.NOT_EQUALS ? !result : result;
7311
+ }
7312
+ }
7263
7313
  }
7264
7314
 
7265
7315
  class MockClient extends MedplumClient {
@@ -7316,25 +7366,27 @@ class MockFetchClient {
7316
7366
  this.initMockRepo();
7317
7367
  }
7318
7368
  mockFetch(url, options) {
7319
- const method = options.method || 'GET';
7320
- const path = url.replace('https://example.com/', '');
7321
- if (this.debug) {
7322
- console.log('MockClient', method, path);
7323
- }
7324
- const response = this.mockHandler(method, path, options);
7325
- if (this.debug && !response) {
7326
- console.log('MockClient: not found', method, path);
7327
- }
7328
- if (this.debug) {
7329
- console.log('MockClient', JSON.stringify(response, null, 2));
7330
- }
7331
- if ((response === null || response === void 0 ? void 0 : response.resourceType) === 'OperationOutcome' && !isOk(response)) {
7332
- return Promise.reject(response);
7333
- }
7334
- return Promise.resolve({
7335
- ok: true,
7336
- blob: () => Promise.resolve(response),
7337
- json: () => Promise.resolve(response),
7369
+ return __awaiter(this, void 0, void 0, function* () {
7370
+ const method = options.method || 'GET';
7371
+ const path = url.replace('https://example.com/', '');
7372
+ if (this.debug) {
7373
+ console.log('MockClient', method, path);
7374
+ }
7375
+ const response = yield this.mockHandler(method, path, options);
7376
+ if (this.debug && !response) {
7377
+ console.log('MockClient: not found', method, path);
7378
+ }
7379
+ if (this.debug) {
7380
+ console.log('MockClient', JSON.stringify(response, null, 2));
7381
+ }
7382
+ if ((response === null || response === void 0 ? void 0 : response.resourceType) === 'OperationOutcome' && !isOk(response)) {
7383
+ return Promise.reject(response);
7384
+ }
7385
+ return Promise.resolve({
7386
+ ok: true,
7387
+ blob: () => Promise.resolve(response),
7388
+ json: () => Promise.resolve(response),
7389
+ });
7338
7390
  });
7339
7391
  }
7340
7392
  mockCreatePdf(docDefinition, tableLayouts, fonts) {
@@ -7347,21 +7399,23 @@ class MockFetchClient {
7347
7399
  return Promise.resolve({});
7348
7400
  }
7349
7401
  mockHandler(method, path, options) {
7350
- if (path.startsWith('admin/')) {
7351
- return this.mockAdminHandler(method, path);
7352
- }
7353
- else if (path.startsWith('auth/')) {
7354
- return this.mockAuthHandler(method, path, options);
7355
- }
7356
- else if (path.startsWith('oauth2/')) {
7357
- return this.mockOAuthHandler(method, path, options);
7358
- }
7359
- else if (path.startsWith('fhir/R4')) {
7360
- return this.mockFhirHandler(method, path, options);
7361
- }
7362
- else {
7363
- return null;
7364
- }
7402
+ return __awaiter(this, void 0, void 0, function* () {
7403
+ if (path.startsWith('admin/')) {
7404
+ return this.mockAdminHandler(method, path);
7405
+ }
7406
+ else if (path.startsWith('auth/')) {
7407
+ return this.mockAuthHandler(method, path, options);
7408
+ }
7409
+ else if (path.startsWith('oauth2/')) {
7410
+ return this.mockOAuthHandler(method, path, options);
7411
+ }
7412
+ else if (path.startsWith('fhir/R4')) {
7413
+ return this.mockFhirHandler(method, path, options);
7414
+ }
7415
+ else {
7416
+ return null;
7417
+ }
7418
+ });
7365
7419
  }
7366
7420
  mockAdminHandler(_method, path) {
7367
7421
  if (path.startsWith('admin/projects/123')) {
@@ -7556,57 +7610,66 @@ class MockFetchClient {
7556
7610
  DrAliceSmithSlots.forEach((slot) => this.mockRepo.createResource(slot));
7557
7611
  }
7558
7612
  mockFhirHandler(method, url, options) {
7559
- const path = url.includes('?') ? url.substring(0, url.indexOf('?')) : url;
7560
- const match = /fhir\/R4\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?/.exec(path);
7561
- const resourceType = match === null || match === void 0 ? void 0 : match[1];
7562
- const id = match === null || match === void 0 ? void 0 : match[2];
7563
- const operation = match === null || match === void 0 ? void 0 : match[3];
7564
- const versionId = match === null || match === void 0 ? void 0 : match[4];
7565
- if (path.startsWith('fhir/R4/ValueSet/$expand')) {
7566
- return exampleValueSet;
7567
- }
7568
- else if (path === 'fhir/R4/$graphql') {
7569
- return this.mockFhirGraphqlHandler(method, path, options);
7570
- }
7571
- else if (path === 'not-found' || id === 'not-found') {
7572
- return notFound;
7573
- }
7574
- else if (method === 'POST') {
7575
- if (resourceType && id && operation) {
7576
- return {};
7613
+ return __awaiter(this, void 0, void 0, function* () {
7614
+ const path = url.includes('?') ? url.substring(0, url.indexOf('?')) : url;
7615
+ const match = /fhir\/R4\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?/.exec(path);
7616
+ const resourceType = match === null || match === void 0 ? void 0 : match[1];
7617
+ const id = match === null || match === void 0 ? void 0 : match[2];
7618
+ const operation = match === null || match === void 0 ? void 0 : match[3];
7619
+ const versionId = match === null || match === void 0 ? void 0 : match[4];
7620
+ if (path.startsWith('fhir/R4/ValueSet/$expand')) {
7621
+ return exampleValueSet;
7577
7622
  }
7578
- else if (resourceType) {
7579
- return this.mockRepo.createResource(JSON.parse(options.body));
7623
+ else if (path === 'fhir/R4/$graphql') {
7624
+ return this.mockFhirGraphqlHandler(method, path, options);
7580
7625
  }
7581
- else {
7582
- return this.mockFhirBatchHandler(method, path, options);
7583
- }
7584
- }
7585
- else if (method === 'GET') {
7586
- if (resourceType && id && versionId) {
7587
- return this.mockRepo.readVersion(resourceType, id, versionId);
7626
+ else if (path === 'not-found' || id === 'not-found') {
7627
+ return notFound;
7588
7628
  }
7589
- else if (resourceType && id && operation === '_history') {
7590
- return this.mockRepo.readHistory(resourceType, id);
7629
+ else if (method === 'POST') {
7630
+ if (resourceType && id && operation) {
7631
+ return allOk;
7632
+ }
7633
+ else if (resourceType) {
7634
+ return this.mockRepo.createResource(JSON.parse(options.body));
7635
+ }
7636
+ else {
7637
+ return this.mockFhirBatchHandler(method, path, options);
7638
+ }
7591
7639
  }
7592
- else if (resourceType && id) {
7593
- return this.mockRepo.readResource(resourceType, id);
7640
+ else if (method === 'GET') {
7641
+ if (resourceType && id && versionId) {
7642
+ return this.mockRepo.readVersion(resourceType, id, versionId);
7643
+ }
7644
+ else if (resourceType && id && operation === '_history') {
7645
+ return this.mockRepo.readHistory(resourceType, id);
7646
+ }
7647
+ else if (resourceType && id === '$csv') {
7648
+ return allOk;
7649
+ }
7650
+ else if (resourceType && id) {
7651
+ return this.mockRepo.readResource(resourceType, id);
7652
+ }
7653
+ else if (resourceType) {
7654
+ return this.mockRepo.search(parseSearchDefinition(url));
7655
+ }
7594
7656
  }
7595
- else if (resourceType) {
7596
- return this.mockRepo.search(parseSearchDefinition(url));
7657
+ else if (method === 'PUT') {
7658
+ return this.mockRepo.updateResource(JSON.parse(options.body));
7597
7659
  }
7598
- }
7599
- else if (method === 'PUT') {
7600
- return this.mockRepo.updateResource(JSON.parse(options.body));
7601
- }
7602
- else if (method === 'DELETE') {
7603
- if (resourceType && id) {
7604
- return this.mockRepo.deleteResource(resourceType, id);
7660
+ else if (method === 'PATCH') {
7661
+ if (resourceType && id) {
7662
+ return this.mockRepo.patchResource(resourceType, id, JSON.parse(options.body));
7663
+ }
7605
7664
  }
7606
- else {
7607
- return notFound;
7665
+ else if (method === 'DELETE') {
7666
+ if (resourceType && id) {
7667
+ this.mockRepo.deleteResource(resourceType, id);
7668
+ return allOk;
7669
+ }
7608
7670
  }
7609
- }
7671
+ throw notFound;
7672
+ });
7610
7673
  }
7611
7674
  mockFhirGraphqlHandler(_method, _path, options) {
7612
7675
  const { body } = options;
@@ -7629,19 +7692,37 @@ class MockFetchClient {
7629
7692
  return GraphQLSchemaResponse;
7630
7693
  }
7631
7694
  mockFhirBatchHandler(_method, _path, options) {
7632
- var _a;
7633
- const { body } = options;
7634
- const request = JSON.parse(body);
7635
- return {
7636
- resourceType: 'Bundle',
7637
- type: 'batch-response',
7638
- entry: (_a = request.entry) === null || _a === void 0 ? void 0 : _a.map((e) => {
7639
- var _a, _b;
7640
- const url = 'fhir/R4/' + ((_a = e === null || e === void 0 ? void 0 : e.request) === null || _a === void 0 ? void 0 : _a.url);
7641
- const method = (_b = e === null || e === void 0 ? void 0 : e.request) === null || _b === void 0 ? void 0 : _b.method;
7642
- const resource = this.mockHandler(method, url, null);
7695
+ return __awaiter(this, void 0, void 0, function* () {
7696
+ const { body } = options;
7697
+ const request = JSON.parse(body);
7698
+ let entry;
7699
+ if (request.entry) {
7700
+ entry = [];
7701
+ for (const input of request.entry) {
7702
+ entry.push(yield this.handleBatchEntry(input));
7703
+ }
7704
+ }
7705
+ return {
7706
+ resourceType: 'Bundle',
7707
+ type: 'batch-response',
7708
+ entry,
7709
+ };
7710
+ });
7711
+ }
7712
+ handleBatchEntry(input) {
7713
+ var _a, _b;
7714
+ return __awaiter(this, void 0, void 0, function* () {
7715
+ try {
7716
+ const url = 'fhir/R4/' + ((_a = input.request) === null || _a === void 0 ? void 0 : _a.url);
7717
+ const method = (_b = input.request) === null || _b === void 0 ? void 0 : _b.method;
7718
+ const resource = yield this.mockHandler(method, url, {
7719
+ body: input.resource ? JSON.stringify(input.resource) : undefined,
7720
+ });
7643
7721
  if ((resource === null || resource === void 0 ? void 0 : resource.resourceType) === 'OperationOutcome') {
7644
- return { resource, response: { status: getStatus(resource).toString() } };
7722
+ return {
7723
+ resource,
7724
+ response: { status: getStatus(resource).toString() },
7725
+ };
7645
7726
  }
7646
7727
  else if (resource) {
7647
7728
  return { resource, response: { status: '200' } };
@@ -7649,8 +7730,17 @@ class MockFetchClient {
7649
7730
  else {
7650
7731
  return { resource: notFound, response: { status: '404' } };
7651
7732
  }
7652
- }),
7653
- };
7733
+ }
7734
+ catch (err) {
7735
+ const outcome = err;
7736
+ return {
7737
+ response: {
7738
+ status: getStatus(outcome).toString(),
7739
+ outcome,
7740
+ },
7741
+ };
7742
+ }
7743
+ });
7654
7744
  }
7655
7745
  }
7656
7746