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-1962-1963: Validation refactoring and error handling #828

Merged
merged 114 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
d13b876
added console logs and stubbing files out
AlfredRosenthal Sep 30, 2022
f28c95c
Merge branch 'dev' into BHBC-1963
AlfredRosenthal Sep 30, 2022
b4c51dc
improved error codes
anissa-agahchen Sep 30, 2022
03df104
stubbed out occurrence service and repo
AlfredRosenthal Sep 30, 2022
083b524
S3 and file prep
AlfredRosenthal Oct 3, 2022
8c571c7
new functions to validation service, validation repo stubbed out
AlfredRosenthal Oct 3, 2022
6e3aba6
small POC of what the error handling could look like
anissa-agahchen Oct 3, 2022
b5dcbd1
submission service repo added
AlfredRosenthal Oct 3, 2022
9359309
finished stubing out functions
AlfredRosenthal Oct 4, 2022
40a9321
getting working started
AlfredRosenthal Oct 4, 2022
4d9f41a
further in process
AlfredRosenthal Oct 4, 2022
2a640af
happy path working
AlfredRosenthal Oct 4, 2022
d71cd91
more clean up, moved more code around
AlfredRosenthal Oct 5, 2022
1ec867e
clean up
AlfredRosenthal Oct 5, 2022
b33f74d
converted all error codes from hard-coded to coming from the submissi…
anissa-agahchen Oct 5, 2022
4d67960
updated transform, validate and process endpoints
AlfredRosenthal Oct 5, 2022
7a68e99
fixed view occurrence endpoint
AlfredRosenthal Oct 5, 2022
3cf404b
scrape occurrences is wired up with new service
AlfredRosenthal Oct 5, 2022
25ca83a
database migration to add more submission status and message types
anissa-agahchen Oct 5, 2022
63696e7
fix broken tests
anissa-agahchen Oct 5, 2022
ef8fc47
tweak
anissa-agahchen Oct 5, 2022
0047f9c
updated dwc validation endpoint
AlfredRosenthal Oct 5, 2022
b74d827
ran format and ling fix
AlfredRosenthal Oct 5, 2022
e091568
clenaed up endpoints for better connection handling
AlfredRosenthal Oct 6, 2022
2c0ebae
fixed dwc parsing
AlfredRosenthal Oct 6, 2022
5ee52d0
added comments to new service
AlfredRosenthal Oct 6, 2022
8df6607
started writing tests
AlfredRosenthal Oct 6, 2022
772f6e1
occurrence repo tests added
AlfredRosenthal Oct 6, 2022
87f87a2
ran lint and format fix
AlfredRosenthal Oct 6, 2022
a6bc8c0
added validation repo test
AlfredRosenthal Oct 6, 2022
adba47b
submission repo tests added
AlfredRosenthal Oct 6, 2022
99bda48
Merge branch 'BHBC-1963' into BHBC-1962
AlfredRosenthal Oct 6, 2022
bb4ed93
fixed import errors
AlfredRosenthal Oct 6, 2022
4b9cc95
stubbed out try catch on process
AlfredRosenthal Oct 11, 2022
2f90ec9
updated message types and submission types
anissa-agahchen Oct 11, 2022
d097788
updated exception throws
AlfredRosenthal Oct 11, 2022
d6bb2b8
conflict resolved
AlfredRosenthal Oct 11, 2022
44cb0a0
fixed errors
AlfredRosenthal Oct 11, 2022
1f16c7e
clean up errors
AlfredRosenthal Oct 11, 2022
a738382
tempalte scrape and upload wrapped in trycatch
AlfredRosenthal Oct 11, 2022
35f7b01
added more trycatches for process steps
AlfredRosenthal Oct 11, 2022
e77ed9c
added throws to s3 functions
AlfredRosenthal Oct 11, 2022
18a5d9c
fixing resent header issue
AlfredRosenthal Oct 11, 2022
1e735ab
wrapping prep work into function
AlfredRosenthal Oct 11, 2022
06f7df2
stubbing out error class
AlfredRosenthal Oct 11, 2022
604d3da
added new message objects to throw instead
AlfredRosenthal Oct 12, 2022
8c178df
updating validation results fucntion
AlfredRosenthal Oct 12, 2022
1854127
updated migrations and message throws for prep
AlfredRosenthal Oct 12, 2022
9127a30
updated migrations with new errors
AlfredRosenthal Oct 12, 2022
b71ee8f
updating enum
AlfredRosenthal Oct 12, 2022
fd6ed8e
replaced enum
AlfredRosenthal Oct 12, 2022
ee185cf
clean up
AlfredRosenthal Oct 12, 2022
6e8b4f3
all new errors added
AlfredRosenthal Oct 12, 2022
84f6254
fixing parent try catches
AlfredRosenthal Oct 12, 2022
7026a44
got the rejected status displaying
anissa-agahchen Oct 12, 2022
8922717
merge conflicts
anissa-agahchen Oct 12, 2022
ca88886
test setup
AlfredRosenthal Oct 12, 2022
4ba5528
updated endpoints with new error catching
AlfredRosenthal Oct 12, 2022
2fead93
all message types are accounted for in app
anissa-agahchen Oct 12, 2022
2ba18e9
front-end changes
anissa-agahchen Oct 12, 2022
138f46c
fixing up dwc endpoint
AlfredRosenthal Oct 12, 2022
8ae9d7a
pulled changes
AlfredRosenthal Oct 12, 2022
38af83b
submission status working
AlfredRosenthal Oct 12, 2022
9511ef2
ran lint fix
AlfredRosenthal Oct 12, 2022
693592f
updated more throws
AlfredRosenthal Oct 12, 2022
53ddc24
fixed content validation error
AlfredRosenthal Oct 13, 2022
7c40d49
fix front-end issue
anissa-agahchen Oct 13, 2022
2228046
fixed bug
anissa-agahchen Oct 13, 2022
0b1ef16
updated logic for transform state and verbage
AlfredRosenthal Oct 13, 2022
a8135ce
making tests compile
anissa-agahchen Oct 13, 2022
0448458
merge with dev
anissa-agahchen Oct 13, 2022
b576e4c
text change
anissa-agahchen Oct 13, 2022
0a53398
skip broken tests
anissa-agahchen Oct 13, 2022
ce73abf
tested parse XLSX
AlfredRosenthal Oct 13, 2022
5266216
more tests
AlfredRosenthal Oct 14, 2022
27ae885
testing template validation
AlfredRosenthal Oct 14, 2022
a5a86e2
update to user errors
anissa-agahchen Oct 14, 2022
7f1e2e4
improvements to use error
anissa-agahchen Oct 14, 2022
0d802f8
pulled changes
AlfredRosenthal Oct 14, 2022
9c233a3
fixed spelling mistakes
AlfredRosenthal Oct 14, 2022
2d161e4
validated persisted validation results
AlfredRosenthal Oct 14, 2022
9ac0c3c
fixing format, fixed spelling mistakes
AlfredRosenthal Oct 14, 2022
3218d64
get schema tested
AlfredRosenthal Oct 14, 2022
7b5e811
transformation schema tested
AlfredRosenthal Oct 14, 2022
91ce7d8
44% covered
AlfredRosenthal Oct 14, 2022
7777e07
prep dwc archive tested
AlfredRosenthal Oct 14, 2022
bd21df6
dwc/validate tests
anissa-agahchen Oct 14, 2022
a173872
merge conflicts
anissa-agahchen Oct 14, 2022
b6841bf
persist transformation tested
AlfredRosenthal Oct 14, 2022
35496c3
xlsx/validate
anissa-agahchen Oct 14, 2022
778c7e0
xlsx/process and xlsx/trasnform
anissa-agahchen Oct 15, 2022
5b1b922
merged changes
AlfredRosenthal Oct 15, 2022
b1845d6
more
AlfredRosenthal Oct 15, 2022
eaf32d7
65% coverage
AlfredRosenthal Oct 17, 2022
813303c
error-service tests
anissa-agahchen Oct 17, 2022
0ad22fe
validate file tested
AlfredRosenthal Oct 17, 2022
c08a7df
Merge branch 'BHBC-1962' of https://github.com/bcgov/biohubbc into BH…
AlfredRosenthal Oct 17, 2022
32395a5
finish coverage for repo files
KjartanE Oct 17, 2022
bfac311
Merge branch 'BHBC-1962' of https://github.com/bcgov/biohubbc into BH…
KjartanE Oct 17, 2022
512fbdc
pulled changes, added dwc process test
AlfredRosenthal Oct 17, 2022
5a499ec
process file tested
AlfredRosenthal Oct 17, 2022
5c30507
validate dwc tested
AlfredRosenthal Oct 17, 2022
2759fca
last function tested
AlfredRosenthal Oct 17, 2022
2ea15e0
s3key for biohub
anissa-agahchen Oct 17, 2022
f9bfd8a
merge conflicts
anissa-agahchen Oct 17, 2022
f819a94
removed some code smells
AlfredRosenthal Oct 17, 2022
b1ef792
Merge branch 'BHBC-1962' of https://github.com/bcgov/biohubbc into BH…
AlfredRosenthal Oct 17, 2022
cc41f76
fixed tests
anissa-agahchen Oct 17, 2022
ab6ba64
merge conflicts
anissa-agahchen Oct 17, 2022
aa31fa8
fixed more code smells
AlfredRosenthal Oct 17, 2022
db64126
remove coments
anissa-agahchen Oct 17, 2022
a8f8484
fixed various code smells
AlfredRosenthal Oct 17, 2022
29cc0de
fixed the last code smell
AlfredRosenthal Oct 17, 2022
8102a3d
ran lint fix
AlfredRosenthal Oct 17, 2022
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,568 changes: 14 additions & 12,554 deletions api/package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import multer from 'multer';
import { OpenAPIV3 } from 'openapi-types';
import swaggerUIExperss from 'swagger-ui-express';
import { defaultPoolConfig, initDBPool } from './database/db';
import { ensureHTTPError, HTTPErrorType } from './errors/custom-error';
import { ensureHTTPError, HTTPErrorType } from './errors/http-error';
import { rootAPIDoc } from './openapi/root-api-doc';
import { authenticateRequest } from './request-handlers/security/authentication';
import { getLogger } from './utils/logger';
Expand Down Expand Up @@ -88,6 +88,11 @@ const openAPIFramework = initialize({
// If `next` is not included express will silently skip calling the `errorMiddleware` entirely.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
errorMiddleware: function (error, req, res, next) {
if (res.headersSent) {
// response has already been sent
return;
}

// Ensure all errors (intentionally thrown or not) are in the same format as specified by the schema
const httpError = ensureHTTPError(error);

Expand Down
3 changes: 2 additions & 1 deletion api/src/constants/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
export enum SYSTEM_IDENTITY_SOURCE {
DATABASE = 'DATABASE',
IDIR = 'IDIR',
BCEID = 'BCEID'
BCEID = 'BCEID',
SYSTEM = 'SYSTEM'
}

export enum SCHEMAS {
Expand Down
40 changes: 39 additions & 1 deletion api/src/constants/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,43 @@ export enum SUBMISSION_STATUS_TYPE {
'AWAITING CURRATION' = 'Awaiting Curration',
'REJECTED' = 'Rejected',
'ON HOLD' = 'On Hold',
'SYSTEM_ERROR' = 'System Error'
'SYSTEM_ERROR' = 'System Error',

//Failure
'FAILED_OCCURRENCE_PREPARATION' = 'Failed to prepare submission',
'INVALID_MEDIA' = 'Media is not valid',
'FAILED_VALIDATION' = 'Failed to validate',
'FAILED_TRANSFORMED' = 'Failed to transform',
'FAILED_PROCESSING_OCCURRENCE_DATA' = 'Failed to process occurrence data'
}

export enum SUBMISSION_MESSAGE_TYPE {
//message types that match the submission_message_type table
'DUPLICATE_HEADER' = 'Duplicate header',
'UNKNOWN_HEADER' = 'Unknown Header',
'MISSING_REQUIRED_HEADER' = 'Missing Required Header',
'MISSING_RECOMMENDED_HEADER' = 'Missing Recommended Header',
'MISCELLANEOUS' = 'Miscellaneous',
'MISSING_REQUIRED_FIELD' = 'Missing Required Field',
'UNEXPECTED_FORMAT' = 'Unexpected Format',
'OUT_OF_RANGE' = 'Out of Range',
'INVALID_VALUE' = 'Invalid Value',
'MISSING_VALIDATION_SCHEMA' = 'Missing Validation Schema',
'FAILED_GET_OCCURRENCE' = 'Failed to Get Occurrence Submission',
'FAILED_GET_FILE_FROM_S3' = 'Failed to get file from S3',
'FAILED_UPLOAD_FILE_TO_S3' = 'Failed to upload file to S3',
'FAILED_PARSE_SUBMISSION' = 'Failed to parse submission',
'FAILED_PREP_DWC_ARCHIVE' = 'Failed to prep DarwinCore Archive',
'FAILED_PREP_XLSX' = 'Failed to prep XLSX',
'FAILED_PERSIST_PARSE_ERRORS' = 'Failed to persist parse errors',
'FAILED_GET_VALIDATION_RULES' = 'Failed to get validation rules',
'FAILED_GET_TRANSFORMATION_RULES' = 'Failed to get transformation rules',
'FAILED_PERSIST_TRANSFORMATION_RESULTS' = 'Failed to persist transformation results',
'FAILED_TRANSFORM_XLSX' = 'Failed to transform XLSX',
'FAILED_VALIDATE_DWC_ARCHIVE' = 'Failed to validate DarwinCore Archive',
'FAILED_PERSIST_VALIDATION_RESULTS' = 'Failed to persist validation results',
'FAILED_UPDATE_OCCURRENCE_SUBMISSION' = 'Failed to update occurrence submission',
'FAILED_TO_GET_TRANSFORM_SCHEMA' = 'Unable to get transform schema for submission',
'INVALID_MEDIA' = 'Media is invalid',
'UNSUPPORTED_FILE_TYPE' = 'File submitted is not a supported type'
}
2 changes: 1 addition & 1 deletion api/src/database/db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as pg from 'pg';
import Sinon from 'sinon';
import SQL from 'sql-template-strings';
import { SYSTEM_IDENTITY_SOURCE } from '../constants/database';
import { HTTPError } from '../errors/custom-error';
import { HTTPError } from '../errors/http-error';
import { setSystemUserContextSQL } from '../queries/database/user-context-queries';
import * as db from './db';
import { getAPIUserDBConnection, getDBConnection, getDBPool, getKnex, IDBConnection, initDBPool } from './db';
Expand Down
2 changes: 1 addition & 1 deletion api/src/database/db.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import knex, { Knex } from 'knex';
import * as pg from 'pg';
import { SQLStatement } from 'sql-template-strings';
import { ApiExecuteSQLError, ApiGeneralError } from '../errors/custom-error';
import { ApiExecuteSQLError, ApiGeneralError } from '../errors/api-error';
import { queries } from '../queries/queries';
import { getUserIdentifier, getUserIdentitySource } from '../utils/keycloak-utils';
import { getLogger } from '../utils/logger';
Expand Down
25 changes: 25 additions & 0 deletions api/src/errors/api-error.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { expect } from 'chai';
import { describe } from 'mocha';
import { ApiErrorType, ApiExecuteSQLError, ApiGeneralError, ApiUnknownError } from './api-error';

describe('ApiError', () => {
describe('No error value provided', () => {
let message: string;

before(() => {
message = 'response message';
});

it('Creates Api General error', function () {
expect(new ApiGeneralError(message).name).to.equal(ApiErrorType.GENERAL);
});

it('Creates Api Unknown error', function () {
expect(new ApiUnknownError(message).name).to.equal(ApiErrorType.UNKNOWN);
});

it('Creates Api execute SQL error', function () {
expect(new ApiExecuteSQLError(message).name).to.equal(ApiErrorType.EXECUTE_SQL);
});
});
});
82 changes: 82 additions & 0 deletions api/src/errors/api-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
export enum ApiErrorType {
BUILD_SQL = 'Error constructing SQL query',
EXECUTE_SQL = 'Error executing SQL query',
GENERAL = 'Error',
UNKNOWN = 'Unknown Error'
}

export class ApiError extends Error {
errors?: (string | object)[];

constructor(name: ApiErrorType, message: string, errors?: (string | object)[], stack?: string) {
super(message);

this.name = name;
this.errors = errors || [];
this.stack = stack;

if (stack) {
this.stack = stack;
}

if (!this.stack) {
Error.captureStackTrace(this);
}
}
}

/**
* Api encountered an error.
*
* @export
* @class ApiGeneralError
* @extends {ApiError}
*/
export class ApiGeneralError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.GENERAL, message, errors);
}
}

/**
* API encountered an unknown/unexpected error.
*
* @export
* @class ApiUnknownError
* @extends {ApiError}
*/
export class ApiUnknownError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.UNKNOWN, message, errors);
}
}

