@openstax/ts-utils 1.27.1 → 1.27.3
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/middleware/lambdaCorsResponseMiddleware.js +3 -1
- package/dist/cjs/services/accountsGateway/index.js +5 -4
- package/dist/cjs/services/apiGateway/index.d.ts +9 -10
- package/dist/cjs/services/documentStore/unversioned/dynamodb.d.ts +2 -0
- package/dist/cjs/services/documentStore/unversioned/dynamodb.js +145 -143
- package/dist/cjs/services/documentStore/versioned/dynamodb.d.ts +2 -0
- package/dist/cjs/services/documentStore/versioned/dynamodb.js +125 -123
- package/dist/cjs/services/fileServer/s3FileServer.d.ts +1 -1
- package/dist/cjs/services/fileServer/s3FileServer.js +5 -2
- package/dist/cjs/services/launchParams/verifier.js +3 -1
- package/dist/cjs/services/searchProvider/index.d.ts +4 -0
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/middleware/lambdaCorsResponseMiddleware.js +3 -1
- package/dist/esm/services/accountsGateway/index.js +5 -4
- package/dist/esm/services/apiGateway/index.d.ts +9 -10
- package/dist/esm/services/documentStore/unversioned/dynamodb.d.ts +2 -0
- package/dist/esm/services/documentStore/unversioned/dynamodb.js +145 -143
- package/dist/esm/services/documentStore/versioned/dynamodb.d.ts +2 -0
- package/dist/esm/services/documentStore/versioned/dynamodb.js +125 -123
- package/dist/esm/services/fileServer/s3FileServer.d.ts +1 -1
- package/dist/esm/services/fileServer/s3FileServer.js +5 -2
- package/dist/esm/services/launchParams/verifier.js +3 -1
- package/dist/esm/services/searchProvider/index.d.ts +4 -0
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -3,135 +3,137 @@ import { once } from '../../..';
|
|
|
3
3
|
import { resolveConfigValue } from '../../../config';
|
|
4
4
|
import { ifDefined } from '../../../guards';
|
|
5
5
|
import { decodeDynamoDocument, encodeDynamoAttribute, encodeDynamoDocument } from '../dynamoEncoding';
|
|
6
|
-
const dynamodb = once(() => new DynamoDB({ apiVersion: '2012-08-10' }));
|
|
7
6
|
// i'm not really excited about getAuthor being required, but ts is getting confused about the type when unspecified
|
|
8
|
-
export const dynamoVersionedDocumentStore = (initializer) =>
|
|
7
|
+
export const dynamoVersionedDocumentStore = (initializer) => {
|
|
9
8
|
const init = ifDefined(initializer, {});
|
|
10
|
-
const
|
|
11
|
-
return {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
':hkv'
|
|
38
|
-
|
|
39
|
-
|
|
9
|
+
const dynamodb = once(() => { var _a; return (_a = init.dynamoClient) !== null && _a !== void 0 ? _a : new DynamoDB({ apiVersion: '2012-08-10' }); });
|
|
10
|
+
return () => (configProvider) => (_, hashKey, options) => {
|
|
11
|
+
const tableName = once(() => resolveConfigValue(configProvider[ifDefined(init.configSpace, 'dynamodb')].tableName));
|
|
12
|
+
return {
|
|
13
|
+
loadAllDocumentsTheBadWay: async () => {
|
|
14
|
+
const loadAllResults = async (ExclusiveStartKey) => {
|
|
15
|
+
var _a;
|
|
16
|
+
const cmd = new ScanCommand({ TableName: await tableName(), ExclusiveStartKey });
|
|
17
|
+
const result = await dynamodb().send(cmd);
|
|
18
|
+
const resultItems = ((_a = result.Items) === null || _a === void 0 ? void 0 : _a.map(decodeDynamoDocument)) || [];
|
|
19
|
+
if (result.LastEvaluatedKey) {
|
|
20
|
+
return [...resultItems, ...await loadAllResults(result.LastEvaluatedKey)];
|
|
21
|
+
}
|
|
22
|
+
return resultItems;
|
|
23
|
+
};
|
|
24
|
+
const allResults = await loadAllResults().then(results => results.reduce((result, document) => {
|
|
25
|
+
const current = result.get(document[hashKey]);
|
|
26
|
+
if (!current || current.timestamp < document.timestamp) {
|
|
27
|
+
return result.set(document[hashKey], document);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}, new Map()));
|
|
31
|
+
return Array.from(allResults.values());
|
|
32
|
+
},
|
|
33
|
+
getVersions: async (id, startVersion) => {
|
|
34
|
+
const cmd = new QueryCommand({
|
|
35
|
+
TableName: await tableName(),
|
|
36
|
+
KeyConditionExpression: '#hk = :hkv',
|
|
37
|
+
ExpressionAttributeValues: {
|
|
38
|
+
':hkv': encodeDynamoAttribute(id)
|
|
39
|
+
},
|
|
40
|
+
ExpressionAttributeNames: {
|
|
41
|
+
'#hk': hashKey.toString()
|
|
42
|
+
},
|
|
43
|
+
...(startVersion
|
|
44
|
+
? { ExclusiveStartKey: {
|
|
45
|
+
[hashKey]: encodeDynamoAttribute(id),
|
|
46
|
+
timestamp: { N: startVersion.toString() }
|
|
47
|
+
} }
|
|
48
|
+
: {}),
|
|
49
|
+
ScanIndexForward: false,
|
|
50
|
+
});
|
|
51
|
+
return dynamodb().send(cmd).then(result => {
|
|
52
|
+
var _a;
|
|
53
|
+
const items = (_a = result.Items) === null || _a === void 0 ? void 0 : _a.map(decodeDynamoDocument);
|
|
54
|
+
if (!items || items.length === 0) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
items,
|
|
59
|
+
nextPageToken: result.LastEvaluatedKey ? decodeDynamoDocument(result.LastEvaluatedKey).timestamp : undefined
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
getItem: async (id, timestamp) => {
|
|
64
|
+
let keyConditionExpression = '#hk = :hkv';
|
|
65
|
+
const expressionAttributeNames = {
|
|
40
66
|
'#hk': hashKey.toString()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
-
return dynamodb().send(cmd).then(result => {
|
|
51
|
-
var _a;
|
|
52
|
-
const items = (_a = result.Items) === null || _a === void 0 ? void 0 : _a.map(decodeDynamoDocument);
|
|
53
|
-
if (!items || items.length === 0) {
|
|
54
|
-
return undefined;
|
|
67
|
+
};
|
|
68
|
+
const expressionAttributeValues = {
|
|
69
|
+
':hkv': encodeDynamoAttribute(id)
|
|
70
|
+
};
|
|
71
|
+
if (timestamp) {
|
|
72
|
+
keyConditionExpression += ' and #ts = :tsv';
|
|
73
|
+
expressionAttributeNames['#ts'] = 'timestamp';
|
|
74
|
+
expressionAttributeValues[':tsv'] = encodeDynamoAttribute(timestamp);
|
|
55
75
|
}
|
|
76
|
+
const cmd = new QueryCommand({
|
|
77
|
+
TableName: await tableName(),
|
|
78
|
+
KeyConditionExpression: keyConditionExpression,
|
|
79
|
+
ExpressionAttributeNames: expressionAttributeNames,
|
|
80
|
+
ExpressionAttributeValues: expressionAttributeValues,
|
|
81
|
+
ScanIndexForward: false,
|
|
82
|
+
Limit: 1
|
|
83
|
+
});
|
|
84
|
+
return dynamodb().send(cmd).then(result => {
|
|
85
|
+
var _a;
|
|
86
|
+
return (_a = result.Items) === null || _a === void 0 ? void 0 : _a.map(decodeDynamoDocument)[0];
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
/* prepares a new version of a document with the given data, then allows some additional
|
|
90
|
+
* changes to be input to a `save` function that actually saves it. useful for additional
|
|
91
|
+
* changes based on the new document version or author. the document version and author
|
|
92
|
+
* cannot be modified */
|
|
93
|
+
prepareItem: async (item, ...authorArgs) => {
|
|
94
|
+
// this getAuthor type is terrible
|
|
95
|
+
const author = (options === null || options === void 0 ? void 0 : options.getAuthor) ? await options.getAuthor(...authorArgs) : authorArgs[0];
|
|
96
|
+
const timestamp = new Date().getTime();
|
|
56
97
|
return {
|
|
57
|
-
|
|
58
|
-
|
|
98
|
+
document: { ...item, timestamp, author },
|
|
99
|
+
save: async (changes) => {
|
|
100
|
+
var _a;
|
|
101
|
+
const document = {
|
|
102
|
+
...item,
|
|
103
|
+
...changes,
|
|
104
|
+
timestamp,
|
|
105
|
+
author
|
|
106
|
+
};
|
|
107
|
+
const cmd = new PutItemCommand({
|
|
108
|
+
TableName: await tableName(),
|
|
109
|
+
Item: encodeDynamoDocument(document),
|
|
110
|
+
});
|
|
111
|
+
const updatedDoc = await dynamodb().send(cmd)
|
|
112
|
+
.then(() => document);
|
|
113
|
+
await ((_a = options === null || options === void 0 ? void 0 : options.afterWrite) === null || _a === void 0 ? void 0 : _a.call(options, updatedDoc));
|
|
114
|
+
return updatedDoc;
|
|
115
|
+
}
|
|
59
116
|
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let keyConditionExpression = '#hk = :hkv';
|
|
64
|
-
const expressionAttributeNames = {
|
|
65
|
-
'#hk': hashKey.toString()
|
|
66
|
-
};
|
|
67
|
-
const expressionAttributeValues = {
|
|
68
|
-
':hkv': encodeDynamoAttribute(id)
|
|
69
|
-
};
|
|
70
|
-
if (timestamp) {
|
|
71
|
-
keyConditionExpression += ' and #ts = :tsv';
|
|
72
|
-
expressionAttributeNames['#ts'] = 'timestamp';
|
|
73
|
-
expressionAttributeValues[':tsv'] = encodeDynamoAttribute(timestamp);
|
|
74
|
-
}
|
|
75
|
-
const cmd = new QueryCommand({
|
|
76
|
-
TableName: await tableName(),
|
|
77
|
-
KeyConditionExpression: keyConditionExpression,
|
|
78
|
-
ExpressionAttributeNames: expressionAttributeNames,
|
|
79
|
-
ExpressionAttributeValues: expressionAttributeValues,
|
|
80
|
-
ScanIndexForward: false,
|
|
81
|
-
Limit: 1
|
|
82
|
-
});
|
|
83
|
-
return dynamodb().send(cmd).then(result => {
|
|
117
|
+
},
|
|
118
|
+
/* saves a new version of a document with the given data */
|
|
119
|
+
putItem: async (item, ...authorArgs) => {
|
|
84
120
|
var _a;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
...changes,
|
|
103
|
-
timestamp,
|
|
104
|
-
author
|
|
105
|
-
};
|
|
106
|
-
const cmd = new PutItemCommand({
|
|
107
|
-
TableName: await tableName(),
|
|
108
|
-
Item: encodeDynamoDocument(document),
|
|
109
|
-
});
|
|
110
|
-
const updatedDoc = await dynamodb().send(cmd)
|
|
111
|
-
.then(() => document);
|
|
112
|
-
await ((_a = options === null || options === void 0 ? void 0 : options.afterWrite) === null || _a === void 0 ? void 0 : _a.call(options, updatedDoc));
|
|
113
|
-
return updatedDoc;
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
},
|
|
117
|
-
/* saves a new version of a document with the given data */
|
|
118
|
-
putItem: async (item, ...authorArgs) => {
|
|
119
|
-
var _a;
|
|
120
|
-
// this getAuthor type is terrible
|
|
121
|
-
const author = (options === null || options === void 0 ? void 0 : options.getAuthor) ? await options.getAuthor(...authorArgs) : authorArgs[0];
|
|
122
|
-
const document = {
|
|
123
|
-
...item,
|
|
124
|
-
timestamp: new Date().getTime(),
|
|
125
|
-
author
|
|
126
|
-
};
|
|
127
|
-
const cmd = new PutItemCommand({
|
|
128
|
-
TableName: await tableName(),
|
|
129
|
-
Item: encodeDynamoDocument(document),
|
|
130
|
-
});
|
|
131
|
-
const updatedDoc = await dynamodb().send(cmd)
|
|
132
|
-
.then(() => document);
|
|
133
|
-
await ((_a = options === null || options === void 0 ? void 0 : options.afterWrite) === null || _a === void 0 ? void 0 : _a.call(options, updatedDoc));
|
|
134
|
-
return updatedDoc;
|
|
135
|
-
},
|
|
121
|
+
// this getAuthor type is terrible
|
|
122
|
+
const author = (options === null || options === void 0 ? void 0 : options.getAuthor) ? await options.getAuthor(...authorArgs) : authorArgs[0];
|
|
123
|
+
const document = {
|
|
124
|
+
...item,
|
|
125
|
+
timestamp: new Date().getTime(),
|
|
126
|
+
author
|
|
127
|
+
};
|
|
128
|
+
const cmd = new PutItemCommand({
|
|
129
|
+
TableName: await tableName(),
|
|
130
|
+
Item: encodeDynamoDocument(document),
|
|
131
|
+
});
|
|
132
|
+
const updatedDoc = await dynamodb().send(cmd)
|
|
133
|
+
.then(() => document);
|
|
134
|
+
await ((_a = options === null || options === void 0 ? void 0 : options.afterWrite) === null || _a === void 0 ? void 0 : _a.call(options, updatedDoc));
|
|
135
|
+
return updatedDoc;
|
|
136
|
+
},
|
|
137
|
+
};
|
|
136
138
|
};
|
|
137
139
|
};
|
|
@@ -7,7 +7,7 @@ export declare type Config = {
|
|
|
7
7
|
};
|
|
8
8
|
interface Initializer<C> {
|
|
9
9
|
configSpace?: C;
|
|
10
|
-
|
|
10
|
+
getS3Client?: (...args: ConstructorParameters<typeof S3Client>) => S3Client;
|
|
11
11
|
}
|
|
12
12
|
export declare const s3FileServer: <C extends string = "deployed">(initializer: Initializer<C>) => (configProvider: { [key in C]: {
|
|
13
13
|
bucketName: import("../../config").ConfigValueProvider<string>;
|
|
@@ -9,8 +9,11 @@ export const s3FileServer = (initializer) => (configProvider) => {
|
|
|
9
9
|
const config = configProvider[ifDefined(initializer.configSpace, 'deployed')];
|
|
10
10
|
const bucketName = once(() => resolveConfigValue(config.bucketName));
|
|
11
11
|
const bucketRegion = once(() => resolveConfigValue(config.bucketRegion));
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const s3Service = once(async () => {
|
|
13
|
+
var _a, _b;
|
|
14
|
+
const args = { apiVersion: '2012-08-10', region: await bucketRegion() };
|
|
15
|
+
return (_b = (_a = initializer.getS3Client) === null || _a === void 0 ? void 0 : _a.call(initializer, args)) !== null && _b !== void 0 ? _b : new S3Client(args);
|
|
16
|
+
});
|
|
14
17
|
/*
|
|
15
18
|
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html
|
|
16
19
|
*/
|
|
@@ -4,11 +4,13 @@ import { memoize } from '../..';
|
|
|
4
4
|
import { resolveConfigValue } from '../../config';
|
|
5
5
|
import { InvalidRequestError, SessionExpiredError } from '../../errors';
|
|
6
6
|
import { ifDefined } from '../../guards';
|
|
7
|
+
import { once } from '../../misc/helpers';
|
|
7
8
|
/**
|
|
8
9
|
* Creates a class that can verify launch params
|
|
9
10
|
*/
|
|
10
11
|
export const createLaunchVerifier = ({ configSpace, fetcher }) => (configProvider) => {
|
|
11
12
|
const config = configProvider[ifDefined(configSpace, 'launch')];
|
|
13
|
+
const getTrustedDomain = once(() => resolveConfigValue(config.trustedDomain));
|
|
12
14
|
const getJwksClient = memoize((jwksUri) => new JwksClient({ fetcher, jwksUri }));
|
|
13
15
|
const getJwksKey = memoize(async (jwksUri, kid) => {
|
|
14
16
|
const client = getJwksClient(jwksUri);
|
|
@@ -24,7 +26,7 @@ export const createLaunchVerifier = ({ configSpace, fetcher }) => (configProvide
|
|
|
24
26
|
try {
|
|
25
27
|
const jwksUrl = new URL('/.well-known/jwks.json', iss);
|
|
26
28
|
const launchDomain = jwksUrl.hostname;
|
|
27
|
-
const trustedDomain = await
|
|
29
|
+
const trustedDomain = await getTrustedDomain();
|
|
28
30
|
if (launchDomain !== trustedDomain && !launchDomain.endsWith(`.${trustedDomain}`)) {
|
|
29
31
|
return callback(new Error(`Untrusted launch domain: "${launchDomain}"`));
|
|
30
32
|
}
|