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

Commit

Permalink
feat: add Batch bundle support (#602)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssvegaraju committed Apr 6, 2022
1 parent 46a2ac0 commit be2b645
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 13 deletions.
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',
},
},
{
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"

fhir-works-on-aws-persistence-ddb@3.10.1:
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"

fhir-works-on-aws-persistence-ddb@3.11.0:
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 @@ fhir-works-on-aws-routing@6.4.1:
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

0 comments on commit be2b645

Please sign in to comment.