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

Import Observations and See Validation Status/Errors #446

Merged
merged 44 commits into from
Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
df51c4d
file number max, and invalid file type working
anissa-agahchen Aug 9, 2021
c5f587b
Merge branch 'dev' of https://github.com/bcgov/biohubbc into aa-bhbc-…
anissa-agahchen Aug 10, 2021
729465b
Merge branch 'dev' of https://github.com/bcgov/biohubbc into aa-bhbc-…
anissa-agahchen Aug 10, 2021
0b2e3d7
useeffect to fetch status working - with basic alerts
anissa-agahchen Aug 11, 2021
94081d4
Merge branch 'dev' of https://github.com/bcgov/biohubbc into aa-bhbc-…
anissa-agahchen Aug 11, 2021
fd2dfb5
Merge branch 'dev' into aa-bhbc-1233
sdevalapurkar Aug 11, 2021
f6edb79
tweaks
anissa-agahchen Aug 11, 2021
08beba0
Merge branch 'aa-bhbc-1233' of https://github.com/bcgov/biohubbc into…
anissa-agahchen Aug 11, 2021
1747049
first pass end-to-end
anissa-agahchen Aug 12, 2021
235e3ce
fixed broken tests
anissa-agahchen Aug 12, 2021
26b4921
Merge branch 'dev' of https://github.com/bcgov/biohubbc into aa-bhbc-…
anissa-agahchen Aug 12, 2021
bd09ad1
minor
anissa-agahchen Aug 12, 2021
f623132
renders working
anissa-agahchen Aug 15, 2021
d110286
API returns submission data with list of errors if they exist
anissa-agahchen Aug 16, 2021
d67c496
merge conflict
anissa-agahchen Aug 16, 2021
da5b160
clean up
anissa-agahchen Aug 16, 2021
bd2a4c7
clean up
anissa-agahchen Aug 16, 2021
e3fdc8e
Alert box used for statuses
anissa-agahchen Aug 16, 2021
a828bc8
start adding tests
sdevalapurkar Aug 16, 2021
3b550ec
one code smell
sdevalapurkar Aug 16, 2021
1046c38
tests and clean up
sdevalapurkar Aug 16, 2021
1c4f6db
more tests and cleanup
sdevalapurkar Aug 16, 2021
fefb54c
dialog working
anissa-agahchen Aug 16, 2021
5725187
Merge branch 'aa-bhbc-1233' of https://github.com/bcgov/biohubbc into…
anissa-agahchen Aug 16, 2021
04aabd9
camel vs snake
sdevalapurkar Aug 16, 2021
1c5b3d1
fix
sdevalapurkar Aug 17, 2021
487f7a9
fix test and snap
sdevalapurkar Aug 17, 2021
1266d8c
functionality complete
anissa-agahchen Aug 17, 2021
dde5651
Merge branch 'aa-bhbc-1233' of https://github.com/bcgov/biohubbc into…
anissa-agahchen Aug 17, 2021
924ecee
address feedback
anissa-agahchen Aug 17, 2021
364ae46
UI improvements
anissa-agahchen Aug 17, 2021
659f3e4
code smell removed
anissa-agahchen Aug 17, 2021
4b29940
clean up
anissa-agahchen Aug 17, 2021
63f050d
clean up
anissa-agahchen Aug 17, 2021
c0fbbc8
clean up
anissa-agahchen Aug 17, 2021
7ac950c
Merge branch 'dev' of https://github.com/bcgov/biohubbc into aa-bhbc-…
anissa-agahchen Aug 17, 2021
f682dee
working version
anissa-agahchen Aug 18, 2021
0afe38f
Add useInterval hook, minor tweak to progress spinners
NickPhura Aug 18, 2021
901c893
final bits
anissa-agahchen Aug 18, 2021
22d1107
clean up
anissa-agahchen Aug 18, 2021
d0b4804
Merge branch 'dev' into aa-bhbc-1233
sdevalapurkar Aug 18, 2021
d8c061a
Merge branch 'dev' of https://github.com/bcgov/biohubbc into aa-bhbc-…
anissa-agahchen Aug 18, 2021
be2d6dc
more
anissa-agahchen Aug 18, 2021
6f8c6b9
Merge branch 'aa-bhbc-1233' of https://github.com/bcgov/biohubbc into…
anissa-agahchen Aug 18, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('getObservationSubmission', () => {
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj);

try {
const result = observationSubmission.getObservationSubmission();
const result = observationSubmission.getOccurenceSubmission();
await result(
{ ...sampleReq, params: { ...sampleReq.params, surveyId: null } },
(null as unknown) as any,
Expand All @@ -84,27 +84,26 @@ describe('getObservationSubmission', () => {
sinon.stub(survey_occurrence_queries, 'getLatestSurveyOccurrenceSubmissionSQL').returns(null);

try {
const result = observationSubmission.getObservationSubmission();
const result = observationSubmission.getOccurenceSubmission();

await result(sampleReq, (null as unknown) as any, (null as unknown) as any);
expect.fail();
} catch (actualError) {
expect(actualError.status).to.equal(400);
expect(actualError.message).to.equal('Failed to build SQL get statement');
expect(actualError.message).to.equal('Failed to build SQL getLatestSurveyOccurrenceSubmissionSQL statement');
}
});

it('should return an observation submission, on success', async () => {
it('should return an observation submission, on success with no rejected files', async () => {
const mockQuery = sinon.stub();

mockQuery.resolves({
rows: [
{
id: 13,
file_name: 'filename.txt',
create_date: '2020-01-01',
update_date: '',
file_size: 0
file_name: 'dwca_moose.zip',
submission_status_type_name: 'Darwin Core Validated',
message: 'some message'
}
]
});
Expand All @@ -119,13 +118,107 @@ describe('getObservationSubmission', () => {

sinon.stub(survey_occurrence_queries, 'getLatestSurveyOccurrenceSubmissionSQL').returns(SQL`something`);

const result = observationSubmission.getObservationSubmission();
const result = observationSubmission.getOccurenceSubmission();

await result(sampleReq, sampleRes as any, (null as unknown) as any);

expect(actualResult).to.be.eql({
id: 13,
fileName: 'filename.txt'
fileName: 'dwca_moose.zip',
status: 'Darwin Core Validated',
messages: []
});
});

it('should throw a 400 error with rejected files when failed to getOccurrenceSubmissionMessagesSQL', async () => {
const mockQuery = sinon.stub();

mockQuery.resolves({
rows: [
{
id: 13,
file_name: 'dwca_moose.zip',
message: 'some message',
submission_status_type_name: 'Rejected'
}
]
});

sinon.stub(db, 'getDBConnection').returns({
...dbConnectionObj,
systemUserId: () => {
return 20;
},
query: mockQuery
});

sinon.stub(survey_occurrence_queries, 'getLatestSurveyOccurrenceSubmissionSQL').returns(SQL`something`);
sinon.stub(survey_occurrence_queries, 'getOccurrenceSubmissionMessagesSQL').returns(null);

try {
const result = observationSubmission.getOccurenceSubmission();

await result(sampleReq, (null as unknown) as any, (null as unknown) as any);
expect.fail();
} catch (actualError) {
expect(actualError.status).to.equal(400);
expect(actualError.message).to.equal('Failed to build SQL getOccurrenceSubmissionMessagesSQL statement');
}
});

it('should return an observation submission on success, with rejected files', async () => {
const mockQuery = sinon.stub();

mockQuery
.onFirstCall()
.resolves({
rows: [
{
id: 13,
file_name: 'dwca_moose.zip',
message: 'some message',
submission_status_type_name: 'Rejected'
}
]
})
.onSecondCall()
.resolves({
rows: [
{
id: 1,
type: 'type',
status: 'status',
message: 'some error message'
},
{
id: 2,
type: 'type',
status: 'status',
message: 'some other error message'
}
]
});

sinon.stub(db, 'getDBConnection').returns({
...dbConnectionObj,
systemUserId: () => {
return 20;
},
query: mockQuery
});

sinon.stub(survey_occurrence_queries, 'getLatestSurveyOccurrenceSubmissionSQL').returns(SQL`something`);
sinon.stub(survey_occurrence_queries, 'getOccurrenceSubmissionMessagesSQL').returns(SQL`something`);

const result = observationSubmission.getOccurenceSubmission();

await result(sampleReq, sampleRes as any, (null as unknown) as any);

expect(actualResult).to.be.eql({
id: 13,
fileName: 'dwca_moose.zip',
status: 'Rejected',
messages: ['some error message', 'some other error message']
});
});

Expand All @@ -144,7 +237,7 @@ describe('getObservationSubmission', () => {

sinon.stub(survey_occurrence_queries, 'getLatestSurveyOccurrenceSubmissionSQL').returns(SQL`something`);

const result = observationSubmission.getObservationSubmission();
const result = observationSubmission.getOccurenceSubmission();

await result(sampleReq, sampleRes as any, (null as unknown) as any);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import { Operation } from 'express-openapi';
import { SYSTEM_ROLE } from '../../../../../../../constants/roles';
import { getDBConnection } from '../../../../../../../database/db';
import { HTTP400 } from '../../../../../../../errors/CustomError';
import { getLatestSurveyOccurrenceSubmissionSQL } from '../../../../../../../queries/survey/survey-occurrence-queries';
import {
getLatestSurveyOccurrenceSubmissionSQL,
getOccurrenceSubmissionMessagesSQL
} from '../../../../../../../queries/survey/survey-occurrence-queries';
import { getLogger } from '../../../../../../../utils/logger';

const defaultLog = getLogger('/api/project/{projectId}/survey/{surveyId}/observation/submission/get');

export const GET: Operation = [getObservationSubmission()];
export const GET: Operation = [getOccurenceSubmission()];

GET.apiDoc = {
description: 'Fetches an observation submission for a survey.',
description: 'Fetches an observation occurrence submission for a survey.',
tags: ['observation_submission'],
security: [
{
Expand Down Expand Up @@ -50,8 +53,20 @@ GET.apiDoc = {
type: 'number'
},
fileName: {
description: 'The file name of the attachment',
description: 'The file name of the submission',
type: 'string'
},
status: {
description: 'The validation status of the submission',
type: 'string'
},
messages: {
description: 'The validation status messages of the submission',
type: 'array',
items: {
type: 'string',
description: 'A validation status message of the submission'
}
}
}
}
Expand All @@ -76,9 +91,9 @@ GET.apiDoc = {
}
};

export function getObservationSubmission(): RequestHandler {
export function getOccurenceSubmission(): RequestHandler {
return async (req, res) => {
defaultLog.debug({ label: 'Get an observation submission', message: 'params', req_params: req.params });
defaultLog.debug({ label: 'Get an occurrence submission', message: 'params', req_params: req.params });

if (!req.params.surveyId) {
throw new HTTP400('Missing required path param `surveyId`');
Expand All @@ -87,33 +102,62 @@ export function getObservationSubmission(): RequestHandler {
const connection = getDBConnection(req['keycloak_token']);

try {
const getObservationSubmissionSQLStatement = getLatestSurveyOccurrenceSubmissionSQL(Number(req.params.surveyId));
const getOccurrenceSubmissionSQLStatement = getLatestSurveyOccurrenceSubmissionSQL(Number(req.params.surveyId));

if (!getObservationSubmissionSQLStatement) {
throw new HTTP400('Failed to build SQL get statement');
if (!getOccurrenceSubmissionSQLStatement) {
throw new HTTP400('Failed to build SQL getLatestSurveyOccurrenceSubmissionSQL statement');
}

await connection.open();

const observationSubmissionData = await connection.query(
getObservationSubmissionSQLStatement.text,
getObservationSubmissionSQLStatement.values
const occurrenceSubmissionData = await connection.query(
getOccurrenceSubmissionSQLStatement.text,
getOccurrenceSubmissionSQLStatement.values
);

let messageList = [];

if (
occurrenceSubmissionData &&
occurrenceSubmissionData.rows &&
occurrenceSubmissionData.rows[0] &&
occurrenceSubmissionData.rows[0].submission_status_type_name === 'Rejected'
NickPhura marked this conversation as resolved.
Show resolved Hide resolved
) {
const occurrence_submission_id = occurrenceSubmissionData.rows[0].id;
const getSubmissionErrorListSQLStatement = getOccurrenceSubmissionMessagesSQL(Number(occurrence_submission_id));

if (!getSubmissionErrorListSQLStatement) {
throw new HTTP400('Failed to build SQL getOccurrenceSubmissionMessagesSQL statement');
}

const submissionErrorListData = await connection.query(
getSubmissionErrorListSQLStatement.text,
getSubmissionErrorListSQLStatement.values
);

messageList =
(submissionErrorListData &&
submissionErrorListData.rows &&
submissionErrorListData.rows.map((row) => row.message)) ||
[];
}

await connection.commit();

const getObservationSubmissionData =
(observationSubmissionData &&
observationSubmissionData.rows &&
observationSubmissionData.rows[0] && {
id: observationSubmissionData.rows[0].id,
fileName: observationSubmissionData.rows[0].file_name
const getOccurrenceSubmissionData =
(occurrenceSubmissionData &&
occurrenceSubmissionData.rows &&
occurrenceSubmissionData.rows[0] && {
id: occurrenceSubmissionData.rows[0].id,
fileName: occurrenceSubmissionData.rows[0].file_name,
status: occurrenceSubmissionData.rows[0].submission_status_type_name,
messages: messageList
}) ||
null;

return res.status(200).json(getObservationSubmissionData);
return res.status(200).json(getOccurrenceSubmissionData);
} catch (error) {
defaultLog.debug({ label: 'getObservationSubmission', message: 'error', error });
defaultLog.debug({ label: 'getOccurenceSubmission', message: 'error', error });
await connection.rollback();
throw error;
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function uploadMedia(): RequestHandler {

await uploadFileToS3(rawMediaFile, key, metadata);

return res.status(200).send();
return res.status(200).send({ submissionId });
} catch (error) {
defaultLog.debug({ label: 'uploadMedia', message: 'error', error });
await connection.rollback();
Expand Down
65 changes: 64 additions & 1 deletion api/src/queries/survey/survey-occurrence-queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import { describe } from 'mocha';
import {
deleteSurveyOccurrencesSQL,
getLatestSurveyOccurrenceSubmissionSQL,
getOccurrenceSubmissionMessagesSQL,
getSurveyOccurrenceSubmissionSQL,
insertSurveyOccurrenceSubmissionSQL,
insertSurveySubmissionMessageSQL,
insertSurveySubmissionStatusSQL,
updateSurveyOccurrenceSubmissionWithKeySQL
} from './survey-occurrence-queries';

Expand Down Expand Up @@ -83,7 +86,7 @@ describe('updateSurveyOccurrenceSubmissionwithKeySQL', () => {
});

describe('getSurveyOccurrenceSubmissionSQL', () => {
it('returns null response when null submissionId provided', () => {
it('returns null response when null occurrenceSubmissionId provided', () => {
const response = getSurveyOccurrenceSubmissionSQL((null as unknown) as number);

expect(response).to.be.null;
Expand All @@ -95,3 +98,63 @@ describe('getSurveyOccurrenceSubmissionSQL', () => {
expect(response).to.not.be.null;
});
});

describe('insertSurveySubmissionStatusSQL', () => {
it('returns null response when null occurrenceSubmissionId provided', () => {
const response = insertSurveySubmissionStatusSQL((null as unknown) as number, 'type');

expect(response).to.be.null;
});

it('returns null response when null submissionStatusType provided', () => {
const response = insertSurveySubmissionStatusSQL(1, (null as unknown) as string);

expect(response).to.be.null;
});

it('returns non null response when valid params provided', () => {
const response = insertSurveySubmissionStatusSQL(1, 'type');

expect(response).to.not.be.null;
});
});

describe('insertSurveySubmissionMessageSQL', () => {
it('returns null response when null occurrenceSubmissionId provided', () => {
const response = insertSurveySubmissionMessageSQL((null as unknown) as number, 'type', 'message');

expect(response).to.be.null;
});

it('returns null response when null submissionStatusType provided', () => {
const response = insertSurveySubmissionMessageSQL(1, (null as unknown) as string, 'message');

expect(response).to.be.null;
});

it('returns null response when null submissionMessage provided', () => {
const response = insertSurveySubmissionMessageSQL(1, 'type', (null as unknown) as string);

expect(response).to.be.null;
});

it('returns non null response when valid params provided', () => {
const response = insertSurveySubmissionMessageSQL(1, 'type', 'message');

expect(response).to.not.be.null;
});
});

describe('getOccurrenceSubmissionMessagesSQL', () => {
it('returns null response when null occurrenceSubmissionId provided', () => {
const response = getOccurrenceSubmissionMessagesSQL((null as unknown) as number);

expect(response).to.be.null;
});

it('returns non null response when valid params provided', () => {
const response = getOccurrenceSubmissionMessagesSQL(1);

expect(response).to.not.be.null;
});
});
Loading