Skip to content

Commit

Permalink
Submission Meta Endpoint / Dataloader
Browse files Browse the repository at this point in the history
Return meta (security state, publish state, etc) of a submission
(previously, status information was being returned in the submission GET endpoint, which can now be removed since it is being returned by this new endpoint).
  • Loading branch information
KjartanE authored Dec 21, 2023
1 parent e755672 commit d18dc67
Show file tree
Hide file tree
Showing 16 changed files with 446 additions and 283 deletions.
2 changes: 2 additions & 0 deletions api/src/paths/administrative/submission/reviewed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('getReviewedSubmissionsForAdmins', () => {
uuid: '123-456-789',
security_review_timestamp: '2023-12-12',
submitted_timestamp: '2023-12-12',
publish_timestamp: '2023-12-12',
source_system: 'SIMS',
name: 'name',
description: 'description',
Expand All @@ -68,6 +69,7 @@ describe('getReviewedSubmissionsForAdmins', () => {
security_review_timestamp: '2023-12-12',
submitted_timestamp: '2023-12-12',
source_system: 'SIMS',
publish_timestamp: '2023-12-12',
name: 'name',
description: 'description',
create_date: '2023-12-12',
Expand Down
2 changes: 2 additions & 0 deletions api/src/paths/administrative/submission/unreviewed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('getUnreviewedSubmissionsForAdmins', () => {
uuid: '123-456-789',
security_review_timestamp: null,
submitted_timestamp: '2023-12-12',
publish_timestamp: '2023-12-12',
source_system: 'SIMS',
name: 'name',
description: 'description',
Expand All @@ -67,6 +68,7 @@ describe('getUnreviewedSubmissionsForAdmins', () => {
uuid: '789-456-123',
security_review_timestamp: null,
submitted_timestamp: '2023-12-12',
publish_timestamp: '2023-12-12',
source_system: 'SIMS',
name: 'name',
description: 'description',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('patchSubmissionRecord', () => {
submission_id: 3,
uuid: '999-456-123',
security_review_timestamp: '2023-12-12',
publish_timestamp: '2023-12-12',
submitted_timestamp: '2023-12-12',
source_system: 'SIMS',
name: 'name',
Expand Down
73 changes: 73 additions & 0 deletions api/src/paths/submission/{submissionId}/features/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import chai, { expect } from 'chai';
import { describe } from 'mocha';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import * as index from '.';
import * as db from '../../../../database/db';
import { HTTP400, HTTPError } from '../../../../errors/http-error';
import { SubmissionService } from '../../../../services/submission-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db';

chai.use(sinonChai);

describe('index', () => {
describe('getSubmissionFeatures', () => {
afterEach(() => {
sinon.restore();
});

it('throws error if submissionService throws error', async () => {
const dbConnectionObj = getMockDBConnection();

sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

const getSubmissionAndFeaturesBySubmissionIdStub = sinon
.stub(SubmissionService.prototype, 'getSubmissionFeaturesBySubmissionId')
.throws(new HTTP400('Error', ['Error']));

const requestHandler = index.getSubmissionFeatures();

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

mockReq.params = {
submissionId: '1'
};

try {
await requestHandler(mockReq, mockRes, mockNext);

expect.fail();
} catch (error) {
expect(getSubmissionAndFeaturesBySubmissionIdStub).to.have.been.calledOnce;
expect((error as HTTPError).status).to.equal(400);
expect((error as HTTPError).message).to.equal('Error');
}
});

it('should return 200 on success', async () => {
const dbConnectionObj = getMockDBConnection();

sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

const mockResponse = [] as unknown as any;

const getSubmissionAndFeaturesBySubmissionIdStub = sinon
.stub(SubmissionService.prototype, 'getSubmissionFeaturesBySubmissionId')
.resolves(mockResponse);

const requestHandler = index.getSubmissionFeatures();

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

mockReq.params = {
submissionId: '1'
};

await requestHandler(mockReq, mockRes, mockNext);

expect(getSubmissionAndFeaturesBySubmissionIdStub).to.have.been.calledOnce;
expect(mockRes.statusValue).to.eql(200);
expect(mockRes.jsonValue).to.eql(mockResponse);
});
});
});
174 changes: 174 additions & 0 deletions api/src/paths/submission/{submissionId}/features/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { RequestHandler } from 'express';
import { Operation } from 'express-openapi';
import { getAPIUserDBConnection, getDBConnection } from '../../../../database/db';
import { defaultErrorResponses } from '../../../../openapi/schemas/http-responses';
import { SubmissionService } from '../../../../services/submission-service';
import { getLogger } from '../../../../utils/logger';

const defaultLog = getLogger('paths/submission/{submissionId}');

export const GET: Operation = [getSubmissionFeatures()];

GET.apiDoc = {
description: 'Retrieves a submission record from the submission table',
tags: ['eml'],
security: [
{
OptionalBearer: []
}
],
parameters: [
{
description: 'Submission ID.',
in: 'path',
name: 'submissionId',
schema: {
type: 'integer',
minimum: 1
},
required: true
}
],
responses: {
200: {
description: 'A submission record and all child submission feature records.',
content: {
'application/json': {
schema: {
type: 'array',
items: {
type: 'object',
required: ['feature_type_name', 'feature_type_display_name', 'features'],
properties: {
feature_type_name: {
type: 'string'
},
feature_type_display_name: {
type: 'string'
},
features: {
type: 'array',
items: {
type: 'object',
required: [
'submission_feature_id',
'submission_id',
'feature_type_id',
'data',
'parent_submission_feature_id',
'record_effective_date',
'record_end_date',
'create_date',
'create_user',
'update_date',
'update_user',
'revision_count',
'feature_type_name',
'feature_type_display_name',
'submission_feature_security_ids'
],
properties: {
submission_feature_id: {
type: 'integer',
minimum: 1
},
submission_id: {
type: 'integer',
minimum: 1
},
feature_type_id: {
type: 'integer',
minimum: 1
},
data: {
type: 'object',
properties: {}
},
parent_submission_feature_id: {
type: 'integer',
minimum: 1,
nullable: true
},
record_effective_date: {
type: 'string'
},
record_end_date: {
type: 'string',
nullable: true
},
create_date: {
type: 'string'
},
create_user: {
type: 'integer',
minimum: 1
},
update_date: {
type: 'string',
nullable: true
},
update_user: {
type: 'integer',
minimum: 1,
nullable: true
},
revision_count: {
type: 'integer',
minimum: 0
},
feature_type_name: {
type: 'string'
},
feature_type_display_name: {
type: 'string'
},
submission_feature_security_ids: {
type: 'array',
items: {
type: 'integer',
minimum: 1
}
}
}
}
}
}
}
}
}
}
},
...defaultErrorResponses
}
};

/**
* Retrieves all child submission feature records.
*
* @returns {RequestHandler}
*/
export function getSubmissionFeatures(): RequestHandler {
return async (req, res) => {
const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection();

const submissionId = Number(req.params.submissionId);

try {
await connection.open();

const submissionService = new SubmissionService(connection);

const result = await submissionService.getSubmissionFeaturesBySubmissionId(submissionId);

await connection.commit();

res.status(200).json(result);
} catch (error) {
defaultLog.error({ label: 'getSubmissionFeatures', message: 'error', error });
await connection.rollback();
throw error;
} finally {
connection.release();
}
};
}
33 changes: 8 additions & 25 deletions api/src/paths/submission/{submissionId}/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import chai, { expect } from 'chai';
import { describe } from 'mocha';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import * as index from '.';
import * as db from '../../../database/db';
import { HTTP400, HTTPError } from '../../../errors/http-error';
import { SECURITY_APPLIED_STATUS } from '../../../repositories/security-repository';
import { SubmissionRecordWithSecurity } from '../../../repositories/submission-repository';
import { SubmissionService } from '../../../services/submission-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db';
import * as index from './index';

chai.use(sinonChai);

describe('index', () => {
describe('getSubmissionInformation', () => {
describe('getSubmissionRecordWithSecurity', () => {
afterEach(() => {
sinon.restore();
});
Expand All @@ -23,10 +23,10 @@ describe('index', () => {
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

const getSubmissionAndFeaturesBySubmissionIdStub = sinon
.stub(SubmissionService.prototype, 'getSubmissionAndFeaturesBySubmissionId')
.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionIdWithSecurity')
.throws(new HTTP400('Error', ['Error']));

const requestHandler = index.getSubmissionInformation();
const requestHandler = index.getSubmissionRecordWithSecurity();

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

Expand All @@ -50,30 +50,13 @@ describe('index', () => {

sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

const mockResponse = {
submission: {
submission_id: 1,
uuid: 'string',
security_review_timestamp: null,
submitted_timestamp: 'string',
source_system: 'string',
name: 'string',
description: null,
create_date: 'string',
create_user: 1,
update_date: null,
update_user: null,
revision_count: 1,
security: SECURITY_APPLIED_STATUS.SECURED
},
submissionFeatures: []
};
const mockResponse = { submissionId: 1 } as unknown as SubmissionRecordWithSecurity;

const getSubmissionAndFeaturesBySubmissionIdStub = sinon
.stub(SubmissionService.prototype, 'getSubmissionAndFeaturesBySubmissionId')
.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionIdWithSecurity')
.resolves(mockResponse);

const requestHandler = index.getSubmissionInformation();
const requestHandler = index.getSubmissionRecordWithSecurity();

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

Expand Down
Loading

0 comments on commit d18dc67

Please sign in to comment.