@openstax/ts-utils 1.6.2 → 1.8.0
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/{fetch.d.ts → fetch/index.d.ts} +1 -1
- package/dist/{esm/middleware.d.ts → cjs/middleware/index.d.ts} +2 -2
- package/dist/cjs/misc/helpers.js +12 -1
- package/dist/cjs/{pagination.d.ts → pagination/index.d.ts} +1 -1
- package/dist/cjs/{pagination.js → pagination/index.js} +3 -3
- package/dist/cjs/routing/index.d.ts +0 -3
- package/dist/cjs/routing/index.js +7 -9
- package/dist/cjs/services/authProvider/decryption.js +5 -5
- package/dist/cjs/services/authProvider/index.d.ts +0 -2
- package/dist/cjs/services/authProvider/subrequest.js +6 -6
- package/dist/cjs/services/exercisesGateway/index.d.ts +3 -6
- package/dist/cjs/services/exercisesGateway/index.js +7 -7
- package/dist/cjs/services/lrsGateway/xapiUtils.d.ts +4 -2
- package/dist/cjs/services/lrsGateway/xapiUtils.js +10 -7
- package/dist/cjs/services/versionedDocumentStore/dynamodb.d.ts +1 -4
- package/dist/cjs/services/versionedDocumentStore/dynamodb.js +13 -13
- package/dist/cjs/services/versionedDocumentStore/file-system.d.ts +1 -4
- package/dist/cjs/services/versionedDocumentStore/file-system.js +13 -13
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types.d.ts +4 -0
- package/dist/esm/{fetch.d.ts → fetch/index.d.ts} +1 -1
- package/dist/{cjs/middleware.d.ts → esm/middleware/index.d.ts} +2 -2
- package/dist/esm/misc/helpers.js +12 -1
- package/dist/esm/{pagination.d.ts → pagination/index.d.ts} +1 -1
- package/dist/esm/{pagination.js → pagination/index.js} +3 -3
- package/dist/esm/routing/index.d.ts +0 -3
- package/dist/esm/routing/index.js +7 -9
- package/dist/esm/services/authProvider/decryption.js +5 -5
- package/dist/esm/services/authProvider/index.d.ts +0 -2
- package/dist/esm/services/authProvider/subrequest.js +6 -6
- package/dist/esm/services/exercisesGateway/index.d.ts +3 -6
- package/dist/esm/services/exercisesGateway/index.js +7 -7
- package/dist/esm/services/lrsGateway/xapiUtils.d.ts +4 -2
- package/dist/esm/services/lrsGateway/xapiUtils.js +10 -7
- package/dist/esm/services/versionedDocumentStore/dynamodb.d.ts +1 -4
- package/dist/esm/services/versionedDocumentStore/dynamodb.js +13 -13
- package/dist/esm/services/versionedDocumentStore/file-system.d.ts +1 -4
- package/dist/esm/services/versionedDocumentStore/file-system.js +13 -13
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/dist/esm/types.d.ts +4 -0
- package/package.json +108 -13
- package/dist/cjs/profile.d.ts +0 -61
- package/dist/cjs/profile.js +0 -199
- package/dist/esm/profile.d.ts +0 -61
- package/dist/esm/profile.js +0 -191
- /package/dist/cjs/{assertions.d.ts → assertions/index.d.ts} +0 -0
- /package/dist/cjs/{assertions.js → assertions/index.js} +0 -0
- /package/dist/cjs/{errors.d.ts → errors/index.d.ts} +0 -0
- /package/dist/cjs/{errors.js → errors/index.js} +0 -0
- /package/dist/cjs/{fetch.js → fetch/index.js} +0 -0
- /package/dist/cjs/{guards.d.ts → guards/index.d.ts} +0 -0
- /package/dist/cjs/{guards.js → guards/index.js} +0 -0
- /package/dist/cjs/{middleware.js → middleware/index.js} +0 -0
- /package/dist/esm/{assertions.d.ts → assertions/index.d.ts} +0 -0
- /package/dist/esm/{assertions.js → assertions/index.js} +0 -0
- /package/dist/esm/{errors.d.ts → errors/index.d.ts} +0 -0
- /package/dist/esm/{errors.js → errors/index.js} +0 -0
- /package/dist/esm/{fetch.js → fetch/index.js} +0 -0
- /package/dist/esm/{guards.d.ts → guards/index.d.ts} +0 -0
- /package/dist/esm/{guards.js → guards/index.js} +0 -0
- /package/dist/esm/{middleware.js → middleware/index.js} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TupleZip } from '../types';
|
|
2
2
|
export declare type MiddlewareProvider<Sa, M, A extends any[], R> = (app: Sa, appBinder?: ((app: Sa, provider: MiddlewareProvider<Sa, M, A, R>) => (middleware: M, ...args: A) => R)) => (middleware: M, ...args: A) => R;
|
|
3
3
|
export declare type MiddlewareTransformProvider<Sa, M, A extends any[], R> = (app: Sa) => (middleware: M, ...args: A) => Omit<M, keyof R> & R;
|
|
4
4
|
export declare type MiddlewareInput<S> = S extends MiddlewareProvider<any, infer M, any, any> ? M : never;
|
|
5
5
|
export declare type ServiceMiddlewareProviderResult<M, S> = [M] extends [never] ? never : [M] extends [MiddlewareInput<S>] ? S extends MiddlewareTransformProvider<any, M, any, infer R> ? (Omit<R, keyof M> & M) extends R ? Omit<R, keyof M> & M : R extends M ? R : Omit<M, keyof R> & R : S extends MiddlewareProvider<any, M, any, infer R> ? R : never : never;
|
|
6
6
|
export declare type ServiceMiddlewareProviderArgs<S> = S extends MiddlewareProvider<any, any, infer A, any> ? A : never;
|
|
7
7
|
export declare type ServiceMiddlewareProviderChainResult<C, M> = C extends [infer S1, ...infer S] ? S extends never[] ? ServiceMiddlewareProviderResult<M, S1> : ServiceMiddlewareProviderChainResult<S, ServiceMiddlewareProviderResult<M, S1>> : C extends unknown ? M : never;
|
|
8
|
-
export declare type ServiceMiddlewareProviderChainArgs<C> = C extends [infer S1, ...infer S] ? S extends never[] ? ServiceMiddlewareProviderArgs<S1> :
|
|
8
|
+
export declare type ServiceMiddlewareProviderChainArgs<C> = C extends [infer S1, ...infer S] ? S extends never[] ? ServiceMiddlewareProviderArgs<S1> : TupleZip<ServiceMiddlewareProviderChainArgs<S>, ServiceMiddlewareProviderArgs<S1>> : C extends unknown ? [] : never;
|
|
9
9
|
/**
|
|
10
10
|
* Creates a middleware composer for given AppServices and input value types. The composer accepts a list of middleware
|
|
11
11
|
* that it assembles into a chain. The function returned by the composer takes a value of the given input value type,
|
package/dist/cjs/misc/helpers.js
CHANGED
|
@@ -117,7 +117,18 @@ exports.mapFind = mapFind;
|
|
|
117
117
|
const once = (fn) => {
|
|
118
118
|
const initialValue = {};
|
|
119
119
|
let result = initialValue;
|
|
120
|
-
return ((...args) =>
|
|
120
|
+
return ((...args) => {
|
|
121
|
+
if (result !== initialValue) {
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
result = fn(...args);
|
|
125
|
+
if (typeof result === 'object' && result instanceof Promise) {
|
|
126
|
+
// Clear the result when possible but do not return a Promise that resolves to the initialValue
|
|
127
|
+
result.catch(() => result = initialValue);
|
|
128
|
+
}
|
|
129
|
+
// If this is a rejected Promise, it should be returned before catch() actually overwrites it
|
|
130
|
+
return result;
|
|
131
|
+
});
|
|
121
132
|
};
|
|
122
133
|
exports.once = once;
|
|
123
134
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { QueryParams, RouteMatchRecord } from '
|
|
1
|
+
import { QueryParams, RouteMatchRecord } from '../routing';
|
|
2
2
|
export declare type PaginationHandler<Pa, Q extends QueryParams> = <R>(queryParams: Q, match: RouteMatchRecord<R>) => Pa & {
|
|
3
3
|
getUnusedQueryParams: () => Q;
|
|
4
4
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pageNumberPagination = exports.loadMorePagination = exports.createPaginationMiddleware = void 0;
|
|
4
|
-
const assertions_1 = require("
|
|
5
|
-
const errors_1 = require("
|
|
6
|
-
const routing_1 = require("
|
|
4
|
+
const assertions_1 = require("../assertions");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const routing_1 = require("../routing");
|
|
7
7
|
/**
|
|
8
8
|
* helper to create middleware with the given paginator. aside from taking care of annoying to write pagination logic, these helpers also make
|
|
9
9
|
* sure that all item list responses have the same formatting.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Track } from '../profile';
|
|
2
1
|
import { Logger } from '../services/logger';
|
|
3
2
|
/** HTTP query parameters */
|
|
4
3
|
export declare type QueryParams = Record<string, string | undefined | string[] | null>;
|
|
@@ -35,7 +34,6 @@ export declare type PayloadForRoute<R> = RequestServicesForRoute<R> extends {
|
|
|
35
34
|
} ? RequestServicesForRoute<R>['payload'] : undefined;
|
|
36
35
|
declare type RequestServiceProvider<Sa, Sr, Ri> = (app: Sa) => <R>(middleware: {
|
|
37
36
|
request: Ri;
|
|
38
|
-
profile: Track;
|
|
39
37
|
logger: Logger;
|
|
40
38
|
}, match: RouteMatchRecord<R>) => Sr;
|
|
41
39
|
declare type RouteHandler<P, Sr, Ro> = (params: P, request: Sr) => Ro;
|
|
@@ -168,7 +166,6 @@ declare type RequestResponder<Sa, Ri, Ro> = {
|
|
|
168
166
|
(services: CompatibleServices<Sa>): (request: Ri) => Ro | undefined;
|
|
169
167
|
<RoF>(services: CompatibleServices<Sa>, responseMiddleware: (app: Sa) => (response: Ro | undefined, request: {
|
|
170
168
|
request: Ri;
|
|
171
|
-
profile: Track;
|
|
172
169
|
logger: Logger;
|
|
173
170
|
}) => RoF): (request: Ri) => RoF;
|
|
174
171
|
};
|
|
@@ -33,7 +33,6 @@ exports.METHOD = exports.apiHtmlResponse = exports.apiTextResponse = exports.api
|
|
|
33
33
|
const pathToRegexp = __importStar(require("path-to-regexp"));
|
|
34
34
|
const query_string_1 = __importDefault(require("query-string"));
|
|
35
35
|
const helpers_1 = require("../misc/helpers");
|
|
36
|
-
const profile_1 = require("../profile");
|
|
37
36
|
const console_1 = require("../services/logger/console");
|
|
38
37
|
/**
|
|
39
38
|
* Makes a createRoute function that can be used to create routes (this is a factory factory). The
|
|
@@ -136,11 +135,11 @@ exports.renderAnyRouteUrl = (0, exports.makeRenderRouteUrl)();
|
|
|
136
135
|
const bindRoute = (services, appBinder, pathExtractor, matcher) => (route) => {
|
|
137
136
|
const getParamsFromPath = pathToRegexp.match(route.path, { decode: decodeURIComponent });
|
|
138
137
|
const boundServiceProvider = route.requestServiceProvider && appBinder(services, route.requestServiceProvider);
|
|
139
|
-
return (request,
|
|
138
|
+
return (request, logger) => {
|
|
140
139
|
const path = pathExtractor(request);
|
|
141
140
|
const match = getParamsFromPath(path);
|
|
142
141
|
if ((!matcher || matcher(request, route)) && match) {
|
|
143
|
-
return
|
|
142
|
+
return () => route.handler(match.params, boundServiceProvider ? boundServiceProvider({ request, logger }, { route, params: match.params }) : undefined);
|
|
144
143
|
}
|
|
145
144
|
};
|
|
146
145
|
};
|
|
@@ -189,19 +188,18 @@ const makeGetRequestResponder = () => ({ routes, pathExtractor, routeMatcher, er
|
|
|
189
188
|
// types are getting complicated quickly here.
|
|
190
189
|
const isPromise = (thing) => thing instanceof Promise;
|
|
191
190
|
return (request) => {
|
|
192
|
-
const { end, ...profile } = (0, profile_1.createProfile)(new Date().toISOString()).start();
|
|
193
191
|
const logger = appLogger.createSubContext();
|
|
194
192
|
if (logExtractor) {
|
|
195
193
|
logger.setContext(logExtractor(request));
|
|
196
194
|
}
|
|
197
195
|
try {
|
|
198
|
-
const executor = (0, helpers_1.mapFind)(boundRoutes, (route) => route(request,
|
|
196
|
+
const executor = (0, helpers_1.mapFind)(boundRoutes, (route) => route(request, logger));
|
|
199
197
|
if (executor) {
|
|
200
198
|
const result = boundResponseMiddleware ?
|
|
201
|
-
boundResponseMiddleware(executor(), { request,
|
|
199
|
+
boundResponseMiddleware(executor(), { request, logger }) : executor();
|
|
202
200
|
if (isPromise(result) && errorHandler) {
|
|
203
201
|
const errorHandlerWithMiddleware = (e) => boundResponseMiddleware ?
|
|
204
|
-
boundResponseMiddleware(errorHandler(e, logger), { request,
|
|
202
|
+
boundResponseMiddleware(errorHandler(e, logger), { request, logger }) : errorHandler(e, logger);
|
|
205
203
|
return result.catch(errorHandlerWithMiddleware);
|
|
206
204
|
}
|
|
207
205
|
else {
|
|
@@ -209,13 +207,13 @@ const makeGetRequestResponder = () => ({ routes, pathExtractor, routeMatcher, er
|
|
|
209
207
|
}
|
|
210
208
|
}
|
|
211
209
|
else if (boundResponseMiddleware) {
|
|
212
|
-
return boundResponseMiddleware(undefined, { request,
|
|
210
|
+
return boundResponseMiddleware(undefined, { request, logger });
|
|
213
211
|
}
|
|
214
212
|
}
|
|
215
213
|
catch (e) {
|
|
216
214
|
if (errorHandler && e instanceof Error) {
|
|
217
215
|
return boundResponseMiddleware
|
|
218
|
-
? boundResponseMiddleware(errorHandler(e, logger), { request,
|
|
216
|
+
? boundResponseMiddleware(errorHandler(e, logger), { request, logger })
|
|
219
217
|
: errorHandler(e, logger);
|
|
220
218
|
}
|
|
221
219
|
throw e;
|
|
@@ -11,22 +11,22 @@ const decryptionAuthProvider = (initializer) => (configProvider) => {
|
|
|
11
11
|
const cookieName = (0, helpers_1.once)(() => (0, resolveConfigValue_1.resolveConfigValue)(config.cookieName));
|
|
12
12
|
const encryptionPrivateKey = (0, helpers_1.once)(() => (0, resolveConfigValue_1.resolveConfigValue)(config.encryptionPrivateKey));
|
|
13
13
|
const signaturePublicKey = (0, helpers_1.once)(() => (0, resolveConfigValue_1.resolveConfigValue)(config.signaturePublicKey));
|
|
14
|
-
return ({ request
|
|
14
|
+
return ({ request }) => {
|
|
15
15
|
let user;
|
|
16
|
-
const getAuthorizedFetchConfig =
|
|
16
|
+
const getAuthorizedFetchConfig = async () => {
|
|
17
17
|
const [token, headers] = (0, _1.getAuthTokenOrCookie)(request, await cookieName());
|
|
18
18
|
if (!token) {
|
|
19
19
|
return {};
|
|
20
20
|
}
|
|
21
21
|
return { headers };
|
|
22
|
-
}
|
|
23
|
-
const loadUser =
|
|
22
|
+
};
|
|
23
|
+
const loadUser = async () => {
|
|
24
24
|
const [token] = (0, _1.getAuthTokenOrCookie)(request, await cookieName());
|
|
25
25
|
if (!token) {
|
|
26
26
|
return undefined;
|
|
27
27
|
}
|
|
28
28
|
return (0, decryptAndVerify_1.decryptAndVerify)(token, await encryptionPrivateKey(), await signaturePublicKey());
|
|
29
|
-
}
|
|
29
|
+
};
|
|
30
30
|
return {
|
|
31
31
|
getAuthorizedFetchConfig,
|
|
32
32
|
getUser: async () => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { FetchConfig } from '../../fetch';
|
|
2
|
-
import type { Track } from '../../profile';
|
|
3
2
|
import type { HttpHeaders } from '../../routing';
|
|
4
3
|
export interface TokenUser {
|
|
5
4
|
id: number;
|
|
@@ -41,7 +40,6 @@ export declare type CookieAuthProviderRequest = {
|
|
|
41
40
|
};
|
|
42
41
|
export declare type CookieAuthProvider = (inputs: {
|
|
43
42
|
request: CookieAuthProviderRequest;
|
|
44
|
-
profile: Track;
|
|
45
43
|
}) => AuthProvider;
|
|
46
44
|
export declare type StubAuthProvider = (user: User | undefined) => AuthProvider;
|
|
47
45
|
export declare const stubAuthProvider: (user?: User | undefined) => AuthProvider;
|
|
@@ -9,23 +9,23 @@ const subrequestAuthProvider = (initializer) => (configProvider) => {
|
|
|
9
9
|
const config = configProvider[(0, guards_1.ifDefined)(initializer.configSpace, 'subrequest')];
|
|
10
10
|
const cookieName = (0, __1.once)(() => (0, config_1.resolveConfigValue)(config.cookieName));
|
|
11
11
|
const accountsBase = (0, __1.once)(() => (0, config_1.resolveConfigValue)(config.accountsBase));
|
|
12
|
-
return ({ request
|
|
12
|
+
return ({ request }) => {
|
|
13
13
|
let user;
|
|
14
|
-
const getAuthorizedFetchConfig =
|
|
14
|
+
const getAuthorizedFetchConfig = async () => {
|
|
15
15
|
const [token, headers] = (0, _1.getAuthTokenOrCookie)(request, await cookieName());
|
|
16
16
|
if (!token) {
|
|
17
17
|
return {};
|
|
18
18
|
}
|
|
19
19
|
return { headers };
|
|
20
|
-
}
|
|
21
|
-
const loadUser =
|
|
20
|
+
};
|
|
21
|
+
const loadUser = async () => {
|
|
22
22
|
const [token, headers] = (0, _1.getAuthTokenOrCookie)(request, await cookieName());
|
|
23
23
|
if (!token) {
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return initializer.fetch((await accountsBase()).replace(/\/+$/, '') + '/api/user', { headers })
|
|
27
27
|
.then(response => response.json());
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
29
|
return {
|
|
30
30
|
getAuthorizedFetchConfig,
|
|
31
31
|
getUser: async () => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ConfigProviderForConfig } from '../../config';
|
|
2
2
|
import { GenericFetch } from '../../fetch';
|
|
3
|
-
import { Track } from '../../profile';
|
|
4
3
|
export declare type Config = {
|
|
5
4
|
defaultCorrectness?: string;
|
|
6
5
|
exercisesHost: string;
|
|
@@ -63,12 +62,10 @@ export declare const exercisesGateway: <C extends string = "exercises">(initiali
|
|
|
63
62
|
defaultCorrectness?: import("../../config").ConfigValueProvider<string> | undefined;
|
|
64
63
|
exercisesHost: import("../../config").ConfigValueProvider<string>;
|
|
65
64
|
exercisesAuthToken: import("../../config").ConfigValueProvider<string>;
|
|
66
|
-
}; }) => ({
|
|
67
|
-
|
|
68
|
-
}) => {
|
|
69
|
-
searchDigest: (query: string, page?: any, per_page?: any) => Promise<string>;
|
|
65
|
+
}; }) => (_: {}) => {
|
|
66
|
+
searchDigest: (query: string, page?: number, per_page?: number) => Promise<string>;
|
|
70
67
|
get: (uuid: string) => Promise<Exercise | undefined>;
|
|
71
|
-
search: (query: string, page?:
|
|
68
|
+
search: (query: string, page?: number, per_page?: number) => Promise<ExercisesSearchResultsWithDigest>;
|
|
72
69
|
};
|
|
73
70
|
export declare type ExercisesGateway = ReturnType<ReturnType<ReturnType<typeof exercisesGateway>>>;
|
|
74
71
|
export {};
|
|
@@ -58,7 +58,7 @@ const exercisesGateway = (initializer) => (configProvider) => {
|
|
|
58
58
|
}
|
|
59
59
|
return exercise;
|
|
60
60
|
};
|
|
61
|
-
return (
|
|
61
|
+
return (_) => {
|
|
62
62
|
const request = async (method, path, query = undefined) => {
|
|
63
63
|
const host = (await exercisesHost()).replace(/\/+$/, '');
|
|
64
64
|
const baseUrl = `${host}/api/${path}`;
|
|
@@ -70,22 +70,22 @@ const exercisesGateway = (initializer) => (configProvider) => {
|
|
|
70
70
|
method,
|
|
71
71
|
});
|
|
72
72
|
};
|
|
73
|
-
const searchDigest =
|
|
73
|
+
const searchDigest = async (query, page = 1, per_page = 100) => {
|
|
74
74
|
const response = await request(routing_1.METHOD.HEAD, 'exercises', { query, page, per_page });
|
|
75
75
|
return (0, assertions_1.assertString)(response.headers.get('X-Digest'), 'OpenStax Exercises search endpoint HEAD did not return an X-Digest header');
|
|
76
|
-
}
|
|
77
|
-
const search =
|
|
76
|
+
};
|
|
77
|
+
const search = async (query, page = 1, per_page = 100) => {
|
|
78
78
|
const response = await request(routing_1.METHOD.GET, 'exercises', { query, page, per_page });
|
|
79
79
|
const digest = (0, assertions_1.assertString)(response.headers.get('X-Digest'), 'OpenStax Exercises search endpoint GET did not return an X-Digest header');
|
|
80
80
|
const { items, total_count } = await response.json();
|
|
81
81
|
return { digest, items: await Promise.all(items.map(doDefaultCorrectness)), total_count };
|
|
82
|
-
}
|
|
83
|
-
const get =
|
|
82
|
+
};
|
|
83
|
+
const get = async (uuid) => {
|
|
84
84
|
const response = await request(routing_1.METHOD.GET, `exercises/${uuid}`);
|
|
85
85
|
return response.status === 404
|
|
86
86
|
? undefined
|
|
87
87
|
: response.json().then(doDefaultCorrectness);
|
|
88
|
-
}
|
|
88
|
+
};
|
|
89
89
|
return {
|
|
90
90
|
searchDigest,
|
|
91
91
|
get,
|
|
@@ -9,7 +9,7 @@ export interface Grade {
|
|
|
9
9
|
comment?: string;
|
|
10
10
|
activityProgress: 'Initialized' | 'Started' | 'inProgress' | 'Submitted' | 'Completed';
|
|
11
11
|
gradingProgress: 'FullyGraded' | 'Pending' | 'PendingManual' | 'Failed' | 'NotReady';
|
|
12
|
-
userId
|
|
12
|
+
userId?: string;
|
|
13
13
|
}
|
|
14
14
|
export declare const getRegistrationAttemptInfo: (lrs: LrsGateway, registration: string, options?: {
|
|
15
15
|
activity?: string | undefined;
|
|
@@ -25,7 +25,7 @@ export declare const getScoreGrade: (score: {
|
|
|
25
25
|
raw?: number;
|
|
26
26
|
min?: number;
|
|
27
27
|
max?: number;
|
|
28
|
-
}, completed: boolean, userId
|
|
28
|
+
}, completed: boolean, userId?: string | undefined, maxScore?: number | undefined) => Grade;
|
|
29
29
|
export declare type Progress = {
|
|
30
30
|
scaled: number;
|
|
31
31
|
max?: number;
|
|
@@ -34,6 +34,7 @@ export declare type Progress = {
|
|
|
34
34
|
export declare type GradeAndProgress = {
|
|
35
35
|
grade: Grade;
|
|
36
36
|
progress: Progress;
|
|
37
|
+
name?: string;
|
|
37
38
|
};
|
|
38
39
|
export declare const getCurrentGrade: (services: {
|
|
39
40
|
lrs: LrsGateway;
|
|
@@ -41,6 +42,7 @@ export declare const getCurrentGrade: (services: {
|
|
|
41
42
|
}, registration: string, options?: {
|
|
42
43
|
currentPreference?: "latest" | "oldest" | undefined;
|
|
43
44
|
incompleteAttemptCallback?: ((info: ActivityState) => Promise<GradeAndProgress>) | undefined;
|
|
45
|
+
name?: string | undefined;
|
|
44
46
|
scoreMaximum?: number | undefined;
|
|
45
47
|
userId?: string | undefined;
|
|
46
48
|
} | undefined) => Promise<GradeAndProgress | null>;
|
|
@@ -48,17 +48,20 @@ const getScoreGrade = (score, completed, userId, maxScore) => {
|
|
|
48
48
|
};
|
|
49
49
|
};
|
|
50
50
|
exports.getScoreGrade = getScoreGrade;
|
|
51
|
-
// These methods
|
|
52
|
-
const getCompletedActivityStateGradeAndProgress = (state, userId
|
|
51
|
+
// These methods assign 0's to incomplete activities
|
|
52
|
+
const getCompletedActivityStateGradeAndProgress = ({ name, scoreMaximum, state, userId }) => {
|
|
53
53
|
var _a, _b;
|
|
54
54
|
return ({
|
|
55
|
-
grade: (0, exports.getScoreGrade)(((_b = (_a = state.currentAttemptCompleted) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.score) || {}, !!state.currentAttemptCompleted, userId,
|
|
55
|
+
grade: (0, exports.getScoreGrade)(((_b = (_a = state.currentAttemptCompleted) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.score) || {}, !!state.currentAttemptCompleted, userId, scoreMaximum),
|
|
56
56
|
progress: {
|
|
57
57
|
scaled: state.currentAttemptCompleted ? 1 : 0,
|
|
58
58
|
},
|
|
59
|
+
name,
|
|
59
60
|
});
|
|
60
61
|
};
|
|
61
|
-
const getCompletedUserInfosGradeAndProgress = (infos, scoreMaximum) => infos.map(({ data, fullName, platformUserId }) => getCompletedActivityStateGradeAndProgress(
|
|
62
|
+
const getCompletedUserInfosGradeAndProgress = (infos, scoreMaximum) => infos.map(({ data, fullName, platformUserId }) => getCompletedActivityStateGradeAndProgress({
|
|
63
|
+
name: fullName, scoreMaximum, userId: platformUserId, state: data
|
|
64
|
+
}));
|
|
62
65
|
const getCurrentGrade = async (services, registration, options) => {
|
|
63
66
|
var _a;
|
|
64
67
|
const user = await services.ltiAuthProvider.getUser();
|
|
@@ -66,14 +69,14 @@ const getCurrentGrade = async (services, registration, options) => {
|
|
|
66
69
|
return null;
|
|
67
70
|
}
|
|
68
71
|
const userId = (_a = options === null || options === void 0 ? void 0 : options.userId) !== null && _a !== void 0 ? _a : user.uuid;
|
|
69
|
-
const { currentPreference, incompleteAttemptCallback, scoreMaximum } = options !== null && options !== void 0 ? options : {};
|
|
72
|
+
const { currentPreference, incompleteAttemptCallback, name, scoreMaximum } = options !== null && options !== void 0 ? options : {};
|
|
70
73
|
const infoPerUser = await (0, exports.getRegistrationAttemptInfo)(services.lrs, registration, { currentPreference });
|
|
71
74
|
const userInfo = infoPerUser[user.uuid];
|
|
72
75
|
if (!userInfo) {
|
|
73
|
-
return getCompletedActivityStateGradeAndProgress((0, attempt_utils_1.resolveAttemptInfo)([]), userId
|
|
76
|
+
return getCompletedActivityStateGradeAndProgress({ name, scoreMaximum, state: (0, attempt_utils_1.resolveAttemptInfo)([]), userId });
|
|
74
77
|
}
|
|
75
78
|
if (userInfo.currentAttemptCompleted || !incompleteAttemptCallback) {
|
|
76
|
-
return getCompletedActivityStateGradeAndProgress(userInfo, userId
|
|
79
|
+
return getCompletedActivityStateGradeAndProgress({ name, scoreMaximum, state: userInfo, userId });
|
|
77
80
|
}
|
|
78
81
|
return incompleteAttemptCallback(userInfo);
|
|
79
82
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ConfigProviderForConfig } from '../../config';
|
|
2
|
-
import { Track } from '../../profile';
|
|
3
2
|
import { TDocument, VersionedDocumentAuthor } from '.';
|
|
4
3
|
declare type DynamoConfig = {
|
|
5
4
|
tableName: string;
|
|
@@ -9,9 +8,7 @@ interface Initializer<C> {
|
|
|
9
8
|
}
|
|
10
9
|
export declare const dynamoVersionedDocumentStore: <C extends string = "dynamodb">(initializer?: Initializer<C> | undefined) => <T extends TDocument<T>>() => (configProvider: { [key in C]: {
|
|
11
10
|
tableName: import("../../config").ConfigValueProvider<string>;
|
|
12
|
-
}; }) => <K extends keyof T, A extends ((...a: any[]) => Promise<VersionedDocumentAuthor>) | undefined>({
|
|
13
|
-
profile: Track;
|
|
14
|
-
}, hashKey: K, getAuthor: A) => {
|
|
11
|
+
}; }) => <K extends keyof T, A extends ((...a: any[]) => Promise<VersionedDocumentAuthor>) | undefined>(_: {}, hashKey: K, getAuthor: A) => {
|
|
15
12
|
loadAllDocumentsTheBadWay: () => Promise<T[]>;
|
|
16
13
|
getVersions: (id: T[K], startVersion?: number | undefined) => Promise<{
|
|
17
14
|
items: T[];
|
|
@@ -51,11 +51,11 @@ const decodeDynamoAttribute = (value) => {
|
|
|
51
51
|
};
|
|
52
52
|
const decodeDynamoDocument = (document) => Object.fromEntries(Object.entries(document).map(([key, value]) => ([key, decodeDynamoAttribute(value)])));
|
|
53
53
|
// i'm not really excited about getAuthor being required, but ts is getting confused about the type when unspecified
|
|
54
|
-
const dynamoVersionedDocumentStore = (initializer) => () => (configProvider) => (
|
|
54
|
+
const dynamoVersionedDocumentStore = (initializer) => () => (configProvider) => (_, hashKey, getAuthor) => {
|
|
55
55
|
const options = (0, guards_1.ifDefined)(initializer, {});
|
|
56
56
|
const tableName = (0, __1.once)(() => (0, config_1.resolveConfigValue)(configProvider[(0, guards_1.ifDefined)(options.configSpace, 'dynamodb')].tableName));
|
|
57
57
|
return {
|
|
58
|
-
loadAllDocumentsTheBadWay:
|
|
58
|
+
loadAllDocumentsTheBadWay: async () => {
|
|
59
59
|
const loadAllResults = async (ExclusiveStartKey) => {
|
|
60
60
|
var _a;
|
|
61
61
|
const cmd = new client_dynamodb_1.ScanCommand({ TableName: await tableName(), ExclusiveStartKey });
|
|
@@ -74,8 +74,8 @@ const dynamoVersionedDocumentStore = (initializer) => () => (configProvider) =>
|
|
|
74
74
|
return result;
|
|
75
75
|
}, new Map()));
|
|
76
76
|
return Array.from(allResults.values());
|
|
77
|
-
}
|
|
78
|
-
getVersions:
|
|
77
|
+
},
|
|
78
|
+
getVersions: async (id, startVersion) => {
|
|
79
79
|
const cmd = new client_dynamodb_1.QueryCommand({
|
|
80
80
|
TableName: await tableName(),
|
|
81
81
|
KeyConditionExpression: '#hk = :hkv',
|
|
@@ -104,8 +104,8 @@ const dynamoVersionedDocumentStore = (initializer) => () => (configProvider) =>
|
|
|
104
104
|
nextPageToken: result.LastEvaluatedKey ? decodeDynamoDocument(result.LastEvaluatedKey).timestamp : undefined
|
|
105
105
|
};
|
|
106
106
|
});
|
|
107
|
-
}
|
|
108
|
-
getItem:
|
|
107
|
+
},
|
|
108
|
+
getItem: async (id, timestamp) => {
|
|
109
109
|
let keyConditionExpression = '#hk = :hkv';
|
|
110
110
|
const expressionAttributeNames = {
|
|
111
111
|
'#hk': hashKey.toString()
|
|
@@ -130,18 +130,18 @@ const dynamoVersionedDocumentStore = (initializer) => () => (configProvider) =>
|
|
|
130
130
|
var _a;
|
|
131
131
|
return (_a = result.Items) === null || _a === void 0 ? void 0 : _a.map(decodeDynamoDocument)[0];
|
|
132
132
|
});
|
|
133
|
-
}
|
|
133
|
+
},
|
|
134
134
|
/* prepares a new version of a document with the given data, then allows some additional
|
|
135
135
|
* changes to be input to a `save` function that actually saves it. useful for additional
|
|
136
136
|
* changes based on the new document version or author. the document version and author
|
|
137
137
|
* cannot be modified */
|
|
138
|
-
prepareItem:
|
|
138
|
+
prepareItem: async (item, ...authorArgs) => {
|
|
139
139
|
// this getAuthor type is terrible
|
|
140
140
|
const author = getAuthor ? await getAuthor(...authorArgs) : authorArgs[0];
|
|
141
141
|
const timestamp = new Date().getTime();
|
|
142
142
|
return {
|
|
143
143
|
document: { ...item, timestamp, author },
|
|
144
|
-
save:
|
|
144
|
+
save: async (changes) => {
|
|
145
145
|
const document = {
|
|
146
146
|
...item,
|
|
147
147
|
...changes,
|
|
@@ -154,11 +154,11 @@ const dynamoVersionedDocumentStore = (initializer) => () => (configProvider) =>
|
|
|
154
154
|
});
|
|
155
155
|
return dynamodb().send(cmd)
|
|
156
156
|
.then(() => document);
|
|
157
|
-
}
|
|
157
|
+
}
|
|
158
158
|
};
|
|
159
|
-
}
|
|
159
|
+
},
|
|
160
160
|
/* saves a new version of a document with the given data */
|
|
161
|
-
putItem:
|
|
161
|
+
putItem: async (item, ...authorArgs) => {
|
|
162
162
|
// this getAuthor type is terrible
|
|
163
163
|
const author = getAuthor ? await getAuthor(...authorArgs) : authorArgs[0];
|
|
164
164
|
const document = {
|
|
@@ -172,7 +172,7 @@ const dynamoVersionedDocumentStore = (initializer) => () => (configProvider) =>
|
|
|
172
172
|
});
|
|
173
173
|
return dynamodb().send(cmd)
|
|
174
174
|
.then(() => document);
|
|
175
|
-
}
|
|
175
|
+
},
|
|
176
176
|
};
|
|
177
177
|
};
|
|
178
178
|
exports.dynamoVersionedDocumentStore = dynamoVersionedDocumentStore;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ConfigProviderForConfig } from '../../config';
|
|
2
|
-
import { Track } from '../../profile';
|
|
3
2
|
import { TDocument, VersionedDocumentAuthor } from '.';
|
|
4
3
|
declare type Config = {
|
|
5
4
|
tableName: string;
|
|
@@ -11,9 +10,7 @@ interface Initializer<C> {
|
|
|
11
10
|
}
|
|
12
11
|
export declare const fileSystemVersionedDocumentStore: <C extends string = "fileSystem">(initializer: Initializer<C>) => <T extends TDocument<T>>() => (configProvider: { [key in C]: {
|
|
13
12
|
tableName: import("../../config").ConfigValueProvider<string>;
|
|
14
|
-
}; }) => <K extends keyof T, A extends ((...a: any[]) => Promise<VersionedDocumentAuthor>) | undefined>({
|
|
15
|
-
profile: Track;
|
|
16
|
-
}, hashKey: K, getAuthor: A) => {
|
|
13
|
+
}; }) => <K extends keyof T, A extends ((...a: any[]) => Promise<VersionedDocumentAuthor>) | undefined>(_: {}, hashKey: K, getAuthor: A) => {
|
|
17
14
|
loadAllDocumentsTheBadWay: () => Promise<T[]>;
|
|
18
15
|
getVersions: (id: T[K], startVersion?: number | undefined) => Promise<{
|
|
19
16
|
items: T[];
|
|
@@ -33,7 +33,7 @@ const __1 = require("../..");
|
|
|
33
33
|
const config_1 = require("../../config");
|
|
34
34
|
const guards_1 = require("../../guards");
|
|
35
35
|
const PAGE_LIMIT = 5;
|
|
36
|
-
const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider) => (
|
|
36
|
+
const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider) => (_, hashKey, getAuthor) => {
|
|
37
37
|
const tableName = (0, config_1.resolveConfigValue)(configProvider[initializer.configSpace || 'fileSystem'].tableName);
|
|
38
38
|
const filePath = tableName.then((table) => path_1.default.join(initializer.dataDir, table));
|
|
39
39
|
const { readFile, writeFile } = (0, guards_1.ifDefined)(initializer.fs, fsModule);
|
|
@@ -59,14 +59,14 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
|
|
|
59
59
|
}));
|
|
60
60
|
let previousSave;
|
|
61
61
|
return {
|
|
62
|
-
loadAllDocumentsTheBadWay:
|
|
62
|
+
loadAllDocumentsTheBadWay: async () => {
|
|
63
63
|
await load;
|
|
64
64
|
return Object.entries(data || []).map(([, versions]) => {
|
|
65
65
|
const versionsList = Object.values(versions);
|
|
66
66
|
return versionsList[versionsList.length - 1];
|
|
67
67
|
});
|
|
68
|
-
}
|
|
69
|
-
getVersions:
|
|
68
|
+
},
|
|
69
|
+
getVersions: async (id, startVersion) => {
|
|
70
70
|
await load;
|
|
71
71
|
const versions = data === null || data === void 0 ? void 0 : data[(0, __1.hashValue)(id)];
|
|
72
72
|
const versionsList = versions ? Object.values(versions).reverse() : undefined;
|
|
@@ -80,8 +80,8 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
|
|
|
80
80
|
items,
|
|
81
81
|
nextPageToken: hasMore ? items[items.length - 1].timestamp : undefined
|
|
82
82
|
};
|
|
83
|
-
}
|
|
84
|
-
getItem:
|
|
83
|
+
},
|
|
84
|
+
getItem: async (id, timestamp) => {
|
|
85
85
|
await load;
|
|
86
86
|
const versions = data === null || data === void 0 ? void 0 : data[(0, __1.hashValue)(id)];
|
|
87
87
|
if (timestamp) {
|
|
@@ -89,14 +89,14 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
|
|
|
89
89
|
}
|
|
90
90
|
const versionsList = versions ? Object.values(versions) : [];
|
|
91
91
|
return versionsList[versionsList.length - 1];
|
|
92
|
-
}
|
|
93
|
-
prepareItem:
|
|
92
|
+
},
|
|
93
|
+
prepareItem: async (item, ...authorArgs) => {
|
|
94
94
|
// this getAuthor type is terrible
|
|
95
95
|
const author = getAuthor ? await getAuthor(...authorArgs) : authorArgs[0];
|
|
96
96
|
const timestamp = new Date().getTime();
|
|
97
97
|
return {
|
|
98
98
|
document: { ...item, timestamp, author },
|
|
99
|
-
save:
|
|
99
|
+
save: async (changes) => {
|
|
100
100
|
await load;
|
|
101
101
|
await previousSave;
|
|
102
102
|
const save = previousSave = new Promise(resolve => (async () => {
|
|
@@ -109,10 +109,10 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
|
|
|
109
109
|
writeFile(path, JSON.stringify(data, null, 2), () => resolve(document));
|
|
110
110
|
})());
|
|
111
111
|
return await save;
|
|
112
|
-
}
|
|
112
|
+
}
|
|
113
113
|
};
|
|
114
|
-
}
|
|
115
|
-
putItem:
|
|
114
|
+
},
|
|
115
|
+
putItem: async (item, ...authorArgs) => {
|
|
116
116
|
await load;
|
|
117
117
|
await previousSave;
|
|
118
118
|
const save = previousSave = new Promise(resolve => (async () => {
|
|
@@ -127,7 +127,7 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
|
|
|
127
127
|
writeFile(path, JSON.stringify(data, null, 2), () => resolve(document));
|
|
128
128
|
})());
|
|
129
129
|
return await save;
|
|
130
|
-
}
|
|
130
|
+
},
|
|
131
131
|
};
|
|
132
132
|
};
|
|
133
133
|
exports.fileSystemVersionedDocumentStore = fileSystemVersionedDocumentStore;
|