Skip to content

Commit

Permalink
SIMSBIOHUB-13: Lock Submitted Data and Files (#1022)
Browse files Browse the repository at this point in the history
  • Loading branch information
KjartanE authored May 5, 2023
1 parent 5a0a300 commit f3d3d71
Show file tree
Hide file tree
Showing 29 changed files with 903 additions and 492 deletions.
7 changes: 4 additions & 3 deletions api/src/paths/project/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import sinonChai from 'sinon-chai';
import { SYSTEM_ROLE } from '../../constants/roles';
import * as db from '../../database/db';
import { HTTPError } from '../../errors/http-error';
import { PublishStatus } from '../../repositories/history-publish-repository';
import * as authorization from '../../request-handlers/security/authorization';
import { ProjectService } from '../../services/project-service';
import { getMockDBConnection } from '../../__mocks__/db';
Expand Down Expand Up @@ -84,16 +85,16 @@ describe('list', () => {
end_date: null,
completion_status: 'done'
},
projectSupplementaryData: { has_unpublished_content: true }
projectSupplementaryData: { publishStatus: 'SUBMITTED' }
}
];

const getProjectListStub = sinon
.stub(ProjectService.prototype, 'getProjectList')
.resolves([expectedResponse1[0].projectData]);
const getSurveyHasUnpublishedContentStub = sinon
.stub(ProjectService.prototype, 'doesProjectHaveUnpublishedContent')
.resolves(expectedResponse1[0].projectSupplementaryData.has_unpublished_content);
.stub(ProjectService.prototype, 'projectPublishStatus')
.resolves(PublishStatus.SUBMITTED);

const result = list.getProjectList();

