diff --git a/packages/core/src/outcomes.ts b/packages/core/src/outcomes.ts index efdfc2ca86..b08e6a298d 100644 --- a/packages/core/src/outcomes.ts +++ b/packages/core/src/outcomes.ts @@ -181,7 +181,7 @@ export function badRequest(details: string, expression?: string): OperationOutco details: { text: details, }, - expression: expression ? [expression] : undefined, + ...(expression ? { expression: [expression] } : undefined), }, ], }; diff --git a/packages/server/src/fhir/operations/agentreloadconfig.test.ts b/packages/server/src/fhir/operations/agentreloadconfig.test.ts index bf6ecd9920..1c7f1f6859 100644 --- a/packages/server/src/fhir/operations/agentreloadconfig.test.ts +++ b/packages/server/src/fhir/operations/agentreloadconfig.test.ts @@ -5,9 +5,10 @@ import { AgentTransmitResponse, ContentType, allOk, + badRequest, serverError, } from '@medplum/core'; -import { Agent, Bundle, Parameters } from '@medplum/fhirtypes'; +import { Agent, Bundle, OperationOutcome, Parameters } from '@medplum/fhirtypes'; import express from 'express'; import { randomUUID } from 'node:crypto'; import { Server } from 'node:http'; @@ -116,47 +117,102 @@ describe('Agent/$reload-config', () => { .set('Authorization', 'Bearer ' + accessToken); expect(res.status).toBe(200); - const bundle = res.body as Bundle; + const outcome = res.body as OperationOutcome; - expectBundleToContainOutcome(bundle, agents[0], allOk); + expect(outcome).toMatchObject(allOk); cleanup(); }); test('Agent error during reload', async () => { - const { cleanup } = await mockAgentResponse( - agents[0], - accessToken, - 'agent:reloadconfig:request', - { type: 'agent:error', body: 'Something is broken' } - ); + // Multi agent example + const handlePromises = [] as Promise[]; + for (let i = 0; i < agents.length; i++) { + handlePromises[i] = mockAgentResponse( + agents[i], + accessToken, + 'agent:reloadconfig:request', + i === 0 + ? { type: 'agent:error', body: 'Something is broken' } + : { type: 'agent:reloadconfig:response', statusCode: 200 } + ); + } + const handles = await Promise.all(handlePromises); - const res = await request(app) - .get(`/fhir/R4/Agent/${agents[0].id as string}/$reload-config`) + let res = await request(app) + .get('/fhir/R4/Agent/$reload-config') .set('Authorization', 'Bearer ' + accessToken); expect(res.status).toBe(200); const bundle = res.body as Bundle; - expectBundleToContainOutcome(bundle, agents[0], serverError(new Error('Something is broken'))); - cleanup(); + for (let i = 0; i < agents.length; i++) { + if (i === 0) { + expectBundleToContainOutcome(bundle, agents[0], badRequest('Something is broken')); + continue; + } + expectBundleToContainOutcome(bundle, agents[i], allOk); + } + + // Agent by ID + res = await request(app) + .get(`/fhir/R4/Agent/${agents[0].id as string}/$reload-config`) + .set('Authorization', 'Bearer ' + accessToken); + + expect(res.status).toBe(400); + + const outcome = res.body as OperationOutcome; + expect(outcome).toMatchObject(badRequest('Something is broken')); + + for (const handle of handles) { + handle.cleanup(); + } }); test('Invalid response from agent during reload', async () => { - const { cleanup } = await mockAgentResponse( - agents[0], - accessToken, - 'agent:reloadconfig:request', - { type: 'agent:transmit:response', remote: '8.8.8.8', contentType: ContentType.PING, body: 'PING' } - ); + // Multi agent example + const handlePromises = [] as Promise[]; + for (let i = 0; i < agents.length; i++) { + handlePromises[i] = mockAgentResponse< + AgentReloadConfigRequest, + AgentReloadConfigResponse | AgentTransmitResponse + >( + agents[i], + accessToken, + 'agent:reloadconfig:request', + i === 0 + ? { type: 'agent:transmit:response', remote: '8.8.8.8', contentType: ContentType.PING, body: 'PING' } + : { type: 'agent:reloadconfig:response', statusCode: 200 } + ); + } + const handles = await Promise.all(handlePromises); - const res = await request(app) - .get(`/fhir/R4/Agent/${agents[0].id as string}/$reload-config`) + let res = await request(app) + .get('/fhir/R4/Agent/$reload-config') .set('Authorization', 'Bearer ' + accessToken); expect(res.status).toBe(200); const bundle = res.body as Bundle; - expectBundleToContainOutcome(bundle, agents[0], serverError(new Error('Invalid response received from agent'))); - cleanup(); + for (let i = 0; i < agents.length; i++) { + if (i === 0) { + expectBundleToContainOutcome(bundle, agents[0], serverError(new Error('Invalid response received from agent'))); + continue; + } + expectBundleToContainOutcome(bundle, agents[i], allOk); + } + + // Single agent by ID + res = await request(app) + .get(`/fhir/R4/Agent/${agents[0].id as string}/$reload-config`) + .set('Authorization', 'Bearer ' + accessToken); + + expect(res.status).toBe(500); + + const outcome = res.body as OperationOutcome; + expect(outcome).toMatchObject(serverError(new Error('Invalid response received from agent'))); + + for (const handle of handles) { + handle.cleanup(); + } }); }); diff --git a/packages/server/src/fhir/operations/agentreloadconfig.ts b/packages/server/src/fhir/operations/agentreloadconfig.ts index 21a11177ef..4182dcd277 100644 --- a/packages/server/src/fhir/operations/agentreloadconfig.ts +++ b/packages/server/src/fhir/operations/agentreloadconfig.ts @@ -1,4 +1,4 @@ -import { AgentReloadConfigResponse, OperationOutcomeError, serverError } from '@medplum/core'; +import { AgentReloadConfigResponse, OperationOutcomeError, badRequest, serverError } from '@medplum/core'; import { FhirRequest, FhirResponse } from '@medplum/fhir-router'; import { Agent, OperationDefinition } from '@medplum/fhirtypes'; import { handleBulkAgentOperation, publishAgentRequest } from './utils/agentutils'; @@ -47,7 +47,7 @@ async function reloadConfig(agent: Agent): Promise { } if (result.type === 'agent:error') { - throw new OperationOutcomeError(serverError(new Error(result.body))); + throw new OperationOutcomeError(badRequest(result.body)); } throw new OperationOutcomeError(serverError(new Error('Invalid response received from agent'))); diff --git a/packages/server/src/fhir/operations/utils/agentutils.ts b/packages/server/src/fhir/operations/utils/agentutils.ts index 7b5fe9f8b2..a9d3b341fe 100644 --- a/packages/server/src/fhir/operations/utils/agentutils.ts +++ b/packages/server/src/fhir/operations/utils/agentutils.ts @@ -108,6 +108,10 @@ export async function handleBulkAgentOperation( return [badRequest('No agent(s) for given query')]; } + if (req.params.id) { + return handler(agents[0]); + } + const promises = agents.map((agent: Agent) => handler(agent)); const results = await Promise.allSettled(promises); const entries = [] as BundleEntry[];