@medplum/mock 0.9.0 → 0.9.3
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 +32 -0
- package/dist/cjs/index.js +194 -26
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +1 -30
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/esm/index.js +194 -26
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +1 -30
- package/dist/esm/index.min.js.map +1 -1
- package/dist/types/client.d.ts +1 -1
- package/dist/types/mocks/types.d.ts +20 -2
- package/package.json +6 -7
package/README.md
CHANGED
|
@@ -8,6 +8,38 @@ For example:
|
|
|
8
8
|
- `GET fhir/R4/Practitioner/123` returns Dr. Alice Smith
|
|
9
9
|
- `GET fhir/R4/Organization/123` returns Test Organization
|
|
10
10
|
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add as a dependency:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @medplum/mock
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Note the following peer dependencies:
|
|
20
|
+
- [@medplum/core](https://www.npmjs.com/package/@medplum/core)
|
|
21
|
+
- [@medplum/fhirpath](https://www.npmjs.com/package/@medplum/fhirpath)
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
Create a new mock client:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const client = new MockClient();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Read a `Patient` resource:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const patient = await client.readResource('Patient', '123');
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`MockClient` is API compatible with `MedplumClient` in [@medplum/core](https://www.npmjs.com/package/@medplum/core). Please refer to `MedplumClient` for full documentation on client capabilities.
|
|
38
|
+
|
|
39
|
+
## About Medplum
|
|
40
|
+
|
|
41
|
+
Medplum is a healthcare platform that helps you quickly develop high-quality compliant applications. Medplum includes a FHIR server, React component library, and developer app.
|
|
42
|
+
|
|
11
43
|
## License
|
|
12
44
|
|
|
13
45
|
Apache 2.0. Copyright © Medplum 2022
|
package/dist/cjs/index.js
CHANGED
|
@@ -444,6 +444,7 @@
|
|
|
444
444
|
},
|
|
445
445
|
],
|
|
446
446
|
specimen: [core.createReference(HomerSimpsonSpecimen)],
|
|
447
|
+
authoredOn: '2020-01-01T12:00:00Z',
|
|
447
448
|
};
|
|
448
449
|
const BartSimpson = {
|
|
449
450
|
resourceType: 'Patient',
|
|
@@ -6216,6 +6217,136 @@
|
|
|
6216
6217
|
}
|
|
6217
6218
|
]
|
|
6218
6219
|
}
|
|
6220
|
+
},
|
|
6221
|
+
{
|
|
6222
|
+
resourceType: "StructureDefinition",
|
|
6223
|
+
id: "Bot",
|
|
6224
|
+
name: "Bot",
|
|
6225
|
+
description: "Bot account for automated actions.",
|
|
6226
|
+
snapshot: {
|
|
6227
|
+
element: [
|
|
6228
|
+
{
|
|
6229
|
+
id: "Bot.id",
|
|
6230
|
+
path: "Bot.id",
|
|
6231
|
+
definition: "The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.",
|
|
6232
|
+
min: 0,
|
|
6233
|
+
max: "1",
|
|
6234
|
+
type: [
|
|
6235
|
+
{
|
|
6236
|
+
code: "string"
|
|
6237
|
+
}
|
|
6238
|
+
]
|
|
6239
|
+
},
|
|
6240
|
+
{
|
|
6241
|
+
id: "Bot.meta",
|
|
6242
|
+
path: "Bot.meta",
|
|
6243
|
+
definition: "The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content might not always be associated with version changes to the resource.",
|
|
6244
|
+
min: 0,
|
|
6245
|
+
max: "1",
|
|
6246
|
+
type: [
|
|
6247
|
+
{
|
|
6248
|
+
code: "Meta"
|
|
6249
|
+
}
|
|
6250
|
+
]
|
|
6251
|
+
},
|
|
6252
|
+
{
|
|
6253
|
+
id: "Bot.implicitRules",
|
|
6254
|
+
path: "Bot.implicitRules",
|
|
6255
|
+
definition: "A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content. Often, this is a reference to an implementation guide that defines the special rules along with other profiles etc.",
|
|
6256
|
+
min: 0,
|
|
6257
|
+
max: "1",
|
|
6258
|
+
type: [
|
|
6259
|
+
{
|
|
6260
|
+
code: "uri"
|
|
6261
|
+
}
|
|
6262
|
+
]
|
|
6263
|
+
},
|
|
6264
|
+
{
|
|
6265
|
+
id: "Bot.language",
|
|
6266
|
+
path: "Bot.language",
|
|
6267
|
+
definition: "The base language in which the resource is written.",
|
|
6268
|
+
min: 0,
|
|
6269
|
+
max: "1",
|
|
6270
|
+
type: [
|
|
6271
|
+
{
|
|
6272
|
+
code: "code"
|
|
6273
|
+
}
|
|
6274
|
+
]
|
|
6275
|
+
},
|
|
6276
|
+
{
|
|
6277
|
+
id: "Bot.name",
|
|
6278
|
+
path: "Bot.name",
|
|
6279
|
+
definition: "A name associated with the Bot.",
|
|
6280
|
+
min: 0,
|
|
6281
|
+
max: "1",
|
|
6282
|
+
type: [
|
|
6283
|
+
{
|
|
6284
|
+
code: "string"
|
|
6285
|
+
}
|
|
6286
|
+
]
|
|
6287
|
+
},
|
|
6288
|
+
{
|
|
6289
|
+
id: "Bot.description",
|
|
6290
|
+
path: "Bot.description",
|
|
6291
|
+
definition: "A summary, characterization or explanation of the Bot.",
|
|
6292
|
+
min: 0,
|
|
6293
|
+
max: "1",
|
|
6294
|
+
type: [
|
|
6295
|
+
{
|
|
6296
|
+
code: "string"
|
|
6297
|
+
}
|
|
6298
|
+
]
|
|
6299
|
+
},
|
|
6300
|
+
{
|
|
6301
|
+
id: "Bot.runtimeVersion",
|
|
6302
|
+
path: "Bot.runtimeVersion",
|
|
6303
|
+
definition: "The identifier of the bot runtime environment (i.e., vmcontext, awslambda, etc).",
|
|
6304
|
+
min: 0,
|
|
6305
|
+
max: "1",
|
|
6306
|
+
type: [
|
|
6307
|
+
{
|
|
6308
|
+
code: "string"
|
|
6309
|
+
}
|
|
6310
|
+
]
|
|
6311
|
+
},
|
|
6312
|
+
{
|
|
6313
|
+
id: "Bot.photo",
|
|
6314
|
+
path: "Bot.photo",
|
|
6315
|
+
definition: "Image of the bot.",
|
|
6316
|
+
min: 0,
|
|
6317
|
+
max: "1",
|
|
6318
|
+
type: [
|
|
6319
|
+
{
|
|
6320
|
+
code: "Attachment"
|
|
6321
|
+
}
|
|
6322
|
+
]
|
|
6323
|
+
},
|
|
6324
|
+
{
|
|
6325
|
+
id: "Bot.code",
|
|
6326
|
+
path: "Bot.code",
|
|
6327
|
+
definition: "Bot logic script.",
|
|
6328
|
+
min: 0,
|
|
6329
|
+
max: "1",
|
|
6330
|
+
type: [
|
|
6331
|
+
{
|
|
6332
|
+
code: "string"
|
|
6333
|
+
}
|
|
6334
|
+
]
|
|
6335
|
+
},
|
|
6336
|
+
{
|
|
6337
|
+
id: "Bot.runAsUser",
|
|
6338
|
+
path: "Bot.runAsUser",
|
|
6339
|
+
definition: "Optional flag to indicate that the bot should be run as the user.",
|
|
6340
|
+
min: 0,
|
|
6341
|
+
max: "1",
|
|
6342
|
+
type: [
|
|
6343
|
+
{
|
|
6344
|
+
code: "boolean"
|
|
6345
|
+
}
|
|
6346
|
+
]
|
|
6347
|
+
}
|
|
6348
|
+
]
|
|
6349
|
+
}
|
|
6219
6350
|
}
|
|
6220
6351
|
];
|
|
6221
6352
|
|
|
@@ -6311,6 +6442,14 @@
|
|
|
6311
6442
|
expression: 'ServiceRequest.subject',
|
|
6312
6443
|
target: ['Group', 'Device', 'Patient', 'Location'],
|
|
6313
6444
|
},
|
|
6445
|
+
{
|
|
6446
|
+
resourceType: 'SearchParameter',
|
|
6447
|
+
id: 'ServiceRequest-authored',
|
|
6448
|
+
code: 'authored',
|
|
6449
|
+
base: ['ServiceRequest'],
|
|
6450
|
+
type: 'date',
|
|
6451
|
+
expression: 'ServiceRequest.authoredOn',
|
|
6452
|
+
},
|
|
6314
6453
|
{
|
|
6315
6454
|
resourceType: 'SearchParameter',
|
|
6316
6455
|
id: 'Observation-value-quantity',
|
|
@@ -6348,7 +6487,7 @@
|
|
|
6348
6487
|
},
|
|
6349
6488
|
};
|
|
6350
6489
|
|
|
6351
|
-
|
|
6490
|
+
/******************************************************************************
|
|
6352
6491
|
Copyright (c) Microsoft Corporation.
|
|
6353
6492
|
|
|
6354
6493
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
@@ -6376,7 +6515,7 @@
|
|
|
6376
6515
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6377
6516
|
}
|
|
6378
6517
|
|
|
6379
|
-
|
|
6518
|
+
/******************************************************************************
|
|
6380
6519
|
Copyright (c) Microsoft Corporation.
|
|
6381
6520
|
|
|
6382
6521
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
@@ -6968,6 +7107,23 @@
|
|
|
6968
7107
|
eval(context) {
|
|
6969
7108
|
return this.impl(ensureArray(context), ...this.args);
|
|
6970
7109
|
}
|
|
7110
|
+
}
|
|
7111
|
+
class IndexerAtom {
|
|
7112
|
+
constructor(left, expr) {
|
|
7113
|
+
this.left = left;
|
|
7114
|
+
this.expr = expr;
|
|
7115
|
+
}
|
|
7116
|
+
eval(context) {
|
|
7117
|
+
const index = this.expr.eval(context);
|
|
7118
|
+
if (typeof index !== 'number') {
|
|
7119
|
+
throw new Error(`Invalid indexer expression: should return integer}`);
|
|
7120
|
+
}
|
|
7121
|
+
const leftResult = this.left.eval(context);
|
|
7122
|
+
if (!(index in leftResult)) {
|
|
7123
|
+
return [];
|
|
7124
|
+
}
|
|
7125
|
+
return leftResult[index];
|
|
7126
|
+
}
|
|
6971
7127
|
}
|
|
6972
7128
|
|
|
6973
7129
|
function parseDateString(str) {
|
|
@@ -7230,7 +7386,7 @@
|
|
|
7230
7386
|
* See: http://hl7.org/fhirpath/#selectprojection-expression-collection
|
|
7231
7387
|
*/
|
|
7232
7388
|
function select(input, criteria) {
|
|
7233
|
-
return ensureArray(criteria.eval(
|
|
7389
|
+
return ensureArray(input.map((e) => criteria.eval(e)).flat());
|
|
7234
7390
|
}
|
|
7235
7391
|
/**
|
|
7236
7392
|
* A version of select that will repeat the projection and add it to the output
|
|
@@ -8685,7 +8841,7 @@
|
|
|
8685
8841
|
prefix(tokenType, precedence, builder) {
|
|
8686
8842
|
return this.registerPrefix(tokenType, {
|
|
8687
8843
|
parse(parser, token) {
|
|
8688
|
-
const right = parser.
|
|
8844
|
+
const right = parser.consumeAndParse(precedence);
|
|
8689
8845
|
return builder(token, right);
|
|
8690
8846
|
},
|
|
8691
8847
|
});
|
|
@@ -8693,7 +8849,7 @@
|
|
|
8693
8849
|
infixLeft(tokenType, precedence, builder) {
|
|
8694
8850
|
return this.registerInfix(tokenType, {
|
|
8695
8851
|
parse(parser, left, token) {
|
|
8696
|
-
const right = parser.
|
|
8852
|
+
const right = parser.consumeAndParse(precedence);
|
|
8697
8853
|
return builder(left, token, right);
|
|
8698
8854
|
},
|
|
8699
8855
|
precedence,
|
|
@@ -8722,7 +8878,7 @@
|
|
|
8722
8878
|
__classPrivateFieldGet(this, _Parser_instances, "m", _Parser_consume).call(this);
|
|
8723
8879
|
return true;
|
|
8724
8880
|
}
|
|
8725
|
-
|
|
8881
|
+
consumeAndParse(precedence = 100 /* MaximumPrecedence */) {
|
|
8726
8882
|
const token = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_consume).call(this);
|
|
8727
8883
|
const prefix = __classPrivateFieldGet(this, _Parser_prefixParselets, "f")[token.id];
|
|
8728
8884
|
if (!prefix) {
|
|
@@ -8757,13 +8913,23 @@
|
|
|
8757
8913
|
};
|
|
8758
8914
|
const PARENTHESES_PARSELET = {
|
|
8759
8915
|
parse(parser) {
|
|
8760
|
-
const expr = parser.
|
|
8916
|
+
const expr = parser.consumeAndParse();
|
|
8761
8917
|
if (!parser.match(')')) {
|
|
8762
8918
|
throw new Error('Parse error: expected `)`');
|
|
8763
8919
|
}
|
|
8764
8920
|
return expr;
|
|
8765
8921
|
},
|
|
8766
8922
|
};
|
|
8923
|
+
const INDEXER_PARSELET = {
|
|
8924
|
+
parse(parser, left) {
|
|
8925
|
+
const expr = parser.consumeAndParse();
|
|
8926
|
+
if (!parser.match(']')) {
|
|
8927
|
+
throw new Error('Parse error: expected `]`');
|
|
8928
|
+
}
|
|
8929
|
+
return new IndexerAtom(left, expr);
|
|
8930
|
+
},
|
|
8931
|
+
precedence: 2 /* Indexer */,
|
|
8932
|
+
};
|
|
8767
8933
|
const FUNCTION_CALL_PARSELET = {
|
|
8768
8934
|
parse(parser, left) {
|
|
8769
8935
|
if (!(left instanceof SymbolAtom)) {
|
|
@@ -8774,7 +8940,7 @@
|
|
|
8774
8940
|
}
|
|
8775
8941
|
const args = [];
|
|
8776
8942
|
while (!parser.match(')')) {
|
|
8777
|
-
args.push(parser.
|
|
8943
|
+
args.push(parser.consumeAndParse());
|
|
8778
8944
|
parser.match(',');
|
|
8779
8945
|
}
|
|
8780
8946
|
return new FunctionAtom(left.name, args, functions[left.name]);
|
|
@@ -8819,6 +8985,7 @@
|
|
|
8819
8985
|
})
|
|
8820
8986
|
.registerPrefix('{}', { parse: () => new EmptySetAtom() })
|
|
8821
8987
|
.registerPrefix('(', PARENTHESES_PARSELET)
|
|
8988
|
+
.registerInfix('[', INDEXER_PARSELET)
|
|
8822
8989
|
.registerInfix('(', FUNCTION_CALL_PARSELET)
|
|
8823
8990
|
.prefix('+', 3 /* UnaryAdd */, (_, right) => new UnaryOperatorAtom(right, (x) => x))
|
|
8824
8991
|
.prefix('-', 3 /* UnarySubtract */, (_, right) => new ArithemticOperatorAtom(right, right, (_, y) => -y))
|
|
@@ -8871,7 +9038,7 @@
|
|
|
8871
9038
|
*/
|
|
8872
9039
|
function parseFhirPath(input) {
|
|
8873
9040
|
try {
|
|
8874
|
-
return new FhirPathAtom(input, parserBuilder.construct(input).
|
|
9041
|
+
return new FhirPathAtom(input, parserBuilder.construct(input).consumeAndParse());
|
|
8875
9042
|
}
|
|
8876
9043
|
catch (error) {
|
|
8877
9044
|
throw new Error(`FhirPathError on "${input}": ${error}`);
|
|
@@ -8968,7 +9135,7 @@
|
|
|
8968
9135
|
}
|
|
8969
9136
|
if (searchRequest.filters) {
|
|
8970
9137
|
for (const filter of searchRequest.filters) {
|
|
8971
|
-
if (!matchesSearchFilter(resource,
|
|
9138
|
+
if (!matchesSearchFilter(resource, filter)) {
|
|
8972
9139
|
return false;
|
|
8973
9140
|
}
|
|
8974
9141
|
}
|
|
@@ -8981,7 +9148,7 @@
|
|
|
8981
9148
|
* @param filter One of the filters of a subscription criteria.
|
|
8982
9149
|
* @returns True if the resource satisfies the search filter.
|
|
8983
9150
|
*/
|
|
8984
|
-
function matchesSearchFilter(resource,
|
|
9151
|
+
function matchesSearchFilter(resource, filter) {
|
|
8985
9152
|
const expression = filter.code.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
8986
9153
|
const values = evalFhirPath(expression, resource);
|
|
8987
9154
|
const result = values.some((value) => JSON.stringify(value).toLowerCase().includes(filter.value.toLowerCase()));
|
|
@@ -8994,19 +9161,15 @@
|
|
|
8994
9161
|
baseUrl: 'https://example.com/',
|
|
8995
9162
|
clientId: 'my-client-id',
|
|
8996
9163
|
fetch: (url, options) => {
|
|
8997
|
-
const method = options.method;
|
|
9164
|
+
const method = options.method || 'GET';
|
|
8998
9165
|
const path = url.replace('https://example.com/', '');
|
|
8999
9166
|
if (clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.debug) {
|
|
9000
9167
|
console.log('MockClient', method, path);
|
|
9001
9168
|
}
|
|
9002
|
-
const
|
|
9003
|
-
if ((clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.debug) && !
|
|
9169
|
+
const response = mockHandler(method, path, options);
|
|
9170
|
+
if ((clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.debug) && !response) {
|
|
9004
9171
|
console.log('MockClient: not found', method, path);
|
|
9005
9172
|
}
|
|
9006
|
-
const response = Object.assign({ request: {
|
|
9007
|
-
url,
|
|
9008
|
-
options,
|
|
9009
|
-
} }, result);
|
|
9010
9173
|
if (clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.debug) {
|
|
9011
9174
|
console.log('MockClient', JSON.stringify(response, null, 2));
|
|
9012
9175
|
}
|
|
@@ -9039,7 +9202,7 @@
|
|
|
9039
9202
|
}
|
|
9040
9203
|
return super.getActiveLogin();
|
|
9041
9204
|
}
|
|
9042
|
-
createBinary(
|
|
9205
|
+
createBinary(_data, filename, contentType) {
|
|
9043
9206
|
if (filename.endsWith('.exe')) {
|
|
9044
9207
|
return Promise.reject(core.badRequest('Invalid file type'));
|
|
9045
9208
|
}
|
|
@@ -9065,7 +9228,7 @@
|
|
|
9065
9228
|
return null;
|
|
9066
9229
|
}
|
|
9067
9230
|
}
|
|
9068
|
-
function mockAdminHandler(
|
|
9231
|
+
function mockAdminHandler(_method, path) {
|
|
9069
9232
|
if (path.startsWith('admin/projects/123')) {
|
|
9070
9233
|
return {
|
|
9071
9234
|
project: { id: '123', name: 'Project 123' },
|
|
@@ -9095,9 +9258,14 @@
|
|
|
9095
9258
|
if (path.startsWith('auth/resetpassword')) {
|
|
9096
9259
|
return mockResetPasswordHandler(method, path, options);
|
|
9097
9260
|
}
|
|
9261
|
+
if (path.startsWith('auth/me')) {
|
|
9262
|
+
return {
|
|
9263
|
+
profile: { reference: 'Practitioner/123' },
|
|
9264
|
+
};
|
|
9265
|
+
}
|
|
9098
9266
|
return null;
|
|
9099
9267
|
}
|
|
9100
|
-
function mockChangePasswordHandler(
|
|
9268
|
+
function mockChangePasswordHandler(_method, _path, options) {
|
|
9101
9269
|
const { body } = options;
|
|
9102
9270
|
const { oldPassword } = JSON.parse(body);
|
|
9103
9271
|
if (oldPassword === 'orange') {
|
|
@@ -9117,7 +9285,7 @@
|
|
|
9117
9285
|
};
|
|
9118
9286
|
}
|
|
9119
9287
|
}
|
|
9120
|
-
function mockLoginHandler(
|
|
9288
|
+
function mockLoginHandler(_method, _path, options) {
|
|
9121
9289
|
const { body } = options;
|
|
9122
9290
|
const { password } = JSON.parse(body);
|
|
9123
9291
|
if (password === 'password') {
|
|
@@ -9139,7 +9307,7 @@
|
|
|
9139
9307
|
};
|
|
9140
9308
|
}
|
|
9141
9309
|
}
|
|
9142
|
-
function mockSetPasswordHandler(
|
|
9310
|
+
function mockSetPasswordHandler(_method, _path, options) {
|
|
9143
9311
|
const { body } = options;
|
|
9144
9312
|
const { password } = JSON.parse(body);
|
|
9145
9313
|
if (password === 'orange') {
|
|
@@ -9159,7 +9327,7 @@
|
|
|
9159
9327
|
};
|
|
9160
9328
|
}
|
|
9161
9329
|
}
|
|
9162
|
-
function mockRegisterHandler(
|
|
9330
|
+
function mockRegisterHandler(_method, _path, options) {
|
|
9163
9331
|
const { body } = options;
|
|
9164
9332
|
const { email, password } = JSON.parse(body);
|
|
9165
9333
|
if (email === 'george@example.com' && password === 'password') {
|
|
@@ -9178,7 +9346,7 @@
|
|
|
9178
9346
|
};
|
|
9179
9347
|
}
|
|
9180
9348
|
}
|
|
9181
|
-
function mockResetPasswordHandler(
|
|
9349
|
+
function mockResetPasswordHandler(_method, _path, options) {
|
|
9182
9350
|
const { body } = options;
|
|
9183
9351
|
const { email } = JSON.parse(body);
|
|
9184
9352
|
if (email === 'admin@example.com') {
|
|
@@ -9269,7 +9437,7 @@
|
|
|
9269
9437
|
return mockRepo.createResource(JSON.parse(options.body));
|
|
9270
9438
|
}
|
|
9271
9439
|
}
|
|
9272
|
-
function mockFhirBatchHandler(
|
|
9440
|
+
function mockFhirBatchHandler(_method, _path, options) {
|
|
9273
9441
|
var _a;
|
|
9274
9442
|
const { body } = options;
|
|
9275
9443
|
const request = JSON.parse(body);
|