Skip to content

Commit 263efd7

Browse files
authored
SIMSBIOHUB-895: Return 404 for Resource Not Found (#350)
* 404 for resource not found * linter * fix missed test updates
1 parent d962be8 commit 263efd7

File tree

49 files changed

+333
-146
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+333
-146
lines changed

api/src/errors/api-error.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect } from 'chai';
22
import { describe } from 'mocha';
3-
import { ApiErrorType, ApiExecuteSQLError, ApiGeneralError } from './api-error';
3+
import { ApiErrorType, ApiExecuteSQLError, ApiGeneralError, ApiNotFoundError } from './api-error';
44

55
describe('ApiError', () => {
66
describe('No error value provided', () => {
@@ -17,5 +17,9 @@ describe('ApiError', () => {
1717
it('Creates Api execute SQL error', function () {
1818
expect(new ApiExecuteSQLError(message).name).to.equal(ApiErrorType.EXECUTE_SQL);
1919
});
20+
21+
it('Creates Api not found error', function () {
22+
expect(new ApiNotFoundError(message).name).to.equal(ApiErrorType.NOT_FOUND);
23+
});
2024
});
2125
});

api/src/errors/api-error.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export enum ApiErrorType {
55
EXECUTE_SQL = 'Error executing SQL query',
66
GENERAL = 'Error',
77
CONFLICT = 'Conflict',
8+
NOT_FOUND = 'Not Found',
89
UNKNOWN = 'Unknown Error'
910
}
1011

@@ -55,6 +56,19 @@ export class ApiConflictError extends ApiError {
5556
}
5657
}
5758

59+
/**
60+
* Api could not find a requested resource.
61+
*
62+
* @export
63+
* @class ApiNotFoundError
64+
* @extends {ApiError}
65+
*/
66+
export class ApiNotFoundError extends ApiError {
67+
constructor(message: string, errors?: (string | object)[]) {
68+
super(ApiErrorType.NOT_FOUND, message, errors);
69+
}
70+
}
71+
5872
/**
5973
* API executed a query against the database, but the response was missing data, or indicated the query failed.
6074
*

api/src/errors/http-error.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { describe } from 'mocha';
33
import { DatabaseError } from 'pg';
4-
import { ApiError, ApiErrorType } from './api-error';
4+
import { ApiError, ApiErrorType, ApiNotFoundError } from './api-error';
55
import { ensureHTTPError, HTTP400, HTTP401, HTTP403, HTTP409, HTTP500, HTTPError, isAjvError } from './http-error';
66

77
describe('HTTPError', () => {
@@ -56,6 +56,16 @@ describe('ensureHTTPError', () => {
5656
expect(ensuredError.message).to.equal('an api error message');
5757
});
5858

59+
it('returns HTTP404 when an ApiNotFoundError is provided', function () {
60+
const apiError = new ApiNotFoundError('not found');
61+
62+
const ensuredError = ensureHTTPError(apiError);
63+
64+
expect(ensuredError).to.be.instanceof(HTTPError);
65+
expect(ensuredError.status).to.equal(404);
66+
expect(ensuredError.message).to.equal('not found');
67+
});
68+
5969
it('returns a HTTPError when a DatabaseError provided', function () {
6070
const databaseError = new DatabaseError('a db error message', 1, 'error');
6171

api/src/errors/http-error.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DatabaseError } from 'pg';
2-
import { ApiConflictError, ApiError } from './api-error';
2+
import { ApiConflictError, ApiError, ApiNotFoundError } from './api-error';
33
import { BaseError } from './base-error';
44

55
export enum HTTPErrorType {
@@ -153,6 +153,10 @@ export const ensureHTTPError = (error: HTTPError | ApiError | Error | any): HTTP
153153
return HTTP409.fromApiError(error);
154154
}
155155

156+
if (error instanceof ApiNotFoundError) {
157+
return HTTP404.fromApiError(error);
158+
}
159+
156160
if (error instanceof ApiError) {
157161
return HTTP500.fromApiError(error);
158162
}

api/src/openapi/schemas/http-responses.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ export const defaultErrorResponses = {
88
403: {
99
$ref: '#/components/responses/403'
1010
},
11+
404: {
12+
$ref: '#/components/responses/404'
13+
},
1114
409: {
1215
$ref: '#/components/responses/409'
1316
},

api/src/paths/cart/{cartId}/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ GET.apiDoc = {
6969
}
7070
}
7171
},
72-
404: {
73-
description: 'Cart not found'
74-
},
7572
...defaultErrorResponses
7673
}
7774
};

api/src/paths/data-request/{dataRequestId}/index.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,6 @@ GET.apiDoc = {
9393
}
9494
}
9595
},
96-
404: {
97-
description: 'Data request not found'
98-
},
9996
...defaultErrorResponses
10097
}
10198
};
@@ -130,9 +127,6 @@ PUT.apiDoc = {
130127
200: {
131128
description: 'Data request updated successfully'
132129
},
133-
404: {
134-
description: 'Data request not found'
135-
},
136130
...defaultErrorResponses
137131
}
138132
};
@@ -160,9 +154,6 @@ DELETE.apiDoc = {
160154
200: {
161155
description: 'Data request deleted successfully'
162156
},
163-
404: {
164-
description: 'Data request not found'
165-
},
166157
...defaultErrorResponses
167158
}
168159
};

api/src/repositories/authorization/policy-repository.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe } from 'mocha';
33
import { QueryResult } from 'pg';
44
import sinon from 'sinon';
55
import sinonChai from 'sinon-chai';
6-
import { ApiExecuteSQLError } from '../../errors/api-error';
6+
import { ApiExecuteSQLError, ApiNotFoundError } from '../../errors/api-error';
77
import { Policy } from '../../models/policy';
88
import { getMockDBConnection } from '../../__mocks__/db';
99
import { PolicyRepository } from './policy-repository';
@@ -77,7 +77,8 @@ describe('PolicyRepository', () => {
7777
await repository.getPolicy('1');
7878
expect.fail();
7979
} catch (error) {
80-
expect((error as ApiExecuteSQLError).message).to.equal('Failed to get policy');
80+
expect(error).to.be.instanceOf(ApiNotFoundError);
81+
expect((error as ApiNotFoundError).message).to.equal('Policy not found');
8182
}
8283
});
8384
});

api/src/repositories/authorization/policy-repository.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Knex } from 'knex';
22
import SQL from 'sql-template-strings';
33
import { getKnex } from '../../database/db';
44
import { FeatureUrn } from '../../database/urn-utils.interface';
5-
import { ApiExecuteSQLError } from '../../errors/api-error';
5+
import { ApiExecuteSQLError, ApiNotFoundError } from '../../errors/api-error';
66
import { CountResult } from '../../models/count';
77
import { CreatePolicy, Policy, UpdatePolicy } from '../../models/policy';
88
import { PolicyEffect } from '../../models/policy-statement';
@@ -58,10 +58,14 @@ export class PolicyRepository extends BaseRepository {
5858
async getPolicy(policyId: string): Promise<Policy> {
5959
const response = await this.getPolicies({ policyId });
6060

61+
if (response.length === 0) {
62+
throw new ApiNotFoundError('Policy not found', ['PolicyRepository->getPolicy', { policyId }]);
63+
}
64+
6165
if (response.length !== 1) {
62-
throw new ApiExecuteSQLError('Failed to get policy', [
66+
throw new ApiExecuteSQLError('Unexpected row count', [
6367
'PolicyRepository->getPolicy',
64-
'rowCount was null or undefined, expected rowCount = 1'
68+
`expected rowCount=1, actual rowCount=${response.length}`
6569
]);
6670
}
6771

api/src/repositories/authorization/policy-statement-condition-repository.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe } from 'mocha';
33
import { QueryResult } from 'pg';
44
import sinon from 'sinon';
55
import sinonChai from 'sinon-chai';
6-
import { ApiExecuteSQLError } from '../../errors/api-error';
6+
import { ApiExecuteSQLError, ApiNotFoundError } from '../../errors/api-error';
77
import { PolicyConditionOperator } from '../../models/policy-statement-condition';
88
import { getMockDBConnection } from '../../__mocks__/db';
99
import { PolicyStatementConditionRepository } from './policy-statement-condition-repository';
@@ -105,7 +105,8 @@ describe('PolicyStatementConditionRepository', () => {
105105
await repository.getPolicyStatementCondition('1');
106106
expect.fail();
107107
} catch (error) {
108-
expect((error as ApiExecuteSQLError).message).to.equal('Failed to get policy statement condition');
108+
expect(error).to.be.instanceOf(ApiNotFoundError);
109+
expect((error as ApiNotFoundError).message).to.equal('Policy statement condition not found');
109110
}
110111
});
111112
});

0 commit comments

Comments
 (0)