Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

feat: add Batch bundle support #602

Merged
merged 7 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ FHIR Works on AWS is powered by single-function components. These functions prov
+ [Routing](https://github.com/awslabs/fhir-works-on-aws-routing) - Accepts HTTP FHIR requests, routes it to the other components, logs the errors, transforms output to HTTP responses and generates the [Capability Statement](https://www.hl7.org/fhir/capabilitystatement.html).
+ [Authorization](https://github.com/awslabs/fhir-works-on-aws-authz-rbac) - Accepts the access token found in HTTP header and the action the request is trying to perform. It then determines if that action is permitted.
+ [Persistence](https://github.com/awslabs/fhir-works-on-aws-persistence-ddb) - Contains the business logic for creating, reading, updating, and deleting the FHIR record from the database. FHIR also supports ‘conditional’ CRUD actions and patching.
+ Bundle - Supports multiple incoming requests as one request. Think of someone wanting to create five patients at once instead of five individual function calls. There are two types of bundles, batch and transaction. We currently only support transaction bundles.
+ Bundle - Supports multiple incoming requests as one request. Think of someone wanting to create five patients at once instead of five individual function calls. There are two types of bundles, batch and transaction. We currently only support transaction bundles of size 25 entries or fewer, but support batch bundles of up to 750 entries. This 750 limit was drawn from the Lambda payload limit of 6MB and an average request size of 4KB, divided in half to allow for flexibility in request size. This limit can also be configured in the `config.ts`, by specifying the `maxBatchSize` when constructing the `DynamoDBBundleService`.
+ [Search](https://github.com/awslabs/fhir-works-on-aws-search-es) - Enables system-wide searching (/?name=bob) and type searching (/Patient/?name=bob).
+ History - (*Not implemented*) Searches all archived/older versioned resources. This can be done at a system, type or instance level.

Expand Down
278 changes: 278 additions & 0 deletions integration-tests/batchBundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
import { AxiosInstance } from 'axios';
import { getFhirClient } from './utils';

jest.setTimeout(60 * 1000);

const generateGetRequests = (id: string, amount: number) => {
const requests = [];
for (let i = 0; i < amount; i += 1) {
requests.push({
request: {
method: 'GET',
url: `/Patient/${id}`,
},
});
}
return requests;
};

const generateGetResponses = (id: string, amount: number) => {
const responses = [];
for (let i = 0; i < amount; i += 1) {
responses.push({
response: {
status: '200 OK',
location: `Patient/${id}`,
etag: '1',
},
});
}
return responses;
};

describe('Batch bundles', () => {
let client: AxiosInstance;
beforeAll(async () => {
client = await getFhirClient();
});

// expect get and delete to fail in this batch, but batch should succeed
const firstBatch = {
resourceType: 'Bundle',
type: 'batch',
entry: [
{
request: {
method: 'GET',
url: '/Patient/someRandomResource',
},
},
{
request: {
method: 'DELETE',
url: '/Patient/someResource',
},
},
{
resource: {
id: 'createdResource',
resourceType: 'Patient',
text: {
status: 'generated',
div: '<div xmlns="http://www.w3.org/1999/xhtml">Some narrative</div>',
},
active: true,
name: [
{
use: 'official',
family: 'Chalmers',
given: ['Peter', 'James'],
},
],
gender: 'male',
birthDate: '1974-12-25',
},
request: {
method: 'POST',
url: '/Patient/',
},
},
{
resource: {
id: 'resourceToDelete',
resourceType: 'Patient',
text: {
status: 'generated',
div: '<div xmlns="http://www.w3.org/1999/xhtml">Some narrative</div>',
},
active: true,
name: [
{
use: 'official',
family: 'Chalmers',
given: ['Peter', 'James'],
},
],
gender: 'male',
birthDate: '1974-12-25',
},
request: {
method: 'POST',
url: '/Patient/',
},
},
{
resource: {
id: 'resourceToGet',
resourceType: 'Patient',
text: {
status: 'generated',
div: '<div xmlns="http://www.w3.org/1999/xhtml">Some narrative</div>',
},
active: true,
name: [
{
use: 'official',
family: 'Chalmers',
given: ['Peter', 'James'],
},
],
gender: 'male',
birthDate: '1974-12-25',
},
request: {
method: 'POST',
url: '/Patient/',
},
},
],
};

test('post multiple batches with failures', async () => {
const response = await client.post('/', firstBatch);
expect(response.data).toMatchObject({
resourceType: 'Bundle',
type: 'batch-response',
entry: [
{
response: {
status: '404 Not Found',
location: 'Patient/someRandomResource',
},
},
{
response: {
status: '404 Not Found',
location: 'Patient/someResource',
},
},
{
response: {
status: '201 Created',
etag: '1',
ssvegaraju marked this conversation as resolved.
Show resolved Hide resolved
},
},
{
response: {
status: '201 Created',
etag: '1',
},
},
{
response: {
status: '201 Created',
etag: '1',
},
},
],
});

const createdResourceId = response.data.entry[2].response.location;
const deleteResourceId = response.data.entry[3].response.location;
const getResourceId = response.data.entry[4].response.location;

const secondBatch = {
resourceType: 'Bundle',
type: 'batch',
entry: [
{
request: {
method: 'GET',
url: `/${getResourceId}`,
},
},
{
request: {
method: 'DELETE',
url: `/${deleteResourceId}`,
},
},
{
resource: {
id: `${createdResourceId.replace('Patient/', '')}`,
resourceType: 'Patient',
text: {
status: 'generated',
div: '<div xmlns="http://www.w3.org/1999/xhtml">Some narrative</div>',
},
active: true,
name: [
{
use: 'official',
family: 'Chalmers',
given: ['Peter', 'James'],
},
],
gender: 'female',
birthDate: '1974-12-25',
},
request: {
method: 'PUT',
url: `/${createdResourceId}`,
},
},
],
};

const secondResponse = await client.post('/', secondBatch);
expect(secondResponse.data).toMatchObject({
resourceType: 'Bundle',
type: 'batch-response',
entry: [
{
response: {
status: '200 OK',
location: `${getResourceId}`,
etag: '1',
},
},
{
response: {
status: '200 OK',
location: `${deleteResourceId}`,
etag: '1',
},
},
{
response: {
status: '200 OK',
location: `${createdResourceId}`,
etag: '2',
},
},
],
});
});

test('bulk test', async () => {
const postRequest = {
resourceType: 'Patient',
active: true,
name: [
{
family: 'Emily',
given: ['Tester'],
},
],
gender: 'female',
birthDate: '1995-09-24',
id: 'test',
};

const response = await client.post('/Patient', postRequest);
expect(response.status).toEqual(201);
const { id } = response.data;
const requests = generateGetRequests(id, 101);
const batchResponse = await client.post('/', {
resourceType: 'Bundle',
type: 'batch',
entry: requests,
});
expect(batchResponse.status).toEqual(200);
expect(batchResponse.data).toMatchObject({
resourceType: 'Bundle',
type: 'batch-response',
entry: generateGetResponses(id, 101),
});
});
});
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
"axios": "^0.21.4",
"fhir-works-on-aws-authz-rbac": "5.0.0",
"fhir-works-on-aws-interface": "12.0.0",
"fhir-works-on-aws-persistence-ddb": "3.10.1",
"fhir-works-on-aws-routing": "6.4.1",
"fhir-works-on-aws-persistence-ddb": "3.11.0",
"fhir-works-on-aws-routing": "6.5.0",
"fhir-works-on-aws-search-es": "3.12.0",
"lodash": "^4.17.21",
"p-settle": "^4.1.1",
Expand Down
2 changes: 2 additions & 0 deletions serverless.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,8 @@ resources:
- 'dynamodb:UpdateItem'
- 'dynamodb:DeleteItem'
- 'dynamodb:BatchWriteItem'
- 'dynamodb:PartiQLInsert'
- 'dynamodb:PartiQLUpdate'
Resource:
- !GetAtt ResourceDynamoDBTableV2.Arn
- !Join ['', [!GetAtt ResourceDynamoDBTableV2.Arn, '/index/*']]
Expand Down
28 changes: 18 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6280,27 +6280,35 @@ fhir-works-on-aws-interface@^10.0.0:
winston "^3.3.3"
winston-transport "^4.4.0"

[email protected]:
version "3.10.1"
resolved "https://registry.yarnpkg.com/fhir-works-on-aws-persistence-ddb/-/fhir-works-on-aws-persistence-ddb-3.10.1.tgz#1bcfa13a8ba791e6904b1c8a0a57169254e3b3a9"
integrity sha512-ry0qivUYVXM+0e3xMsRN193+eFkw0fnir1qNhXKwwiOs953pDIzlG6MB7Er5OiHnvOL8KaqM/QRSgUdujsc9dw==
fhir-works-on-aws-interface@^12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/fhir-works-on-aws-interface/-/fhir-works-on-aws-interface-12.1.0.tgz#74ff054cdf22eae0122c9ec32562475bcdd2cccf"
integrity sha512-LYbmbNz+CzPvH0QH+Sn8zEFzdfSUDd17hC5xjbMrxHOzQSuNEpIELbQ/rdtiNXc+V3xFsQubYsXoHl69wHZN3w==
dependencies:
winston "^3.3.3"
winston-transport "^4.4.0"

[email protected]:
version "3.11.0"
resolved "https://registry.yarnpkg.com/fhir-works-on-aws-persistence-ddb/-/fhir-works-on-aws-persistence-ddb-3.11.0.tgz#517d3039cc0f09ade43d5768cf28e3a731067067"
integrity sha512-UIEyBuCxR1XfuMrNyzVbq/LGJg2uDJrNFa3R4USt0lMomP4ZlTDIu7s4n3/vvcEHgewFPmyzNqvfgzRdH7UJpg==
dependencies:
"@elastic/elasticsearch" "7.13.0"
"@types/aws-lambda" "^8.10.83"
aws-elasticsearch-connector "^8.2.0"
aws-sdk "^2.1000.0"
aws-xray-sdk "^3.3.3"
fhir-works-on-aws-interface "^12.0.0"
fhir-works-on-aws-interface "^12.1.0"
flat "^5.0.2"
lodash "^4.17.20"
mime-types "^2.1.26"
promise.allsettled "^1.0.2"
uuid "^3.4.0"

fhir-works-on-aws-routing@6.4.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/fhir-works-on-aws-routing/-/fhir-works-on-aws-routing-6.4.1.tgz#f2fcdc031015b2fc67f6284995b34c72fc3f030e"
integrity sha512-j4CxZQ7tB11QXqSVClGv2qsAORYlQmNl5OIC1G3m2zUR/XQ877tOBaxvXuJoQowQm86dCMTp2GAZprBU/NBirw==
fhir-works-on-aws-routing@6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/fhir-works-on-aws-routing/-/fhir-works-on-aws-routing-6.5.0.tgz#98ed2e765b729337d3f932853140e97c7933313b"
integrity sha512-PBe09TZ+SsZ78GxgZDlZb7jXF0y2oAtZDveGvdfg/MRlATlYZLo2OBO+nBSIj4Ag6S92H3LGPV3mq+XFQjvhMQ==
dependencies:
"@types/cors" "^2.8.7"
"@types/express-serve-static-core" "^4.17.2"
Expand All @@ -6311,7 +6319,7 @@ [email protected]:
cors "^2.8.5"
errorhandler "^1.5.1"
express "^4.17.1"
fhir-works-on-aws-interface "^12.0.0"
fhir-works-on-aws-interface "^12.1.0"
flat "^5.0.0"
http-errors "^1.8.0"
lodash "^4.17.15"
Expand Down