Expand Down
11 changes: 6 additions & 5 deletions api/src/paths/project/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,11 @@ GET.apiDoc = {
},
projectSupplementaryData: {
type: 'object',
required: ['has_unpublished_content'],
required: ['publishStatus'],
properties: {
has_unpublished_content: {
type: 'boolean'
publishStatus: {
type: 'string',
enum: ['NO_DATA', 'UNSUBMITTED', 'SUBMITTED']
}
}
}
Expand Down Expand Up @@ -183,11 +184,11 @@ export function getProjectList(): RequestHandler {

const projectListWithStatus = await Promise.all(
projects.map(async (project: any) => {
const status = await projectService.doesProjectHaveUnpublishedContent(project.id);
const status = await projectService.projectPublishStatus(project.id);

return {
projectData: project,
projectSupplementaryData: { has_unpublished_content: status }
projectSupplementaryData: { publishStatus: status }
};
})
);
Expand Down
7 changes: 4 additions & 3 deletions api/src/paths/project/{projectId}/survey/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sinonChai from 'sinon-chai';
import * as db from '../../../../database/db';
import { HTTPError } from '../../../../errors/http-error';
import { SurveyObject } from '../../../../models/survey-view';
import { PublishStatus } from '../../../../repositories/history-publish-repository';
import { SurveyService } from '../../../../services/survey-service';
import { getMockDBConnection } from '../../../../__mocks__/db';
import * as surveys from './list';
Expand Down Expand Up @@ -74,8 +75,8 @@ describe('survey list', () => {
.resolves(({ survey_details: { id: 1 } } as unknown) as SurveyObject);

const getSurveysPublishStub = sinon
.stub(SurveyService.prototype, 'doesSurveyHaveUnpublishedContent')
.resolves(true);
.stub(SurveyService.prototype, 'surveyPublishStatus')
.resolves(PublishStatus.SUBMITTED);

const sampleReq = {
keycloak_token: {},
Expand All @@ -88,7 +89,7 @@ describe('survey list', () => {
const expectedResponse = [
{
surveyData: { survey_details: { id: 1 } },
surveySupplementaryData: { has_unpublished_content: true }
surveySupplementaryData: { publishStatus: 'SUBMITTED' }
}
];

Expand Down
11 changes: 6 additions & 5 deletions api/src/paths/project/{projectId}/survey/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,11 @@ GET.apiDoc = {
},
surveySupplementaryData: {
type: 'object',
required: ['has_unpublished_content'],
required: ['publishStatus'],
properties: {
has_unpublished_content: {
type: 'boolean'
publishStatus: {
type: 'string',
enum: ['NO_DATA', 'UNSUBMITTED', 'SUBMITTED']
}
}
}
Expand Down Expand Up @@ -360,11 +361,11 @@ export function getSurveyList(): RequestHandler {
const surveys = await Promise.all(
surveyIds.map(async (surveyId) => {
const survey = await surveyService.getSurveyById(surveyId);
const surveyPublishStatus = await surveyService.doesSurveyHaveUnpublishedContent(surveyId);
const surveyPublishStatus = await surveyService.surveyPublishStatus(surveyId);

return {
surveyData: survey,
surveySupplementaryData: { has_unpublished_content: surveyPublishStatus }
surveySupplementaryData: { publishStatus: surveyPublishStatus }
};
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe('getSummarySubmission', () => {
it('should return null if the survey has no summary submission, on success', async () => {
const mockQuery = sinon.stub();

mockQuery.resolves({ rows: undefined });
mockQuery.resolves({ rows: [] });

sinon.stub(db, 'getDBConnection').returns({
...dbConnectionObj,
Expand Down
92 changes: 92 additions & 0 deletions api/src/paths/publish/project.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import Ajv from 'ajv';
import chai, { expect } from 'chai';
import { describe } from 'mocha';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import * as db from '../../database/db';
import { HTTPError } from '../../errors/http-error';
import { PlatformService } from '../../services/platform-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db';
import { POST, publishProject } from './project';

chai.use(sinonChai);

describe('project', () => {
describe('openapi schema', () => {
const ajv = new Ajv();

it('is valid openapi v3 schema', () => {
expect(ajv.validateSchema((POST.apiDoc as unknown) as object)).to.be.true;
});
});

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

it('submits selected data to biohub', async () => {
const dbConnectionObj = getMockDBConnection();

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

sinon.stub(PlatformService.prototype, 'submitProjectDataToBioHub').resolves({ uuid: 'test-uuid' });

const sampleReq = {
keycloak_token: {},
body: {
projectId: 1,
surveyId: 1,
data: {
observations: [],
summary: [],
reports: [],
attachments: []
}
},
params: {}
} as any;

let actualResult: any = null;
const sampleRes = {
status: () => {
return {
json: (response: any) => {
actualResult = response;
}
};
}
};

const { mockNext } = getRequestHandlerMocks();

const requestHandler = publishProject();

await requestHandler(sampleReq, (sampleRes as unknown) as any, mockNext);

expect(actualResult).to.eql({ uuid: 'test-uuid' });
});

it('catches error, calls rollback, and re-throws error', async () => {
const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() });

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

sinon.stub(PlatformService.prototype, 'submitProjectDataToBioHub').rejects(new Error('a test error'));

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

try {
const requestHandler = publishProject();

await requestHandler(mockReq, mockRes, mockNext);
expect.fail();
} catch (actualError) {
expect(dbConnectionObj.rollback).to.have.been.called;
expect(dbConnectionObj.release).to.have.been.called;

expect((actualError as HTTPError).message).to.equal('a test error');
}
});
});
});
64 changes: 32 additions & 32 deletions api/src/repositories/history-publish-repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,59 +518,59 @@ describe('HistoryPublishRepository', () => {
});
});

describe('getCountSurveyUnpublishedAttachments', () => {
it('should return a positive count of unpublished survey attachments if exists', async () => {
describe('getSurveyAttachmentsWithPublishData', () => {
it('should return survey attachments with publish data if exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 1, rows: [{ count: 1 }] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const surveyId = 1;
const response = await repository.getCountSurveyUnpublishedAttachments(surveyId);
const response = await repository.getSurveyAttachmentsWithPublishData(surveyId);

expect(response.rows[0]).to.be.eql({ count: 1 });
expect(response).to.be.eql([{ count: 1 }]);
});

it('should return a count of 0 if no unpublished survey_attachment record exists', async () => {
it('should return empty array if no survey_attachment record exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 0, rows: [{ count: 0 }] } as any) as Promise<QueryResult<any>>)
sql: async () => (({ rowCount: 0, rows: [] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const surveyId = 1;
const response = await repository.getCountSurveyUnpublishedAttachments(surveyId);
const response = await repository.getSurveyAttachmentsWithPublishData(surveyId);

expect(response.rows[0]).to.be.eql({ count: 0 });
expect(response).to.be.eql([]);
});
});

describe('getCountSurveyUnpublishedReports', () => {
it('should return a positive count of unpublished survey reports if exists', async () => {
describe('getSurveyReportsWithPublishData', () => {
it('should return survey report attachments with publish data if exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 1, rows: [{ count: 1 }] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const surveyId = 1;
const response = await repository.getCountSurveyUnpublishedReports(surveyId);
const response = await repository.getSurveyReportsWithPublishData(surveyId);

expect(response.rows[0]).to.be.eql({ count: 1 });
expect(response).to.be.eql([{ count: 1 }]);
});

it('should return a count of 0 if no unpublished survey_report_attachment record exists', async () => {
it('should return empty array if no survey_report_attachment record exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 0, rows: [{ count: 0 }] } as any) as Promise<QueryResult<any>>)
sql: async () => (({ rowCount: 0, rows: [] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const surveyId = 1;
const response = await repository.getCountSurveyUnpublishedReports(surveyId);
const response = await repository.getSurveyReportsWithPublishData(surveyId);

expect(response.rows[0]).to.be.eql({ count: 0 });
expect(response).to.be.eql([]);
});
});

Expand Down Expand Up @@ -709,59 +709,59 @@ describe('HistoryPublishRepository', () => {
});
});

describe('getCountProjectUnpublishedAttachments', () => {
it('should return a positive count of unpublished survey attachments if exists', async () => {
describe('getProjectAttachmentsWithPublishData', () => {
it('should return project attachments with publish data if exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 1, rows: [{ count: 1 }] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const projectId = 1;
const response = await repository.getCountProjectUnpublishedAttachments(projectId);
const response = await repository.getProjectAttachmentsWithPublishData(projectId);

expect(response.rows[0]).to.be.eql({ count: 1 });
expect(response).to.be.eql([{ count: 1 }]);
});

it('should return a count of 0 if no unpublished project_attachment record exists', async () => {
it('should return empty array if no project_attachment record exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 0, rows: [{ count: 0 }] } as any) as Promise<QueryResult<any>>)
sql: async () => (({ rowCount: 0, rows: [] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const projectId = 1;
const response = await repository.getCountProjectUnpublishedAttachments(projectId);
const response = await repository.getProjectAttachmentsWithPublishData(projectId);

expect(response.rows[0]).to.be.eql({ count: 0 });
expect(response).to.be.eql([]);
});
});

describe('getCountProjectUnpublishedReports', () => {
it('should return a positive count of unpublished project reports if exists', async () => {
describe('getProjectReportsWithPublishData', () => {
it('should return project report attachments with publish data if exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 1, rows: [{ count: 1 }] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const projectId = 1;
const response = await repository.getCountProjectUnpublishedReports(projectId);
const response = await repository.getProjectReportsWithPublishData(projectId);

expect(response.rows[0]).to.be.eql({ count: 1 });
expect(response).to.be.eql([{ count: 1 }]);
});

it('should return a count of 0 if no unpublished survey_report_attachment record exists', async () => {
it('should return empty array if no project_report_attachment record exists', async () => {
const mockConnection = getMockDBConnection({
sql: async () => (({ rowCount: 0, rows: [{ count: 0 }] } as any) as Promise<QueryResult<any>>)
sql: async () => (({ rowCount: 0, rows: [] } as any) as Promise<QueryResult<any>>)
});

const repository = new HistoryPublishRepository(mockConnection);

const projectId = 1;
const response = await repository.getCountProjectUnpublishedReports(projectId);
const response = await repository.getProjectReportsWithPublishData(projectId);

expect(response.rows[0]).to.be.eql({ count: 0 });
expect(response).to.be.eql([]);
});
});
});
Loading

0 comments on commit f3d3d71

Please sign in to comment.