From aa5b163bb24f92b4177e0b9ad8cdeba51d7258a0 Mon Sep 17 00:00:00 2001 From: Vladimir Ignatov Date: Thu, 21 Jul 2022 16:00:57 -0400 Subject: [PATCH] #153 --- src/Client.ts | 6 +++--- src/HttpError.ts | 2 +- src/lib.ts | 26 ++++++++++++++++++++++++-- src/smart.ts | 4 ++-- test/Client.test.ts | 28 ++++++++++++++-------------- test/lib.test.ts | 2 +- test/smart.test.ts | 4 ++-- 7 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/Client.ts b/src/Client.ts index d07a0c3f..1318d7d1 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -665,7 +665,7 @@ export default class Client body: JSON.stringify(resource), headers: { // TODO: Do we need to alternate with "application/json+fhir"? - "Content-Type": "application/json", + "content-type": "application/json", ...(requestOptions || {}).headers } }); @@ -692,7 +692,7 @@ export default class Client body: JSON.stringify(resource), headers: { // TODO: Do we need to alternate with "application/json+fhir"? - "Content-Type": "application/json", + "content-type": "application/json", ...(requestOptions || {}).headers } }); @@ -809,7 +809,7 @@ export default class Client if (authHeader) { requestOptions.headers = { ...requestOptions.headers, - Authorization: authHeader + authorization: authHeader }; } return requestOptions; diff --git a/src/HttpError.ts b/src/HttpError.ts index 003acdcc..b92964f7 100644 --- a/src/HttpError.ts +++ b/src/HttpError.ts @@ -38,7 +38,7 @@ export default class HttpError extends Error { if (!this.response.bodyUsed) { try { - const type = this.response.headers.get("Content-Type") || "text/plain"; + const type = this.response.headers.get("content-type") || "text/plain"; if (type.match(/\bjson\b/i)) { let body = await this.response.json(); if (body.error) { diff --git a/src/lib.ts b/src/lib.ts index 91dad19a..03a670b7 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -80,6 +80,28 @@ export function responseToJSON(resp: Response): Promise { return resp.text().then(text => text.length ? JSON.parse(text) : ""); } +export function loweCaseKeys | any[] | undefined>(obj: T): T { + + // Can be undefined to signal that this key should be removed + if (!obj) { + return obj as T + } + + // Arrays are valid values in case of recursive calls + if (Array.isArray(obj)) { + return obj.map(v => v && typeof v === "object" ? loweCaseKeys(v) : v) as unknown as T; + } + + // Plain object + let out: Record = {}; + Object.keys(obj).forEach(key => { + const lowerKey = key.toLowerCase() + const v = (obj as Record)[key] + out[lowerKey] = v && typeof v == "object" ? loweCaseKeys(v) : v; + }); + return out as T; +} + /** * This is our built-in request function. It does a few things by default * (unless told otherwise): @@ -101,12 +123,12 @@ export function request( ...options, headers: { accept: "application/json", - ...options.headers + ...loweCaseKeys(options.headers) } }) .then(checkResponse) .then((res: Response) => { - const type = res.headers.get("Content-Type") + ""; + const type = res.headers.get("content-type") + ""; if (type.match(/\bjson\b/i)) { return responseToJSON(res).then(body => ({ res, body })); } diff --git a/src/smart.ts b/src/smart.ts index c59ccbce..77d4d52e 100644 --- a/src/smart.ts +++ b/src/smart.ts @@ -610,10 +610,10 @@ export async function buildTokenRequest(env: fhirclient.Adapter, code: string, s // Basic authentication is required, where the username is the app’s // client_id and the password is the app’s client_secret (see example). if (clientSecret) { - requestOptions.headers.Authorization = "Basic " + env.btoa( + requestOptions.headers.authorization = "Basic " + env.btoa( clientId + ":" + clientSecret ); - debug("Using state.clientSecret to construct the authorization header: %s", requestOptions.headers.Authorization); + debug("Using state.clientSecret to construct the authorization header: %s", requestOptions.headers.authorization); } else if (clientPrivateJwk) { const clientPrivateKey = await security.importKey(clientPrivateJwk) diff --git a/test/Client.test.ts b/test/Client.test.ts index 48d77a32..8c3a27c8 100644 --- a/test/Client.test.ts +++ b/test/Client.test.ts @@ -2709,7 +2709,7 @@ describe("FHIR.client", () => { const client = new Client(env, mockUrl); mockServer.mock({ - headers: { "Content-Type": "text/plain" }, + headers: { "content-type": "text/plain" }, status: 200, body: "This is a text" }); @@ -2728,7 +2728,7 @@ describe("FHIR.client", () => { const goal64 = file.toString("base64"); mockServer.mock({ - headers: { "Content-Type": "image/png" }, + headers: { "content-type": "image/png" }, status: 200, file: "json.png" }); @@ -3816,7 +3816,7 @@ describe("FHIR.client", () => { mockServer.mock({ status: 200, body: resource, - headers: { "Content-Type": "application/json" } + headers: { "content-type": "application/json" } }); result = await client.create(resource, { includeResponse: true }); expect(result.body).to.equal(resource); @@ -3831,7 +3831,7 @@ describe("FHIR.client", () => { method : "POST", body : JSON.stringify(resource), headers: { - "Content-Type": "application/json" + "content-type": "application/json" } }); @@ -3844,7 +3844,7 @@ describe("FHIR.client", () => { signal: "whatever", headers: { "x-custom": "value", - "Content-Type": "application/fhir+json" + "content-type": "application/fhir+json" } }); expect(result).to.equal({ @@ -3854,7 +3854,7 @@ describe("FHIR.client", () => { signal : "whatever", headers: { "x-custom": "value", - "Content-Type": "application/fhir+json" + "content-type": "application/fhir+json" } }); @@ -3872,7 +3872,7 @@ describe("FHIR.client", () => { body : JSON.stringify(resource), signal : "whatever", headers: { - "Content-Type": "application/json" + "content-type": "application/json" } }); }); @@ -3888,7 +3888,7 @@ describe("FHIR.client", () => { mockServer.mock({ status: 200, body: resource, - headers: { "Content-Type": "application/json" } + headers: { "content-type": "application/json" } }); result = await client.update(resource, { includeResponse: true }); expect(result.body).to.equal(resource); @@ -3903,7 +3903,7 @@ describe("FHIR.client", () => { method : "PUT", body : JSON.stringify(resource), headers: { - "Content-Type": "application/json" + "content-type": "application/json" } }); @@ -3916,7 +3916,7 @@ describe("FHIR.client", () => { signal: "whatever", headers: { "x-custom": "value", - "Content-Type": "application/fhir+json" + "content-type": "application/fhir+json" } }); expect(result).to.equal({ @@ -3926,7 +3926,7 @@ describe("FHIR.client", () => { signal: "whatever", headers: { "x-custom": "value", - "Content-Type": "application/fhir+json" + "content-type": "application/fhir+json" } }); @@ -3944,7 +3944,7 @@ describe("FHIR.client", () => { body : JSON.stringify(resource), signal: "whatever", headers: { - "Content-Type": "application/json" + "content-type": "application/json" } }); }); @@ -3960,7 +3960,7 @@ describe("FHIR.client", () => { mockServer.mock({ status: 200, body: { result: "success" }, - headers: { "Content-Type": "application/json" } + headers: { "content-type": "application/json" } }); result = await client.delete("Patient/2", { includeResponse: true }); @@ -4012,7 +4012,7 @@ describe("FHIR.client", () => { mockServer.mock({ status: 200, body: { result: "success" }, - headers: { "Content-Type": "application/json" } + headers: { "content-type": "application/json" } }); result = await client.patch("Patient/2", [{ op: "remove", path: "/x" }], { includeResponse: true }); diff --git a/test/lib.test.ts b/test/lib.test.ts index e8c97982..d095a365 100644 --- a/test/lib.test.ts +++ b/test/lib.test.ts @@ -315,7 +315,7 @@ describe("Lib", () => { const result = await lib.request(mockUrl, { includeResponse: true }); expect(result.body).to.equal({ result: "success" }); - expect(result.response.headers.get("Content-Type")).to.startWith("application/json"); + expect(result.response.headers.get("content-type")).to.startWith("application/json"); }); }); }); diff --git a/test/smart.test.ts b/test/smart.test.ts index 26476891..9e6da5e0 100644 --- a/test/smart.test.ts +++ b/test/smart.test.ts @@ -40,8 +40,8 @@ describe("smart", () => { clientSecret: "test-secret" }); - const authz = requestOptions.headers?.['Authorization'] as string; - expect(authz).to.exist; + const authz = requestOptions.headers?.['authorization'] as string; + expect(authz).to.exist(); expect(authz).to.startWith("Basic ") });