@openstax/ts-utils 1.21.11 → 1.23.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/assertions/index.d.ts +85 -0
- package/dist/cjs/assertions/index.js +157 -0
- package/dist/cjs/aws/ssmService.d.ts +5 -0
- package/dist/cjs/aws/ssmService.js +9 -0
- package/dist/cjs/config/awsParameterConfig.d.ts +10 -0
- package/dist/cjs/config/awsParameterConfig.js +26 -0
- package/dist/cjs/config/envConfig.d.ts +24 -0
- package/dist/cjs/config/envConfig.js +57 -0
- package/dist/cjs/config/index.d.ts +48 -0
- package/dist/cjs/config/index.js +35 -0
- package/dist/cjs/config/lambdaParameterConfig.d.ts +12 -0
- package/dist/cjs/config/lambdaParameterConfig.js +45 -0
- package/dist/cjs/config/replaceConfig.d.ts +14 -0
- package/dist/cjs/config/replaceConfig.js +22 -0
- package/dist/cjs/config/resolveConfigValue.d.ts +5 -0
- package/dist/cjs/config/resolveConfigValue.js +12 -0
- package/dist/cjs/errors/index.d.ts +77 -0
- package/dist/cjs/errors/index.js +109 -0
- package/dist/cjs/fetch/fetchStatusRetry.d.ts +7 -0
- package/dist/cjs/fetch/fetchStatusRetry.js +16 -0
- package/dist/cjs/fetch/index.d.ts +64 -0
- package/dist/cjs/fetch/index.js +55 -0
- package/dist/cjs/guards/index.d.ts +30 -0
- package/dist/cjs/guards/index.js +35 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/middleware/apiErrorHandler.d.ts +24 -0
- package/dist/cjs/middleware/apiErrorHandler.js +41 -0
- package/dist/cjs/middleware/apiSlowResponseMiddleware.d.ts +23 -0
- package/dist/cjs/middleware/apiSlowResponseMiddleware.js +54 -0
- package/dist/cjs/middleware/index.d.ts +47 -0
- package/dist/cjs/middleware/index.js +48 -0
- package/dist/cjs/middleware/lambdaCorsResponseMiddleware.d.ts +20 -0
- package/dist/cjs/middleware/lambdaCorsResponseMiddleware.js +42 -0
- package/dist/cjs/middleware/throwNotFoundMiddleware.d.ts +4 -0
- package/dist/cjs/middleware/throwNotFoundMiddleware.js +14 -0
- package/dist/cjs/misc/hashValue.d.ts +10 -0
- package/dist/cjs/misc/hashValue.js +17 -0
- package/dist/cjs/misc/helpers.d.ts +124 -0
- package/dist/cjs/misc/helpers.js +214 -0
- package/dist/cjs/misc/merge.d.ts +21 -0
- package/dist/cjs/misc/merge.js +45 -0
- package/dist/cjs/misc/partitionSequence.d.ts +35 -0
- package/dist/cjs/misc/partitionSequence.js +55 -0
- package/dist/cjs/pagination/index.d.ts +91 -0
- package/dist/cjs/pagination/index.js +83 -0
- package/dist/cjs/routing/helpers.d.ts +57 -0
- package/dist/cjs/routing/helpers.js +90 -0
- package/dist/cjs/routing/index.d.ts +272 -0
- package/dist/cjs/routing/index.js +270 -0
- package/dist/cjs/routing/validators/zod.d.ts +4 -0
- package/dist/cjs/routing/validators/zod.js +12 -0
- package/dist/cjs/services/accountsGateway/index.d.ts +85 -0
- package/dist/cjs/services/accountsGateway/index.js +118 -0
- package/dist/cjs/services/apiGateway/index.d.ts +63 -0
- package/dist/cjs/services/apiGateway/index.js +108 -0
- package/dist/cjs/services/authProvider/browser.d.ts +74 -0
- package/dist/cjs/services/authProvider/browser.js +154 -0
- package/dist/cjs/services/authProvider/decryption.d.ts +19 -0
- package/dist/cjs/services/authProvider/decryption.js +61 -0
- package/dist/cjs/services/authProvider/index.d.ts +61 -0
- package/dist/cjs/services/authProvider/index.js +26 -0
- package/dist/cjs/services/authProvider/subrequest.d.ts +16 -0
- package/dist/cjs/services/authProvider/subrequest.js +50 -0
- package/dist/cjs/services/authProvider/utils/decryptAndVerify.d.ts +29 -0
- package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +91 -0
- package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.d.ts +26 -0
- package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.js +47 -0
- package/dist/cjs/services/authProvider/utils/userRoleValidator.d.ts +13 -0
- package/dist/cjs/services/authProvider/utils/userRoleValidator.js +37 -0
- package/dist/cjs/services/documentStore/dynamoEncoding.d.ts +10 -0
- package/dist/cjs/services/documentStore/dynamoEncoding.js +52 -0
- package/dist/cjs/services/documentStore/index.d.ts +14 -0
- package/dist/cjs/services/documentStore/index.js +2 -0
- package/dist/cjs/services/documentStore/unversioned/dynamodb.d.ts +16 -0
- package/dist/cjs/services/documentStore/unversioned/dynamodb.js +122 -0
- package/dist/cjs/services/documentStore/unversioned/file-system.d.ts +18 -0
- package/dist/cjs/services/documentStore/unversioned/file-system.js +121 -0
- package/dist/cjs/services/documentStore/unversioned/index.d.ts +2 -0
- package/dist/cjs/services/documentStore/unversioned/index.js +2 -0
- package/dist/cjs/services/documentStore/versioned/dynamodb.d.ts +22 -0
- package/dist/cjs/services/documentStore/versioned/dynamodb.js +135 -0
- package/dist/cjs/services/documentStore/versioned/file-system.d.ts +24 -0
- package/dist/cjs/services/documentStore/versioned/file-system.js +62 -0
- package/dist/cjs/services/documentStore/versioned/index.d.ts +17 -0
- package/dist/cjs/services/documentStore/versioned/index.js +2 -0
- package/dist/cjs/services/exercisesGateway/index.d.ts +71 -0
- package/dist/cjs/services/exercisesGateway/index.js +97 -0
- package/dist/cjs/services/fileServer/index.d.ts +17 -0
- package/dist/cjs/services/fileServer/index.js +19 -0
- package/dist/cjs/services/fileServer/localFileServer.d.ts +13 -0
- package/dist/cjs/services/fileServer/localFileServer.js +23 -0
- package/dist/cjs/services/fileServer/s3FileServer.d.ts +16 -0
- package/dist/cjs/services/fileServer/s3FileServer.js +25 -0
- package/dist/cjs/services/launchParams/index.d.ts +2 -0
- package/dist/cjs/services/launchParams/index.js +7 -0
- package/dist/cjs/services/launchParams/signer.d.ts +27 -0
- package/dist/cjs/services/launchParams/signer.js +58 -0
- package/dist/cjs/services/launchParams/verifier.d.ts +22 -0
- package/dist/cjs/services/launchParams/verifier.js +94 -0
- package/dist/cjs/services/logger/console.d.ts +4 -0
- package/dist/cjs/services/logger/console.js +12 -0
- package/dist/cjs/services/logger/index.d.ts +39 -0
- package/dist/cjs/services/logger/index.js +31 -0
- package/dist/cjs/services/lrsGateway/addStatementDefaultFields.d.ts +5 -0
- package/dist/cjs/services/lrsGateway/addStatementDefaultFields.js +21 -0
- package/dist/cjs/services/lrsGateway/attempt-utils.d.ts +70 -0
- package/dist/cjs/services/lrsGateway/attempt-utils.js +258 -0
- package/dist/cjs/services/lrsGateway/file-system.d.ts +17 -0
- package/dist/cjs/services/lrsGateway/file-system.js +140 -0
- package/dist/cjs/services/lrsGateway/index.d.ts +125 -0
- package/dist/cjs/services/lrsGateway/index.js +138 -0
- package/dist/cjs/services/lrsGateway/xapiUtils.d.ts +61 -0
- package/dist/cjs/services/lrsGateway/xapiUtils.js +94 -0
- package/dist/cjs/services/postgresConnection/index.d.ts +35 -0
- package/dist/cjs/services/postgresConnection/index.js +63 -0
- package/dist/cjs/services/searchProvider/index.d.ts +31 -0
- package/dist/cjs/services/searchProvider/index.js +2 -0
- package/dist/cjs/services/searchProvider/memorySearchTheBadWay.d.ts +14 -0
- package/dist/cjs/services/searchProvider/memorySearchTheBadWay.js +89 -0
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -0
- package/dist/cjs/types.d.ts +31 -0
- package/dist/cjs/types.js +2 -0
- package/dist/esm/assertions/index.d.ts +85 -0
- package/dist/esm/assertions/index.js +146 -0
- package/dist/esm/aws/ssmService.d.ts +5 -0
- package/dist/esm/aws/ssmService.js +6 -0
- package/dist/esm/config/awsParameterConfig.d.ts +10 -0
- package/dist/esm/config/awsParameterConfig.js +22 -0
- package/dist/esm/config/envConfig.d.ts +24 -0
- package/dist/esm/config/envConfig.js +53 -0
- package/dist/esm/config/index.d.ts +48 -0
- package/dist/esm/config/index.js +17 -0
- package/dist/esm/config/lambdaParameterConfig.d.ts +12 -0
- package/dist/esm/config/lambdaParameterConfig.js +38 -0
- package/dist/esm/config/replaceConfig.d.ts +14 -0
- package/dist/esm/config/replaceConfig.js +18 -0
- package/dist/esm/config/resolveConfigValue.d.ts +5 -0
- package/dist/esm/config/resolveConfigValue.js +8 -0
- package/dist/esm/errors/index.d.ts +77 -0
- package/dist/esm/errors/index.js +99 -0
- package/dist/esm/fetch/fetchStatusRetry.d.ts +7 -0
- package/dist/esm/fetch/fetchStatusRetry.js +12 -0
- package/dist/esm/fetch/index.d.ts +64 -0
- package/dist/esm/fetch/index.js +46 -0
- package/dist/esm/guards/index.d.ts +30 -0
- package/dist/esm/guards/index.js +28 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/middleware/apiErrorHandler.d.ts +24 -0
- package/dist/esm/middleware/apiErrorHandler.js +37 -0
- package/dist/esm/middleware/apiSlowResponseMiddleware.d.ts +23 -0
- package/dist/esm/middleware/apiSlowResponseMiddleware.js +50 -0
- package/dist/esm/middleware/index.d.ts +47 -0
- package/dist/esm/middleware/index.js +44 -0
- package/dist/esm/middleware/lambdaCorsResponseMiddleware.d.ts +20 -0
- package/dist/esm/middleware/lambdaCorsResponseMiddleware.js +38 -0
- package/dist/esm/middleware/throwNotFoundMiddleware.d.ts +4 -0
- package/dist/esm/middleware/throwNotFoundMiddleware.js +10 -0
- package/dist/esm/misc/hashValue.d.ts +10 -0
- package/dist/esm/misc/hashValue.js +13 -0
- package/dist/esm/misc/helpers.d.ts +124 -0
- package/dist/esm/misc/helpers.js +199 -0
- package/dist/esm/misc/merge.d.ts +21 -0
- package/dist/esm/misc/merge.js +40 -0
- package/dist/esm/misc/partitionSequence.d.ts +35 -0
- package/dist/esm/misc/partitionSequence.js +48 -0
- package/dist/esm/pagination/index.d.ts +91 -0
- package/dist/esm/pagination/index.js +77 -0
- package/dist/esm/routing/helpers.d.ts +57 -0
- package/dist/esm/routing/helpers.js +83 -0
- package/dist/esm/routing/index.d.ts +272 -0
- package/dist/esm/routing/index.js +232 -0
- package/dist/esm/routing/validators/zod.d.ts +4 -0
- package/dist/esm/routing/validators/zod.js +8 -0
- package/dist/esm/services/accountsGateway/index.d.ts +85 -0
- package/dist/esm/services/accountsGateway/index.js +111 -0
- package/dist/esm/services/apiGateway/index.d.ts +63 -0
- package/dist/esm/services/apiGateway/index.js +77 -0
- package/dist/esm/services/authProvider/browser.d.ts +74 -0
- package/dist/esm/services/authProvider/browser.js +150 -0
- package/dist/esm/services/authProvider/decryption.d.ts +19 -0
- package/dist/esm/services/authProvider/decryption.js +57 -0
- package/dist/esm/services/authProvider/index.d.ts +61 -0
- package/dist/esm/services/authProvider/index.js +18 -0
- package/dist/esm/services/authProvider/subrequest.d.ts +16 -0
- package/dist/esm/services/authProvider/subrequest.js +43 -0
- package/dist/esm/services/authProvider/utils/decryptAndVerify.d.ts +29 -0
- package/dist/esm/services/authProvider/utils/decryptAndVerify.js +85 -0
- package/dist/esm/services/authProvider/utils/embeddedAuthProvider.d.ts +26 -0
- package/dist/esm/services/authProvider/utils/embeddedAuthProvider.js +40 -0
- package/dist/esm/services/authProvider/utils/userRoleValidator.d.ts +13 -0
- package/dist/esm/services/authProvider/utils/userRoleValidator.js +33 -0
- package/dist/esm/services/documentStore/dynamoEncoding.d.ts +10 -0
- package/dist/esm/services/documentStore/dynamoEncoding.js +45 -0
- package/dist/esm/services/documentStore/index.d.ts +14 -0
- package/dist/esm/services/documentStore/index.js +1 -0
- package/dist/esm/services/documentStore/unversioned/dynamodb.d.ts +16 -0
- package/dist/esm/services/documentStore/unversioned/dynamodb.js +118 -0
- package/dist/esm/services/documentStore/unversioned/file-system.d.ts +18 -0
- package/dist/esm/services/documentStore/unversioned/file-system.js +91 -0
- package/dist/esm/services/documentStore/unversioned/index.d.ts +2 -0
- package/dist/esm/services/documentStore/unversioned/index.js +1 -0
- package/dist/esm/services/documentStore/versioned/dynamodb.d.ts +22 -0
- package/dist/esm/services/documentStore/versioned/dynamodb.js +131 -0
- package/dist/esm/services/documentStore/versioned/file-system.d.ts +24 -0
- package/dist/esm/services/documentStore/versioned/file-system.js +58 -0
- package/dist/esm/services/documentStore/versioned/index.d.ts +17 -0
- package/dist/esm/services/documentStore/versioned/index.js +1 -0
- package/dist/esm/services/exercisesGateway/index.d.ts +71 -0
- package/dist/esm/services/exercisesGateway/index.js +70 -0
- package/dist/esm/services/fileServer/index.d.ts +17 -0
- package/dist/esm/services/fileServer/index.js +13 -0
- package/dist/esm/services/fileServer/localFileServer.d.ts +13 -0
- package/dist/esm/services/fileServer/localFileServer.js +16 -0
- package/dist/esm/services/fileServer/s3FileServer.d.ts +16 -0
- package/dist/esm/services/fileServer/s3FileServer.js +21 -0
- package/dist/esm/services/launchParams/index.d.ts +2 -0
- package/dist/esm/services/launchParams/index.js +2 -0
- package/dist/esm/services/launchParams/signer.d.ts +27 -0
- package/dist/esm/services/launchParams/signer.js +51 -0
- package/dist/esm/services/launchParams/verifier.d.ts +22 -0
- package/dist/esm/services/launchParams/verifier.js +67 -0
- package/dist/esm/services/logger/console.d.ts +4 -0
- package/dist/esm/services/logger/console.js +8 -0
- package/dist/esm/services/logger/index.d.ts +39 -0
- package/dist/esm/services/logger/index.js +27 -0
- package/dist/esm/services/lrsGateway/addStatementDefaultFields.d.ts +5 -0
- package/dist/esm/services/lrsGateway/addStatementDefaultFields.js +14 -0
- package/dist/esm/services/lrsGateway/attempt-utils.d.ts +70 -0
- package/dist/esm/services/lrsGateway/attempt-utils.js +236 -0
- package/dist/esm/services/lrsGateway/file-system.d.ts +17 -0
- package/dist/esm/services/lrsGateway/file-system.js +110 -0
- package/dist/esm/services/lrsGateway/index.d.ts +125 -0
- package/dist/esm/services/lrsGateway/index.js +111 -0
- package/dist/esm/services/lrsGateway/xapiUtils.d.ts +61 -0
- package/dist/esm/services/lrsGateway/xapiUtils.js +84 -0
- package/dist/esm/services/postgresConnection/index.d.ts +35 -0
- package/dist/esm/services/postgresConnection/index.js +56 -0
- package/dist/esm/services/searchProvider/index.d.ts +31 -0
- package/dist/esm/services/searchProvider/index.js +1 -0
- package/dist/esm/services/searchProvider/memorySearchTheBadWay.d.ts +14 -0
- package/dist/esm/services/searchProvider/memorySearchTheBadWay.js +85 -0
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -0
- package/dist/esm/types.d.ts +31 -0
- package/dist/esm/types.js +1 -0
- package/package.json +16 -16
- package/script/bin/deploy.bash +8 -0
- package/script/bin/get-env-param.bash +3 -3
- package/script/bin/init-params-script.bash +10 -1
- package/script/bin/upload-params.bash +3 -3
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { FetchConfig } from '../../fetch';
|
|
2
|
+
import type { HttpHeaders, QueryParams } from '../../routing';
|
|
3
|
+
import type { Logger } from '../logger';
|
|
4
|
+
export declare type ConsentPreferences = {
|
|
5
|
+
consent_preferences: {
|
|
6
|
+
accepted: string[];
|
|
7
|
+
rejected: string[];
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare type TokenUser = {
|
|
11
|
+
id: number;
|
|
12
|
+
name: string;
|
|
13
|
+
first_name: string;
|
|
14
|
+
last_name: string;
|
|
15
|
+
full_name: string;
|
|
16
|
+
uuid: string;
|
|
17
|
+
faculty_status: string;
|
|
18
|
+
is_administrator: boolean;
|
|
19
|
+
} & Partial<ConsentPreferences>;
|
|
20
|
+
export interface ApiUser extends TokenUser {
|
|
21
|
+
contact_infos: Array<{
|
|
22
|
+
type: string;
|
|
23
|
+
value: string;
|
|
24
|
+
is_verified: boolean;
|
|
25
|
+
is_guessed_preferred: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
applications: Array<{
|
|
28
|
+
id: number;
|
|
29
|
+
name: string;
|
|
30
|
+
roles: string[];
|
|
31
|
+
}>;
|
|
32
|
+
external_ids: string[];
|
|
33
|
+
is_not_gdpr_location: boolean;
|
|
34
|
+
self_reported_role: string;
|
|
35
|
+
signed_contract_names: string[];
|
|
36
|
+
using_openstax: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare type User = TokenUser | ApiUser;
|
|
39
|
+
export declare type AuthProvider = {
|
|
40
|
+
getUser: () => Promise<User | undefined>;
|
|
41
|
+
/**
|
|
42
|
+
* gets second argument for `fetch` that has authentication token or cookie
|
|
43
|
+
*/
|
|
44
|
+
getAuthorizedFetchConfig: () => Promise<FetchConfig>;
|
|
45
|
+
};
|
|
46
|
+
export declare type CookieAuthProviderRequest = {
|
|
47
|
+
cookies?: string[];
|
|
48
|
+
headers: HttpHeaders;
|
|
49
|
+
queryStringParameters?: QueryParams;
|
|
50
|
+
};
|
|
51
|
+
export declare type CookieAuthProvider<T extends AuthProvider = AuthProvider> = (inputs: {
|
|
52
|
+
request: CookieAuthProviderRequest;
|
|
53
|
+
logger: Logger;
|
|
54
|
+
}) => T;
|
|
55
|
+
export declare type StubAuthProvider = (user: User | undefined) => AuthProvider;
|
|
56
|
+
export declare const stubAuthProvider: (user?: User | undefined) => AuthProvider;
|
|
57
|
+
export declare const getAuthTokenOrCookie: (request: CookieAuthProviderRequest, cookieName: string, queryKey?: string) => [string, {
|
|
58
|
+
Authorization: string;
|
|
59
|
+
}] | [string, {
|
|
60
|
+
cookie: string;
|
|
61
|
+
}];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import cookie from 'cookie';
|
|
2
|
+
import { tuple } from '../../misc/helpers';
|
|
3
|
+
import { getHeader } from '../../routing/helpers';
|
|
4
|
+
export const stubAuthProvider = (user) => ({
|
|
5
|
+
getUser: () => Promise.resolve(user),
|
|
6
|
+
getAuthorizedFetchConfig: () => Promise.resolve(user ? { headers: { Authorization: user.uuid } } : {})
|
|
7
|
+
});
|
|
8
|
+
export const getAuthTokenOrCookie = (request, cookieName, queryKey = 'auth') => {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
const authParam = request.queryStringParameters ? request.queryStringParameters[queryKey] : undefined;
|
|
11
|
+
const authHeader = getHeader(request.headers, 'authorization');
|
|
12
|
+
const cookieValue = cookie.parse((_b = (_a = request.cookies) === null || _a === void 0 ? void 0 : _a.join('; ')) !== null && _b !== void 0 ? _b : '')[cookieName];
|
|
13
|
+
return typeof authParam === 'string'
|
|
14
|
+
? tuple(authParam, { Authorization: `Bearer ${authParam}` })
|
|
15
|
+
: authHeader && authHeader.length >= 8 && authHeader.startsWith('Bearer ')
|
|
16
|
+
? tuple(authHeader.slice(7), { Authorization: authHeader })
|
|
17
|
+
: tuple(cookieValue, { cookie: cookie.serialize(cookieName, cookieValue) });
|
|
18
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ConfigProviderForConfig } from '../../config';
|
|
2
|
+
import { GenericFetch } from '../../fetch';
|
|
3
|
+
import { CookieAuthProvider } from '.';
|
|
4
|
+
declare type Config = {
|
|
5
|
+
cookieName: string;
|
|
6
|
+
accountsBase: string;
|
|
7
|
+
};
|
|
8
|
+
interface Initializer<C> {
|
|
9
|
+
configSpace?: C;
|
|
10
|
+
fetch: GenericFetch;
|
|
11
|
+
}
|
|
12
|
+
export declare const subrequestAuthProvider: <C extends string = "subrequest">(initializer: Initializer<C>) => (configProvider: { [key in C]: {
|
|
13
|
+
cookieName: import("../../config").ConfigValueProvider<string>;
|
|
14
|
+
accountsBase: import("../../config").ConfigValueProvider<string>;
|
|
15
|
+
}; }) => CookieAuthProvider;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import cookie from 'cookie';
|
|
2
|
+
import { once } from '../..';
|
|
3
|
+
import { resolveConfigValue } from '../../config';
|
|
4
|
+
import { ifDefined } from '../../guards';
|
|
5
|
+
import { getAuthTokenOrCookie } from '.';
|
|
6
|
+
export const subrequestAuthProvider = (initializer) => (configProvider) => {
|
|
7
|
+
const config = configProvider[ifDefined(initializer.configSpace, 'subrequest')];
|
|
8
|
+
const cookieName = once(() => resolveConfigValue(config.cookieName));
|
|
9
|
+
const accountsBase = once(() => resolveConfigValue(config.accountsBase));
|
|
10
|
+
return ({ request, logger }) => {
|
|
11
|
+
let user;
|
|
12
|
+
const getAuthorizedFetchConfig = async () => {
|
|
13
|
+
const [token, headers] = getAuthTokenOrCookie(request, await cookieName());
|
|
14
|
+
if (!token) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
return { headers };
|
|
18
|
+
};
|
|
19
|
+
const loadUser = async () => {
|
|
20
|
+
const resolvedCookieName = await cookieName();
|
|
21
|
+
const [token] = getAuthTokenOrCookie(request, resolvedCookieName);
|
|
22
|
+
if (!token) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const headers = { cookie: cookie.serialize(resolvedCookieName, token) };
|
|
26
|
+
const user = await initializer.fetch((await accountsBase()).replace(/\/+$/, '') + '/api/user', { headers })
|
|
27
|
+
.then(response => response.json());
|
|
28
|
+
if (user) {
|
|
29
|
+
logger.setContext({ user: user.uuid });
|
|
30
|
+
}
|
|
31
|
+
return user;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
getAuthorizedFetchConfig,
|
|
35
|
+
getUser: async () => {
|
|
36
|
+
if (!user) {
|
|
37
|
+
user = await loadUser();
|
|
38
|
+
}
|
|
39
|
+
return user;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { User } from '..';
|
|
3
|
+
export declare const decryptJwe: (jwe: string, encryptionPrivateKey: Buffer | string) => string | undefined;
|
|
4
|
+
declare type MaybeAccountsSSOToken = {
|
|
5
|
+
iss?: string;
|
|
6
|
+
sub?: User | string;
|
|
7
|
+
aud?: string;
|
|
8
|
+
exp?: number;
|
|
9
|
+
nbf?: number;
|
|
10
|
+
iat?: number;
|
|
11
|
+
jti?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const verifyJws: (jws: string, signaturePublicKey: Buffer | string) => MaybeAccountsSSOToken | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Decrypts and verifies a SSO cookie.
|
|
16
|
+
*
|
|
17
|
+
* @param token the encrypted token
|
|
18
|
+
* @param encryptionPrivateKey the private key used to encrypt the token
|
|
19
|
+
* @param signaturePublicKey the public key used to verify the decrypted token
|
|
20
|
+
* @returns {user: User; exp: number} (success) or {error: string} (failure)
|
|
21
|
+
*/
|
|
22
|
+
export declare const decryptAndVerify: (token: string, encryptionPrivateKey: string, signaturePublicKey: string) => {
|
|
23
|
+
user: User;
|
|
24
|
+
exp: number;
|
|
25
|
+
} | {
|
|
26
|
+
error: string;
|
|
27
|
+
exp?: number;
|
|
28
|
+
};
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createDecipheriv, verify } from 'crypto';
|
|
2
|
+
import { isPlainObject } from '../../../guards';
|
|
3
|
+
export const decryptJwe = (jwe, encryptionPrivateKey) => {
|
|
4
|
+
const jweParts = jwe.split('.', 6);
|
|
5
|
+
if (jweParts.length !== 5 || jweParts[1]) {
|
|
6
|
+
return undefined;
|
|
7
|
+
} // Invalid/unsupported JWE
|
|
8
|
+
const header = JSON.parse(Buffer.from(jweParts[0], 'base64url').toString());
|
|
9
|
+
if (header.alg !== 'dir' || header.enc !== 'A256GCM') {
|
|
10
|
+
// Unsupported signature/encryption algorithm
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const aad = Buffer.from(jweParts[0]);
|
|
14
|
+
const iv = Buffer.from(jweParts[2], 'base64url');
|
|
15
|
+
const cipherText = Buffer.from(jweParts[3], 'base64url');
|
|
16
|
+
const authTag = Buffer.from(jweParts[4], 'base64url');
|
|
17
|
+
// Verify token signature and decrypt
|
|
18
|
+
const decipher = createDecipheriv('aes-256-gcm', encryptionPrivateKey, iv, { authTagLength: 16 });
|
|
19
|
+
decipher.setAAD(aad, { plaintextLength: cipherText.length });
|
|
20
|
+
try {
|
|
21
|
+
decipher.setAuthTag(authTag);
|
|
22
|
+
return `${decipher.update(cipherText)}${decipher.final()}`;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
// Invalid cipherText or authTag
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const issuer = 'OpenStax Accounts';
|
|
30
|
+
const audience = 'OpenStax';
|
|
31
|
+
const clockTolerance = 300; // 5 minutes
|
|
32
|
+
export const verifyJws = (jws, signaturePublicKey) => {
|
|
33
|
+
const jwsParts = jws.split('.', 4);
|
|
34
|
+
if (jwsParts.length !== 3) {
|
|
35
|
+
return undefined;
|
|
36
|
+
} // Invalid JWS
|
|
37
|
+
const header = JSON.parse(Buffer.from(jwsParts[0], 'base64url').toString());
|
|
38
|
+
if (header.alg !== 'RS256' || header.typ !== 'JWT') {
|
|
39
|
+
return undefined;
|
|
40
|
+
} // Unsupported JWS
|
|
41
|
+
const signedContent = Buffer.from(`${jwsParts[0]}.${jwsParts[1]}`);
|
|
42
|
+
const signature = Buffer.from(jwsParts[2], 'base64url');
|
|
43
|
+
if (!verify('RSA-SHA256', signedContent, signaturePublicKey, signature)) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const payload = Buffer.from(jwsParts[1], 'base64url').toString();
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(payload);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Decrypts and verifies a SSO cookie.
|
|
56
|
+
*
|
|
57
|
+
* @param token the encrypted token
|
|
58
|
+
* @param encryptionPrivateKey the private key used to encrypt the token
|
|
59
|
+
* @param signaturePublicKey the public key used to verify the decrypted token
|
|
60
|
+
* @returns {user: User; exp: number} (success) or {error: string} (failure)
|
|
61
|
+
*/
|
|
62
|
+
export const decryptAndVerify = (token, encryptionPrivateKey, signaturePublicKey) => {
|
|
63
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
64
|
+
const jws = decryptJwe(token, encryptionPrivateKey);
|
|
65
|
+
if (!jws) {
|
|
66
|
+
return { error: 'invalid token' };
|
|
67
|
+
}
|
|
68
|
+
const payload = verifyJws(jws, signaturePublicKey);
|
|
69
|
+
// Ensure payload contains all the claims we expect
|
|
70
|
+
// Normally "sub" would be a string but Accounts uses an object for it instead
|
|
71
|
+
if (!isPlainObject(payload) ||
|
|
72
|
+
!isPlainObject(payload.sub) || !payload.sub.uuid ||
|
|
73
|
+
payload.iss !== issuer ||
|
|
74
|
+
payload.aud !== audience ||
|
|
75
|
+
!payload.exp ||
|
|
76
|
+
!payload.nbf || payload.nbf > timestamp + clockTolerance ||
|
|
77
|
+
!payload.iat || payload.iat > timestamp + clockTolerance ||
|
|
78
|
+
!payload.jti) {
|
|
79
|
+
return { error: 'invalid token' };
|
|
80
|
+
}
|
|
81
|
+
if (payload.exp < timestamp - clockTolerance) {
|
|
82
|
+
return { error: 'expired token', exp: payload.exp };
|
|
83
|
+
}
|
|
84
|
+
return { user: payload.sub, exp: payload.exp };
|
|
85
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { User } from '..';
|
|
2
|
+
import { Window } from '../browser';
|
|
3
|
+
export declare type UserData = {
|
|
4
|
+
user?: User;
|
|
5
|
+
token: string | null;
|
|
6
|
+
};
|
|
7
|
+
declare type UserDataLoader = () => Promise<UserData>;
|
|
8
|
+
export declare enum PostMessageTypes {
|
|
9
|
+
ReceiveUser = "receive-user",
|
|
10
|
+
RequestUser = "request-user"
|
|
11
|
+
}
|
|
12
|
+
export declare const embeddedAuthProvider: (getUserData: UserDataLoader, { authQuery, window }: {
|
|
13
|
+
authQuery?: {
|
|
14
|
+
key: string;
|
|
15
|
+
value: string | null;
|
|
16
|
+
} | undefined;
|
|
17
|
+
window: Window;
|
|
18
|
+
}) => {
|
|
19
|
+
embeddedQueryKey: string;
|
|
20
|
+
embeddedQueryValue: string;
|
|
21
|
+
getAuthorizedEmbedUrl: (urlString: string, extraParams?: {
|
|
22
|
+
[key: string]: string;
|
|
23
|
+
} | undefined) => string;
|
|
24
|
+
unmount: () => void;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import queryString from 'query-string';
|
|
2
|
+
export var PostMessageTypes;
|
|
3
|
+
(function (PostMessageTypes) {
|
|
4
|
+
PostMessageTypes["ReceiveUser"] = "receive-user";
|
|
5
|
+
PostMessageTypes["RequestUser"] = "request-user";
|
|
6
|
+
})(PostMessageTypes || (PostMessageTypes = {}));
|
|
7
|
+
export const embeddedAuthProvider = (getUserData, { authQuery, window }) => {
|
|
8
|
+
const trustedEmbeds = new Set();
|
|
9
|
+
const embeddedQueryKey = 'embedded';
|
|
10
|
+
const embeddedQueryValue = 'true';
|
|
11
|
+
const messageHandler = event => {
|
|
12
|
+
if (event.data.type === PostMessageTypes.RequestUser && trustedEmbeds.has(event.origin)) {
|
|
13
|
+
getUserData().then(data => {
|
|
14
|
+
event.source.postMessage({ type: PostMessageTypes.ReceiveUser, userData: data }, event.origin);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
window.addEventListener('message', messageHandler);
|
|
19
|
+
const getAuthorizedEmbedUrl = (urlString, extraParams) => {
|
|
20
|
+
const url = new URL(urlString);
|
|
21
|
+
trustedEmbeds.add(url.origin);
|
|
22
|
+
const params = queryString.parse(url.search);
|
|
23
|
+
url.search = queryString.stringify({
|
|
24
|
+
...params,
|
|
25
|
+
...extraParams,
|
|
26
|
+
...(authQuery && authQuery.value ? { [authQuery.key]: authQuery.value } : { auth: 'embedded' }),
|
|
27
|
+
[embeddedQueryKey]: embeddedQueryValue,
|
|
28
|
+
subcontent: 'true',
|
|
29
|
+
});
|
|
30
|
+
return url.href;
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
embeddedQueryKey,
|
|
34
|
+
embeddedQueryValue,
|
|
35
|
+
getAuthorizedEmbedUrl,
|
|
36
|
+
unmount: () => {
|
|
37
|
+
window.removeEventListener('message', messageHandler);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AuthProvider } from '..';
|
|
2
|
+
import { AssertionFailed } from '../../../assertions';
|
|
3
|
+
import { ConfigProviderForConfig } from '../../../config';
|
|
4
|
+
declare type Config = {
|
|
5
|
+
application: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const createUserRoleValidator: (auth: AuthProvider, config: ConfigProviderForConfig<Config>) => {
|
|
8
|
+
getUserRoles: () => Promise<string[]>;
|
|
9
|
+
userHasRole: (role: string[]) => Promise<boolean>;
|
|
10
|
+
assertUserRole: (role: string[], fail?: AssertionFailed) => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
export declare type UserRoleValidator = ReturnType<typeof createUserRoleValidator>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { doThrow } from '../../../assertions';
|
|
2
|
+
import { resolveConfigValue } from '../../../config/resolveConfigValue';
|
|
3
|
+
import { UnauthorizedError } from '../../../errors';
|
|
4
|
+
import { once } from '../../../misc/helpers';
|
|
5
|
+
export const createUserRoleValidator = (auth, config) => {
|
|
6
|
+
const application = once(() => resolveConfigValue(config.application));
|
|
7
|
+
const getUserRoles = async () => {
|
|
8
|
+
var _a;
|
|
9
|
+
const user = await auth.getUser();
|
|
10
|
+
const appName = await application();
|
|
11
|
+
if (!user || !('applications' in user)) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return ((_a = user.applications.find(a => a.name === appName)) === null || _a === void 0 ? void 0 : _a.roles) || [];
|
|
15
|
+
};
|
|
16
|
+
const userHasRole = async (role) => {
|
|
17
|
+
const roles = await getUserRoles();
|
|
18
|
+
if (!roles.some(r => role.includes(r))) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
23
|
+
const assertUserRole = async (role, fail = new UnauthorizedError()) => {
|
|
24
|
+
if (!await userHasRole(role)) {
|
|
25
|
+
return doThrow(fail);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
getUserRoles,
|
|
30
|
+
userHasRole,
|
|
31
|
+
assertUserRole
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AttributeValue } from '@aws-sdk/client-dynamodb';
|
|
2
|
+
import { DocumentBaseType, DocumentBaseValueTypes } from '.';
|
|
3
|
+
export declare const encodeDynamoAttribute: (value: DocumentBaseValueTypes) => AttributeValue;
|
|
4
|
+
export declare const encodeDynamoDocument: (base: DocumentBaseType) => {
|
|
5
|
+
[k: string]: AttributeValue;
|
|
6
|
+
};
|
|
7
|
+
export declare const decodeDynamoAttribute: (value: AttributeValue) => DocumentBaseValueTypes;
|
|
8
|
+
export declare const decodeDynamoDocument: <T extends DocumentBaseType>(document: {
|
|
9
|
+
[key: string]: AttributeValue;
|
|
10
|
+
}) => T;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isPlainObject } from '../../guards';
|
|
2
|
+
export const encodeDynamoAttribute = (value) => {
|
|
3
|
+
if (typeof value === 'string') {
|
|
4
|
+
return { S: value };
|
|
5
|
+
}
|
|
6
|
+
if (typeof value === 'number') {
|
|
7
|
+
return { N: value.toString() };
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === 'boolean') {
|
|
10
|
+
return { BOOL: value };
|
|
11
|
+
}
|
|
12
|
+
if (value === null) {
|
|
13
|
+
return { NULL: true };
|
|
14
|
+
}
|
|
15
|
+
if (value instanceof Array) {
|
|
16
|
+
return { L: value.map(encodeDynamoAttribute) };
|
|
17
|
+
}
|
|
18
|
+
if (isPlainObject(value)) {
|
|
19
|
+
return { M: encodeDynamoDocument(value) };
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`unknown attribute type ${typeof value} with value ${value}.`);
|
|
22
|
+
};
|
|
23
|
+
export const encodeDynamoDocument = (base) => Object.fromEntries(Object.entries(base).map(([key, value]) => ([key, encodeDynamoAttribute(value)])));
|
|
24
|
+
export const decodeDynamoAttribute = (value) => {
|
|
25
|
+
if (value.S !== undefined) {
|
|
26
|
+
return value.S;
|
|
27
|
+
}
|
|
28
|
+
if (value.N !== undefined) {
|
|
29
|
+
return parseFloat(value.N);
|
|
30
|
+
}
|
|
31
|
+
if (value.BOOL !== undefined) {
|
|
32
|
+
return value.BOOL;
|
|
33
|
+
}
|
|
34
|
+
if (value.NULL !== undefined) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (value.L !== undefined) {
|
|
38
|
+
return value.L.map(decodeDynamoAttribute);
|
|
39
|
+
}
|
|
40
|
+
if (value.M !== undefined) {
|
|
41
|
+
return decodeDynamoDocument(value.M);
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`unknown attribute type: ${JSON.stringify(value)}.`);
|
|
44
|
+
};
|
|
45
|
+
export const decodeDynamoDocument = (document) => Object.fromEntries(Object.entries(document).map(([key, value]) => ([key, decodeDynamoAttribute(value)])));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare type Config = {
|
|
2
|
+
tableName: string;
|
|
3
|
+
};
|
|
4
|
+
export declare type DocumentBaseMapType = {
|
|
5
|
+
[key: string]: DocumentBaseValueTypes;
|
|
6
|
+
};
|
|
7
|
+
export declare type DocumentBaseListType = DocumentBaseValueTypes[];
|
|
8
|
+
export declare type DocumentBaseValueTypes = number | boolean | string | null | DocumentBaseMapType | DocumentBaseListType;
|
|
9
|
+
export declare type DocumentBaseType = {
|
|
10
|
+
[key: string]: DocumentBaseValueTypes;
|
|
11
|
+
};
|
|
12
|
+
export declare type TDocument<T> = {
|
|
13
|
+
[k in keyof T]: DocumentBaseValueTypes;
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Config, TDocument } from '..';
|
|
2
|
+
import { ConfigProviderForConfig } from '../../../config';
|
|
3
|
+
interface Initializer<C> {
|
|
4
|
+
configSpace?: C;
|
|
5
|
+
}
|
|
6
|
+
export declare const dynamoUnversionedDocumentStore: <C extends string = "dynamodb">(initializer?: Initializer<C> | undefined) => <T extends TDocument<T>>() => (configProvider: { [key in C]: {
|
|
7
|
+
tableName: import("../../../config").ConfigValueProvider<string>;
|
|
8
|
+
}; }) => <K extends keyof T>(_: {}, hashKey: K) => {
|
|
9
|
+
loadAllDocumentsTheBadWay: () => Promise<T[]>;
|
|
10
|
+
batchGetItem: (ids: T[K][]) => Promise<T[]>;
|
|
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>;
|
|
14
|
+
putItem: (item: T) => Promise<T>;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { BatchGetItemCommand, DynamoDB, GetItemCommand, PutItemCommand, ScanCommand, UpdateItemCommand } from '@aws-sdk/client-dynamodb';
|
|
2
|
+
import { once } from '../../..';
|
|
3
|
+
import { resolveConfigValue } from '../../../config';
|
|
4
|
+
import { NotFoundError } from '../../../errors';
|
|
5
|
+
import { ifDefined } from '../../../guards';
|
|
6
|
+
import { decodeDynamoDocument, encodeDynamoAttribute, encodeDynamoDocument } from '../dynamoEncoding';
|
|
7
|
+
const dynamodb = once(() => new DynamoDB({ apiVersion: '2012-08-10' }));
|
|
8
|
+
export const dynamoUnversionedDocumentStore = (initializer) => () => (configProvider) => (_, hashKey) => {
|
|
9
|
+
const options = ifDefined(initializer, {});
|
|
10
|
+
const tableName = once(() => resolveConfigValue(configProvider[ifDefined(options.configSpace, 'dynamodb')].tableName));
|
|
11
|
+
return {
|
|
12
|
+
loadAllDocumentsTheBadWay: async () => {
|
|
13
|
+
const loadAllResults = async (ExclusiveStartKey) => {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const cmd = new ScanCommand({ TableName: await tableName(), ExclusiveStartKey });
|
|
16
|
+
const result = await dynamodb().send(cmd);
|
|
17
|
+
const resultItems = (_b = (_a = result.Items) === null || _a === void 0 ? void 0 : _a.map((item) => decodeDynamoDocument(item))) !== null && _b !== void 0 ? _b : [];
|
|
18
|
+
if (result.LastEvaluatedKey) {
|
|
19
|
+
return [...resultItems, ...await loadAllResults(result.LastEvaluatedKey)];
|
|
20
|
+
}
|
|
21
|
+
return resultItems;
|
|
22
|
+
};
|
|
23
|
+
return loadAllResults();
|
|
24
|
+
},
|
|
25
|
+
batchGetItem: async (ids) => {
|
|
26
|
+
const table = await tableName();
|
|
27
|
+
const key = hashKey.toString();
|
|
28
|
+
const getBatches = async (requestItems) => {
|
|
29
|
+
const cmd = new BatchGetItemCommand({
|
|
30
|
+
RequestItems: requestItems !== null && requestItems !== void 0 ? requestItems : { [table]: { Keys: ids.map((id) => ({ [key]: encodeDynamoAttribute(id) })) } },
|
|
31
|
+
});
|
|
32
|
+
const response = await dynamodb().send(cmd);
|
|
33
|
+
const currentResponses = response.Responses ?
|
|
34
|
+
response.Responses[table].map(response => decodeDynamoDocument(response)) : [];
|
|
35
|
+
return currentResponses.concat(response.UnprocessedKeys ? await getBatches(response.UnprocessedKeys) : []);
|
|
36
|
+
};
|
|
37
|
+
return getBatches();
|
|
38
|
+
},
|
|
39
|
+
getItem: async (id) => {
|
|
40
|
+
const cmd = new GetItemCommand({
|
|
41
|
+
Key: { [hashKey.toString()]: encodeDynamoAttribute(id) },
|
|
42
|
+
TableName: await tableName(),
|
|
43
|
+
});
|
|
44
|
+
return dynamodb().send(cmd).then(result => result.Item ? decodeDynamoDocument(result.Item) : undefined);
|
|
45
|
+
},
|
|
46
|
+
/* atomically increments the given item attribute by 1 */
|
|
47
|
+
incrementItemAttribute: async (id, attribute) => {
|
|
48
|
+
const key = hashKey.toString();
|
|
49
|
+
const field = attribute.toString();
|
|
50
|
+
const cmd = new UpdateItemCommand({
|
|
51
|
+
Key: { [key]: encodeDynamoAttribute(id) },
|
|
52
|
+
TableName: await tableName(),
|
|
53
|
+
UpdateExpression: 'ADD #f :one',
|
|
54
|
+
ConditionExpression: 'attribute_exists(#k)',
|
|
55
|
+
ExpressionAttributeNames: { '#k': hashKey.toString(), '#f': field },
|
|
56
|
+
ExpressionAttributeValues: { ':one': { N: '1' } },
|
|
57
|
+
ReturnValues: 'UPDATED_NEW',
|
|
58
|
+
});
|
|
59
|
+
return dynamodb().send(cmd).then((item) => {
|
|
60
|
+
var _a;
|
|
61
|
+
const result = (_a = item.Attributes) === null || _a === void 0 ? void 0 : _a[field]['N'];
|
|
62
|
+
if (!result) {
|
|
63
|
+
throw new NotFoundError(`Item with ${key} "${id}" does not exist`);
|
|
64
|
+
}
|
|
65
|
+
return parseFloat(result);
|
|
66
|
+
}).catch((error) => {
|
|
67
|
+
throw error.name === 'ConditionalCheckFailedException' ?
|
|
68
|
+
new NotFoundError(`Item with ${key} "${id}" does not exist`) : error;
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
/* replaces only specified attributes with the given data */
|
|
72
|
+
patchItem: async (item) => {
|
|
73
|
+
const id = item[hashKey];
|
|
74
|
+
const key = hashKey.toString();
|
|
75
|
+
if (!id) {
|
|
76
|
+
throw new Error(`Key attribute "${key}" is required for patchItem`);
|
|
77
|
+
}
|
|
78
|
+
const entries = Object.entries(item).filter(([field]) => field !== key);
|
|
79
|
+
if (entries.length === 0) {
|
|
80
|
+
throw new Error('No attributes to update');
|
|
81
|
+
}
|
|
82
|
+
const updates = [];
|
|
83
|
+
const expressionAttributeNames = { '#k': key };
|
|
84
|
+
const expressionAttributeValues = {};
|
|
85
|
+
entries.forEach(([field, value], index) => {
|
|
86
|
+
updates.push(`#f${index} = :f${index}`);
|
|
87
|
+
expressionAttributeNames[`#f${index}`] = field;
|
|
88
|
+
expressionAttributeValues[`:f${index}`] = encodeDynamoAttribute(value);
|
|
89
|
+
});
|
|
90
|
+
const cmd = new UpdateItemCommand({
|
|
91
|
+
Key: { [key]: encodeDynamoAttribute(id) },
|
|
92
|
+
TableName: await tableName(),
|
|
93
|
+
UpdateExpression: `SET ${updates.join(', ')}`,
|
|
94
|
+
ConditionExpression: 'attribute_exists(#k)',
|
|
95
|
+
ExpressionAttributeNames: expressionAttributeNames,
|
|
96
|
+
ExpressionAttributeValues: expressionAttributeValues,
|
|
97
|
+
ReturnValues: 'ALL_NEW',
|
|
98
|
+
});
|
|
99
|
+
return dynamodb().send(cmd).then((item) => {
|
|
100
|
+
if (!item.Attributes) {
|
|
101
|
+
throw new NotFoundError(`Item with ${key} "${id}" does not exist`);
|
|
102
|
+
}
|
|
103
|
+
return decodeDynamoDocument(item.Attributes);
|
|
104
|
+
}).catch((error) => {
|
|
105
|
+
throw error.name === 'ConditionalCheckFailedException' ?
|
|
106
|
+
new NotFoundError(`Item with ${key} "${id}" does not exist`) : error;
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
/* replaces the entire document with the given data */
|
|
110
|
+
putItem: async (item) => {
|
|
111
|
+
const cmd = new PutItemCommand({
|
|
112
|
+
TableName: await tableName(),
|
|
113
|
+
Item: encodeDynamoDocument(item),
|
|
114
|
+
});
|
|
115
|
+
return dynamodb().send(cmd).then(() => item);
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Config, TDocument } from '..';
|
|
2
|
+
import { ConfigProviderForConfig } from '../../../config';
|
|
3
|
+
interface Initializer<C> {
|
|
4
|
+
dataDir: string;
|
|
5
|
+
fs?: Pick<typeof import('fs'), 'mkdir' | 'readdir' | 'readFile' | 'writeFile'>;
|
|
6
|
+
configSpace?: C;
|
|
7
|
+
}
|
|
8
|
+
export declare const fileSystemUnversionedDocumentStore: <C extends string = "fileSystem">(initializer: Initializer<C>) => <T extends TDocument<T>>() => (configProvider: { [key in C]: {
|
|
9
|
+
tableName: import("../../../config").ConfigValueProvider<string>;
|
|
10
|
+
}; }) => <K extends keyof T>(_: {}, hashKey: K) => {
|
|
11
|
+
loadAllDocumentsTheBadWay: () => Promise<T[]>;
|
|
12
|
+
batchGetItem: (ids: T[K][]) => Promise<Exclude<Awaited<T>, undefined>[]>;
|
|
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>;
|
|
16
|
+
putItem: (item: T) => Promise<T>;
|
|
17
|
+
};
|
|
18
|
+
export {};
|