diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 45077be381..a3476d5e23 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -1003,7 +1003,8 @@ export class MedplumClient extends EventTarget { } }); } else { - promise = this.request('GET', url, options); + options.method = 'GET'; + promise = this.request(url, options); } const readablePromise = new ReadablePromise(promise); @@ -1031,7 +1032,8 @@ export class MedplumClient extends EventTarget { this.setRequestContentType(options, contentType); } this.invalidateUrl(url); - return this.request('POST', url, options); + options.method = 'POST'; + return this.request(url, options); } /** @@ -1054,7 +1056,8 @@ export class MedplumClient extends EventTarget { this.setRequestContentType(options, contentType); } this.invalidateUrl(url); - return this.request('PUT', url, options); + options.method = 'PUT'; + return this.request(url, options); } /** @@ -1074,7 +1077,8 @@ export class MedplumClient extends EventTarget { this.setRequestBody(options, operations); this.setRequestContentType(options, ContentType.JSON_PATCH); this.invalidateUrl(url); - return this.request('PATCH', url, options); + options.method = 'PATCH'; + return this.request(url, options); } /** @@ -1089,10 +1093,11 @@ export class MedplumClient extends EventTarget { * @param options - Optional fetch options. * @returns Promise to the response content. */ - delete(url: URL | string, options?: MedplumRequestOptions): Promise { + delete(url: URL | string, options: MedplumRequestOptions = {}): Promise { url = url.toString(); this.invalidateUrl(url); - return this.request('DELETE', url, options); + options.method = 'DELETE'; + return this.request(url, options); } /** @@ -2932,7 +2937,22 @@ export class MedplumClient extends EventTarget { const headers = options.headers as Record; headers['Prefer'] = 'respond-async'; - return this.request('POST', url, options); + options.method = 'POST'; + return this.request(url, options); + } + + /** + * Wraps `fetch` execution with token refresh and retry logic. + * @param url - The URL to request + * @param options - Optional fetch options + * @returns The response + */ + async wrappedFetch(url: string, options: RequestInit): Promise { + await this.refreshIfExpired(); + + this.addFetchOptionsDefaults(options); + + return this.fetchWithRetry(url, options); } /** @@ -3005,28 +3025,17 @@ export class MedplumClient extends EventTarget { /** * Makes an HTTP request. - * @param method - The HTTP method (GET, POST, etc). * @param url - The target URL. * @param options - Optional fetch request init options. * @param state - Optional request state. * @returns The JSON content body if available. */ - private async request( - method: string, - url: string, - options: MedplumRequestOptions = {}, - state: RequestState = {} - ): Promise { - await this.refreshIfExpired(); - - options.method = method; - this.addFetchOptionsDefaults(options); - - const response = await this.fetchWithRetry(url, options); + private async request(url: string, options: MedplumRequestOptions = {}, state: RequestState = {}): Promise { + const response = await this.wrappedFetch(url, options); if (response.status === 401) { // Refresh and try again - return this.handleUnauthenticated(method, url, options); + return this.handleUnauthenticated(url, options); } if (response.status === 204 || response.status === 304) { @@ -3052,7 +3061,7 @@ export class MedplumClient extends EventTarget { ) { const contentLocation = await tryGetContentLocation(response, obj); if (contentLocation) { - return this.request('GET', contentLocation, { ...options, body: undefined }); + return this.request(contentLocation, { ...options, method: 'GET', body: undefined }); } } @@ -3147,7 +3156,7 @@ export class MedplumClient extends EventTarget { await sleep(retryDelay); state.pollCount++; } - return this.request('GET', statusUrl, statusOptions, state); + return this.request(statusUrl, { ...statusOptions, method: 'GET' }, state); } /** @@ -3167,7 +3176,8 @@ export class MedplumClient extends EventTarget { if (entries.length === 1) { const entry = entries[0]; try { - entry.resolve(await this.request(entry.method, concatUrls(this.fhirBaseUrl, entry.url), entry.options)); + entry.options.method = entry.method; + entry.resolve(await this.request(concatUrls(this.fhirBaseUrl, entry.url), entry.options)); } catch (err) { entry.reject(new OperationOutcomeError(normalizeOperationOutcome(err))); } @@ -3281,14 +3291,13 @@ export class MedplumClient extends EventTarget { * Handles an unauthenticated response from the server. * First, tries to refresh the access token and retry the request. * Otherwise, calls unauthenticated callbacks and rejects. - * @param method - The HTTP method of the original request. * @param url - The URL of the original request. * @param options - Optional fetch request init options. * @returns The result of the retry. */ - private handleUnauthenticated(method: string, url: string, options: MedplumRequestOptions): Promise { + private handleUnauthenticated(url: string, options: MedplumRequestOptions): Promise { if (this.refresh()) { - return this.request(method, url, options); + return this.request(url, options); } this.clearActiveLogin(); if (this.onUnauthenticated) {