Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BHBC-2140: Release to Test #937

Merged
merged 6 commits into from
Feb 8, 2023
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
12,525 changes: 12,511 additions & 14 deletions api/package-lock.json

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions api/src/json-schema/validation-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ export const submissionValidationSchema = {
title: 'Column Validation',
description: 'The validators that can be applied against a column within a file/sheet.',
anyOf: [
{
$ref: '#/$defs/column_required_validator'
},
{
$ref: '#/$defs/column_format_validator'
},
Expand Down Expand Up @@ -331,6 +334,25 @@ export const submissionValidationSchema = {
},
additionalProperties: false
},
column_required_validator: {
description: 'Validates that this column value is not empty',
type: 'object',
properties: {
column_required_validator: {
type: 'object',
properties: {
name: {
type: 'string'
},
description: {
type: 'string'
}
},
additionalProperties: false
}
},
additionalProperties: false
},
column_format_validator: {
description: 'Validates that this column value matches a regex',
type: 'object',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ describe('getObservationSubmission', () => {
};

const response = requestValidator.validateRequest(request);
console.log(JSON.stringify(response));
expect(response.status).to.equal(400);
expect(response.errors.length).to.equal(1);
expect(response.errors[0].path).to.equal('projectId');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ export function getOccurrenceSubmission(): RequestHandler {
[
// Submission statuses for validation completion
SUBMISSION_STATUS_TYPE.TEMPLATE_VALIDATED,
SUBMISSION_STATUS_TYPE.DARWIN_CORE_VALIDATED
SUBMISSION_STATUS_TYPE.DARWIN_CORE_VALIDATED,
SUBMISSION_STATUS_TYPE.TEMPLATE_TRANSFORMED
].includes(occurrenceSubmission.submission_status_type_name));

const messageTypes: IMessageTypeGroup[] = willFetchAdditionalMessages
Expand Down
34 changes: 0 additions & 34 deletions api/src/paths/xlsx/process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,11 @@ describe('xlsx/process', () => {
mockReq['keycloak_token'] = 'token';

const processXLSXFileStub = sinon.stub(ValidationService.prototype, 'processXLSXFile').resolves();
const processDWCFileStub = sinon.stub(ValidationService.prototype, 'processDWCFile').resolves();

const requestHandler = process.processFile();
await requestHandler(mockReq, mockRes, mockNext);
expect(mockRes.statusValue).to.equal(200);
expect(processXLSXFileStub).to.have.been.calledOnceWith(mockReq.body.occurrence_submission_id);
expect(processDWCFileStub).to.have.been.calledOnceWith(mockReq.body.occurrence_submission_id);
expect(mockRes.jsonValue).to.eql({ status: 'success' });
});

Expand Down Expand Up @@ -209,38 +207,6 @@ describe('xlsx/process', () => {
}
});

it('catches an error on processDWCFile', async () => {
const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() });
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

const processXLSXFileStub = sinon.stub(ValidationService.prototype, 'processXLSXFile').resolves();
const processDWCFileStub = sinon
.stub(ValidationService.prototype, 'processDWCFile')
.throws(new Error('test processDWCFile error'));
const errorServiceStub = sinon.stub(ErrorService.prototype, 'insertSubmissionStatus').resolves();

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();
mockReq['keycloak_token'] = 'token';

mockReq.body = {
occurrence_submission_id: '123-456-789'
};

const requestHandler = process.processFile();

try {
await requestHandler(mockReq, mockRes, mockNext);
expect.fail();
} catch (actualError) {
expect(processXLSXFileStub).to.have.been.calledOnceWith(mockReq.body.occurrence_submission_id);
expect(processDWCFileStub).to.have.been.calledOnceWith(mockReq.body.occurrence_submission_id);
expect(errorServiceStub).to.have.been.calledOnce;
expect(dbConnectionObj.rollback).to.have.been.calledOnce;
expect(dbConnectionObj.release).to.have.been.calledOnce;
expect((actualError as Error).message).to.equal('test processDWCFile error');
}
});

