@openstax/ts-utils 1.40.2 → 1.41.2
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/services/accountsGateway/index.d.ts +3 -0
- package/dist/cjs/services/accountsGateway/index.js +1 -0
- package/dist/cjs/services/documentStore/dynamoEncoding.js +2 -2
- package/dist/cjs/services/documentStore/unversioned/file-system.d.ts +0 -1
- package/dist/cjs/services/documentStore/unversioned/file-system.js +0 -19
- package/dist/cjs/services/documentStore/versioned/file-system.d.ts +0 -1
- package/dist/cjs/services/documentStore/versioned/file-system.js +1 -12
- package/dist/cjs/services/lrsGateway/batching.d.ts +46 -0
- package/dist/cjs/services/lrsGateway/batching.js +106 -0
- package/dist/cjs/services/lrsGateway/file-system.js +52 -2
- package/dist/cjs/services/lrsGateway/index.d.ts +13 -0
- package/dist/cjs/services/lrsGateway/index.js +151 -55
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/services/accountsGateway/index.d.ts +3 -0
- package/dist/esm/services/accountsGateway/index.js +1 -0
- package/dist/esm/services/documentStore/dynamoEncoding.js +2 -2
- package/dist/esm/services/documentStore/unversioned/file-system.d.ts +0 -1
- package/dist/esm/services/documentStore/unversioned/file-system.js +0 -19
- package/dist/esm/services/documentStore/versioned/file-system.d.ts +0 -1
- package/dist/esm/services/documentStore/versioned/file-system.js +1 -12
- package/dist/esm/services/lrsGateway/batching.d.ts +46 -0
- package/dist/esm/services/lrsGateway/batching.js +102 -0
- package/dist/esm/services/lrsGateway/file-system.js +52 -2
- package/dist/esm/services/lrsGateway/index.d.ts +13 -0
- package/dist/esm/services/lrsGateway/index.js +151 -22
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/cjs/services/documentStore/fileSystemAssert.d.ts +0 -1
- package/dist/cjs/services/documentStore/fileSystemAssert.js +0 -14
- package/dist/esm/services/documentStore/fileSystemAssert.d.ts +0 -1
- package/dist/esm/services/documentStore/fileSystemAssert.js +0 -10
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as queryString from 'query-string';
|
|
2
1
|
import { once } from '../..';
|
|
3
2
|
import { assertDefined } from '../../assertions';
|
|
4
3
|
import { resolveConfigValue } from '../../config';
|
|
@@ -8,6 +7,7 @@ import { ifDefined } from '../../guards';
|
|
|
8
7
|
import { retryWithDelay } from '../../misc/helpers';
|
|
9
8
|
import { METHOD } from '../../routing';
|
|
10
9
|
import { addStatementDefaultFields } from './addStatementDefaultFields';
|
|
10
|
+
import { RequestBatcher } from './batching';
|
|
11
11
|
export const lrsGateway = (initializer) => (configProvider) => {
|
|
12
12
|
const config = configProvider[ifDefined(initializer.configSpace, 'lrs')];
|
|
13
13
|
const lrsHost = once(() => resolveConfigValue(config.lrsHost));
|
|
@@ -19,20 +19,51 @@ export const lrsGateway = (initializer) => (configProvider) => {
|
|
|
19
19
|
wait: 1000,
|
|
20
20
|
status: [502]
|
|
21
21
|
});
|
|
22
|
+
// Initialize request batcher if batching is enabled
|
|
23
|
+
const enableBatching = initializer.enableBatching !== false;
|
|
24
|
+
const batcher = new RequestBatcher(fetcher, {
|
|
25
|
+
batchEndpoint: '/api/batch',
|
|
26
|
+
getAuthHeader: lrsAuthorization,
|
|
27
|
+
getHost: lrsHost,
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Makes a fetch request, optionally through the request batcher.
|
|
31
|
+
* Automatically batches concurrent requests when batching is enabled.
|
|
32
|
+
*/
|
|
33
|
+
const makeFetch = async (options) => {
|
|
34
|
+
return enableBatching ? batcher.queueRequest(options) : batcher.singleRequest(options);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Formats an agent parameter into a full XapiAgent object.
|
|
38
|
+
* Accepts either a UUID string or a full XapiAgent object.
|
|
39
|
+
*/
|
|
40
|
+
const formatAgent = (agent) => {
|
|
41
|
+
if (typeof agent === 'string') {
|
|
42
|
+
return {
|
|
43
|
+
objectType: 'Agent',
|
|
44
|
+
account: {
|
|
45
|
+
homePage: 'https://openstax.org',
|
|
46
|
+
name: agent,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return agent;
|
|
51
|
+
};
|
|
22
52
|
// Note: This method actually uses POST
|
|
23
53
|
const putXapiStatements = async (statements, user) => {
|
|
24
54
|
const userObj = user
|
|
25
55
|
? { uuid: user }
|
|
26
56
|
: assertDefined(await authProvider.getUser(), new UnauthorizedError);
|
|
27
57
|
const statementsWithDefaults = statements.map(statement => addStatementDefaultFields(statement, userObj));
|
|
28
|
-
const response = await
|
|
29
|
-
|
|
58
|
+
const response = await makeFetch({
|
|
59
|
+
path: '/data/xAPI/statements',
|
|
60
|
+
method: METHOD.POST,
|
|
30
61
|
headers: {
|
|
31
62
|
Authorization: await lrsAuthorization(),
|
|
32
63
|
'Content-Type': 'application/json',
|
|
33
64
|
'X-Experience-API-Version': '1.0.0',
|
|
34
65
|
},
|
|
35
|
-
|
|
66
|
+
body: JSON.stringify(statementsWithDefaults),
|
|
36
67
|
});
|
|
37
68
|
if (![200, 201].includes(response.status)) {
|
|
38
69
|
throw new Error(`Unexpected LRS POST statements response code ${response.status} with body:
|
|
@@ -52,29 +83,51 @@ ${await response.text()}`);
|
|
|
52
83
|
}
|
|
53
84
|
return response.json();
|
|
54
85
|
};
|
|
55
|
-
const getMoreXapiStatements = async (more) =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
86
|
+
const getMoreXapiStatements = async (more) => {
|
|
87
|
+
// 'more' is a full path with query string, so we don't use makeFetch batching here
|
|
88
|
+
const host = await lrsHost();
|
|
89
|
+
return fetcher(host.replace(/\/+$/, '') + more, {
|
|
90
|
+
headers: {
|
|
91
|
+
Authorization: await lrsAuthorization(),
|
|
92
|
+
'X-Experience-API-Version': '1.0.0',
|
|
93
|
+
},
|
|
94
|
+
}).then(formatGetXapiStatementsResponse);
|
|
95
|
+
};
|
|
96
|
+
const fetchXapiStatements = async ({ user, anyUser, ...options }) => {
|
|
97
|
+
const queryParams = {};
|
|
98
|
+
// Add filter params
|
|
99
|
+
if (options.verb)
|
|
100
|
+
queryParams.verb = options.verb;
|
|
101
|
+
if (options.activity)
|
|
102
|
+
queryParams.activity = options.activity;
|
|
103
|
+
if (options.registration)
|
|
104
|
+
queryParams.registration = options.registration;
|
|
105
|
+
if (options.related_activities !== undefined)
|
|
106
|
+
queryParams.related_activities = String(options.related_activities);
|
|
107
|
+
if (options.since)
|
|
108
|
+
queryParams.since = options.since;
|
|
109
|
+
if (options.until)
|
|
110
|
+
queryParams.until = options.until;
|
|
111
|
+
// Add agent unless anyUser is true
|
|
112
|
+
if (anyUser !== true) {
|
|
113
|
+
queryParams.agent = JSON.stringify({
|
|
65
114
|
account: {
|
|
66
115
|
homePage: 'https://openstax.org',
|
|
67
116
|
name: user || assertDefined(await authProvider.getUser(), new UnauthorizedError()).uuid,
|
|
68
117
|
},
|
|
69
118
|
objectType: 'Agent',
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return makeFetch({
|
|
122
|
+
path: '/data/xAPI/statements',
|
|
123
|
+
method: METHOD.GET,
|
|
124
|
+
queryParams,
|
|
125
|
+
headers: {
|
|
126
|
+
Authorization: await lrsAuthorization(),
|
|
127
|
+
'X-Experience-API-Version': '1.0.0',
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
};
|
|
78
131
|
const getXapiStatements = async (params) => {
|
|
79
132
|
const { ensureSync, ...fetchParams } = params;
|
|
80
133
|
if (ensureSync) {
|
|
@@ -103,11 +156,87 @@ ${await response.text()}`);
|
|
|
103
156
|
};
|
|
104
157
|
return loadRemaining(await getXapiStatements(params));
|
|
105
158
|
};
|
|
159
|
+
/**
|
|
160
|
+
* Get a single state document from the xAPI State API.
|
|
161
|
+
*
|
|
162
|
+
* @param activityId - The activity ID
|
|
163
|
+
* @param agent - The agent (UUID string or full XapiAgent object)
|
|
164
|
+
* @param stateId - The state document identifier
|
|
165
|
+
* @param registration - Optional registration UUID
|
|
166
|
+
* @returns The state document as a JSON object, or null if not found
|
|
167
|
+
*/
|
|
168
|
+
const getState = async (activityId, agent, stateId, registration) => {
|
|
169
|
+
const agentObj = formatAgent(agent);
|
|
170
|
+
const queryParams = {
|
|
171
|
+
activityId,
|
|
172
|
+
agent: JSON.stringify(agentObj),
|
|
173
|
+
stateId,
|
|
174
|
+
};
|
|
175
|
+
if (registration) {
|
|
176
|
+
queryParams.registration = registration;
|
|
177
|
+
}
|
|
178
|
+
const response = await makeFetch({
|
|
179
|
+
path: '/data/xAPI/activities/state',
|
|
180
|
+
method: METHOD.GET,
|
|
181
|
+
queryParams,
|
|
182
|
+
headers: {
|
|
183
|
+
Authorization: await lrsAuthorization(),
|
|
184
|
+
'X-Experience-API-Version': '1.0.0',
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
if (response.status === 404) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
if (response.status !== 200) {
|
|
191
|
+
throw new Error(`Unexpected LRS GET state response code ${response.status} with body:
|
|
192
|
+
|
|
193
|
+
${await response.text()}`);
|
|
194
|
+
}
|
|
195
|
+
return response.json();
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Create or update a state document in the xAPI State API.
|
|
199
|
+
*
|
|
200
|
+
* @param activityId - The activity ID
|
|
201
|
+
* @param agent - The agent (UUID string or full XapiAgent object)
|
|
202
|
+
* @param stateId - The state document identifier
|
|
203
|
+
* @param body - The state document content (JSON object)
|
|
204
|
+
* @param registration - Optional registration UUID
|
|
205
|
+
*/
|
|
206
|
+
const setState = async (activityId, agent, stateId, body, registration) => {
|
|
207
|
+
const agentObj = formatAgent(agent);
|
|
208
|
+
const queryParams = {
|
|
209
|
+
activityId,
|
|
210
|
+
agent: JSON.stringify(agentObj),
|
|
211
|
+
stateId,
|
|
212
|
+
};
|
|
213
|
+
if (registration) {
|
|
214
|
+
queryParams.registration = registration;
|
|
215
|
+
}
|
|
216
|
+
const response = await makeFetch({
|
|
217
|
+
path: '/data/xAPI/activities/state',
|
|
218
|
+
method: METHOD.PUT,
|
|
219
|
+
queryParams,
|
|
220
|
+
headers: {
|
|
221
|
+
Authorization: await lrsAuthorization(),
|
|
222
|
+
'Content-Type': 'application/json',
|
|
223
|
+
'X-Experience-API-Version': '1.0.0',
|
|
224
|
+
},
|
|
225
|
+
body: JSON.stringify(body),
|
|
226
|
+
});
|
|
227
|
+
if (response.status !== 204) {
|
|
228
|
+
throw new Error(`Unexpected LRS PUT state response code ${response.status} with body:
|
|
229
|
+
|
|
230
|
+
${await response.text()}`);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
106
233
|
return {
|
|
107
234
|
putXapiStatements,
|
|
108
235
|
getXapiStatements,
|
|
109
236
|
getMoreXapiStatements,
|
|
110
237
|
getAllXapiStatements,
|
|
238
|
+
getState,
|
|
239
|
+
setState,
|
|
111
240
|
};
|
|
112
241
|
};
|
|
113
242
|
};
|