Skip to content

Commit dd57208

Browse files
committed
fix: Handles errors correctly
1 parent 05a821a commit dd57208

File tree

4 files changed

+98
-24
lines changed

4 files changed

+98
-24
lines changed

src/apps/activities/_functions/deleteActivityProfile/deleteActivityProfileRecordFromMongo.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ export async function deleteActivityProfileRecordFromMongo(
5454
});
5555

5656
// Returns the result of the deletion if the document was deleted.
57-
if (opResult.value !== undefined) {
58-
const deletedDoc = opResult.value;
57+
const deletedDoc = opResult.value as MongoActivityProfileDoc | null;
58+
if (deletedDoc !== null) {
5959
await client.close();
6060
return {
6161
contentType: deletedDoc.contentType,
Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
import { NO_CONTENT } from 'http-status-codes';
1+
import { BAD_REQUEST, NO_CONTENT, UNAUTHORIZED } from 'http-status-codes';
2+
import NoModel from 'jscommons/dist/errors/NoModel';
3+
import Unauthorised from 'jscommons/dist/errors/Unauthorised';
4+
import { Warnings } from 'rulr';
5+
import stringToStream from 'string-to-stream';
26
import getActivityId from '../../expressPresenter/utils/getActivityId';
37
import getEtag from '../../expressPresenter/utils/getEtag';
48
import getProfileId from '../../expressPresenter/utils/getProfileId';
59
import validateVersionHeader from '../../expressPresenter/utils/validateVersionHeader';
6-
import { xapiHeaderVersion } from '../../utils/constants';
10+
import { jsonContentType, xapiHeaderVersion } from '../../utils/constants';
711
import { deleteActivityProfile } from './deleteActivityProfile';
812
import { AuthConfig } from './utils/getAuthConfig/AuthConfig';
913
import { getClient } from './utils/getClient/getClient';
1014
import { FileStorageConfig } from './utils/getFileStorageConfig/FileStorageConfig';
1115
import { MongoRecordStorageConfig } from './utils/getRecordStorageConfig/RecordStorageConfig';
1216
import { TrackingConfig } from './utils/getTrackingConfig/TrackingConfig';
1317
import { HttpRequest, HttpResponse } from './utils/HttpInterfaces';
18+
import { translateWarning } from './utils/translateWarning';
1419

1520
export interface DeleteActivityProfileViaHttpConfig {
1621
readonly authConfig: AuthConfig;
@@ -23,18 +28,61 @@ export async function deleteActivityProfileViaHttp(
2328
config: DeleteActivityProfileViaHttpConfig,
2429
req: HttpRequest,
2530
): Promise<HttpResponse> {
26-
const client = await getClient(config, req.headers.authorization);
27-
validateVersionHeader(req.headers['x-experience-api-version']);
31+
try {
32+
const client = await getClient(config, req.headers.authorization);
33+
validateVersionHeader(req.headers['x-experience-api-version']);
2834

29-
const ifMatch = getEtag(req.headers['if-match']);
30-
const activityId = getActivityId(req.query.activityId);
31-
const profileId = getProfileId(req.query.profileId);
35+
const ifMatch = getEtag(req.headers['if-match']);
36+
const activityId = getActivityId(req.query.activityId);
37+
const profileId = getProfileId(req.query.profileId);
3238

33-
await deleteActivityProfile(config, { activityId, client, profileId, ifMatch });
34-
return {
35-
statusCode: NO_CONTENT,
36-
headers: {
37-
'X-Experience-API-Version': xapiHeaderVersion,
38-
},
39-
};
39+
await deleteActivityProfile(config, { activityId, client, profileId, ifMatch });
40+
return {
41+
statusCode: NO_CONTENT,
42+
headers: {
43+
'X-Experience-API-Version': xapiHeaderVersion,
44+
},
45+
};
46+
} catch (err) {
47+
if (err instanceof Unauthorised) {
48+
return {
49+
statusCode: UNAUTHORIZED,
50+
headers: {
51+
'X-Experience-API-Version': xapiHeaderVersion,
52+
'Content-Type': jsonContentType,
53+
},
54+
body: stringToStream(JSON.stringify({
55+
message: 'Unauthorized',
56+
requestId: req.requestId,
57+
})),
58+
};
59+
}
60+
if (err instanceof NoModel) {
61+
return {
62+
statusCode: UNAUTHORIZED,
63+
headers: {
64+
'X-Experience-API-Version': xapiHeaderVersion,
65+
'Content-Type': jsonContentType,
66+
},
67+
body: stringToStream(JSON.stringify({
68+
message: `No ${err.modelName} found`,
69+
requestId: req.requestId,
70+
})),
71+
};
72+
}
73+
if (err instanceof Warnings) {
74+
return {
75+
statusCode: BAD_REQUEST,
76+
headers: {
77+
'X-Experience-API-Version': xapiHeaderVersion,
78+
'Content-Type': jsonContentType,
79+
},
80+
body: stringToStream(JSON.stringify({
81+
warnings: err.warnings.map(translateWarning),
82+
requestId: req.requestId,
83+
})),
84+
};
85+
}
86+
throw err;
87+
}
4088
}

src/apps/activities/_functions/deleteActivityProfile/utils/createExpressHandler.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ export function createExpressHandler<Config>(handler: HttpHandler<Config>) {
3737
return (config: Config) => {
3838
return async (req: express.Request, res: express.Response) => {
3939
const requestId = createV4UUID();
40-
const headers = getExpressRequestHeaders(req);
41-
const query = getExpressRequestQuery(req);
4240
try {
4341
const body = req;
42+
const headers = getExpressRequestHeaders(req);
43+
const query = getExpressRequestQuery(req);
4444
const response = await handler(config, { requestId, query, body, headers });
4545
res.writeHead(response.statusCode, response.headers);
4646
if (response.body !== undefined) {
@@ -49,12 +49,7 @@ export function createExpressHandler<Config>(handler: HttpHandler<Config>) {
4949
res.end();
5050
} catch (err) {
5151
// tslint:disable-next-line: no-console
52-
console.error({
53-
requestId,
54-
errorMessage: err?.message,
55-
requestHeaders: headers,
56-
errorStack: err?.stack,
57-
});
52+
console.error(requestId, err);
5853
res.status(INTERNAL_SERVER_ERROR);
5954
res.send(`Internal Server Error ${requestId}`);
6055
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import XapiTypeWarning from '@learninglocker/xapi-validation/dist/warnings/TypeWarning';
2+
import stringPath from 'jscommons/dist/translatorFactory/utils/stringPath';
3+
import { RequiredWarning, RestrictedKeysWarning, TypeWarning, Warning } from 'rulr';
4+
5+
export function translateWarning(warning: Warning) {
6+
if (warning instanceof XapiTypeWarning) {
7+
const path = stringPath(warning.path);
8+
const dataString = JSON.stringify(warning.data);
9+
return `Expected ${warning.typeName} in ${path}. Received '${dataString}'`;
10+
}
11+
if (warning instanceof TypeWarning) {
12+
const path = stringPath(warning.path);
13+
const typeName = warning.type.name;
14+
const dataString = JSON.stringify(warning.data);
15+
return `Expected '${path}' to be '${typeName}'. Received '${dataString}'`;
16+
}
17+
if (warning instanceof RequiredWarning) {
18+
const path = stringPath(warning.path);
19+
return `Missing required value in '${path}'`;
20+
}
21+
if (warning instanceof RestrictedKeysWarning) {
22+
const path = stringPath(warning.path);
23+
const keys = warning.keys.join(', ');
24+
return `Unknown keys (${keys}) set in '${path}'`;
25+
}
26+
{
27+
const path = stringPath(warning.path);
28+
const dataString = JSON.stringify(warning.data);
29+
return `Problem in '${path}'. Received '${dataString}'`;
30+
}
31+
}

0 commit comments

Comments
 (0)