/**
* API executed a query against the database, but the response was missing data, or indicated the query failed.
*
* Examples:
* - A query to select rows that are expected to exist returns with `rows=[]`.
* - A query to insert a new record returns with `rowCount=0` indicating no new row was added.
*
* @export
* @class ApiExecuteSQLError
* @extends {ApiError}
*/
export class ApiExecuteSQLError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.EXECUTE_SQL, message, errors);
}
}

/**
* API failed to build SQL a query.
*
* @export
* @class ApiBuildSQLError
* @extends {ApiError}
*/
export class ApiBuildSQLError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.BUILD_SQL, message, errors);
}
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,8 @@
import { expect } from 'chai';
import { describe } from 'mocha';
import { DatabaseError } from 'pg';
import {
ApiBuildSQLError,
ApiError,
ApiErrorType,
ApiExecuteSQLError,
ApiGeneralError,
ApiUnknownError,
ensureHTTPError,
HTTP400,
HTTP401,
HTTP403,
HTTP409,
HTTP500,
HTTPError
} from './custom-error';

describe('ApiError', () => {
describe('No error value provided', () => {
let message: string;

before(() => {
message = 'response message';
});

it('Creates Api General error', function () {
expect(new ApiGeneralError(message).name).to.equal(ApiErrorType.GENERAL);
});

it('Creates Api Unknown error', function () {
expect(new ApiUnknownError(message).name).to.equal(ApiErrorType.UNKNOWN);
});

it('Creates Api build SQL error', function () {
expect(new ApiBuildSQLError(message).name).to.equal(ApiErrorType.BUILD_SQL);
});

it('Creates Api execute SQL error', function () {
expect(new ApiExecuteSQLError(message).name).to.equal(ApiErrorType.EXECUTE_SQL);
});
});
});
import { ApiError, ApiErrorType } from './api-error';
import { ensureHTTPError, HTTP400, HTTP401, HTTP403, HTTP409, HTTP500, HTTPError } from './http-error';

