@openstax/ts-utils 1.1.39 → 1.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/config/lambdaParameterConfig.js +1 -1
- package/dist/cjs/errors.d.ts +4 -0
- package/dist/cjs/errors.js +6 -1
- package/dist/cjs/services/apiGateway/index.js +19 -10
- package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +6 -2
- package/dist/cjs/services/lrsGateway/attempt-utils.js +3 -6
- package/dist/cjs/services/lrsGateway/index.js +44 -35
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/config/lambdaParameterConfig.js +1 -1
- package/dist/esm/errors.d.ts +4 -0
- package/dist/esm/errors.js +4 -0
- package/dist/esm/services/apiGateway/index.js +19 -10
- package/dist/esm/services/authProvider/utils/decryptAndVerify.js +6 -2
- package/dist/esm/services/lrsGateway/attempt-utils.js +3 -6
- package/dist/esm/services/lrsGateway/index.js +44 -35
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -19,7 +19,7 @@ const lambdaParameterConfig = (parameterName) => async () => {
|
|
|
19
19
|
if (!lambdaExtensionReadyPromise) {
|
|
20
20
|
// This request will return 400 Bad Request,
|
|
21
21
|
// but we only care that it'll block until the extension is ready
|
|
22
|
-
lambdaExtensionReadyPromise = (0, node_fetch_1.default)(lambdaExtensionUrl);
|
|
22
|
+
lambdaExtensionReadyPromise = (0, helpers_1.retryWithDelay)(() => (0, node_fetch_1.default)(lambdaExtensionUrl));
|
|
23
23
|
}
|
|
24
24
|
await lambdaExtensionReadyPromise;
|
|
25
25
|
const resp = await (0, helpers_1.retryWithDelay)(() => (0, node_fetch_1.default)(
|
package/dist/cjs/errors.d.ts
CHANGED
|
@@ -10,3 +10,7 @@ export declare class NotFoundError extends Error {
|
|
|
10
10
|
static readonly TYPE = "NotFoundError";
|
|
11
11
|
static matches: (e: any) => e is typeof NotFoundError;
|
|
12
12
|
}
|
|
13
|
+
export declare class SessionExpiredError extends Error {
|
|
14
|
+
static readonly TYPE = "SessionExpiredError";
|
|
15
|
+
static matches: (e: any) => e is typeof SessionExpiredError;
|
|
16
|
+
}
|
package/dist/cjs/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NotFoundError = exports.UnauthorizedError = exports.InvalidRequestError = void 0;
|
|
3
|
+
exports.SessionExpiredError = exports.NotFoundError = exports.UnauthorizedError = exports.InvalidRequestError = void 0;
|
|
4
4
|
/*
|
|
5
5
|
* if code is split into multiple bundles, sometimes each bundle
|
|
6
6
|
* will get its own definition of this module and then instanceof checks
|
|
@@ -30,3 +30,8 @@ class NotFoundError extends Error {
|
|
|
30
30
|
exports.NotFoundError = NotFoundError;
|
|
31
31
|
NotFoundError.TYPE = 'NotFoundError';
|
|
32
32
|
NotFoundError.matches = errorIsType(NotFoundError);
|
|
33
|
+
class SessionExpiredError extends Error {
|
|
34
|
+
}
|
|
35
|
+
exports.SessionExpiredError = SessionExpiredError;
|
|
36
|
+
SessionExpiredError.TYPE = 'SessionExpiredError';
|
|
37
|
+
SessionExpiredError.matches = errorIsType(SessionExpiredError);
|
|
@@ -31,6 +31,7 @@ const pathToRegexp = __importStar(require("path-to-regexp"));
|
|
|
31
31
|
const query_string_1 = __importDefault(require("query-string"));
|
|
32
32
|
const __1 = require("../..");
|
|
33
33
|
const config_1 = require("../../config");
|
|
34
|
+
const errors_1 = require("../../errors");
|
|
34
35
|
const loadResponse = (response) => () => {
|
|
35
36
|
const [contentType] = (response.headers.get('content-type') || '').split(';');
|
|
36
37
|
switch (contentType) {
|
|
@@ -61,16 +62,24 @@ const makeRouteClient = (initializer, config, route, authProvider) => {
|
|
|
61
62
|
...fetchConfig === null || fetchConfig === void 0 ? void 0 : fetchConfig.headers,
|
|
62
63
|
...(body ? { 'content-type': 'application/json' } : {}),
|
|
63
64
|
}
|
|
64
|
-
})).then(response =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
})).then(response => {
|
|
66
|
+
if (response.status === 401) {
|
|
67
|
+
throw new errors_1.UnauthorizedError();
|
|
68
|
+
}
|
|
69
|
+
if (response.status === 440) {
|
|
70
|
+
throw new errors_1.SessionExpiredError();
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
status: response.status,
|
|
74
|
+
acceptStatus: (...status) => {
|
|
75
|
+
if (!status.includes(response.status)) {
|
|
76
|
+
throw new Error('unexpected response from api');
|
|
77
|
+
}
|
|
78
|
+
return { status: response.status, load: (0, exports.loadResponse)(response) };
|
|
79
|
+
},
|
|
80
|
+
load: (0, exports.loadResponse)(response),
|
|
81
|
+
};
|
|
82
|
+
});
|
|
74
83
|
};
|
|
75
84
|
routeClient.renderUrl = renderUrl;
|
|
76
85
|
return routeClient;
|
|
@@ -30,6 +30,7 @@ exports.decryptAndVerify = void 0;
|
|
|
30
30
|
const crypto = __importStar(require("crypto"));
|
|
31
31
|
const util_1 = require("util");
|
|
32
32
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
33
|
+
const errors_1 = require("../../../errors");
|
|
33
34
|
const guards_1 = require("../../../guards");
|
|
34
35
|
const decrypt = (input, key) => {
|
|
35
36
|
const splitInput = input.split('.');
|
|
@@ -51,7 +52,7 @@ const decryptAndVerify = (token, encryptionPrivateKey, signaturePublicKey) => {
|
|
|
51
52
|
// Decrypt SSO cookie
|
|
52
53
|
const plaintext = decrypt(token, encryptionPrivateKey);
|
|
53
54
|
const payload = jsonwebtoken_1.default.verify(plaintext, signaturePublicKey, {
|
|
54
|
-
clockTolerance: 300 // 5 minutes
|
|
55
|
+
clockTolerance: 300 // 5 minutes
|
|
55
56
|
});
|
|
56
57
|
if (!(0, guards_1.isPlainObject)(payload) || !(0, guards_1.isPlainObject)(payload.sub) || !payload.sub.uuid) {
|
|
57
58
|
return undefined;
|
|
@@ -59,7 +60,10 @@ const decryptAndVerify = (token, encryptionPrivateKey, signaturePublicKey) => {
|
|
|
59
60
|
// TS is confused because the library types the `sub` as a string
|
|
60
61
|
return payload.sub;
|
|
61
62
|
}
|
|
62
|
-
catch {
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (err instanceof jsonwebtoken_1.default.TokenExpiredError) {
|
|
65
|
+
throw new errors_1.SessionExpiredError();
|
|
66
|
+
}
|
|
63
67
|
return undefined;
|
|
64
68
|
}
|
|
65
69
|
};
|
|
@@ -205,8 +205,7 @@ const createAttemptStatement = (activity, parentActivity) => {
|
|
|
205
205
|
exports.createAttemptStatement = createAttemptStatement;
|
|
206
206
|
/* resolves with the statement id */
|
|
207
207
|
const putAttemptStatement = async (gateway, activity, parentActivity) => {
|
|
208
|
-
return await gateway.putXapiStatements([(0, exports.createAttemptStatement)(activity, parentActivity)])
|
|
209
|
-
.then(statements => statements[0]);
|
|
208
|
+
return (await gateway.putXapiStatements([(0, exports.createAttemptStatement)(activity, parentActivity)]))[0];
|
|
210
209
|
};
|
|
211
210
|
exports.putAttemptStatement = putAttemptStatement;
|
|
212
211
|
/*
|
|
@@ -230,8 +229,7 @@ const createAttemptActivityStatement = (attemptStatement, verb, result) => {
|
|
|
230
229
|
};
|
|
231
230
|
exports.createAttemptActivityStatement = createAttemptActivityStatement;
|
|
232
231
|
const putAttemptActivityStatement = async (gateway, attemptStatement, verb, result) => {
|
|
233
|
-
return await gateway.putXapiStatements([(0, exports.createAttemptActivityStatement)(attemptStatement, verb, result)])
|
|
234
|
-
.then(statements => statements[0]);
|
|
232
|
+
return (await gateway.putXapiStatements([(0, exports.createAttemptActivityStatement)(attemptStatement, verb, result)]))[0];
|
|
235
233
|
};
|
|
236
234
|
exports.putAttemptActivityStatement = putAttemptActivityStatement;
|
|
237
235
|
/*
|
|
@@ -269,7 +267,6 @@ const createCompletedStatement = (attemptStatement, result) => {
|
|
|
269
267
|
};
|
|
270
268
|
exports.createCompletedStatement = createCompletedStatement;
|
|
271
269
|
const putCompletedStatement = async (gateway, attemptStatement, result) => {
|
|
272
|
-
return await gateway.putXapiStatements([(0, exports.createCompletedStatement)(attemptStatement, result)])
|
|
273
|
-
.then(statements => statements[0]);
|
|
270
|
+
return (await gateway.putXapiStatements([(0, exports.createCompletedStatement)(attemptStatement, result)]))[0];
|
|
274
271
|
};
|
|
275
272
|
exports.putCompletedStatement = putCompletedStatement;
|
|
@@ -40,6 +40,7 @@ const lrsGateway = (initializer) => (configProvider) => {
|
|
|
40
40
|
const lrsHost = (0, __1.once)(() => (0, config_1.resolveConfigValue)(config.lrsHost));
|
|
41
41
|
const lrsAuthorization = (0, __1.once)(() => (0, config_1.resolveConfigValue)(config.lrsAuthorization));
|
|
42
42
|
return (authProvider) => {
|
|
43
|
+
// Note: This method actually uses POST
|
|
43
44
|
const putXapiStatements = async (statements) => {
|
|
44
45
|
const user = (0, assertions_1.assertDefined)(await authProvider.getUser(), new errors_1.UnauthorizedError);
|
|
45
46
|
const statementsWithDefaults = statements.map(statement => ({
|
|
@@ -53,7 +54,7 @@ const lrsGateway = (initializer) => (configProvider) => {
|
|
|
53
54
|
},
|
|
54
55
|
timestamp: (0, formatISO_1.default)(new Date())
|
|
55
56
|
}));
|
|
56
|
-
|
|
57
|
+
const response = await initializer.fetch((await lrsHost()).replace(/\/+$/, '') + '/data/xAPI/statements', {
|
|
57
58
|
body: JSON.stringify(statementsWithDefaults),
|
|
58
59
|
headers: {
|
|
59
60
|
Authorization: await lrsAuthorization(),
|
|
@@ -61,42 +62,50 @@ const lrsGateway = (initializer) => (configProvider) => {
|
|
|
61
62
|
'X-Experience-API-Version': '1.0.0',
|
|
62
63
|
},
|
|
63
64
|
method: routing_1.METHOD.POST,
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
});
|
|
66
|
+
if (![200, 201].includes(response.status)) {
|
|
67
|
+
throw new Error(`Unexpected LRS POST statements response code ${response.status} with body:
|
|
68
|
+
|
|
69
|
+
${await response.text()}`);
|
|
70
|
+
}
|
|
71
|
+
const ids = await response.json();
|
|
72
|
+
return ids.map((id, index) => ({ id, ...statementsWithDefaults[index] }));
|
|
67
73
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
// Note: This code does not currently handle a single statement response,
|
|
75
|
+
// which can return 404 if the statement is not found
|
|
76
|
+
const formatGetXapiStatementsResponse = async (responsePromise) => {
|
|
77
|
+
const response = await responsePromise;
|
|
78
|
+
if (response.status !== 200) {
|
|
79
|
+
throw new Error(`Unexpected LRS GET statements response code ${response.status} with body:
|
|
80
|
+
|
|
81
|
+
${await response.text()}`);
|
|
82
|
+
}
|
|
83
|
+
return response.json();
|
|
77
84
|
};
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
},
|
|
85
|
+
const getMoreXapiStatements = async (more) => formatGetXapiStatementsResponse(initializer.fetch((await lrsHost()).replace(/\/+$/, '') + more, {
|
|
86
|
+
headers: {
|
|
87
|
+
Authorization: await lrsAuthorization(),
|
|
88
|
+
'X-Experience-API-Version': '1.0.0',
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
91
|
+
const getXapiStatements = async ({ user, anyUser, ...options }) => formatGetXapiStatementsResponse(initializer.fetch((await lrsHost()).replace(/\/+$/, '') + '/data/xAPI/statements?' + queryString.stringify({
|
|
92
|
+
...options,
|
|
93
|
+
...(anyUser === true ? {} : {
|
|
94
|
+
agent: JSON.stringify({
|
|
95
|
+
account: {
|
|
96
|
+
homePage: 'https://openstax.org',
|
|
97
|
+
name: user || (0, assertions_1.assertDefined)(await authProvider.getUser(), new errors_1.UnauthorizedError()).uuid,
|
|
98
|
+
},
|
|
99
|
+
objectType: 'Agent',
|
|
100
|
+
}),
|
|
95
101
|
})
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
}), {
|
|
103
|
+
headers: {
|
|
104
|
+
Authorization: await lrsAuthorization(),
|
|
105
|
+
'X-Experience-API-Version': '1.0.0',
|
|
106
|
+
},
|
|
107
|
+
}));
|
|
108
|
+
const getAllXapiStatements = async (...args) => {
|
|
100
109
|
const loadRemaining = async (result) => {
|
|
101
110
|
if (!result.more) {
|
|
102
111
|
return result.statements;
|
|
@@ -104,7 +113,7 @@ const lrsGateway = (initializer) => (configProvider) => {
|
|
|
104
113
|
const { more, statements } = await getMoreXapiStatements(result.more);
|
|
105
114
|
return loadRemaining({ more, statements: [...result.statements, ...statements] });
|
|
106
115
|
};
|
|
107
|
-
return getXapiStatements(...args)
|
|
116
|
+
return loadRemaining(await getXapiStatements(...args));
|
|
108
117
|
};
|
|
109
118
|
return {
|
|
110
119
|
putXapiStatements,
|