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

feat: Group export map additional parameters needed #107

Merged
merged 5 commits into from
Aug 2, 2021
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
6 changes: 5 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ export function generateServerlessRouter(

// Export
if (fhirConfig.profile.bulkDataAccess) {
const exportRoute = new ExportRoute(fhirConfig.profile.bulkDataAccess, fhirConfig.auth.authorization);
const exportRoute = new ExportRoute(
fhirConfig.profile.bulkDataAccess,
fhirConfig.auth.authorization,
fhirConfig.profile.fhirVersion,
);
mainRouter.use('/', exportRoute.router);
}

Expand Down
55 changes: 48 additions & 7 deletions src/router/bundle/bundleHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const genericResource: GenericResource = {
typeHistory: stubs.history,
typeSearch: stubs.search,
};

const dummyServerUrl: string = 'https://dummy-server-url';
rsmayda marked this conversation as resolved.
Show resolved Hide resolved

const resources = {};

const SUPPORTED_R4_RESOURCES = [
Expand Down Expand Up @@ -414,7 +417,7 @@ describe('ERROR Cases: Validation of Bundle request', () => {
bundleRequestJSON.type = 'batch';

await expect(
bundleHandlerR4.processBatch(bundleRequestJSON, practitionerDecoded, dummyRequestContext),
bundleHandlerR4.processBatch(bundleRequestJSON, practitionerDecoded, dummyRequestContext, dummyServerUrl),
).rejects.toThrowError(new createError.BadRequest('Currently this server only support transaction Bundles'));
});

Expand All @@ -431,7 +434,12 @@ describe('ERROR Cases: Validation of Bundle request', () => {
bundleRequestJSON.entry.push(invalidReadRequest);

await expect(
bundleHandlerR4.processTransaction(bundleRequestJSON, practitionerDecoded, dummyRequestContext),
bundleHandlerR4.processTransaction(
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(
new InvalidResourceError(
'Failed to parse request body as JSON resource. Error was: data.entry[0].request should NOT have additional properties',
Expand All @@ -444,7 +452,12 @@ describe('ERROR Cases: Validation of Bundle request', () => {
bundleRequestJSON.total = 'abc';

await expect(
bundleHandlerSTU3.processTransaction(bundleRequestJSON, practitionerDecoded, dummyRequestContext),
bundleHandlerSTU3.processTransaction(
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(
new InvalidResourceError(
'Failed to parse request body as JSON resource. Error was: data.total should be number, data.total should match pattern "[0]|([1-9][0-9]*)"',
Expand All @@ -457,7 +470,12 @@ describe('ERROR Cases: Validation of Bundle request', () => {
delete bundleRequestJSON.resourceType;

await expect(
bundleHandlerSTU3.processTransaction(bundleRequestJSON, practitionerDecoded, dummyRequestContext),
bundleHandlerSTU3.processTransaction(
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(new InvalidResourceError("resource should have required property 'resourceType'"));
});

Expand All @@ -474,7 +492,12 @@ describe('ERROR Cases: Validation of Bundle request', () => {
bundleRequestJSON.entry.push(searchRequest);

await expect(
bundleHandlerR4.processTransaction(bundleRequestJSON, practitionerDecoded, dummyRequestContext),
bundleHandlerR4.processTransaction(
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(new createError.BadRequest('We currently do not support SEARCH entries in the Bundle'));
});

Expand All @@ -491,7 +514,12 @@ describe('ERROR Cases: Validation of Bundle request', () => {
bundleRequestJSON.entry.push(vreadRequest);

await expect(
bundleHandlerR4.processTransaction(bundleRequestJSON, practitionerDecoded, dummyRequestContext),
bundleHandlerR4.processTransaction(
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(new createError.BadRequest('We currently do not support V_READ entries in the Bundle'));
});

Expand All @@ -507,7 +535,12 @@ describe('ERROR Cases: Validation of Bundle request', () => {
bundleRequestJSON.entry.push(readRequest);
}
await expect(
bundleHandlerR4.processTransaction(bundleRequestJSON, practitionerDecoded, dummyRequestContext),
bundleHandlerR4.processTransaction(
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(
new createError.BadRequest(
`Maximum number of entries for a Bundle is ${MAX_BUNDLE_ENTRIES}. There are currently ${bundleRequestJSON.entry.length} entries in this Bundle`,
Expand All @@ -526,6 +559,7 @@ describe('SUCCESS Cases: Testing Bundle with CRUD entries', () => {
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
);

const expectedResult = {
Expand Down Expand Up @@ -608,6 +642,7 @@ describe('SUCCESS Cases: Testing Bundle with CRUD entries', () => {
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl
);

expect(actualResult).toMatchObject({
Expand Down Expand Up @@ -671,6 +706,7 @@ describe('ERROR Cases: Bundle not authorized', () => {
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(new UnauthorizedError('An entry within the Bundle is not authorized'));
});
Expand Down Expand Up @@ -764,6 +800,7 @@ describe('ERROR Cases: Bundle not authorized', () => {
bundleRequestJSON,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).resolves.toMatchObject(expectedResult);
});
Expand Down Expand Up @@ -824,6 +861,7 @@ describe('SERVER-CAPABILITIES Cases: Validating Bundle request is allowed given
bundleRequestJsonCreatePatient,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(
new createError.BadRequest('Server does not support these resource and operations: {Patient: create}'),
Expand Down Expand Up @@ -860,6 +898,7 @@ describe('SERVER-CAPABILITIES Cases: Validating Bundle request is allowed given
bundleRequestJsonCreatePatient,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
),
).rejects.toThrowError(
new createError.BadRequest('Server does not support these resource and operations: {Patient: create}'),
Expand Down Expand Up @@ -908,6 +947,7 @@ describe('SERVER-CAPABILITIES Cases: Validating Bundle request is allowed given
bundleRequestJsonCreatePatient,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
);

// CHECK
Expand Down Expand Up @@ -938,6 +978,7 @@ describe('SERVER-CAPABILITIES Cases: Validating Bundle request is allowed given
bundleRequestJsonCreatePatient,
practitionerDecoded,
dummyRequestContext,
dummyServerUrl,
);

// CHECK
Expand Down
36 changes: 30 additions & 6 deletions src/router/routes/exportRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@

/* eslint-disable no-underscore-dangle */
import express, { Router } from 'express';
import { Authorization, BulkDataAccess, ExportType, InitiateExportRequest } from 'fhir-works-on-aws-interface';
import {
Authorization,
BulkDataAccess,
ExportType,
FhirVersion,
InitiateExportRequest,
} from 'fhir-works-on-aws-interface';
import createHttpError from 'http-errors';
import RouteHelper from './routeHelper';
import ExportHandler from '../handlers/exportHandler';
Expand All @@ -16,18 +22,32 @@ export default class ExportRoute {

private exportHandler: any;

constructor(bulkDataAccess: BulkDataAccess, authService: Authorization) {
private fhirVersion: FhirVersion;

private authService: Authorization;

constructor(bulkDataAccess: BulkDataAccess, authService: Authorization, fhirVersion: FhirVersion) {
this.router = express.Router();
this.fhirVersion = fhirVersion;
this.authService = authService;
this.exportHandler = new ExportHandler(bulkDataAccess, authService);
this.init();
}

async initiateExportRequests(req: express.Request, res: express.Response, exportType: ExportType) {
const allowedResourceTypes = await this.authService.getAllowedResourceTypesForOperation({
operation: 'read',
userIdentity: res.locals.userIdentity,
requestContext: res.locals.userIdentity,
});
rsmayda marked this conversation as resolved.
Show resolved Hide resolved
const initiateExportRequest: InitiateExportRequest = ExportRouteHelper.buildInitiateExportRequest(
req,
res,
exportType,
allowedResourceTypes,
this.fhirVersion,
);

const jobId = await this.exportHandler.initiateExport(initiateExportRequest);

const exportStatusUrl = `${res.locals.serverUrl}/$export/${jobId}`;
Expand All @@ -46,14 +66,18 @@ export default class ExportRoute {
}),
);

this.router.get(
'/Group/:id/\\$export',
RouteHelper.wrapAsync(async (req: express.Request, res: express.Response) => {
const exportType: ExportType = 'group';
await this.initiateExportRequests(req, res, exportType);
}),
);

this.router.get('/Patient/\\$export', () => {
throw new createHttpError.BadRequest('We currently do not support Patient export');
});

this.router.get('/Group/:id/\\$export', () => {
throw new createHttpError.BadRequest('We currently do not support Group export');
});

// Export Job Status
this.router.get(
'/\\$export/:jobId',
Expand Down
13 changes: 11 additions & 2 deletions src/router/routes/exportRouteHelper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
/* eslint-disable no-underscore-dangle */
import express from 'express';
import { ExportType, InitiateExportRequest } from 'fhir-works-on-aws-interface';
import { ExportType, FhirVersion, InitiateExportRequest } from 'fhir-works-on-aws-interface';
import createHttpError from 'http-errors';
import isString from 'lodash/isString';
import { dateTimeWithTimeZoneRegExp } from '../../regExpressions';

export default class ExportRouteHelper {
static buildInitiateExportRequest(req: express.Request, res: express.Response, exportType: ExportType) {
static buildInitiateExportRequest(
req: express.Request,
res: express.Response,
exportType: ExportType,
allowedResourceTypes: string[],
fhirVersion?: FhirVersion,
) {
if (req.query._outputFormat && req.query._outputFormat !== 'ndjson') {
throw new createHttpError.BadRequest('We only support exporting resources into ndjson formatted file');
}
Expand Down Expand Up @@ -35,6 +41,9 @@ export default class ExportRouteHelper {
type: isString(req.query._type) ? req.query._type : undefined,
groupId: isString(req.params.id) ? req.params.id : undefined,
tenantId: res.locals.tenantId,
serverUrl: res.locals.serverUrl,
fhirVersion,
allowedResourceTypes,
};
return initiateExportRequest;
}
Expand Down