it('catches an error on insertSubmissionStatus', async () => {
const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() });
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);
Expand Down
15 changes: 2 additions & 13 deletions api/src/paths/xlsx/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { authorizeRequestHandler } from '../../request-handlers/security/authori
import { ErrorService } from '../../services/error-service';
import { ValidationService } from '../../services/validation-service';
import { getLogger } from '../../utils/logger';
import { SubmissionError } from '../../utils/submission-error';

const defaultLog = getLogger('paths/xlsx/process');

Expand Down Expand Up @@ -113,18 +112,8 @@ export function processFile(): RequestHandler {

const validationService = new ValidationService(connection);

try {
// process the raw template data
await validationService.processXLSXFile(submissionId, surveyId);
// process the resulting transformed dwc data
await validationService.processDWCFile(submissionId);
} catch (error: any) {
// Since submission errors are caught by the validation service and persisted in the database, anything
// outside of a submission message should be thrown here.
if (!(error instanceof SubmissionError)) {
throw error;
}
}
// process the raw template data
await validationService.processXLSXFile(submissionId, surveyId);

await connection.commit();
} catch (error) {
Expand Down
40 changes: 4 additions & 36 deletions api/src/services/dwc-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ import chai, { expect } from 'chai';
import { describe } from 'mocha';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { ApiError } from '../errors/api-error';
import { IOccurrenceSubmission } from '../repositories/occurrence-repository';
import * as spatial_utils from '../utils/spatial-utils';
import { getMockDBConnection } from '../__mocks__/db';
import { DwCService } from './dwc-service';
import { OccurrenceService } from './occurrence-service';
import { TaxonomyService } from './taxonomy-service';
chai.use(sinonChai);

Expand Down Expand Up @@ -117,26 +114,6 @@ describe('DwCService', () => {
expect(newJson).to.eql(jsonObject);
});

it('throws an error if veratimCoordinates cannot be parsed ', async () => {
const dbConnectionObj = getMockDBConnection();

const dwcService = new DwCService(dbConnectionObj);
const jsonObject = {
item_with_depth_1: {
item_with_depth_2: [{ verbatimCoordinates: '12 12314 12241' }]
}
};

sinon.stub(spatial_utils, 'parseUTMString').returns(null);

try {
await dwcService.decorateLatLong(jsonObject);
expect.fail();
} catch (actualError) {
expect((actualError as ApiError).message).to.equal('Failed to parse UTM String');
}
});

it('succeeds and decorates Lat Long', async () => {
const dbConnectionObj = getMockDBConnection();

Expand Down Expand Up @@ -167,7 +144,8 @@ describe('DwCService', () => {
});
});

describe('decorateDWCASourceData', () => {
//TODO: this needs to be examined thoroughly
describe('decorateDwCJSON', () => {
afterEach(() => {
sinon.restore();
});
Expand All @@ -177,28 +155,18 @@ describe('DwCService', () => {

const dwcService = new DwCService(dbConnectionObj);

const getOccurrenceSubmissionStub = sinon
.stub(OccurrenceService.prototype, 'getOccurrenceSubmission')
.resolves(({ id: 1, darwin_core_source: { id: 2 } } as unknown) as IOccurrenceSubmission);

const decorateLatLongStub = sinon
.stub(DwCService.prototype, 'decorateLatLong')
.resolves({ id: 2, lat: 1, long: 2 });
const decorateTaxonIDsStub = sinon
.stub(DwCService.prototype, 'decorateTaxonIDs')
.resolves({ id: 2, lat: 1, long: 2, taxonID: 3 });

const updateDWCSourceForOccurrenceSubmissionStub = sinon
.stub(OccurrenceService.prototype, 'updateDWCSourceForOccurrenceSubmission')
.resolves(1);

const response = await dwcService.decorateDWCASourceData(1);
const response = await dwcService.decorateDwCJSON({});

expect(response).to.eql(true);
expect(getOccurrenceSubmissionStub).to.be.calledOnce;
expect(response).to.eql({ id: 2, lat: 1, long: 2, taxonID: 3 });
expect(decorateLatLongStub).to.be.calledOnce;
expect(decorateTaxonIDsStub).to.be.calledOnce;
expect(updateDWCSourceForOccurrenceSubmissionStub).to.be.calledOnce;
});
});
});
29 changes: 6 additions & 23 deletions api/src/services/dwc-service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import jsonpatch, { Operation } from 'fast-json-patch';
import { JSONPath } from 'jsonpath-plus';
import { IDBConnection } from '../database/db';
import { ApiError, ApiErrorType } from '../errors/api-error';
import { parseUTMString, utmToLatLng } from '../utils/spatial-utils';
import { DBService } from './db-service';
import { OccurrenceService } from './occurrence-service';
import { TaxonomyService } from './taxonomy-service';
/**
* Service to produce DWC data for a project.
Expand Down Expand Up @@ -72,27 +70,18 @@ export class DwCService extends DBService {
}

/**
* Run all Decoration functions on DWCA Source Data
* Decorates the DwC json object
*
* @param {number} occurrenceSubmissionId
* @return {*} {Promise<boolean>}
* @param {Record<any, any>} jsonObject
* @return {*} {Promise<Record<any, any>>}
* @memberof DwCService
*/
async decorateDWCASourceData(occurrenceSubmissionId: number): Promise<boolean> {
const occurrenceService = new OccurrenceService(this.connection);

const submission = await occurrenceService.getOccurrenceSubmission(occurrenceSubmissionId);
const jsonObject = submission.darwin_core_source;

async decorateDwCJSON(jsonObject: Record<any, any>): Promise<Record<any, any>> {
const latlongDec = await this.decorateLatLong(jsonObject);
const taxonAndLatLongDec = await this.decorateTaxonIDs(latlongDec);

const response = await occurrenceService.updateDWCSourceForOccurrenceSubmission(
occurrenceSubmissionId,
JSON.stringify(taxonAndLatLongDec)
);
const taxonDec = await this.decorateTaxonIDs(latlongDec);

return !!response;
return taxonDec;
}

/**
Expand All @@ -110,7 +99,6 @@ export class DwCService extends DBService {
});

const patchOperations: Operation[] = [];
const errors: string[] = [];

pathsToPatch.forEach(async (item: any) => {
if (
Expand All @@ -125,7 +113,6 @@ export class DwCService extends DBService {
const verbatimCoordinates = parseUTMString(item.value['verbatimCoordinates']);

if (!verbatimCoordinates) {
errors.push('Failed to parse UTM String');
return;
}

Expand All @@ -146,10 +133,6 @@ export class DwCService extends DBService {
patchOperations.push(decimalLatitudePatch, decimalLongitudePatch);
});

if (errors.length) {
throw new ApiError(ApiErrorType.UNKNOWN, errors.join());
}

return jsonpatch.applyPatch(jsonObject, patchOperations).newDocument;
}
}
4 changes: 2 additions & 2 deletions api/src/services/occurrence-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ describe('OccurrenceService', () => {
});
});

describe('updateSurveyOccurrenceSubmission', () => {
describe('updateSurveyOccurrenceSubmissionWithOutputKey', () => {
it('should return a submission id', async () => {
const service = mockService();
sinon.stub(OccurrenceRepository.prototype, 'updateSurveyOccurrenceSubmissionWithOutputKey').resolves({});

const result = await service.updateSurveyOccurrenceSubmission(1, 'file name', 'key');
const result = await service.updateSurveyOccurrenceSubmissionWithOutputKey(1, 'file name', 'key');
expect(result).to.be.eql({});
});
});
Expand Down
6 changes: 5 additions & 1 deletion api/src/services/occurrence-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ export class OccurrenceService extends DBService {
* @param {string} key
* @return {*} {Promise<any>}
*/
async updateSurveyOccurrenceSubmission(submissionId: number, fileName: string, key: string): Promise<any> {
async updateSurveyOccurrenceSubmissionWithOutputKey(
submissionId: number,
fileName: string,
key: string
): Promise<any> {
return this.occurrenceRepository.updateSurveyOccurrenceSubmissionWithOutputKey(submissionId, fileName, key);
}

Expand Down
Loading