describe('HTTPError', () => {
describe('No error value provided', () => {
Expand Down
84 changes: 1 addition & 83 deletions api/src/errors/custom-error.ts → api/src/errors/http-error.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,5 @@
import { DatabaseError } from 'pg';

export enum ApiErrorType {
BUILD_SQL = 'Error constructing SQL query',
EXECUTE_SQL = 'Error executing SQL query',
GENERAL = 'Error',
UNKNOWN = 'Unknown Error'
}

export class ApiError extends Error {
errors?: (string | object)[];

constructor(name: ApiErrorType, message: string, errors?: (string | object)[], stack?: string) {
super(message);

this.name = name;
this.errors = errors || [];
this.stack = stack;

if (stack) {
this.stack = stack;
}

if (!this.stack) {
Error.captureStackTrace(this);
}
}
}

/**
* Api encountered an error.
*
* @export
* @class ApiGeneralError
* @extends {ApiError}
*/
export class ApiGeneralError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.GENERAL, message, errors);
}
}

/**
* API encountered an unknown/unexpected error.
*
* @export
* @class ApiUnknownError
* @extends {ApiError}
*/
export class ApiUnknownError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.UNKNOWN, message, errors);
}
}

/**
* API failed to build SQL a query.
*
* @export
* @class ApiBuildSQLError
* @extends {ApiError}
*/
export class ApiBuildSQLError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.BUILD_SQL, message, errors);
}
}

/**
* API executed a query against the database, but the response was missing data, or indicated the query failed.
*
* Examples:
* - A query to select rows that are expected to exist returns with `rows=[]`.
* - A query to insert a new record returns with `rowCount=0` indicating no new row was added.
*
* @export
* @class ApiExecuteSQLError
* @extends {ApiError}
*/
export class ApiExecuteSQLError extends ApiError {
constructor(message: string, errors?: (string | object)[]) {
super(ApiErrorType.EXECUTE_SQL, message, errors);
}
}
import { ApiError } from './api-error';

export enum HTTPErrorType {
BAD_REQUEST = 'Bad Request',
Expand Down
29 changes: 0 additions & 29 deletions api/src/models/occurrence-view.ts

This file was deleted.

Loading