Skip to content

Commit

Permalink
Passing headers to resolve refs requests #184
Browse files Browse the repository at this point in the history
  • Loading branch information
vlad-ignatov committed Aug 27, 2024
1 parent 2a1c320 commit 1b69f06
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 27 deletions.
19 changes: 11 additions & 8 deletions src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,26 @@ async function contextualize(
* @param refId
* @param cache A map to store the resolved refs
* @param client The client instance
* @param [signal] The `AbortSignal` if any
* @param requestOptions Only signal and headers are currently used if provided
* @returns The resolved reference
* @private
*/
function getRef(
refId: string,
cache: Record<string, any>,
client: Client,
signal?: AbortSignal
requestOptions: RequestInit
): Promise<fhirclient.JsonObject> {
if (!cache[refId]) {

const { signal, headers } = requestOptions;

// Note that we set cache[refId] immediately! When the promise is
// settled it will be updated. This is to avoid a ref being fetched
// twice because some of these requests are executed in parallel.
cache[refId] = client.request({
url: refId,
headers,
signal
}).then(res => {
cache[refId] = res;
Expand All @@ -106,15 +109,15 @@ function resolveRef(
graph: boolean,
cache: fhirclient.JsonObject,
client: Client,
signal?: AbortSignal
requestOptions: fhirclient.RequestOptions
) {
const node = getPath(obj, path);
if (node) {
const isArray = Array.isArray(node);
return Promise.all(makeArray(node).filter(Boolean).map((item, i) => {
const ref = item.reference;
if (ref) {
return getRef(ref, cache, client, signal).then(sub => {
return getRef(ref, cache, client, requestOptions).then(sub => {
if (graph) {
if (isArray) {
if (path.indexOf("..") > -1) {
Expand Down Expand Up @@ -150,7 +153,7 @@ function resolveRefs(
fhirOptions: fhirclient.FhirOptions,
cache: fhirclient.JsonObject,
client: Client,
signal?: AbortSignal
requestOptions: fhirclient.RequestOptions
) {

// 1. Sanitize paths, remove any invalid ones
Expand Down Expand Up @@ -191,7 +194,7 @@ function resolveRefs(
Object.keys(groups).sort().forEach(len => {
const group = groups[len];
task = task.then(() => Promise.all(group.map((path: string) => {
return resolveRef(obj, path, !!fhirOptions.graph, cache, client, signal);
return resolveRef(obj, path, !!fhirOptions.graph, cache, client, requestOptions);
})));
});
return task;
Expand Down Expand Up @@ -898,7 +901,7 @@ export default class Client
options,
_resolvedRefs,
this,
signal
requestOptions

Check failure on line 904 in src/Client.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Argument of type 'string | URL | RequestOptions' is not assignable to parameter of type 'RequestOptions'.

Check failure on line 904 in src/Client.ts

View workflow job for this annotation

GitHub Actions / build

Argument of type 'string | URL | RequestOptions' is not assignable to parameter of type 'RequestOptions'.

Check failure on line 904 in src/Client.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Argument of type 'string | URL | RequestOptions' is not assignable to parameter of type 'RequestOptions'.
)));
}
else {
Expand All @@ -907,7 +910,7 @@ export default class Client
options,
_resolvedRefs,
this,
signal
requestOptions

Check failure on line 913 in src/Client.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Argument of type 'string | URL | RequestOptions' is not assignable to parameter of type 'RequestOptions'.

Check failure on line 913 in src/Client.ts

View workflow job for this annotation

GitHub Actions / build

Argument of type 'string | URL | RequestOptions' is not assignable to parameter of type 'RequestOptions'.

Check failure on line 913 in src/Client.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Argument of type 'string | URL | RequestOptions' is not assignable to parameter of type 'RequestOptions'.
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ declare namespace fhirclient {
/**
* Supported PKCE Code challenge methods
*/
codeChallengeMethods: string[];
codeChallengeMethods: string[];
}

/**
Expand Down
42 changes: 24 additions & 18 deletions test/Client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1651,9 +1651,7 @@ describe("FHIR.client", () => {

describe ("can resolve nested refs", () => {
crossPlatformTest(async (env) => {
const client = new Client(env, {
serverUrl: mockUrl
});
const client = new Client(env, { serverUrl: mockUrl });

// This is how the user had defined the list, If it works properly,
// the request function should resolve them in different order:
Expand All @@ -1665,49 +1663,57 @@ describe("FHIR.client", () => {
"encounter"
];

function createHandler(json) {
return function handler(req, res, next) {
try {
expect(req.headers['x-custom-header'], "Custom headers not sent on ref requests").to.equal('someCustomKey');
res.json(json)
} catch (ex) {
next(ex)
}
}
}

// 1. Observation
// this request should be sent first!
mockServer.mock({
headers: { "content-type": "application/json" },
status: 200,
body: {
handler: createHandler({
resourceType: "Observation",
encounter: { reference: "encounter/1" },
subject: { reference: "subject/1" }
}
})
});

// 2. Patient (Observation.subject)
// this request should be sent second (even though it might
// reply after #3)
mockServer.mock({
headers: { "content-type": "application/json" },
status: 200,
body: { resourceType: "Patient" }
handler: createHandler({ resourceType: "Patient" })
});

// 3. Encounter
// this request should be sent third (even though it might
// reply before #2)
mockServer.mock({
headers: { "content-type": "application/json" },
status: 200,
body: {
handler: createHandler({
resourceType: "Encounter",
serviceProvider: { reference: "Organization/1" }
}
})
});

// 4. Organization (Encounter.serviceProvider)
// this request should be sent AFTER we have handled the response
// from #3!
mockServer.mock({
headers: { "content-type": "application/json" },
status: 200,
body: { resourceType: "Organization" }
handler: createHandler({ resourceType: "Organization" })
});

const result = await client.request("Observation/id", {
const result = await client.request({
url: "Observation/id",
headers: {
'X-Custom-Header': 'someCustomKey',
}
}, {
resolveReferences: refsToResolve
});

Expand Down

0 comments on commit 1b69f06

Please sign in to comment.