@medplum/core 2.0.20 → 2.0.22
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/cjs/index.cjs +448 -289
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/esm/client.mjs +199 -101
- package/dist/esm/client.mjs.map +1 -1
- package/dist/esm/crypto.mjs +3 -1
- package/dist/esm/crypto.mjs.map +1 -1
- package/dist/esm/fhirpath/functions.mjs +179 -129
- package/dist/esm/fhirpath/functions.mjs.map +1 -1
- package/dist/esm/format.mjs +6 -4
- package/dist/esm/format.mjs.map +1 -1
- package/dist/esm/hl7.mjs +1 -1
- package/dist/esm/hl7.mjs.map +1 -1
- package/dist/esm/index.min.mjs +1 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/jwt.mjs +4 -2
- package/dist/esm/jwt.mjs.map +1 -1
- package/dist/esm/schema.mjs +4 -10
- package/dist/esm/schema.mjs.map +1 -1
- package/dist/esm/search/details.mjs +0 -1
- package/dist/esm/search/details.mjs.map +1 -1
- package/dist/esm/search/match.mjs +1 -0
- package/dist/esm/search/match.mjs.map +1 -1
- package/dist/esm/search/search.mjs +2 -2
- package/dist/esm/search/search.mjs.map +1 -1
- package/dist/esm/sftp.mjs +0 -1
- package/dist/esm/sftp.mjs.map +1 -1
- package/dist/esm/storage.mjs +8 -0
- package/dist/esm/storage.mjs.map +1 -1
- package/dist/esm/types.mjs +1 -0
- package/dist/esm/types.mjs.map +1 -1
- package/dist/esm/utils.mjs +8 -7
- package/dist/esm/utils.mjs.map +1 -1
- package/dist/types/client.d.ts +89 -65
- package/dist/types/config.d.ts +7 -3
- package/dist/types/crypto.d.ts +3 -1
- package/dist/types/hl7.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/jwt.d.ts +2 -1
- package/dist/types/schema.d.ts +4 -10
- package/dist/types/search/details.d.ts +0 -1
- package/dist/types/search/search.d.ts +1 -1
- package/dist/types/storage.d.ts +8 -0
- package/dist/types/typeschema/types.d.ts +55 -0
- package/dist/types/utils.d.ts +4 -4
- package/package.json +1 -1
package/dist/esm/client.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeBase64 } from './base64.mjs';
|
|
1
2
|
import { LRUCache } from './cache.mjs';
|
|
2
3
|
import { getRandomString, encryptSHA256 } from './crypto.mjs';
|
|
3
4
|
import { EventTarget } from './eventtarget.mjs';
|
|
@@ -7,11 +8,10 @@ import { ReadablePromise } from './readablepromise.mjs';
|
|
|
7
8
|
import { ClientStorage } from './storage.mjs';
|
|
8
9
|
import { globalSchema, indexStructureDefinition, indexSearchParameter } from './types.mjs';
|
|
9
10
|
import { createReference, arrayBufferToBase64 } from './utils.mjs';
|
|
10
|
-
import { encodeBase64 } from './base64.mjs';
|
|
11
11
|
|
|
12
12
|
// PKCE auth based on:
|
|
13
13
|
// https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
|
|
14
|
-
const MEDPLUM_VERSION = "2.0.
|
|
14
|
+
const MEDPLUM_VERSION = "2.0.22-f51ac45a" ;
|
|
15
15
|
const DEFAULT_BASE_URL = 'https://api.medplum.com/';
|
|
16
16
|
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
|
|
17
17
|
const DEFAULT_CACHE_TIME = 60000; // 60 seconds
|
|
@@ -98,7 +98,6 @@ var OAuthTokenType;
|
|
|
98
98
|
* <head>
|
|
99
99
|
* <meta name="algolia:pageRank" content="100" />
|
|
100
100
|
* </head>
|
|
101
|
-
|
|
102
101
|
*/
|
|
103
102
|
class MedplumClient extends EventTarget {
|
|
104
103
|
constructor(options) {
|
|
@@ -165,6 +164,9 @@ class MedplumClient extends EventTarget {
|
|
|
165
164
|
* @category Authentication
|
|
166
165
|
*/
|
|
167
166
|
clearActiveLogin() {
|
|
167
|
+
if (this.basicAuth) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
168
170
|
this.storage.setString('activeLogin', undefined);
|
|
169
171
|
this.requestCache?.clear();
|
|
170
172
|
this.accessToken = undefined;
|
|
@@ -210,7 +212,6 @@ class MedplumClient extends EventTarget {
|
|
|
210
212
|
* This is a lower level method for custom requests.
|
|
211
213
|
* For common operations, we recommend using higher level methods
|
|
212
214
|
* such as `readResource()`, `search()`, etc.
|
|
213
|
-
*
|
|
214
215
|
* @category HTTP
|
|
215
216
|
* @param url The target URL.
|
|
216
217
|
* @param options Optional fetch options.
|
|
@@ -250,7 +251,6 @@ class MedplumClient extends EventTarget {
|
|
|
250
251
|
* This is a lower level method for custom requests.
|
|
251
252
|
* For common operations, we recommend using higher level methods
|
|
252
253
|
* such as `createResource()`.
|
|
253
|
-
*
|
|
254
254
|
* @category HTTP
|
|
255
255
|
* @param url The target URL.
|
|
256
256
|
* @param body The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.
|
|
@@ -275,7 +275,6 @@ class MedplumClient extends EventTarget {
|
|
|
275
275
|
* This is a lower level method for custom requests.
|
|
276
276
|
* For common operations, we recommend using higher level methods
|
|
277
277
|
* such as `updateResource()`.
|
|
278
|
-
*
|
|
279
278
|
* @category HTTP
|
|
280
279
|
* @param url The target URL.
|
|
281
280
|
* @param body The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.
|
|
@@ -300,7 +299,6 @@ class MedplumClient extends EventTarget {
|
|
|
300
299
|
* This is a lower level method for custom requests.
|
|
301
300
|
* For common operations, we recommend using higher level methods
|
|
302
301
|
* such as `patchResource()`.
|
|
303
|
-
*
|
|
304
302
|
* @category HTTP
|
|
305
303
|
* @param url The target URL.
|
|
306
304
|
* @param operations Array of JSONPatch operations.
|
|
@@ -321,13 +319,12 @@ class MedplumClient extends EventTarget {
|
|
|
321
319
|
* This is a lower level method for custom requests.
|
|
322
320
|
* For common operations, we recommend using higher level methods
|
|
323
321
|
* such as `deleteResource()`.
|
|
324
|
-
*
|
|
325
322
|
* @category HTTP
|
|
326
323
|
* @param url The target URL.
|
|
327
324
|
* @param options Optional fetch options.
|
|
328
325
|
* @returns Promise to the response content.
|
|
329
326
|
*/
|
|
330
|
-
delete(url, options
|
|
327
|
+
delete(url, options) {
|
|
331
328
|
url = url.toString();
|
|
332
329
|
this.invalidateUrl(url);
|
|
333
330
|
return this.request('DELETE', url, options);
|
|
@@ -338,53 +335,54 @@ class MedplumClient extends EventTarget {
|
|
|
338
335
|
* This method is part of the two different user registration flows:
|
|
339
336
|
* 1) New Practitioner and new Project
|
|
340
337
|
* 2) New Patient registration
|
|
341
|
-
*
|
|
342
338
|
* @category Authentication
|
|
343
339
|
* @param newUserRequest Register request including email and password.
|
|
340
|
+
* @param options Optional fetch options.
|
|
344
341
|
* @returns Promise to the authentication response.
|
|
345
342
|
*/
|
|
346
|
-
async startNewUser(newUserRequest) {
|
|
343
|
+
async startNewUser(newUserRequest, options) {
|
|
347
344
|
const { codeChallengeMethod, codeChallenge } = await this.startPkce();
|
|
348
345
|
return this.post('auth/newuser', {
|
|
349
346
|
...newUserRequest,
|
|
350
347
|
codeChallengeMethod,
|
|
351
348
|
codeChallenge,
|
|
352
|
-
});
|
|
349
|
+
}, undefined, options);
|
|
353
350
|
}
|
|
354
351
|
/**
|
|
355
352
|
* Initiates a new project flow.
|
|
356
353
|
*
|
|
357
354
|
* This requires a partial login from `startNewUser` or `startNewGoogleUser`.
|
|
358
|
-
*
|
|
359
355
|
* @param newProjectRequest Register request including email and password.
|
|
356
|
+
* @param options Optional fetch options.
|
|
360
357
|
* @returns Promise to the authentication response.
|
|
361
358
|
*/
|
|
362
|
-
async startNewProject(newProjectRequest) {
|
|
363
|
-
return this.post('auth/newproject', newProjectRequest);
|
|
359
|
+
async startNewProject(newProjectRequest, options) {
|
|
360
|
+
return this.post('auth/newproject', newProjectRequest, undefined, options);
|
|
364
361
|
}
|
|
365
362
|
/**
|
|
366
363
|
* Initiates a new patient flow.
|
|
367
364
|
*
|
|
368
365
|
* This requires a partial login from `startNewUser` or `startNewGoogleUser`.
|
|
369
|
-
*
|
|
370
366
|
* @param newPatientRequest Register request including email and password.
|
|
367
|
+
* @param options Optional fetch options.
|
|
371
368
|
* @returns Promise to the authentication response.
|
|
372
369
|
*/
|
|
373
|
-
async startNewPatient(newPatientRequest) {
|
|
374
|
-
return this.post('auth/newpatient', newPatientRequest);
|
|
370
|
+
async startNewPatient(newPatientRequest, options) {
|
|
371
|
+
return this.post('auth/newpatient', newPatientRequest, undefined, options);
|
|
375
372
|
}
|
|
376
373
|
/**
|
|
377
374
|
* Initiates a user login flow.
|
|
378
375
|
* @category Authentication
|
|
379
376
|
* @param loginRequest Login request including email and password.
|
|
377
|
+
* @param options Optional fetch options.
|
|
380
378
|
* @returns Promise to the authentication response.
|
|
381
379
|
*/
|
|
382
|
-
async startLogin(loginRequest) {
|
|
380
|
+
async startLogin(loginRequest, options) {
|
|
383
381
|
return this.post('auth/login', {
|
|
384
382
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
385
383
|
clientId: loginRequest.clientId ?? this.clientId,
|
|
386
384
|
scope: loginRequest.scope,
|
|
387
|
-
});
|
|
385
|
+
}, undefined, options);
|
|
388
386
|
}
|
|
389
387
|
/**
|
|
390
388
|
* Tries to sign in with Google authentication.
|
|
@@ -392,14 +390,15 @@ class MedplumClient extends EventTarget {
|
|
|
392
390
|
* See: https://developers.google.com/identity/gsi/web/guides/handle-credential-responses-js-functions
|
|
393
391
|
* @category Authentication
|
|
394
392
|
* @param loginRequest Login request including Google credential response.
|
|
393
|
+
* @param options Optional fetch options.
|
|
395
394
|
* @returns Promise to the authentication response.
|
|
396
395
|
*/
|
|
397
|
-
async startGoogleLogin(loginRequest) {
|
|
396
|
+
async startGoogleLogin(loginRequest, options) {
|
|
398
397
|
return this.post('auth/google', {
|
|
399
398
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
400
399
|
clientId: loginRequest.clientId ?? this.clientId,
|
|
401
400
|
scope: loginRequest.scope,
|
|
402
|
-
});
|
|
401
|
+
}, undefined, options);
|
|
403
402
|
}
|
|
404
403
|
/**
|
|
405
404
|
* Returns the PKCE code challenge and method.
|
|
@@ -430,6 +429,7 @@ class MedplumClient extends EventTarget {
|
|
|
430
429
|
* This may result in navigating away to the sign in page.
|
|
431
430
|
* @category Authentication
|
|
432
431
|
* @param loginParams Optional login parameters.
|
|
432
|
+
* @returns The user profile resource if available.
|
|
433
433
|
*/
|
|
434
434
|
async signInWithRedirect(loginParams) {
|
|
435
435
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -466,6 +466,7 @@ class MedplumClient extends EventTarget {
|
|
|
466
466
|
* Exchange an external access token for a Medplum access token.
|
|
467
467
|
* @param token The access token that was generated by the external identity provider.
|
|
468
468
|
* @param clientId The ID of the `ClientApplication` in your Medplum project that will be making the exchange request.
|
|
469
|
+
* @returns The user profile resource.
|
|
469
470
|
* @category Authentication
|
|
470
471
|
*/
|
|
471
472
|
async exchangeExternalAccessToken(token, clientId) {
|
|
@@ -564,14 +565,13 @@ class MedplumClient extends EventTarget {
|
|
|
564
565
|
* ```
|
|
565
566
|
*
|
|
566
567
|
* See FHIR search for full details: https://www.hl7.org/fhir/search.html
|
|
567
|
-
*
|
|
568
568
|
* @category Search
|
|
569
569
|
* @param resourceType The FHIR resource type.
|
|
570
570
|
* @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
|
|
571
571
|
* @param options Optional fetch options.
|
|
572
572
|
* @returns Promise to the search result bundle.
|
|
573
573
|
*/
|
|
574
|
-
search(resourceType, query, options
|
|
574
|
+
search(resourceType, query, options) {
|
|
575
575
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
576
576
|
const cacheKey = url.toString() + '-search';
|
|
577
577
|
const cached = this.getCacheEntry(cacheKey, options);
|
|
@@ -605,14 +605,13 @@ class MedplumClient extends EventTarget {
|
|
|
605
605
|
* The return value is the resource, if available; otherwise, undefined.
|
|
606
606
|
*
|
|
607
607
|
* See FHIR search for full details: https://www.hl7.org/fhir/search.html
|
|
608
|
-
*
|
|
609
608
|
* @category Search
|
|
610
609
|
* @param resourceType The FHIR resource type.
|
|
611
610
|
* @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
|
|
612
611
|
* @param options Optional fetch options.
|
|
613
612
|
* @returns Promise to the first search result.
|
|
614
613
|
*/
|
|
615
|
-
searchOne(resourceType, query, options
|
|
614
|
+
searchOne(resourceType, query, options) {
|
|
616
615
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
617
616
|
url.searchParams.set('_count', '1');
|
|
618
617
|
url.searchParams.sort();
|
|
@@ -640,14 +639,13 @@ class MedplumClient extends EventTarget {
|
|
|
640
639
|
* The return value is an array of resources.
|
|
641
640
|
*
|
|
642
641
|
* See FHIR search for full details: https://www.hl7.org/fhir/search.html
|
|
643
|
-
*
|
|
644
642
|
* @category Search
|
|
645
643
|
* @param resourceType The FHIR resource type.
|
|
646
644
|
* @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
|
|
647
645
|
* @param options Optional fetch options.
|
|
648
646
|
* @returns Promise to the array of search results.
|
|
649
647
|
*/
|
|
650
|
-
searchResources(resourceType, query, options
|
|
648
|
+
searchResources(resourceType, query, options) {
|
|
651
649
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
652
650
|
const cacheKey = url.toString() + '-searchResources';
|
|
653
651
|
const cached = this.getCacheEntry(cacheKey, options);
|
|
@@ -672,14 +670,13 @@ class MedplumClient extends EventTarget {
|
|
|
672
670
|
* }
|
|
673
671
|
* }
|
|
674
672
|
* ```
|
|
675
|
-
*
|
|
676
673
|
* @category Search
|
|
677
674
|
* @param resourceType The FHIR resource type.
|
|
678
675
|
* @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
|
|
679
676
|
* @param options Optional fetch options.
|
|
680
|
-
* @
|
|
677
|
+
* @yields An async generator, where each result is an array of resources for each page.
|
|
681
678
|
*/
|
|
682
|
-
async *searchResourcePages(resourceType, query, options
|
|
679
|
+
async *searchResourcePages(resourceType, query, options) {
|
|
683
680
|
let url = this.fhirSearchUrl(resourceType, query);
|
|
684
681
|
while (url) {
|
|
685
682
|
const searchParams = new URL(url).searchParams;
|
|
@@ -695,14 +692,13 @@ class MedplumClient extends EventTarget {
|
|
|
695
692
|
/**
|
|
696
693
|
* Searches a ValueSet resource using the "expand" operation.
|
|
697
694
|
* See: https://www.hl7.org/fhir/operation-valueset-expand.html
|
|
698
|
-
*
|
|
699
695
|
* @category Search
|
|
700
696
|
* @param system The ValueSet system url.
|
|
701
697
|
* @param filter The search string.
|
|
702
698
|
* @param options Optional fetch options.
|
|
703
699
|
* @returns Promise to expanded ValueSet.
|
|
704
700
|
*/
|
|
705
|
-
searchValueSet(system, filter, options
|
|
701
|
+
searchValueSet(system, filter, options) {
|
|
706
702
|
const url = this.fhirUrl('ValueSet', '$expand');
|
|
707
703
|
url.searchParams.set('url', system);
|
|
708
704
|
url.searchParams.set('filter', filter);
|
|
@@ -722,8 +718,7 @@ class MedplumClient extends EventTarget {
|
|
|
722
718
|
/**
|
|
723
719
|
* Returns a cached resource if it is available.
|
|
724
720
|
* @category Caching
|
|
725
|
-
* @param
|
|
726
|
-
* @param id The FHIR resource ID.
|
|
721
|
+
* @param reference The FHIR reference.
|
|
727
722
|
* @returns The resource if it is available in the cache; undefined otherwise.
|
|
728
723
|
*/
|
|
729
724
|
getCachedReference(reference) {
|
|
@@ -751,14 +746,13 @@ class MedplumClient extends EventTarget {
|
|
|
751
746
|
* ```
|
|
752
747
|
*
|
|
753
748
|
* See the FHIR "read" operation for full details: https://www.hl7.org/fhir/http.html#read
|
|
754
|
-
*
|
|
755
749
|
* @category Read
|
|
756
750
|
* @param resourceType The FHIR resource type.
|
|
757
751
|
* @param id The resource ID.
|
|
758
752
|
* @param options Optional fetch options.
|
|
759
753
|
* @returns The resource if available; undefined otherwise.
|
|
760
754
|
*/
|
|
761
|
-
readResource(resourceType, id, options
|
|
755
|
+
readResource(resourceType, id, options) {
|
|
762
756
|
return this.get(this.fhirUrl(resourceType, id), options);
|
|
763
757
|
}
|
|
764
758
|
/**
|
|
@@ -775,13 +769,12 @@ class MedplumClient extends EventTarget {
|
|
|
775
769
|
* ```
|
|
776
770
|
*
|
|
777
771
|
* See the FHIR "read" operation for full details: https://www.hl7.org/fhir/http.html#read
|
|
778
|
-
*
|
|
779
772
|
* @category Read
|
|
780
773
|
* @param reference The FHIR reference object.
|
|
781
774
|
* @param options Optional fetch options.
|
|
782
775
|
* @returns The resource if available; undefined otherwise.
|
|
783
776
|
*/
|
|
784
|
-
readReference(reference, options
|
|
777
|
+
readReference(reference, options) {
|
|
785
778
|
const refString = reference?.reference;
|
|
786
779
|
if (!refString) {
|
|
787
780
|
return new ReadablePromise(Promise.reject(new Error('Missing reference')));
|
|
@@ -877,14 +870,13 @@ class MedplumClient extends EventTarget {
|
|
|
877
870
|
* ```
|
|
878
871
|
*
|
|
879
872
|
* See the FHIR "history" operation for full details: https://www.hl7.org/fhir/http.html#history
|
|
880
|
-
*
|
|
881
873
|
* @category Read
|
|
882
874
|
* @param resourceType The FHIR resource type.
|
|
883
875
|
* @param id The resource ID.
|
|
884
876
|
* @param options Optional fetch options.
|
|
885
877
|
* @returns Promise to the resource history.
|
|
886
878
|
*/
|
|
887
|
-
readHistory(resourceType, id, options
|
|
879
|
+
readHistory(resourceType, id, options) {
|
|
888
880
|
return this.get(this.fhirUrl(resourceType, id, '_history'), options);
|
|
889
881
|
}
|
|
890
882
|
/**
|
|
@@ -898,7 +890,6 @@ class MedplumClient extends EventTarget {
|
|
|
898
890
|
* ```
|
|
899
891
|
*
|
|
900
892
|
* See the FHIR "vread" operation for full details: https://www.hl7.org/fhir/http.html#vread
|
|
901
|
-
*
|
|
902
893
|
* @category Read
|
|
903
894
|
* @param resourceType The FHIR resource type.
|
|
904
895
|
* @param id The resource ID.
|
|
@@ -906,7 +897,7 @@ class MedplumClient extends EventTarget {
|
|
|
906
897
|
* @param options Optional fetch options.
|
|
907
898
|
* @returns The resource if available; undefined otherwise.
|
|
908
899
|
*/
|
|
909
|
-
readVersion(resourceType, id, vid, options
|
|
900
|
+
readVersion(resourceType, id, vid, options) {
|
|
910
901
|
return this.get(this.fhirUrl(resourceType, id, '_history', vid), options);
|
|
911
902
|
}
|
|
912
903
|
/**
|
|
@@ -920,13 +911,12 @@ class MedplumClient extends EventTarget {
|
|
|
920
911
|
* ```
|
|
921
912
|
*
|
|
922
913
|
* See the FHIR "patient-everything" operation for full details: https://hl7.org/fhir/operation-patient-everything.html
|
|
923
|
-
*
|
|
924
914
|
* @category Read
|
|
925
915
|
* @param id The Patient Id
|
|
926
916
|
* @param options Optional fetch options.
|
|
927
917
|
* @returns A Bundle of all Resources related to the Patient
|
|
928
918
|
*/
|
|
929
|
-
readPatientEverything(id, options
|
|
919
|
+
readPatientEverything(id, options) {
|
|
930
920
|
return this.get(this.fhirUrl('Patient', id, '$everything'), options);
|
|
931
921
|
}
|
|
932
922
|
/**
|
|
@@ -948,17 +938,17 @@ class MedplumClient extends EventTarget {
|
|
|
948
938
|
* ```
|
|
949
939
|
*
|
|
950
940
|
* See the FHIR "create" operation for full details: https://www.hl7.org/fhir/http.html#create
|
|
951
|
-
*
|
|
952
941
|
* @category Create
|
|
953
942
|
* @param resource The FHIR resource to create.
|
|
943
|
+
* @param options Optional fetch options.
|
|
954
944
|
* @returns The result of the create operation.
|
|
955
945
|
*/
|
|
956
|
-
createResource(resource) {
|
|
946
|
+
createResource(resource, options) {
|
|
957
947
|
if (!resource.resourceType) {
|
|
958
948
|
throw new Error('Missing resourceType');
|
|
959
949
|
}
|
|
960
950
|
this.invalidateSearches(resource.resourceType);
|
|
961
|
-
return this.post(this.fhirUrl(resource.resourceType), resource);
|
|
951
|
+
return this.post(this.fhirUrl(resource.resourceType), resource, undefined, options);
|
|
962
952
|
}
|
|
963
953
|
/**
|
|
964
954
|
* Conditionally create a new FHIR resource only if some equivalent resource does not already exist on the server.
|
|
@@ -994,14 +984,15 @@ class MedplumClient extends EventTarget {
|
|
|
994
984
|
* The query parameter only contains the search parameters (what would be in the URL following the "?").
|
|
995
985
|
*
|
|
996
986
|
* See the FHIR "conditional create" operation for full details: https://www.hl7.org/fhir/http.html#ccreate
|
|
997
|
-
*
|
|
998
987
|
* @category Create
|
|
999
988
|
* @param resource The FHIR resource to create.
|
|
1000
989
|
* @param query The search query for an equivalent resource (should not include resource type or "?").
|
|
990
|
+
* @param options Optional fetch options.
|
|
1001
991
|
* @returns The result of the create operation.
|
|
1002
992
|
*/
|
|
1003
|
-
async createResourceIfNoneExist(resource, query) {
|
|
1004
|
-
return ((await this.searchOne(resource.resourceType, query)) ??
|
|
993
|
+
async createResourceIfNoneExist(resource, query, options) {
|
|
994
|
+
return ((await this.searchOne(resource.resourceType, query, options)) ??
|
|
995
|
+
this.createResource(resource, options));
|
|
1005
996
|
}
|
|
1006
997
|
/**
|
|
1007
998
|
* Creates a FHIR `Binary` resource with the provided data content.
|
|
@@ -1020,11 +1011,11 @@ class MedplumClient extends EventTarget {
|
|
|
1020
1011
|
* ```
|
|
1021
1012
|
*
|
|
1022
1013
|
* See the FHIR "create" operation for full details: https://www.hl7.org/fhir/http.html#create
|
|
1023
|
-
*
|
|
1024
1014
|
* @category Create
|
|
1025
1015
|
* @param data The binary data to upload.
|
|
1026
1016
|
* @param filename Optional filename for the binary.
|
|
1027
1017
|
* @param contentType Content type for the binary.
|
|
1018
|
+
* @param onProgress Optional callback for progress events.
|
|
1028
1019
|
* @returns The result of the create operation.
|
|
1029
1020
|
*/
|
|
1030
1021
|
createBinary(data, filename, contentType, onProgress) {
|
|
@@ -1083,9 +1074,11 @@ class MedplumClient extends EventTarget {
|
|
|
1083
1074
|
* ```
|
|
1084
1075
|
*
|
|
1085
1076
|
* See the pdfmake document definition for full details: https://pdfmake.github.io/docs/0.1/document-definition-object/
|
|
1086
|
-
*
|
|
1087
1077
|
* @category Media
|
|
1088
1078
|
* @param docDefinition The PDF document definition.
|
|
1079
|
+
* @param filename Optional filename for the PDF binary resource.
|
|
1080
|
+
* @param tableLayouts Optional pdfmake custom table layout.
|
|
1081
|
+
* @param fonts Optional pdfmake custom font dictionary.
|
|
1089
1082
|
* @returns The result of the create operation.
|
|
1090
1083
|
*/
|
|
1091
1084
|
async createPdf(docDefinition, filename, tableLayouts, fonts) {
|
|
@@ -1099,13 +1092,13 @@ class MedplumClient extends EventTarget {
|
|
|
1099
1092
|
* Creates a FHIR `Communication` resource with the provided data content.
|
|
1100
1093
|
*
|
|
1101
1094
|
* This is a convenience method to handle commmon cases where a `Communication` resource is created with a `payload`.
|
|
1102
|
-
*
|
|
1103
1095
|
* @category Create
|
|
1104
1096
|
* @param resource The FHIR resource to comment on.
|
|
1105
1097
|
* @param text The text of the comment.
|
|
1098
|
+
* @param options Optional fetch options.
|
|
1106
1099
|
* @returns The result of the create operation.
|
|
1107
1100
|
*/
|
|
1108
|
-
createComment(resource, text) {
|
|
1101
|
+
createComment(resource, text, options) {
|
|
1109
1102
|
const profile = this.getProfile();
|
|
1110
1103
|
let encounter = undefined;
|
|
1111
1104
|
let subject = undefined;
|
|
@@ -1128,7 +1121,7 @@ class MedplumClient extends EventTarget {
|
|
|
1128
1121
|
sender: profile ? createReference(profile) : undefined,
|
|
1129
1122
|
sent: new Date().toISOString(),
|
|
1130
1123
|
payload: [{ contentString: text }],
|
|
1131
|
-
});
|
|
1124
|
+
}, options);
|
|
1132
1125
|
}
|
|
1133
1126
|
/**
|
|
1134
1127
|
* Updates a FHIR resource.
|
|
@@ -1150,12 +1143,12 @@ class MedplumClient extends EventTarget {
|
|
|
1150
1143
|
* ```
|
|
1151
1144
|
*
|
|
1152
1145
|
* See the FHIR "update" operation for full details: https://www.hl7.org/fhir/http.html#update
|
|
1153
|
-
*
|
|
1154
1146
|
* @category Write
|
|
1155
1147
|
* @param resource The FHIR resource to update.
|
|
1148
|
+
* @param options Optional fetch options.
|
|
1156
1149
|
* @returns The result of the update operation.
|
|
1157
1150
|
*/
|
|
1158
|
-
async updateResource(resource) {
|
|
1151
|
+
async updateResource(resource, options) {
|
|
1159
1152
|
if (!resource.resourceType) {
|
|
1160
1153
|
throw new Error('Missing resourceType');
|
|
1161
1154
|
}
|
|
@@ -1163,7 +1156,7 @@ class MedplumClient extends EventTarget {
|
|
|
1163
1156
|
throw new Error('Missing id');
|
|
1164
1157
|
}
|
|
1165
1158
|
this.invalidateSearches(resource.resourceType);
|
|
1166
|
-
let result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource);
|
|
1159
|
+
let result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource, undefined, options);
|
|
1167
1160
|
if (!result) {
|
|
1168
1161
|
// On 304 not modified, result will be undefined
|
|
1169
1162
|
// Return the user input instead
|
|
@@ -1190,16 +1183,16 @@ class MedplumClient extends EventTarget {
|
|
|
1190
1183
|
* See the FHIR "update" operation for full details: https://www.hl7.org/fhir/http.html#patch
|
|
1191
1184
|
*
|
|
1192
1185
|
* See the JSONPatch specification for full details: https://tools.ietf.org/html/rfc6902
|
|
1193
|
-
*
|
|
1194
1186
|
* @category Write
|
|
1195
1187
|
* @param resourceType The FHIR resource type.
|
|
1196
1188
|
* @param id The resource ID.
|
|
1197
1189
|
* @param operations The JSONPatch operations.
|
|
1190
|
+
* @param options Optional fetch options.
|
|
1198
1191
|
* @returns The result of the patch operations.
|
|
1199
1192
|
*/
|
|
1200
|
-
patchResource(resourceType, id, operations) {
|
|
1193
|
+
patchResource(resourceType, id, operations, options) {
|
|
1201
1194
|
this.invalidateSearches(resourceType);
|
|
1202
|
-
return this.patch(this.fhirUrl(resourceType, id), operations);
|
|
1195
|
+
return this.patch(this.fhirUrl(resourceType, id), operations, options);
|
|
1203
1196
|
}
|
|
1204
1197
|
/**
|
|
1205
1198
|
* Deletes a FHIR resource by resource type and ID.
|
|
@@ -1211,16 +1204,16 @@ class MedplumClient extends EventTarget {
|
|
|
1211
1204
|
* ```
|
|
1212
1205
|
*
|
|
1213
1206
|
* See the FHIR "delete" operation for full details: https://www.hl7.org/fhir/http.html#delete
|
|
1214
|
-
*
|
|
1215
1207
|
* @category Delete
|
|
1216
1208
|
* @param resourceType The FHIR resource type.
|
|
1217
1209
|
* @param id The resource ID.
|
|
1210
|
+
* @param options Optional fetch options.
|
|
1218
1211
|
* @returns The result of the delete operation.
|
|
1219
1212
|
*/
|
|
1220
|
-
deleteResource(resourceType, id) {
|
|
1213
|
+
deleteResource(resourceType, id, options) {
|
|
1221
1214
|
this.deleteCacheEntry(this.fhirUrl(resourceType, id).toString());
|
|
1222
1215
|
this.invalidateSearches(resourceType);
|
|
1223
|
-
return this.delete(this.fhirUrl(resourceType, id));
|
|
1216
|
+
return this.delete(this.fhirUrl(resourceType, id), options);
|
|
1224
1217
|
}
|
|
1225
1218
|
/**
|
|
1226
1219
|
* Executes the validate operation with the provided resource.
|
|
@@ -1235,12 +1228,12 @@ class MedplumClient extends EventTarget {
|
|
|
1235
1228
|
* ```
|
|
1236
1229
|
*
|
|
1237
1230
|
* See the FHIR "$validate" operation for full details: https://www.hl7.org/fhir/resource-operation-validate.html
|
|
1238
|
-
*
|
|
1239
1231
|
* @param resource The FHIR resource.
|
|
1232
|
+
* @param options Optional fetch options.
|
|
1240
1233
|
* @returns The validate operation outcome.
|
|
1241
1234
|
*/
|
|
1242
|
-
validateResource(resource) {
|
|
1243
|
-
return this.post(this.fhirUrl(resource.resourceType, '$validate'), resource);
|
|
1235
|
+
validateResource(resource, options) {
|
|
1236
|
+
return this.post(this.fhirUrl(resource.resourceType, '$validate'), resource, undefined, options);
|
|
1244
1237
|
}
|
|
1245
1238
|
/**
|
|
1246
1239
|
* Executes a bot by ID or Identifier.
|
|
@@ -1250,7 +1243,7 @@ class MedplumClient extends EventTarget {
|
|
|
1250
1243
|
* @param options Optional fetch options.
|
|
1251
1244
|
* @returns The Bot return value.
|
|
1252
1245
|
*/
|
|
1253
|
-
executeBot(idOrIdentifier, body, contentType, options
|
|
1246
|
+
executeBot(idOrIdentifier, body, contentType, options) {
|
|
1254
1247
|
let url;
|
|
1255
1248
|
if (typeof idOrIdentifier === 'string') {
|
|
1256
1249
|
const id = idOrIdentifier;
|
|
@@ -1307,10 +1300,11 @@ class MedplumClient extends EventTarget {
|
|
|
1307
1300
|
* See The FHIR "batch/transaction" section for full details: https://hl7.org/fhir/http.html#transaction
|
|
1308
1301
|
* @category Batch
|
|
1309
1302
|
* @param bundle The FHIR batch/transaction bundle.
|
|
1303
|
+
* @param options Optional fetch options.
|
|
1310
1304
|
* @returns The FHIR batch/transaction response bundle.
|
|
1311
1305
|
*/
|
|
1312
|
-
executeBatch(bundle) {
|
|
1313
|
-
return this.post(this.fhirBaseUrl.slice(0, -1), bundle);
|
|
1306
|
+
executeBatch(bundle, options) {
|
|
1307
|
+
return this.post(this.fhirBaseUrl.slice(0, -1), bundle, undefined, options);
|
|
1314
1308
|
}
|
|
1315
1309
|
/**
|
|
1316
1310
|
* Sends an email using the Medplum Email API.
|
|
@@ -1346,11 +1340,12 @@ class MedplumClient extends EventTarget {
|
|
|
1346
1340
|
*
|
|
1347
1341
|
* See options here: https://nodemailer.com/extras/mailcomposer/
|
|
1348
1342
|
* @category Media
|
|
1349
|
-
* @param
|
|
1343
|
+
* @param email The MailComposer options.
|
|
1344
|
+
* @param options Optional fetch options.
|
|
1350
1345
|
* @returns Promise to the operation outcome.
|
|
1351
1346
|
*/
|
|
1352
|
-
sendEmail(email) {
|
|
1353
|
-
return this.post('email/v1/send', email, 'application/json');
|
|
1347
|
+
sendEmail(email, options) {
|
|
1348
|
+
return this.post('email/v1/send', email, 'application/json', options);
|
|
1354
1349
|
}
|
|
1355
1350
|
/**
|
|
1356
1351
|
* Executes a GraphQL query.
|
|
@@ -1392,7 +1387,6 @@ class MedplumClient extends EventTarget {
|
|
|
1392
1387
|
* See the GraphQL documentation for more details: https://graphql.org/learn/
|
|
1393
1388
|
*
|
|
1394
1389
|
* See the FHIR GraphQL documentation for FHIR specific details: https://www.hl7.org/fhir/graphql.html
|
|
1395
|
-
*
|
|
1396
1390
|
* @category Read
|
|
1397
1391
|
* @param query The GraphQL query.
|
|
1398
1392
|
* @param operationName Optional GraphQL operation name.
|
|
@@ -1407,15 +1401,15 @@ class MedplumClient extends EventTarget {
|
|
|
1407
1401
|
*
|
|
1408
1402
|
* Executes the $graph operation on this resource to fetch a Bundle of resources linked to the target resource
|
|
1409
1403
|
* according to a graph definition
|
|
1410
|
-
|
|
1411
1404
|
* @category Read
|
|
1412
1405
|
* @param resourceType The FHIR resource type.
|
|
1413
1406
|
* @param id The resource ID.
|
|
1414
1407
|
* @param graphName `name` parameter of the GraphDefinition
|
|
1408
|
+
* @param options Optional fetch options.
|
|
1415
1409
|
* @returns A Bundle
|
|
1416
1410
|
*/
|
|
1417
|
-
readResourceGraph(resourceType, id, graphName) {
|
|
1418
|
-
return this.get(`${this.fhirUrl(resourceType, id)}/$graph?graph=${graphName}
|
|
1411
|
+
readResourceGraph(resourceType, id, graphName, options) {
|
|
1412
|
+
return this.get(`${this.fhirUrl(resourceType, id)}/$graph?graph=${graphName}`, options);
|
|
1419
1413
|
}
|
|
1420
1414
|
/**
|
|
1421
1415
|
* @category Authentication
|
|
@@ -1425,11 +1419,16 @@ class MedplumClient extends EventTarget {
|
|
|
1425
1419
|
return this.storage.getObject('activeLogin');
|
|
1426
1420
|
}
|
|
1427
1421
|
/**
|
|
1422
|
+
* Sets the active login.
|
|
1423
|
+
* @param login The new active login state.
|
|
1428
1424
|
* @category Authentication
|
|
1429
1425
|
*/
|
|
1430
1426
|
async setActiveLogin(login) {
|
|
1431
1427
|
this.clearActiveLogin();
|
|
1432
1428
|
this.accessToken = login.accessToken;
|
|
1429
|
+
if (this.basicAuth) {
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1433
1432
|
this.refreshToken = login.refreshToken;
|
|
1434
1433
|
this.storage.setObject('activeLogin', login);
|
|
1435
1434
|
this.addLogin(login);
|
|
@@ -1438,6 +1437,7 @@ class MedplumClient extends EventTarget {
|
|
|
1438
1437
|
}
|
|
1439
1438
|
/**
|
|
1440
1439
|
* Returns the current access token.
|
|
1440
|
+
* @returns The current access token.
|
|
1441
1441
|
* @category Authentication
|
|
1442
1442
|
*/
|
|
1443
1443
|
getAccessToken() {
|
|
@@ -1445,6 +1445,7 @@ class MedplumClient extends EventTarget {
|
|
|
1445
1445
|
}
|
|
1446
1446
|
/**
|
|
1447
1447
|
* Sets the current access token.
|
|
1448
|
+
* @param accessToken The new access token.
|
|
1448
1449
|
* @category Authentication
|
|
1449
1450
|
*/
|
|
1450
1451
|
setAccessToken(accessToken) {
|
|
@@ -1454,6 +1455,8 @@ class MedplumClient extends EventTarget {
|
|
|
1454
1455
|
this.config = undefined;
|
|
1455
1456
|
}
|
|
1456
1457
|
/**
|
|
1458
|
+
* Returns the list of available logins.
|
|
1459
|
+
* @returns The list of available logins.
|
|
1457
1460
|
* @category Authentication
|
|
1458
1461
|
*/
|
|
1459
1462
|
getLogins() {
|
|
@@ -1466,6 +1469,9 @@ class MedplumClient extends EventTarget {
|
|
|
1466
1469
|
}
|
|
1467
1470
|
async refreshProfile() {
|
|
1468
1471
|
this.profilePromise = new Promise((resolve, reject) => {
|
|
1472
|
+
if (this.basicAuth) {
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1469
1475
|
this.get('auth/me')
|
|
1470
1476
|
.then((result) => {
|
|
1471
1477
|
this.profilePromise = undefined;
|
|
@@ -1479,18 +1485,26 @@ class MedplumClient extends EventTarget {
|
|
|
1479
1485
|
return this.profilePromise;
|
|
1480
1486
|
}
|
|
1481
1487
|
/**
|
|
1488
|
+
* Returns true if the client is waiting for authentication.
|
|
1489
|
+
* @returns True if the client is waiting for authentication.
|
|
1482
1490
|
* @category Authentication
|
|
1483
1491
|
*/
|
|
1484
1492
|
isLoading() {
|
|
1485
1493
|
return !!this.profilePromise;
|
|
1486
1494
|
}
|
|
1487
1495
|
/**
|
|
1496
|
+
* Returns the current user profile resource if available.
|
|
1497
|
+
* This method does not wait for loading promises.
|
|
1498
|
+
* @returns The current user profile resource if available.
|
|
1488
1499
|
* @category User Profile
|
|
1489
1500
|
*/
|
|
1490
1501
|
getProfile() {
|
|
1491
1502
|
return this.profile;
|
|
1492
1503
|
}
|
|
1493
1504
|
/**
|
|
1505
|
+
* Returns the current user profile resource if available.
|
|
1506
|
+
* This method waits for loading promises.
|
|
1507
|
+
* @returns The current user profile resource if available.
|
|
1494
1508
|
* @category User Profile
|
|
1495
1509
|
*/
|
|
1496
1510
|
async getProfileAsync() {
|
|
@@ -1500,6 +1514,8 @@ class MedplumClient extends EventTarget {
|
|
|
1500
1514
|
return this.getProfile();
|
|
1501
1515
|
}
|
|
1502
1516
|
/**
|
|
1517
|
+
* Returns the current user configuration if available.
|
|
1518
|
+
* @returns The current user configuration if available.
|
|
1503
1519
|
* @category User Profile
|
|
1504
1520
|
*/
|
|
1505
1521
|
getUserConfiguration() {
|
|
@@ -1507,9 +1523,9 @@ class MedplumClient extends EventTarget {
|
|
|
1507
1523
|
}
|
|
1508
1524
|
/**
|
|
1509
1525
|
* Downloads the URL as a blob.
|
|
1510
|
-
*
|
|
1511
1526
|
* @category Read
|
|
1512
1527
|
* @param url The URL to request.
|
|
1528
|
+
* @param options Optional fetch request init options.
|
|
1513
1529
|
* @returns Promise to the response body as a blob.
|
|
1514
1530
|
*/
|
|
1515
1531
|
async download(url, options = {}) {
|
|
@@ -1523,12 +1539,13 @@ class MedplumClient extends EventTarget {
|
|
|
1523
1539
|
/**
|
|
1524
1540
|
* Upload media to the server and create a Media instance for the uploaded content.
|
|
1525
1541
|
* @param contents The contents of the media file, as a string, Uint8Array, File, or Blob.
|
|
1526
|
-
* @param contentType The media type of the content
|
|
1527
|
-
* @param filename The name of the file to be uploaded, or undefined if not applicable
|
|
1528
|
-
* @param additionalFields
|
|
1542
|
+
* @param contentType The media type of the content.
|
|
1543
|
+
* @param filename The name of the file to be uploaded, or undefined if not applicable.
|
|
1544
|
+
* @param additionalFields Additional fields for Media.
|
|
1545
|
+
* @param options Optional fetch options.
|
|
1529
1546
|
* @returns Promise that resolves to the created Media
|
|
1530
1547
|
*/
|
|
1531
|
-
async uploadMedia(contents, contentType, filename, additionalFields) {
|
|
1548
|
+
async uploadMedia(contents, contentType, filename, additionalFields, options) {
|
|
1532
1549
|
const binary = await this.createBinary(contents, filename, contentType);
|
|
1533
1550
|
return this.createResource({
|
|
1534
1551
|
...additionalFields,
|
|
@@ -1538,7 +1555,36 @@ class MedplumClient extends EventTarget {
|
|
|
1538
1555
|
url: 'Binary/' + binary.id,
|
|
1539
1556
|
title: filename,
|
|
1540
1557
|
},
|
|
1541
|
-
});
|
|
1558
|
+
}, options);
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Performs Bulk Data Export operation request flow. See The FHIR "Bulk Data Export" for full details: https://build.fhir.org/ig/HL7/bulk-data/export.html#bulk-data-export
|
|
1562
|
+
* @param exportLevel Optional export level. Defaults to system level export. 'Group/:id' - Group of Patients, 'Patient' - All Patients.
|
|
1563
|
+
* @param resourceTypes A string of comma-delimited FHIR resource types.
|
|
1564
|
+
* @param since Resources will be included in the response if their state has changed after the supplied time (e.g. if Resource.meta.lastUpdated is later than the supplied _since time).
|
|
1565
|
+
* @param options Optional fetch options.
|
|
1566
|
+
* @returns Bulk Data Response containing links to Bulk Data files. See "Response - Complete Status" for full details: https://build.fhir.org/ig/HL7/bulk-data/export.html#response---complete-status
|
|
1567
|
+
*/
|
|
1568
|
+
async bulkExport(exportLevel = '', resourceTypes, since, options = {}) {
|
|
1569
|
+
const fhirPath = exportLevel ? `${exportLevel}/` : exportLevel;
|
|
1570
|
+
const url = this.fhirUrl(`${fhirPath}$export`);
|
|
1571
|
+
if (resourceTypes) {
|
|
1572
|
+
url.searchParams.set('_type', resourceTypes);
|
|
1573
|
+
}
|
|
1574
|
+
if (since) {
|
|
1575
|
+
url.searchParams.set('_since', since);
|
|
1576
|
+
}
|
|
1577
|
+
this.addFetchOptionsDefaults(options);
|
|
1578
|
+
const headers = options.headers;
|
|
1579
|
+
headers['Prefer'] = 'respond-async';
|
|
1580
|
+
const response = await this.fetchWithRetry(url.toString(), options);
|
|
1581
|
+
if (response.status === 202) {
|
|
1582
|
+
const contentLocation = response.headers.get('content-location');
|
|
1583
|
+
if (contentLocation) {
|
|
1584
|
+
return await this.pollStatus(contentLocation);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
return await this.parseResponse(response, 'POST', url.toString());
|
|
1542
1588
|
}
|
|
1543
1589
|
//
|
|
1544
1590
|
// Private helpers
|
|
@@ -1591,10 +1637,10 @@ class MedplumClient extends EventTarget {
|
|
|
1591
1637
|
}
|
|
1592
1638
|
/**
|
|
1593
1639
|
* Makes an HTTP request.
|
|
1594
|
-
* @param
|
|
1595
|
-
* @param
|
|
1596
|
-
* @param
|
|
1597
|
-
* @
|
|
1640
|
+
* @param method The HTTP method (GET, POST, etc).
|
|
1641
|
+
* @param url The target URL.
|
|
1642
|
+
* @param options Optional fetch request init options.
|
|
1643
|
+
* @returns The JSON content body if available.
|
|
1598
1644
|
*/
|
|
1599
1645
|
async request(method, url, options = {}) {
|
|
1600
1646
|
if (this.refreshPromise) {
|
|
@@ -1606,6 +1652,9 @@ class MedplumClient extends EventTarget {
|
|
|
1606
1652
|
options.method = method;
|
|
1607
1653
|
this.addFetchOptionsDefaults(options);
|
|
1608
1654
|
const response = await this.fetchWithRetry(url, options);
|
|
1655
|
+
return await this.parseResponse(response, method, url, options);
|
|
1656
|
+
}
|
|
1657
|
+
async parseResponse(response, method, url, options = {}) {
|
|
1609
1658
|
if (response.status === 401) {
|
|
1610
1659
|
// Refresh and try again
|
|
1611
1660
|
return this.handleUnauthenticated(method, url, options);
|
|
@@ -1638,14 +1687,35 @@ class MedplumClient extends EventTarget {
|
|
|
1638
1687
|
const retryDelay = 200;
|
|
1639
1688
|
let response = undefined;
|
|
1640
1689
|
for (let retry = 0; retry < maxRetries; retry++) {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1690
|
+
try {
|
|
1691
|
+
response = (await this.fetch(url, options));
|
|
1692
|
+
if (response.status < 500) {
|
|
1693
|
+
return response;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
catch (err) {
|
|
1697
|
+
this.retryCatch(retry, maxRetries, err);
|
|
1644
1698
|
}
|
|
1645
1699
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
1646
1700
|
}
|
|
1647
1701
|
return response;
|
|
1648
1702
|
}
|
|
1703
|
+
async pollStatus(statusUrl) {
|
|
1704
|
+
let checkStatus = true;
|
|
1705
|
+
let resultResponse;
|
|
1706
|
+
const retryDelay = 200;
|
|
1707
|
+
while (checkStatus) {
|
|
1708
|
+
const fetchOptions = {};
|
|
1709
|
+
this.addFetchOptionsDefaults(fetchOptions);
|
|
1710
|
+
const statusResponse = await this.fetchWithRetry(statusUrl, fetchOptions);
|
|
1711
|
+
if (statusResponse.status !== 202) {
|
|
1712
|
+
checkStatus = false;
|
|
1713
|
+
resultResponse = statusResponse;
|
|
1714
|
+
}
|
|
1715
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
1716
|
+
}
|
|
1717
|
+
return await this.parseResponse(resultResponse, 'POST', statusUrl);
|
|
1718
|
+
}
|
|
1649
1719
|
/**
|
|
1650
1720
|
* Executes a batch of requests that were automatically batched together.
|
|
1651
1721
|
*/
|
|
@@ -1703,6 +1773,7 @@ class MedplumClient extends EventTarget {
|
|
|
1703
1773
|
headers = {};
|
|
1704
1774
|
options.headers = headers;
|
|
1705
1775
|
}
|
|
1776
|
+
headers['Accept'] = FHIR_CONTENT_TYPE;
|
|
1706
1777
|
headers['X-Medplum'] = 'extended';
|
|
1707
1778
|
if (options.body && !headers['Content-Type']) {
|
|
1708
1779
|
headers['Content-Type'] = FHIR_CONTENT_TYPE;
|
|
@@ -1710,7 +1781,7 @@ class MedplumClient extends EventTarget {
|
|
|
1710
1781
|
if (this.accessToken) {
|
|
1711
1782
|
headers['Authorization'] = 'Bearer ' + this.accessToken;
|
|
1712
1783
|
}
|
|
1713
|
-
if (this.basicAuth) {
|
|
1784
|
+
else if (this.basicAuth) {
|
|
1714
1785
|
headers['Authorization'] = 'Basic ' + this.basicAuth;
|
|
1715
1786
|
}
|
|
1716
1787
|
if (!options.cache) {
|
|
@@ -1754,8 +1825,8 @@ class MedplumClient extends EventTarget {
|
|
|
1754
1825
|
* Otherwise, calls unauthenticated callbacks and rejects.
|
|
1755
1826
|
* @param method The HTTP method of the original request.
|
|
1756
1827
|
* @param url The URL of the original request.
|
|
1757
|
-
* @param
|
|
1758
|
-
* @
|
|
1828
|
+
* @param options Optional fetch request init options.
|
|
1829
|
+
* @returns The result of the retry.
|
|
1759
1830
|
*/
|
|
1760
1831
|
handleUnauthenticated(method, url, options) {
|
|
1761
1832
|
if (this.refresh()) {
|
|
@@ -1771,6 +1842,7 @@ class MedplumClient extends EventTarget {
|
|
|
1771
1842
|
* Starts a new PKCE flow.
|
|
1772
1843
|
* These PKCE values are stateful, and must survive redirects and page refreshes.
|
|
1773
1844
|
* @category Authentication
|
|
1845
|
+
* @returns The PKCE code challenge details.
|
|
1774
1846
|
*/
|
|
1775
1847
|
async startPkce() {
|
|
1776
1848
|
const pkceState = getRandomString();
|
|
@@ -1785,7 +1857,8 @@ class MedplumClient extends EventTarget {
|
|
|
1785
1857
|
/**
|
|
1786
1858
|
* Redirects the user to the login screen for authorization.
|
|
1787
1859
|
* Clears all auth state including local storage and session storage.
|
|
1788
|
-
*
|
|
1860
|
+
* @param loginParams The authorization login parameters.
|
|
1861
|
+
* @see https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
|
1789
1862
|
*/
|
|
1790
1863
|
async requestAuthorization(loginParams) {
|
|
1791
1864
|
const loginRequest = await this.ensureCodeChallenge(loginParams || {});
|
|
@@ -1804,6 +1877,7 @@ class MedplumClient extends EventTarget {
|
|
|
1804
1877
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
|
1805
1878
|
* @param code The authorization code received by URL parameter.
|
|
1806
1879
|
* @param loginParams Optional login parameters.
|
|
1880
|
+
* @returns The user profile resource.
|
|
1807
1881
|
* @category Authentication
|
|
1808
1882
|
*/
|
|
1809
1883
|
processCode(code, loginParams) {
|
|
@@ -1822,7 +1896,8 @@ class MedplumClient extends EventTarget {
|
|
|
1822
1896
|
}
|
|
1823
1897
|
/**
|
|
1824
1898
|
* Tries to refresh the auth tokens.
|
|
1825
|
-
*
|
|
1899
|
+
* @returns The refresh promise if available; otherwise undefined.
|
|
1900
|
+
* @see https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
|
|
1826
1901
|
*/
|
|
1827
1902
|
refresh() {
|
|
1828
1903
|
if (this.refreshPromise) {
|
|
@@ -1875,7 +1950,6 @@ class MedplumClient extends EventTarget {
|
|
|
1875
1950
|
* // Example Search
|
|
1876
1951
|
* await medplum.searchResources('Patient')
|
|
1877
1952
|
* ```
|
|
1878
|
-
*
|
|
1879
1953
|
* @category Authentication
|
|
1880
1954
|
* @param clientId The client ID.
|
|
1881
1955
|
* @param clientSecret The client secret.
|
|
@@ -1898,14 +1972,20 @@ class MedplumClient extends EventTarget {
|
|
|
1898
1972
|
* Makes a POST request to the tokens endpoint.
|
|
1899
1973
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
1900
1974
|
* @param formBody Token parameters in URL encoded format.
|
|
1975
|
+
* @returns The user profile resource.
|
|
1901
1976
|
*/
|
|
1902
1977
|
async fetchTokens(formBody) {
|
|
1903
|
-
const
|
|
1978
|
+
const options = {
|
|
1904
1979
|
method: 'POST',
|
|
1905
1980
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1906
1981
|
body: formBody,
|
|
1907
1982
|
credentials: 'include',
|
|
1908
|
-
}
|
|
1983
|
+
};
|
|
1984
|
+
const headers = options.headers;
|
|
1985
|
+
if (this.basicAuth) {
|
|
1986
|
+
headers['Authorization'] = `Basic ${this.basicAuth}`;
|
|
1987
|
+
}
|
|
1988
|
+
const response = await this.fetch(this.tokenUrl, options);
|
|
1909
1989
|
if (!response.ok) {
|
|
1910
1990
|
this.clearActiveLogin();
|
|
1911
1991
|
throw new Error('Failed to fetch tokens');
|
|
@@ -1918,7 +1998,8 @@ class MedplumClient extends EventTarget {
|
|
|
1918
1998
|
* Verifies the tokens received from the auth server.
|
|
1919
1999
|
* Validates the JWT against the JWKS.
|
|
1920
2000
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
1921
|
-
* @param tokens
|
|
2001
|
+
* @param tokens The token response.
|
|
2002
|
+
* @returns Promise to complete.
|
|
1922
2003
|
*/
|
|
1923
2004
|
async verifyTokens(tokens) {
|
|
1924
2005
|
const token = tokens.access_token;
|
|
@@ -1929,7 +2010,14 @@ class MedplumClient extends EventTarget {
|
|
|
1929
2010
|
throw new Error('Token expired');
|
|
1930
2011
|
}
|
|
1931
2012
|
// Verify app_client_id
|
|
1932
|
-
|
|
2013
|
+
// external tokenPayload
|
|
2014
|
+
if (tokenPayload.cid) {
|
|
2015
|
+
if (tokenPayload.cid !== this.clientId) {
|
|
2016
|
+
this.clearActiveLogin();
|
|
2017
|
+
throw new Error('Token was not issued for this audience');
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
else if (this.clientId && tokenPayload.client_id !== this.clientId) {
|
|
1933
2021
|
this.clearActiveLogin();
|
|
1934
2022
|
throw new Error('Token was not issued for this audience');
|
|
1935
2023
|
}
|
|
@@ -1959,6 +2047,15 @@ class MedplumClient extends EventTarget {
|
|
|
1959
2047
|
// Silently ignore if this environment does not support storage events
|
|
1960
2048
|
}
|
|
1961
2049
|
}
|
|
2050
|
+
retryCatch(retryNumber, maxRetries, err) {
|
|
2051
|
+
// This is for the 1st retry to avoid multiple notifications
|
|
2052
|
+
if (err.message === 'Failed to fetch' && retryNumber === 1) {
|
|
2053
|
+
this.dispatchEvent({ type: 'offline' });
|
|
2054
|
+
}
|
|
2055
|
+
if (retryNumber >= maxRetries - 1) {
|
|
2056
|
+
throw err;
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
1962
2059
|
}
|
|
1963
2060
|
/**
|
|
1964
2061
|
* Returns the default fetch method.
|
|
@@ -1974,6 +2071,7 @@ function getDefaultFetch() {
|
|
|
1974
2071
|
}
|
|
1975
2072
|
/**
|
|
1976
2073
|
* Returns the base URL for the current page.
|
|
2074
|
+
* @returns The window origin string.
|
|
1977
2075
|
* @category HTTP
|
|
1978
2076
|
*/
|
|
1979
2077
|
function getWindowOrigin() {
|