@openstax/ts-utils 1.14.0 → 1.15.1
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/authProvider/subrequest.js +7 -1
- package/dist/cjs/services/documentStore/unversioned/dynamodb.d.ts +2 -0
- package/dist/cjs/services/documentStore/unversioned/dynamodb.js +58 -1
- package/dist/cjs/services/documentStore/unversioned/file-system.d.ts +2 -0
- package/dist/cjs/services/documentStore/unversioned/file-system.js +35 -0
- package/dist/cjs/services/launchParams/verifier.js +28 -4
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/services/authProvider/subrequest.js +4 -1
- package/dist/esm/services/documentStore/unversioned/dynamodb.d.ts +2 -0
- package/dist/esm/services/documentStore/unversioned/dynamodb.js +59 -2
- package/dist/esm/services/documentStore/unversioned/file-system.d.ts +2 -0
- package/dist/esm/services/documentStore/unversioned/file-system.js +35 -0
- package/dist/esm/services/launchParams/verifier.js +6 -2
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import cookie from 'cookie';
|
|
1
2
|
import { once } from '../..';
|
|
2
3
|
import { resolveConfigValue } from '../../config';
|
|
3
4
|
import { ifDefined } from '../../guards';
|
|
@@ -16,10 +17,12 @@ export const subrequestAuthProvider = (initializer) => (configProvider) => {
|
|
|
16
17
|
return { headers };
|
|
17
18
|
};
|
|
18
19
|
const loadUser = async () => {
|
|
19
|
-
const
|
|
20
|
+
const resolvedCookeiName = await cookieName();
|
|
21
|
+
const [token] = getAuthTokenOrCookie(request, resolvedCookeiName);
|
|
20
22
|
if (!token) {
|
|
21
23
|
return undefined;
|
|
22
24
|
}
|
|
25
|
+
const headers = { cookie: cookie.serialize(resolvedCookeiName, token) };
|
|
23
26
|
return initializer.fetch((await accountsBase()).replace(/\/+$/, '') + '/api/user', { headers })
|
|
24
27
|
.then(response => response.json());
|
|
25
28
|
};
|
|
@@ -9,6 +9,8 @@ export declare const dynamoUnversionedDocumentStore: <C extends string = "dynamo
|
|
|
9
9
|
loadAllDocumentsTheBadWay: () => Promise<T[]>;
|
|
10
10
|
batchGetItem: (ids: T[K][]) => Promise<T[]>;
|
|
11
11
|
getItem: (id: T[K]) => Promise<T | undefined>;
|
|
12
|
+
incrementItemAttribute: (id: T[K], attribute: keyof T) => Promise<number>;
|
|
13
|
+
patchItem: (item: Partial<T>) => Promise<T>;
|
|
12
14
|
putItem: (item: T) => Promise<T>;
|
|
13
15
|
};
|
|
14
16
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BatchGetItemCommand, DynamoDB, GetItemCommand, PutItemCommand, ScanCommand } from '@aws-sdk/client-dynamodb';
|
|
1
|
+
import { BatchGetItemCommand, DynamoDB, GetItemCommand, PutItemCommand, ScanCommand, UpdateItemCommand } from '@aws-sdk/client-dynamodb';
|
|
2
2
|
import { once } from '../../..';
|
|
3
3
|
import { resolveConfigValue } from '../../../config';
|
|
4
4
|
import { ifDefined } from '../../../guards';
|
|
@@ -42,7 +42,64 @@ export const dynamoUnversionedDocumentStore = (initializer) => () => (configProv
|
|
|
42
42
|
});
|
|
43
43
|
return dynamodb().send(cmd).then(result => result.Item ? decodeDynamoDocument(result.Item) : undefined);
|
|
44
44
|
},
|
|
45
|
-
/*
|
|
45
|
+
/* atomically increments the given item attribute by 1 */
|
|
46
|
+
incrementItemAttribute: async (id, attribute) => {
|
|
47
|
+
const key = hashKey.toString();
|
|
48
|
+
const field = attribute.toString();
|
|
49
|
+
const cmd = new UpdateItemCommand({
|
|
50
|
+
Key: { [key]: encodeDynamoAttribute(id) },
|
|
51
|
+
TableName: await tableName(),
|
|
52
|
+
UpdateExpression: 'ADD #f :one',
|
|
53
|
+
ConditionExpression: 'attribute_exists(#k)',
|
|
54
|
+
ExpressionAttributeNames: { '#k': hashKey.toString(), '#f': field },
|
|
55
|
+
ExpressionAttributeValues: { ':one': { N: '1' } },
|
|
56
|
+
ReturnValues: 'UPDATED_NEW',
|
|
57
|
+
});
|
|
58
|
+
return dynamodb().send(cmd).then((item) => {
|
|
59
|
+
var _a;
|
|
60
|
+
const result = (_a = item.Attributes) === null || _a === void 0 ? void 0 : _a[field]['N'];
|
|
61
|
+
if (!result) {
|
|
62
|
+
throw new Error(`Item with ${key} "${id}" does not exist`);
|
|
63
|
+
}
|
|
64
|
+
return parseFloat(result);
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
/* replaces only specified attributes with the given data */
|
|
68
|
+
patchItem: async (item) => {
|
|
69
|
+
const id = item[hashKey];
|
|
70
|
+
const key = hashKey.toString();
|
|
71
|
+
if (!id) {
|
|
72
|
+
throw new Error(`Key attribute "${key}" is required for patchItem`);
|
|
73
|
+
}
|
|
74
|
+
const entries = Object.entries(item).filter(([field]) => field !== key);
|
|
75
|
+
if (entries.length === 0) {
|
|
76
|
+
throw new Error('No attributes to update');
|
|
77
|
+
}
|
|
78
|
+
const updates = [];
|
|
79
|
+
const expressionAttributeNames = { '#k': key };
|
|
80
|
+
const expressionAttributeValues = {};
|
|
81
|
+
entries.forEach(([field, value], index) => {
|
|
82
|
+
updates.push(`#f${index} = :f${index}`);
|
|
83
|
+
expressionAttributeNames[`#f${index}`] = field;
|
|
84
|
+
expressionAttributeValues[`:f${index}`] = encodeDynamoAttribute(value);
|
|
85
|
+
});
|
|
86
|
+
const cmd = new UpdateItemCommand({
|
|
87
|
+
Key: { [key]: encodeDynamoAttribute(id) },
|
|
88
|
+
TableName: await tableName(),
|
|
89
|
+
UpdateExpression: `SET ${updates.join(', ')}`,
|
|
90
|
+
ConditionExpression: 'attribute_exists(#k)',
|
|
91
|
+
ExpressionAttributeNames: expressionAttributeNames,
|
|
92
|
+
ExpressionAttributeValues: expressionAttributeValues,
|
|
93
|
+
ReturnValues: 'ALL_NEW',
|
|
94
|
+
});
|
|
95
|
+
return dynamodb().send(cmd).then((item) => {
|
|
96
|
+
if (!item.Attributes) {
|
|
97
|
+
throw new Error(`Item with ${key} "${id}" does not exist`);
|
|
98
|
+
}
|
|
99
|
+
return decodeDynamoDocument(item.Attributes);
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
/* replaces the entire document with the given data */
|
|
46
103
|
putItem: async (item) => {
|
|
47
104
|
const cmd = new PutItemCommand({
|
|
48
105
|
TableName: await tableName(),
|
|
@@ -11,6 +11,8 @@ export declare const fileSystemUnversionedDocumentStore: <C extends string = "fi
|
|
|
11
11
|
loadAllDocumentsTheBadWay: () => Promise<T[]>;
|
|
12
12
|
batchGetItem: (ids: T[K][]) => Promise<Exclude<Awaited<T>, undefined>[]>;
|
|
13
13
|
getItem: (id: T[K]) => Promise<T | undefined>;
|
|
14
|
+
incrementItemAttribute: (id: T[K], attribute: keyof T) => Promise<number>;
|
|
15
|
+
patchItem: (item: Partial<T>) => Promise<T>;
|
|
14
16
|
putItem: (item: T) => Promise<T>;
|
|
15
17
|
};
|
|
16
18
|
export {};
|
|
@@ -44,6 +44,41 @@ export const fileSystemUnversionedDocumentStore = (initializer) => () => (config
|
|
|
44
44
|
return items.filter(isDefined);
|
|
45
45
|
},
|
|
46
46
|
getItem: (id) => load(hashFilename(id)),
|
|
47
|
+
incrementItemAttribute: async (id, attribute) => {
|
|
48
|
+
const filename = hashFilename(id);
|
|
49
|
+
const path = await filePath(filename);
|
|
50
|
+
await mkTableDir;
|
|
51
|
+
const data = await load(filename);
|
|
52
|
+
if (!data) {
|
|
53
|
+
throw new Error(`Item with ${hashKey.toString()} "${id}" does not exist`);
|
|
54
|
+
}
|
|
55
|
+
const newValue = typeof data[attribute] === 'number' ? data[attribute] + 1 : 1;
|
|
56
|
+
const newItem = { ...data, [hashKey]: id, [attribute]: newValue };
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
writeFile(path, JSON.stringify(newItem, null, 2), (err) => err ? reject(err) : resolve(newValue));
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
patchItem: async (item) => {
|
|
62
|
+
const id = item[hashKey];
|
|
63
|
+
if (!id) {
|
|
64
|
+
throw new Error(`Key attribute "${hashKey.toString()}" is required for patchItem`);
|
|
65
|
+
}
|
|
66
|
+
// This check is just to make this adapter consistent with the dynamo adapter
|
|
67
|
+
if (Object.keys(item).filter((key) => key !== hashKey.toString()).length === 0) {
|
|
68
|
+
throw new Error('No attributes to update');
|
|
69
|
+
}
|
|
70
|
+
const filename = hashFilename(id);
|
|
71
|
+
const path = await filePath(filename);
|
|
72
|
+
await mkTableDir;
|
|
73
|
+
const data = await load(filename);
|
|
74
|
+
if (!data) {
|
|
75
|
+
throw new Error(`Item with ${hashKey.toString()} "${id}" does not exist`);
|
|
76
|
+
}
|
|
77
|
+
const newItem = { ...data, ...item };
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
writeFile(path, JSON.stringify(newItem, null, 2), (err) => err ? reject(err) : resolve(newItem));
|
|
80
|
+
});
|
|
81
|
+
},
|
|
47
82
|
putItem: async (item) => {
|
|
48
83
|
const path = await filePath(hashFilename(item[hashKey]));
|
|
49
84
|
await mkTableDir;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import jwt from 'jsonwebtoken';
|
|
1
|
+
import jwt, { TokenExpiredError } from 'jsonwebtoken';
|
|
2
2
|
import { JwksClient } from 'jwks-rsa';
|
|
3
3
|
import { memoize } from '../..';
|
|
4
4
|
import { resolveConfigValue } from '../../config';
|
|
5
|
+
import { SessionExpiredError } from '../../errors';
|
|
5
6
|
import { ifDefined } from '../../guards';
|
|
6
7
|
/**
|
|
7
8
|
* Creates a class that can verify launch params
|
|
@@ -34,7 +35,10 @@ export const createLaunchVerifier = ({ configSpace, fetcher }) => (configProvide
|
|
|
34
35
|
}
|
|
35
36
|
};
|
|
36
37
|
const verify = (token) => new Promise((resolve, reject) => jwt.verify(token, getKey, {}, (err, payload) => {
|
|
37
|
-
if (err) {
|
|
38
|
+
if (err && err instanceof TokenExpiredError) {
|
|
39
|
+
reject(new SessionExpiredError());
|
|
40
|
+
}
|
|
41
|
+
else if (err) {
|
|
38
42
|
reject(err);
|
|
39
43
|
}
|
|
40
44
|
else if (typeof payload !== 'object') {
|