@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,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
26
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
|
+
};
|
|
28
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
29
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
|
+
};
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.METHOD = exports.apiHtmlResponse = exports.apiTextResponse = exports.apiJsonResponse = exports.makeGetRequestResponder = exports.renderAnyRouteUrl = exports.makeRenderRouteUrl = exports.makeCreateRoute = void 0;
|
|
33
|
+
const pathToRegexp = __importStar(require("path-to-regexp"));
|
|
34
|
+
const query_string_1 = __importDefault(require("query-string"));
|
|
35
|
+
const helpers_1 = require("../misc/helpers");
|
|
36
|
+
const console_1 = require("../services/logger/console");
|
|
37
|
+
/**
|
|
38
|
+
* Makes a createRoute function that can be used to create routes (this is a factory factory). The
|
|
39
|
+
* `makeCreateRoute` function is typically called once in the backend and once in the frontend to
|
|
40
|
+
* set the types for the resulting `createRoute` function -- that latter function is called once
|
|
41
|
+
* per route. E.g. for the backend, the call could look like:
|
|
42
|
+
*
|
|
43
|
+
* ```
|
|
44
|
+
* export const createRoute = makeCreateRoute<AppServices, ApiRouteRequest, {
|
|
45
|
+
* method: METHOD;
|
|
46
|
+
* }>();
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Notes:
|
|
50
|
+
* * The `{method: METHOD}` defines the `Ex` extension type; here, the `method` property is only
|
|
51
|
+
* relevant to backend routes.
|
|
52
|
+
* * when defining the `createRoute` method, only the request input format is defined, the output
|
|
53
|
+
* format is derived from the routes.
|
|
54
|
+
*
|
|
55
|
+
* When calling the resulting `createRoute` function, the only required params of the route are the
|
|
56
|
+
* name, path, and handler. Other params can be added to the type and then later used in the
|
|
57
|
+
* routeMatcher.
|
|
58
|
+
*
|
|
59
|
+
* eg when defining requestServiceProvider in line, the types have a hard time, it helps to put in another argument:
|
|
60
|
+
* ```
|
|
61
|
+
* export const exampleRoute = createRoute({
|
|
62
|
+
* name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
|
|
63
|
+
* requestServiceProvider: composeServiceMiddleware({
|
|
64
|
+
* cookieAuthMiddleware,
|
|
65
|
+
* documentStoreMiddleware,
|
|
66
|
+
* }},
|
|
67
|
+
* async(params: {key: string}, services) => {
|
|
68
|
+
* const result = await services.myDocumentStore.getItem(params.key);
|
|
69
|
+
*
|
|
70
|
+
* if (!result) {
|
|
71
|
+
* throw new NotFoundError('requested item not found');
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* return apiJsonResponse(200, result);
|
|
75
|
+
* }
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
* eg when using a pre-existing provider variable the types work better:
|
|
79
|
+
* ```
|
|
80
|
+
* export const exampleRoute = createRoute({
|
|
81
|
+
* name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
|
|
82
|
+
* requestServiceProvider,
|
|
83
|
+
* handler: async(params: {key: string}, services) => {
|
|
84
|
+
* const result = await services.myDocumentStore.getItem(params.key);
|
|
85
|
+
*
|
|
86
|
+
* if (!result) {
|
|
87
|
+
* throw new NotFoundError('requested item not found');
|
|
88
|
+
* }
|
|
89
|
+
*
|
|
90
|
+
* return apiJsonResponse(200, result);
|
|
91
|
+
* }
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
const makeCreateRoute = () => (...args) => {
|
|
96
|
+
return (args.length === 1
|
|
97
|
+
? args[0]
|
|
98
|
+
: { ...args[0], handler: args[1] });
|
|
99
|
+
};
|
|
100
|
+
exports.makeCreateRoute = makeCreateRoute;
|
|
101
|
+
/* begin reverse routing utils */
|
|
102
|
+
/**
|
|
103
|
+
* Makes a renderRouteUrl function that can be used to render route paths (this is a factory
|
|
104
|
+
* factory). The returned function takes a `route`, `params`, and `query` and returns a string
|
|
105
|
+
* with the params and query substituted into the route path.
|
|
106
|
+
*
|
|
107
|
+
* this function is initialized using the Ru type which indicates the specific routes wired into
|
|
108
|
+
* the application, which means that if you try to build a url with a route which is not wired
|
|
109
|
+
* into the router you will get a type error. this is a feature to prevent referencing routes that
|
|
110
|
+
* don't exist or aren't handling requests properly.
|
|
111
|
+
*
|
|
112
|
+
* if you are making a helper function or need to render a route outside your application, you
|
|
113
|
+
* can use the `renderAnyRouteUrl` function
|
|
114
|
+
*/
|
|
115
|
+
const makeRenderRouteUrl = () => (route, params, query = {}) => {
|
|
116
|
+
const getPathForParams = pathToRegexp.compile(route.path, { encode: encodeURIComponent });
|
|
117
|
+
const search = query_string_1.default.stringify(query);
|
|
118
|
+
const path = getPathForParams(params) + (search ? `?${search}` : '');
|
|
119
|
+
return path;
|
|
120
|
+
};
|
|
121
|
+
exports.makeRenderRouteUrl = makeRenderRouteUrl;
|
|
122
|
+
/**
|
|
123
|
+
* A pre-made result from `makeRenderRouteUrl`, this function interpolates parameter and query
|
|
124
|
+
* arguments into a route path.
|
|
125
|
+
*
|
|
126
|
+
* prefer using `renderRouteUrl` initialized with your applications route union
|
|
127
|
+
* when possible to help catch improperly initialized routes.
|
|
128
|
+
*
|
|
129
|
+
* @param route the route that has a `path` to be interpolated
|
|
130
|
+
* @param params the parameters to interpolate into the route path
|
|
131
|
+
* @param query the query parameters to add to the route path
|
|
132
|
+
* @returns the interpolated route path
|
|
133
|
+
*/
|
|
134
|
+
exports.renderAnyRouteUrl = (0, exports.makeRenderRouteUrl)();
|
|
135
|
+
const bindRoute = (services, appBinder, pathExtractor, matcher) => (route) => {
|
|
136
|
+
const getParamsFromPath = pathToRegexp.match(route.path, { decode: decodeURIComponent });
|
|
137
|
+
const boundServiceProvider = route.requestServiceProvider && appBinder(services, route.requestServiceProvider);
|
|
138
|
+
return (request, logger) => {
|
|
139
|
+
const path = pathExtractor(request);
|
|
140
|
+
const match = getParamsFromPath(path);
|
|
141
|
+
if ((!matcher || matcher(request, route)) && match) {
|
|
142
|
+
return () => route.handler(match.params, boundServiceProvider ? boundServiceProvider({ request, logger }, { route, params: match.params }) : undefined);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* A factory factory for creating request responders (functions that take a request and return a
|
|
148
|
+
* response -- these functions let us implement Lambda `handler` functions).
|
|
149
|
+
*
|
|
150
|
+
* Use it in two steps. First, call it with the general business logic that defines routes, logs,
|
|
151
|
+
* errors, etc:
|
|
152
|
+
* ```
|
|
153
|
+
* const getRequestResponder = makeGetRequestResponder<
|
|
154
|
+
* AppServices, TRoutes, ApiRouteRequest, Promise<ApiRouteResponse>
|
|
155
|
+
* >() // this empty invocation helps typescript mix defined and inferred types
|
|
156
|
+
* ({
|
|
157
|
+
* routes: apiRoutes, // the route definitions
|
|
158
|
+
* pathExtractor, // how to get the path out of the request format
|
|
159
|
+
* routeMatcher, // logic for matching route (beyond path matching, optional)
|
|
160
|
+
* errorHandler, // any special error handling
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
* Note here that among other things we're specifying a generic response format that the response
|
|
164
|
+
* and error handling middleware can use, if any routes have responses that don't adhere to this
|
|
165
|
+
* it'll complain about it.
|
|
166
|
+
*
|
|
167
|
+
* Next, we use the `getRequestResponder` to create a responder for a specific lambda entrypoint:
|
|
168
|
+
* ```
|
|
169
|
+
* export const handler: (request: APIGatewayProxyEventV2): Promise<ApiRouteResponse> =>
|
|
170
|
+
* getRequestResponder(
|
|
171
|
+
* lambdaServices, // the AppServices for this entrypoint
|
|
172
|
+
* lambdaMiddleware // environment specific response middleware (like cors)
|
|
173
|
+
* );
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
const makeGetRequestResponder = () => ({ routes, pathExtractor, routeMatcher, errorHandler, logExtractor }) => (services, responseMiddleware) => {
|
|
177
|
+
const appBinderImpl = (app, middleware) => middleware(app, appBinder);
|
|
178
|
+
const appBinder = (0, helpers_1.memoize)(appBinderImpl);
|
|
179
|
+
const boundRoutes = routes().map(bindRoute(services, appBinder, pathExtractor, routeMatcher));
|
|
180
|
+
const boundResponseMiddleware = responseMiddleware ? responseMiddleware(services) : undefined;
|
|
181
|
+
const appLogger = services.logger || (0, console_1.createConsoleLogger)();
|
|
182
|
+
// *note* this opaque promise guard is less generic than i hoped so
|
|
183
|
+
// i'm leaving it here instead of the guards file.
|
|
184
|
+
//
|
|
185
|
+
// its less than ideal because it enforces that the handlers return
|
|
186
|
+
// the same type as the parent promise, usually a handler can be either a
|
|
187
|
+
// promise or a non-promise value and the promise figures it out, but those
|
|
188
|
+
// types are getting complicated quickly here.
|
|
189
|
+
const isPromise = (thing) => thing instanceof Promise;
|
|
190
|
+
return (request) => {
|
|
191
|
+
const logger = appLogger.createSubContext();
|
|
192
|
+
if (logExtractor) {
|
|
193
|
+
logger.setContext(logExtractor(request));
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const executor = (0, helpers_1.mapFind)(boundRoutes, (route) => route(request, logger));
|
|
197
|
+
if (executor) {
|
|
198
|
+
const result = boundResponseMiddleware ?
|
|
199
|
+
boundResponseMiddleware(executor(), { request, logger }) : executor();
|
|
200
|
+
if (isPromise(result) && errorHandler) {
|
|
201
|
+
const errorHandlerWithMiddleware = (e) => boundResponseMiddleware ?
|
|
202
|
+
boundResponseMiddleware(errorHandler(e, logger), { request, logger }) : errorHandler(e, logger);
|
|
203
|
+
return result.catch(errorHandlerWithMiddleware);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (boundResponseMiddleware) {
|
|
210
|
+
return boundResponseMiddleware(undefined, { request, logger });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (e) {
|
|
214
|
+
if (errorHandler && e instanceof Error) {
|
|
215
|
+
return boundResponseMiddleware
|
|
216
|
+
? boundResponseMiddleware(errorHandler(e, logger), { request, logger })
|
|
217
|
+
: errorHandler(e, logger);
|
|
218
|
+
}
|
|
219
|
+
throw e;
|
|
220
|
+
}
|
|
221
|
+
return undefined;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
exports.makeGetRequestResponder = makeGetRequestResponder;
|
|
225
|
+
/**
|
|
226
|
+
* Returns a JSON response. Handles serializing the data to JSON and setting the content-type header.
|
|
227
|
+
* @param statusCode e.g. 201
|
|
228
|
+
* @param data the object to be serialized to JSON
|
|
229
|
+
* @param headers HTTP headers
|
|
230
|
+
* @example
|
|
231
|
+
* return apiJsonResponse(
|
|
232
|
+
* 200, {
|
|
233
|
+
* message: "hello, world!",
|
|
234
|
+
* foo: "bar",
|
|
235
|
+
* },
|
|
236
|
+
* { 'X-Frame-Options': 'DENY' }
|
|
237
|
+
* );
|
|
238
|
+
*/
|
|
239
|
+
const apiJsonResponse = (statusCode, data, headers) => ({ statusCode, data, body: JSON.stringify(data), headers: { ...headers, 'content-type': 'application/json' } });
|
|
240
|
+
exports.apiJsonResponse = apiJsonResponse;
|
|
241
|
+
/**
|
|
242
|
+
* Returns a plain text response. Handles setting the content-type header.
|
|
243
|
+
* @param statusCode e.g. 201
|
|
244
|
+
* @param data some string
|
|
245
|
+
* @param headers HTTP headers
|
|
246
|
+
* @example return apiTextResponse(200, 'some text')
|
|
247
|
+
*/
|
|
248
|
+
const apiTextResponse = (statusCode, data, headers) => ({ statusCode, data, body: data, headers: { ...headers, 'content-type': 'text/plain' } });
|
|
249
|
+
exports.apiTextResponse = apiTextResponse;
|
|
250
|
+
/**
|
|
251
|
+
* Returns an HTML response. Handles setting the content-type header.
|
|
252
|
+
* @param statusCode e.g. 201
|
|
253
|
+
* @param data some string
|
|
254
|
+
* @param headers HTTP headers
|
|
255
|
+
* @example return apiHtmlResponse(200, '<b>some text</b>')
|
|
256
|
+
*/
|
|
257
|
+
const apiHtmlResponse = (statusCode, data, headers) => ({ statusCode, data, body: data, headers: { ...headers, 'content-type': 'text/html' } });
|
|
258
|
+
exports.apiHtmlResponse = apiHtmlResponse;
|
|
259
|
+
/** HTTP method enum */
|
|
260
|
+
var METHOD;
|
|
261
|
+
(function (METHOD) {
|
|
262
|
+
METHOD["GET"] = "GET";
|
|
263
|
+
METHOD["HEAD"] = "HEAD";
|
|
264
|
+
METHOD["POST"] = "POST";
|
|
265
|
+
METHOD["PUT"] = "PUT";
|
|
266
|
+
METHOD["PATCH"] = "PATCH";
|
|
267
|
+
METHOD["DELETE"] = "DELETE";
|
|
268
|
+
METHOD["OPTIONS"] = "OPTIONS";
|
|
269
|
+
})(METHOD = exports.METHOD || (exports.METHOD = {}));
|
|
270
|
+
__exportStar(require("./helpers"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.zodPayloadValidator = void 0;
|
|
4
|
+
const errors_1 = require("../../errors");
|
|
5
|
+
const zodPayloadValidator = (validator) => (input) => {
|
|
6
|
+
const result = validator.safeParse(input);
|
|
7
|
+
if (result.success) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
throw new errors_1.ValidationError(result.error.format());
|
|
11
|
+
};
|
|
12
|
+
exports.zodPayloadValidator = zodPayloadValidator;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ConfigProviderForConfig } from '../../config';
|
|
2
|
+
import { GenericFetch } from '../../fetch';
|
|
3
|
+
import { JsonCompatibleStruct } from '../../routing';
|
|
4
|
+
import { ApiUser, ConsentPreferences } from '../authProvider';
|
|
5
|
+
import { Logger } from '../logger';
|
|
6
|
+
export declare type Config = {
|
|
7
|
+
accountsBase: string;
|
|
8
|
+
accountsAuthToken: string;
|
|
9
|
+
};
|
|
10
|
+
interface Initializer<C> {
|
|
11
|
+
configSpace?: C;
|
|
12
|
+
fetch: GenericFetch;
|
|
13
|
+
}
|
|
14
|
+
export declare type FindUserPayload = ({
|
|
15
|
+
external_id: string;
|
|
16
|
+
} | {
|
|
17
|
+
uuid: string;
|
|
18
|
+
}) & {
|
|
19
|
+
sso?: string;
|
|
20
|
+
};
|
|
21
|
+
export declare type FindOrCreateUserPayload = {
|
|
22
|
+
external_id: string;
|
|
23
|
+
email?: string;
|
|
24
|
+
already_verified?: boolean;
|
|
25
|
+
first_name?: string;
|
|
26
|
+
last_name?: string;
|
|
27
|
+
full_name?: string;
|
|
28
|
+
salesforce_contact_id?: string;
|
|
29
|
+
faculty_status?: string;
|
|
30
|
+
role?: string;
|
|
31
|
+
school_type?: string;
|
|
32
|
+
is_test?: boolean;
|
|
33
|
+
sso?: string;
|
|
34
|
+
};
|
|
35
|
+
export declare type FindOrCreateUserResponse = {
|
|
36
|
+
id: number;
|
|
37
|
+
uuid: string;
|
|
38
|
+
external_ids: string[];
|
|
39
|
+
is_test: boolean;
|
|
40
|
+
sso: string;
|
|
41
|
+
};
|
|
42
|
+
export declare type FindUserResponse = (FindOrCreateUserResponse & {
|
|
43
|
+
external_ids: string[];
|
|
44
|
+
}) | undefined;
|
|
45
|
+
export declare type LinkUserPayload = {
|
|
46
|
+
userId: number;
|
|
47
|
+
externalId: string;
|
|
48
|
+
};
|
|
49
|
+
export declare type LinkUserResponse = {
|
|
50
|
+
user_id: number;
|
|
51
|
+
external_id: string;
|
|
52
|
+
};
|
|
53
|
+
export declare type SearchUsersPayload = {
|
|
54
|
+
q: string;
|
|
55
|
+
order_by?: string;
|
|
56
|
+
};
|
|
57
|
+
export declare type SearchUsersResponse = {
|
|
58
|
+
items: Array<ApiUser & {
|
|
59
|
+
external_ids: string[];
|
|
60
|
+
} & JsonCompatibleStruct>;
|
|
61
|
+
total_count: number;
|
|
62
|
+
};
|
|
63
|
+
export declare type UpdateUserPayload = ConsentPreferences;
|
|
64
|
+
export declare type MappedUserInfo<T> = {
|
|
65
|
+
data: T;
|
|
66
|
+
fullName: string;
|
|
67
|
+
platformUserId?: string;
|
|
68
|
+
uuid: string;
|
|
69
|
+
};
|
|
70
|
+
export declare const accountsGateway: <C extends string = "accounts">(initializer: Initializer<C>) => (configProvider: { [key in C]: {
|
|
71
|
+
accountsBase: import("../../config").ConfigValueProvider<string>;
|
|
72
|
+
accountsAuthToken: import("../../config").ConfigValueProvider<string>;
|
|
73
|
+
}; }) => {
|
|
74
|
+
findOrCreateUser: (body: FindOrCreateUserPayload) => Promise<FindOrCreateUserResponse>;
|
|
75
|
+
findUser: (body: FindUserPayload) => Promise<FindUserResponse>;
|
|
76
|
+
getUser: (token: string) => Promise<ApiUser & JsonCompatibleStruct>;
|
|
77
|
+
linkUser: (body: LinkUserPayload) => Promise<LinkUserResponse>;
|
|
78
|
+
mapUserUuids: <T>(userUuidsMap: {
|
|
79
|
+
[uuid: string]: T;
|
|
80
|
+
}, logger: Logger, platformId?: string | undefined) => Promise<MappedUserInfo<T>[]>;
|
|
81
|
+
searchUsers: (payload: SearchUsersPayload) => Promise<SearchUsersResponse>;
|
|
82
|
+
updateUser: (token: string, body: UpdateUserPayload) => Promise<ApiUser & JsonCompatibleStruct>;
|
|
83
|
+
};
|
|
84
|
+
export declare type AccountsGateway = ReturnType<ReturnType<typeof accountsGateway>>;
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.accountsGateway = void 0;
|
|
7
|
+
const lodash_1 = require("lodash");
|
|
8
|
+
const query_string_1 = __importDefault(require("query-string"));
|
|
9
|
+
const config_1 = require("../../config");
|
|
10
|
+
const guards_1 = require("../../guards");
|
|
11
|
+
const routing_1 = require("../../routing");
|
|
12
|
+
const logger_1 = require("../logger");
|
|
13
|
+
class ApiError extends Error {
|
|
14
|
+
constructor(message, status) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.status = status;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const accountsGateway = (initializer) => (configProvider) => {
|
|
20
|
+
const config = configProvider[(0, guards_1.ifDefined)(initializer.configSpace, 'accounts')];
|
|
21
|
+
const accountsBase = (0, config_1.resolveConfigValue)(config.accountsBase);
|
|
22
|
+
const accountsAuthToken = (0, config_1.resolveConfigValue)(config.accountsAuthToken);
|
|
23
|
+
const request = async (method, path, options, statuses = [200, 201]) => {
|
|
24
|
+
const host = (await accountsBase).replace(/\/+$/, '');
|
|
25
|
+
const url = `${host}/api/${path}`;
|
|
26
|
+
const config = {
|
|
27
|
+
headers: {
|
|
28
|
+
Authorization: `Bearer ${options.token || await accountsAuthToken}`,
|
|
29
|
+
},
|
|
30
|
+
method,
|
|
31
|
+
};
|
|
32
|
+
if (options.body) {
|
|
33
|
+
config.body = JSON.stringify(options.body);
|
|
34
|
+
}
|
|
35
|
+
const response = await initializer.fetch(url, config);
|
|
36
|
+
if (!statuses.includes(response.status)) {
|
|
37
|
+
throw new ApiError(`Received unexpected status code ${response.status} for Accounts API call: ${method} ${url}`, response.status);
|
|
38
|
+
}
|
|
39
|
+
return response.json();
|
|
40
|
+
};
|
|
41
|
+
const findOrCreateUser = async (body) => request(routing_1.METHOD.POST, 'user/find-or-create', { body });
|
|
42
|
+
const findUser = async (body) => {
|
|
43
|
+
try {
|
|
44
|
+
return await request(routing_1.METHOD.POST, 'user/find', { body });
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const getUser = async (token) => request(routing_1.METHOD.GET, 'user', { token });
|
|
56
|
+
const linkUser = async (body) => request(routing_1.METHOD.POST, 'user/external-ids', {
|
|
57
|
+
body: {
|
|
58
|
+
external_id: body.externalId,
|
|
59
|
+
user_id: body.userId,
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const searchUsers = async (payload) => request(routing_1.METHOD.GET, `users?${query_string_1.default.stringify(payload)}`, {});
|
|
63
|
+
const updateUser = async (token, body) => request(routing_1.METHOD.PUT, 'user', { body, token });
|
|
64
|
+
const getPlatformUserId = (externalIds, platformId) => {
|
|
65
|
+
for (const externalId of externalIds) {
|
|
66
|
+
const [userPlatformId, userId] = externalId.split('/', 2);
|
|
67
|
+
if (userPlatformId === platformId) {
|
|
68
|
+
return userId;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/*
|
|
73
|
+
* If a platformId is given, returns an array where
|
|
74
|
+
* the first element is the user id from the platform
|
|
75
|
+
* and the second is the value from the map
|
|
76
|
+
* Otherwise, returns an array where
|
|
77
|
+
* the first element is the user's full_name
|
|
78
|
+
* and the second is the value from the map
|
|
79
|
+
*/
|
|
80
|
+
const mapUserUuids = async (userUuidsMap, logger, platformId) => {
|
|
81
|
+
const results = [];
|
|
82
|
+
// Accounts will not return any results if this search returns more than 10 users
|
|
83
|
+
const chunkedUuids = (0, lodash_1.chunk)(Object.keys(userUuidsMap), 10);
|
|
84
|
+
await Promise.all(chunkedUuids.map(async (uuids) => {
|
|
85
|
+
const { items } = await searchUsers({ q: uuids.map((uuid) => `uuid:${uuid}`).join(' ') });
|
|
86
|
+
const accountsUuids = items.map((user) => user.uuid);
|
|
87
|
+
if (!(0, lodash_1.isEqual)(accountsUuids.sort(), uuids.sort())) {
|
|
88
|
+
logger.logEvent(logger_1.Level.Warn, {
|
|
89
|
+
message: 'Unexpected Accounts user search results',
|
|
90
|
+
uuids,
|
|
91
|
+
accountsUuids,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
items.forEach((user) => {
|
|
95
|
+
const platformUserId = platformId ? getPlatformUserId(user.external_ids, platformId) : undefined;
|
|
96
|
+
if (platformId && !platformUserId) {
|
|
97
|
+
logger.logEvent(logger_1.Level.Warn, {
|
|
98
|
+
message: 'Accounts user has no external_id matching the given platformId',
|
|
99
|
+
accountsUuid: user.uuid,
|
|
100
|
+
platformId,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (!user.full_name) {
|
|
104
|
+
logger.logEvent(logger_1.Level.Warn, {
|
|
105
|
+
message: 'Accounts user has no full_name',
|
|
106
|
+
accountsUuid: user.uuid,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
results.push({
|
|
110
|
+
data: userUuidsMap[user.uuid], fullName: user.full_name, platformUserId, uuid: user.uuid,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}));
|
|
114
|
+
return results;
|
|
115
|
+
};
|
|
116
|
+
return { findOrCreateUser, findUser, getUser, linkUser, mapUserUuids, searchUsers, updateUser };
|
|
117
|
+
};
|
|
118
|
+
exports.accountsGateway = accountsGateway;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Headers } from 'node-fetch';
|
|
2
|
+
import { ConfigProviderForConfig } from '../../config';
|
|
3
|
+
import { ConfigForFetch, GenericFetch, Response } from '../../fetch';
|
|
4
|
+
import { AnyRoute, ApiResponse, OutputForRoute, ParamsForRoute, PayloadForRoute, QueryParams } from '../../routing';
|
|
5
|
+
import { UnwrapPromise } from '../../types';
|
|
6
|
+
import { Logger } from '../logger';
|
|
7
|
+
declare type TResponsePayload<R> = R extends ApiResponse<any, infer P> ? P : never;
|
|
8
|
+
declare type TResponseStatus<R> = R extends ApiResponse<infer S, any> ? S : never;
|
|
9
|
+
declare type RouteClient<R> = {
|
|
10
|
+
(config: {
|
|
11
|
+
fetchConfig?: any;
|
|
12
|
+
query?: QueryParams;
|
|
13
|
+
} & (ParamsForRoute<R> extends undefined ? {} : {
|
|
14
|
+
params: ParamsForRoute<R>;
|
|
15
|
+
}) & (PayloadForRoute<R> extends undefined ? {} : {
|
|
16
|
+
payload: PayloadForRoute<R>;
|
|
17
|
+
})): Promise<UnsafeApiClientResponse<UnwrapPromise<OutputForRoute<R>>>>;
|
|
18
|
+
renderUrl: (config: {
|
|
19
|
+
query?: QueryParams;
|
|
20
|
+
} & (ParamsForRoute<R> extends undefined ? {} : {
|
|
21
|
+
params: ParamsForRoute<R>;
|
|
22
|
+
})) => Promise<string>;
|
|
23
|
+
};
|
|
24
|
+
interface AcceptStatus<Ro> {
|
|
25
|
+
<S extends TResponseStatus<Ro>[]>(...args: S): Promise<ApiClientResponse<Extract<Ro, Record<'statusCode', S[number]>>>>;
|
|
26
|
+
<S extends number[]>(...args: S): Promise<ApiClientResponse<any>>;
|
|
27
|
+
}
|
|
28
|
+
declare type UnsafeApiClientResponse<Ro> = {
|
|
29
|
+
headers: Headers;
|
|
30
|
+
load: () => Promise<any>;
|
|
31
|
+
status: number;
|
|
32
|
+
acceptStatus: AcceptStatus<Ro>;
|
|
33
|
+
};
|
|
34
|
+
declare type ApiClientResponse<Ro> = Ro extends any ? {
|
|
35
|
+
headers: Headers;
|
|
36
|
+
status: TResponseStatus<Ro>;
|
|
37
|
+
load: () => Promise<TResponsePayload<Ro>>;
|
|
38
|
+
} : never;
|
|
39
|
+
declare type MapRoutesToClient<Ru> = [Ru] extends [AnyRoute<Ru>] ? {
|
|
40
|
+
[N in Ru['name']]: RouteClient<Extract<Ru, Record<'name', N>>>;
|
|
41
|
+
} : never;
|
|
42
|
+
declare type MapRoutesToConfig<Ru> = [Ru] extends [AnyRoute<Ru>] ? {
|
|
43
|
+
[N in Ru['name']]: {
|
|
44
|
+
path: string;
|
|
45
|
+
method: string;
|
|
46
|
+
};
|
|
47
|
+
} : never;
|
|
48
|
+
/** Pulls the content out of a response based on the content type */
|
|
49
|
+
export declare const loadResponse: (response: Response) => () => Promise<any>;
|
|
50
|
+
interface MakeApiGateway<F> {
|
|
51
|
+
<Ru>(config: ConfigProviderForConfig<{
|
|
52
|
+
apiBase: string;
|
|
53
|
+
}>, routes: MapRoutesToConfig<Ru>, appProvider?: {
|
|
54
|
+
authProvider?: {
|
|
55
|
+
getAuthorizedFetchConfig: () => Promise<ConfigForFetch<F>>;
|
|
56
|
+
};
|
|
57
|
+
logger?: Logger;
|
|
58
|
+
}): MapRoutesToClient<Ru>;
|
|
59
|
+
}
|
|
60
|
+
export declare const createApiGateway: <F extends GenericFetch<import("../../fetch").FetchConfig, Response>>(initializer: {
|
|
61
|
+
fetch: F;
|
|
62
|
+
}) => MakeApiGateway<F>;
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.createApiGateway = exports.loadResponse = void 0;
|
|
30
|
+
const pathToRegexp = __importStar(require("path-to-regexp"));
|
|
31
|
+
const query_string_1 = __importDefault(require("query-string"));
|
|
32
|
+
const uuid_1 = require("uuid");
|
|
33
|
+
const __1 = require("../..");
|
|
34
|
+
const config_1 = require("../../config");
|
|
35
|
+
const errors_1 = require("../../errors");
|
|
36
|
+
const fetchStatusRetry_1 = require("../../fetch/fetchStatusRetry");
|
|
37
|
+
const logger_1 = require("../logger");
|
|
38
|
+
/** Pulls the content out of a response based on the content type */
|
|
39
|
+
const loadResponse = (response) => () => {
|
|
40
|
+
const [contentType] = (response.headers.get('content-type') || '').split(';');
|
|
41
|
+
switch (contentType) {
|
|
42
|
+
case 'text/plain':
|
|
43
|
+
return response.text();
|
|
44
|
+
case 'application/json':
|
|
45
|
+
return response.json();
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`unknown content type ${contentType}`);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
exports.loadResponse = loadResponse;
|
|
51
|
+
const makeRouteClient = (initializer, config, route, appProvider) => {
|
|
52
|
+
/* TODO this duplicates code with makeRenderRouteUrl, reuse that */
|
|
53
|
+
const renderUrl = async ({ params, query }) => {
|
|
54
|
+
const apiBase = await (0, config_1.resolveConfigValue)(config.apiBase);
|
|
55
|
+
const getPathForParams = pathToRegexp.compile(route.path, { encode: encodeURIComponent });
|
|
56
|
+
const search = query && query_string_1.default.stringify(query);
|
|
57
|
+
return apiBase.replace(/\/+$/, '') + getPathForParams(params || {}) + (search ? `?${search}` : '');
|
|
58
|
+
};
|
|
59
|
+
const routeClient = async ({ params, payload, query, fetchConfig }) => {
|
|
60
|
+
var _a, _b;
|
|
61
|
+
const { fetch } = initializer;
|
|
62
|
+
const url = await renderUrl({ params, query });
|
|
63
|
+
const body = payload ? JSON.stringify(payload) : undefined;
|
|
64
|
+
const baseOptions = (0, __1.merge)((await ((_a = appProvider === null || appProvider === void 0 ? void 0 : appProvider.authProvider) === null || _a === void 0 ? void 0 : _a.getAuthorizedFetchConfig())) || {}, fetchConfig || {});
|
|
65
|
+
const requestId = (0, uuid_1.v4)();
|
|
66
|
+
const requestLogger = (_b = appProvider === null || appProvider === void 0 ? void 0 : appProvider.logger) === null || _b === void 0 ? void 0 : _b.createSubContext();
|
|
67
|
+
requestLogger === null || requestLogger === void 0 ? void 0 : requestLogger.setContext({ requestId, url, timeStamp: new Date().getTime() });
|
|
68
|
+
const fetcher = (0, fetchStatusRetry_1.fetchStatusRetry)(fetch, { retries: 1, status: [502], logger: requestLogger });
|
|
69
|
+
requestLogger === null || requestLogger === void 0 ? void 0 : requestLogger.log('Request Initiated', logger_1.Level.Info);
|
|
70
|
+
return fetcher(url, (0, __1.merge)(baseOptions, {
|
|
71
|
+
method: route.method,
|
|
72
|
+
body,
|
|
73
|
+
headers: {
|
|
74
|
+
...fetchConfig === null || fetchConfig === void 0 ? void 0 : fetchConfig.headers,
|
|
75
|
+
...(body ? { 'content-type': 'application/json' } : {}),
|
|
76
|
+
'X-Request-ID': requestId,
|
|
77
|
+
}
|
|
78
|
+
})).then(response => {
|
|
79
|
+
if (response.status === 401) {
|
|
80
|
+
throw new errors_1.UnauthorizedError();
|
|
81
|
+
}
|
|
82
|
+
if (response.status === 403) {
|
|
83
|
+
throw new errors_1.ForbiddenError();
|
|
84
|
+
}
|
|
85
|
+
if (response.status === 440) {
|
|
86
|
+
throw new errors_1.SessionExpiredError();
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
status: response.status,
|
|
90
|
+
acceptStatus: async (...status) => {
|
|
91
|
+
if (!status.includes(response.status)) {
|
|
92
|
+
throw new Error(`unexpected HTTP ${response.status} response from api: ${await response.text()}`);
|
|
93
|
+
}
|
|
94
|
+
return { status: response.status, headers: response.headers, load: (0, exports.loadResponse)(response) };
|
|
95
|
+
},
|
|
96
|
+
headers: response.headers,
|
|
97
|
+
load: (0, exports.loadResponse)(response),
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
routeClient.renderUrl = renderUrl;
|
|
102
|
+
return routeClient;
|
|
103
|
+
};
|
|
104
|
+
const createApiGateway = (initializer) => (config, routes, appProvider) => {
|
|
105
|
+
return Object.fromEntries(Object.entries(routes)
|
|
106
|
+
.map(([key, routeConfig]) => ([key, makeRouteClient(initializer, config, routeConfig, appProvider)])));
|
|
107
|
+
};
|
|
108
|
+
exports.createApiGateway = createApiGateway;
|