@creator.co/wapi 1.2.1-beta6 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npmpublish.yml +11 -19
- package/.github/workflows/prs.yml +13 -0
- package/jest.config.ts +39 -0
- package/package.json +15 -4
- package/src/API/Request.ts +120 -10
- package/src/API/Response.ts +236 -8
- package/src/API/Utils.ts +56 -1
- package/src/BaseEvent/EventProcessor.ts +84 -11
- package/src/BaseEvent/Process.ts +62 -1
- package/src/BaseEvent/Transaction.ts +5 -8
- package/src/Config/Configuration.ts +111 -5
- package/src/Config/EnvironmentVar.ts +90 -1
- package/src/Crypto/Crypto.ts +77 -18
- package/src/Crypto/JWT.ts +49 -3
- package/src/Globals.ts +141 -3
- package/src/Logger/Logger.ts +173 -19
- package/src/Mailer/Mailer.ts +95 -0
- package/src/Publisher/Publisher.ts +50 -4
- package/src/Server/RouteResolver.ts +142 -0
- package/src/Server/Router.ts +77 -0
- package/src/Server/lib/ContainerServer.ts +48 -1
- package/src/Server/lib/Server.ts +78 -38
- package/src/Server/lib/container/GenericHandler.ts +7 -5
- package/src/Server/lib/container/GenericHandlerEvent.ts +59 -17
- package/src/Server/lib/container/HealthHandler.ts +1 -1
- package/src/Server/lib/container/Proxy.ts +92 -11
- package/src/Server/lib/container/Utils.ts +16 -25
- package/src/Validation/Validator.ts +18 -2
- package/tests/API/Request.test.ts +263 -0
- package/tests/API/Response.test.ts +372 -0
- package/tests/API/Utils.test.ts +157 -0
- package/tests/BaseEvent/EventProcessor.test.ts +278 -0
- package/tests/BaseEvent/Process.test.ts +49 -0
- package/tests/BaseEvent/Transaction.test.ts +231 -0
- package/tests/Config/Config.test.ts +193 -0
- package/tests/Config/EnvironmentVar.test.ts +223 -0
- package/tests/Crypto/Crypto.test.ts +90 -0
- package/tests/Crypto/JWT.test.ts +92 -0
- package/tests/Logger/Logger.test.ts +108 -0
- package/tests/Mailer/Mailer.test.ts +67 -0
- package/tests/Publisher/Publisher.test.ts +60 -0
- package/tests/Server/RouteResolver.test.ts +106 -0
- package/tests/Server/Router.test.ts +38 -0
- package/tests/Server/lib/ContainerServer.test.ts +329 -0
- package/tests/Server/lib/Server.test.ts +12 -0
- package/tests/Server/lib/container/GenericHandler.test.ts +141 -0
- package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
- package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
- package/tests/Server/lib/container/Proxy.test.ts +278 -0
- package/tests/Server/lib/container/Utils.test.ts +48 -0
- package/tests/Test.utils.ts +95 -0
- package/tests/Validation/Validator.test.ts +88 -0
- package/tests/main.test.ts +15 -0
- package/tsconfig.json +1 -0
- package/dist/index.d.ts +0 -11
- package/dist/index.js +0 -24
- package/dist/index.js.map +0 -1
- package/dist/package.json +0 -53
- package/dist/src/API/Request.d.ts +0 -21
- package/dist/src/API/Request.js +0 -86
- package/dist/src/API/Request.js.map +0 -1
- package/dist/src/API/Response.d.ts +0 -39
- package/dist/src/API/Response.js +0 -232
- package/dist/src/API/Response.js.map +0 -1
- package/dist/src/API/Utils.d.ts +0 -8
- package/dist/src/API/Utils.js +0 -49
- package/dist/src/API/Utils.js.map +0 -1
- package/dist/src/BaseEvent/EventProcessor.d.ts +0 -13
- package/dist/src/BaseEvent/EventProcessor.js +0 -151
- package/dist/src/BaseEvent/EventProcessor.js.map +0 -1
- package/dist/src/BaseEvent/Process.d.ts +0 -12
- package/dist/src/BaseEvent/Process.js +0 -114
- package/dist/src/BaseEvent/Process.js.map +0 -1
- package/dist/src/BaseEvent/Transaction.d.ts +0 -29
- package/dist/src/BaseEvent/Transaction.js +0 -248
- package/dist/src/BaseEvent/Transaction.js.map +0 -1
- package/dist/src/Config/Configuration.d.ts +0 -34
- package/dist/src/Config/Configuration.js +0 -93
- package/dist/src/Config/Configuration.js.map +0 -1
- package/dist/src/Config/EnvironmentVar.d.ts +0 -17
- package/dist/src/Config/EnvironmentVar.js +0 -152
- package/dist/src/Config/EnvironmentVar.js.map +0 -1
- package/dist/src/Crypto/Crypto.d.ts +0 -8
- package/dist/src/Crypto/Crypto.js +0 -84
- package/dist/src/Crypto/Crypto.js.map +0 -1
- package/dist/src/Crypto/JWT.d.ts +0 -16
- package/dist/src/Crypto/JWT.js +0 -49
- package/dist/src/Crypto/JWT.js.map +0 -1
- package/dist/src/Globals.d.ts +0 -21
- package/dist/src/Globals.js +0 -35
- package/dist/src/Globals.js.map +0 -1
- package/dist/src/Logger/Logger.d.ts +0 -34
- package/dist/src/Logger/Logger.js +0 -345
- package/dist/src/Logger/Logger.js.map +0 -1
- package/dist/src/Mailer/Mailer.d.ts +0 -12
- package/dist/src/Mailer/Mailer.js +0 -234
- package/dist/src/Mailer/Mailer.js.map +0 -1
- package/dist/src/Publisher/Publisher.d.ts +0 -10
- package/dist/src/Publisher/Publisher.js +0 -109
- package/dist/src/Publisher/Publisher.js.map +0 -1
- package/dist/src/Server/Router.d.ts +0 -27
- package/dist/src/Server/Router.js +0 -22
- package/dist/src/Server/Router.js.map +0 -1
- package/dist/src/Server/lib/ContainerServer.d.ts +0 -11
- package/dist/src/Server/lib/ContainerServer.js +0 -103
- package/dist/src/Server/lib/ContainerServer.js.map +0 -1
- package/dist/src/Server/lib/Server.d.ts +0 -9
- package/dist/src/Server/lib/Server.js +0 -141
- package/dist/src/Server/lib/Server.js.map +0 -1
- package/dist/src/Server/lib/container/GenericHandler.d.ts +0 -4
- package/dist/src/Server/lib/container/GenericHandler.js +0 -136
- package/dist/src/Server/lib/container/GenericHandler.js.map +0 -1
- package/dist/src/Server/lib/container/GenericHandlerEvent.d.ts +0 -14
- package/dist/src/Server/lib/container/GenericHandlerEvent.js +0 -164
- package/dist/src/Server/lib/container/GenericHandlerEvent.js.map +0 -1
- package/dist/src/Server/lib/container/HealthHandler.d.ts +0 -3
- package/dist/src/Server/lib/container/HealthHandler.js +0 -44
- package/dist/src/Server/lib/container/HealthHandler.js.map +0 -1
- package/dist/src/Server/lib/container/Proxy.d.ts +0 -15
- package/dist/src/Server/lib/container/Proxy.js +0 -157
- package/dist/src/Server/lib/container/Proxy.js.map +0 -1
- package/dist/src/Server/lib/container/Utils.d.ts +0 -6
- package/dist/src/Server/lib/container/Utils.js +0 -109
- package/dist/src/Server/lib/container/Utils.js.map +0 -1
- package/dist/src/Validation/Validator.d.ts +0 -5
- package/dist/src/Validation/Validator.js +0 -31
- package/dist/src/Validation/Validator.js.map +0 -1
|
@@ -10,27 +10,29 @@ import Server from "../Server"
|
|
|
10
10
|
export default (serverlessHandler: Server["handleServerlessEvent"]) => {
|
|
11
11
|
return async (request: Request, res: Response) => {
|
|
12
12
|
const startTime = Date.now()
|
|
13
|
-
let resp = null
|
|
14
13
|
try {
|
|
15
14
|
// Generate event with request stuff (http to serverless translation)
|
|
16
15
|
const event = new GenericHandlerEvent(request, serverlessHandler)
|
|
17
16
|
// Invoke
|
|
18
17
|
const invokationResp = await event.invoke()
|
|
19
18
|
// Respond
|
|
20
|
-
|
|
19
|
+
processServerlessResponse(invokationResp, res)
|
|
21
20
|
} catch (e) {
|
|
22
21
|
console.error("[Proxy] - Exception during execution!", e)
|
|
23
22
|
console.error(e.stack)
|
|
24
|
-
|
|
23
|
+
res.status(Globals.Resp_STATUSCODE_EXCEPTION).json({
|
|
25
24
|
...e,
|
|
26
25
|
err: Globals.Resp_MSG_EXCEPTION,
|
|
27
26
|
errCode: Globals.Resp_CODE_EXCEPTION,
|
|
28
27
|
})
|
|
29
28
|
}
|
|
30
29
|
console.debug(`[Proxy] - Request took ${Date.now() - startTime}ms`)
|
|
31
|
-
return resp
|
|
32
30
|
}
|
|
33
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* ${1:Description placeholder}
|
|
34
|
+
*
|
|
35
|
+
**/
|
|
34
36
|
const processServerlessResponse = (
|
|
35
37
|
invokation: GenericHandlerEventResponse,
|
|
36
38
|
res: Response,
|
|
@@ -47,7 +49,7 @@ const processServerlessResponse = (
|
|
|
47
49
|
})
|
|
48
50
|
} else {
|
|
49
51
|
// Check for headers
|
|
50
|
-
if (invokation
|
|
52
|
+
if (invokation.data.headers) {
|
|
51
53
|
for (const hKey of Object.keys(invokation.data.headers)) {
|
|
52
54
|
res.header(hKey, invokation.data.headers[hKey])
|
|
53
55
|
}
|
|
@@ -1,22 +1,51 @@
|
|
|
1
1
|
import type { APIGatewayProxyEvent, Context } from "aws-lambda"
|
|
2
2
|
import * as cuid from "cuid"
|
|
3
3
|
import { Request } from "express"
|
|
4
|
-
import * as unflatten from "unflatten"
|
|
5
4
|
|
|
6
5
|
import {
|
|
7
|
-
parseMultiValueHeaders,
|
|
8
6
|
parseMultiValueQueryStringParameters,
|
|
9
7
|
parseQueryStringParameters,
|
|
10
|
-
nullIfEmpty,
|
|
11
8
|
} from "./Utils"
|
|
12
9
|
import Globals from "../../../Globals"
|
|
13
10
|
import Server from "../Server"
|
|
14
11
|
//
|
|
12
|
+
/**
|
|
13
|
+
* ${1:Description placeholder}
|
|
14
|
+
*
|
|
15
|
+
* @export
|
|
16
|
+
* @typedef {GenericHandlerEventResponse}
|
|
17
|
+
*/
|
|
15
18
|
export type GenericHandlerEventResponse = { err?: Error | string; data?: any }
|
|
16
19
|
//
|
|
20
|
+
/**
|
|
21
|
+
* ${1:Description placeholder}
|
|
22
|
+
*
|
|
23
|
+
* @export
|
|
24
|
+
* @class GenericHandlerEvent
|
|
25
|
+
* @typedef {GenericHandlerEvent}
|
|
26
|
+
*/
|
|
17
27
|
export default class GenericHandlerEvent {
|
|
28
|
+
/**
|
|
29
|
+
* ${1:Description placeholder}
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
* @type {Request}
|
|
33
|
+
*/
|
|
18
34
|
public request: Request
|
|
35
|
+
/**
|
|
36
|
+
* ${1:Description placeholder}
|
|
37
|
+
*
|
|
38
|
+
* @public
|
|
39
|
+
* @type {Server["handleServerlessEvent"]}
|
|
40
|
+
*/
|
|
19
41
|
public serverlessHandler: Server["handleServerlessEvent"]
|
|
42
|
+
/**
|
|
43
|
+
* Creates an instance of GenericHandlerEvent.
|
|
44
|
+
*
|
|
45
|
+
* @constructor
|
|
46
|
+
* @param {Request} request
|
|
47
|
+
* @param {Server["handleServerlessEvent"]} serverlessHandler
|
|
48
|
+
*/
|
|
20
49
|
constructor(
|
|
21
50
|
request: Request,
|
|
22
51
|
serverlessHandler: Server["handleServerlessEvent"],
|
|
@@ -24,6 +53,13 @@ export default class GenericHandlerEvent {
|
|
|
24
53
|
this.request = request
|
|
25
54
|
this.serverlessHandler = serverlessHandler
|
|
26
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* ${1:Description placeholder}
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
* @async
|
|
61
|
+
* @returns {Promise<GenericHandlerEventResponse>}
|
|
62
|
+
*/
|
|
27
63
|
public async invoke(): Promise<GenericHandlerEventResponse> {
|
|
28
64
|
// eslint-disable-next-line no-async-promise-executor
|
|
29
65
|
return new Promise(async (resolve, reject) => {
|
|
@@ -44,24 +80,24 @@ export default class GenericHandlerEvent {
|
|
|
44
80
|
})
|
|
45
81
|
}
|
|
46
82
|
/* private */
|
|
83
|
+
/**
|
|
84
|
+
* ${1:Description placeholder}
|
|
85
|
+
*
|
|
86
|
+
* @private
|
|
87
|
+
* @returns {APIGatewayProxyEvent}
|
|
88
|
+
*/
|
|
47
89
|
private buildEvent(): APIGatewayProxyEvent {
|
|
48
90
|
return {
|
|
49
91
|
body: this.request.body || null, //enforce key
|
|
50
|
-
headers: this.request.headers
|
|
51
|
-
httpMethod: this.request.method
|
|
52
|
-
? this.request.method.toUpperCase()
|
|
53
|
-
: null,
|
|
92
|
+
headers: <any>(this.request.headers || {}),
|
|
93
|
+
httpMethod: this.request.method?.toUpperCase(),
|
|
54
94
|
isBase64Encoded: false,
|
|
55
|
-
multiValueHeaders:
|
|
56
|
-
this.request.headers ? this.request.headers || [] : [],
|
|
57
|
-
),
|
|
95
|
+
multiValueHeaders: <any>(this.request.headers || {}),
|
|
58
96
|
multiValueQueryStringParameters: parseMultiValueQueryStringParameters(
|
|
59
97
|
this.request.url,
|
|
60
98
|
),
|
|
61
99
|
path: this.request.path,
|
|
62
|
-
pathParameters:
|
|
63
|
-
? nullIfEmpty(this.request.params)
|
|
64
|
-
: null,
|
|
100
|
+
pathParameters: null,
|
|
65
101
|
queryStringParameters: this.request.query
|
|
66
102
|
? parseQueryStringParameters(this.request.query)
|
|
67
103
|
: null,
|
|
@@ -89,11 +125,9 @@ export default class GenericHandlerEvent {
|
|
|
89
125
|
principalOrgId: null,
|
|
90
126
|
sourceIp:
|
|
91
127
|
<string>this.request.headers?.["x-forwarded-for"] ||
|
|
92
|
-
this.request.socket
|
|
128
|
+
this.request.socket?.remoteAddress,
|
|
93
129
|
user: null,
|
|
94
|
-
userAgent: this.request.headers
|
|
95
|
-
? this.request.headers["user-agent"]
|
|
96
|
-
: null,
|
|
130
|
+
userAgent: this.request.headers?.["user-agent"],
|
|
97
131
|
userArn: null,
|
|
98
132
|
},
|
|
99
133
|
path: this.request.path,
|
|
@@ -109,6 +143,14 @@ export default class GenericHandlerEvent {
|
|
|
109
143
|
stageVariables: null,
|
|
110
144
|
}
|
|
111
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* ${1:Description placeholder}
|
|
148
|
+
*
|
|
149
|
+
* @private
|
|
150
|
+
* @param {APIGatewayProxyEvent} event
|
|
151
|
+
* @param {(err?: Error | string, data?: any) => void} callback
|
|
152
|
+
* @returns {Context}
|
|
153
|
+
*/
|
|
112
154
|
private buildContext(
|
|
113
155
|
event: APIGatewayProxyEvent,
|
|
114
156
|
callback: (err?: Error | string, data?: any) => void,
|
|
@@ -11,12 +11,59 @@ import Utils from "../../../API/Utils"
|
|
|
11
11
|
import Globals from "../../../Globals"
|
|
12
12
|
import { RouterConfig } from "../../Router"
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* ${1:Description placeholder}
|
|
16
|
+
*
|
|
17
|
+
* @export
|
|
18
|
+
* @class Proxy
|
|
19
|
+
* @typedef {Proxy}
|
|
20
|
+
*/
|
|
14
21
|
export default class Proxy {
|
|
22
|
+
/**
|
|
23
|
+
* ${1:Description placeholder}
|
|
24
|
+
*
|
|
25
|
+
* @private
|
|
26
|
+
* @type {boolean}
|
|
27
|
+
*/
|
|
15
28
|
private stopping: boolean
|
|
29
|
+
/**
|
|
30
|
+
* ${1:Description placeholder}
|
|
31
|
+
*
|
|
32
|
+
* @private
|
|
33
|
+
* @readonly
|
|
34
|
+
* @type {RouterConfig}
|
|
35
|
+
*/
|
|
16
36
|
private readonly config: RouterConfig
|
|
37
|
+
/**
|
|
38
|
+
* ${1:Description placeholder}
|
|
39
|
+
*
|
|
40
|
+
* @private
|
|
41
|
+
* @readonly
|
|
42
|
+
* @type {express.Express}
|
|
43
|
+
*/
|
|
17
44
|
private readonly app: express.Express
|
|
45
|
+
/**
|
|
46
|
+
* ${1:Description placeholder}
|
|
47
|
+
*
|
|
48
|
+
* @private
|
|
49
|
+
* @readonly
|
|
50
|
+
* @type {Server["handleServerlessEvent"]}
|
|
51
|
+
*/
|
|
18
52
|
private readonly serverlessHandler: Server["handleServerlessEvent"]
|
|
53
|
+
/**
|
|
54
|
+
* ${1:Description placeholder}
|
|
55
|
+
*
|
|
56
|
+
* @private
|
|
57
|
+
* @type {HTTPServer}
|
|
58
|
+
*/
|
|
19
59
|
private listener: HTTPServer
|
|
60
|
+
/**
|
|
61
|
+
* Creates an instance of Proxy.
|
|
62
|
+
*
|
|
63
|
+
* @constructor
|
|
64
|
+
* @param {RouterConfig} config
|
|
65
|
+
* @param {Server["handleServerlessEvent"]} serverlessHandler
|
|
66
|
+
*/
|
|
20
67
|
constructor(
|
|
21
68
|
config: RouterConfig,
|
|
22
69
|
serverlessHandler: Server["handleServerlessEvent"],
|
|
@@ -34,11 +81,9 @@ export default class Proxy {
|
|
|
34
81
|
cors(
|
|
35
82
|
corsConfig
|
|
36
83
|
? {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
credentials: !!corsConfig?.allowCredentials,
|
|
41
|
-
},
|
|
84
|
+
origin: corsConfig.origin,
|
|
85
|
+
allowedHeaders: corsConfig.headers,
|
|
86
|
+
credentials: !!corsConfig.allowCredentials,
|
|
42
87
|
}
|
|
43
88
|
: {},
|
|
44
89
|
),
|
|
@@ -52,14 +97,34 @@ export default class Proxy {
|
|
|
52
97
|
// this.listener.listener.keepAliveTimeout = 120e3
|
|
53
98
|
// this.listener.listener.headersTimeout = 120e3
|
|
54
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* ${1:Description placeholder}
|
|
102
|
+
*
|
|
103
|
+
* @async
|
|
104
|
+
* @returns {*}
|
|
105
|
+
*/
|
|
55
106
|
async load() {
|
|
56
107
|
await this.startListeners()
|
|
57
108
|
this.installRoutes()
|
|
58
109
|
}
|
|
59
|
-
|
|
110
|
+
/**
|
|
111
|
+
* ${1:Description placeholder}
|
|
112
|
+
*
|
|
113
|
+
* @async
|
|
114
|
+
* @param {?*} [err]
|
|
115
|
+
* @returns {*}
|
|
116
|
+
*/
|
|
117
|
+
async unload(err?: any) {
|
|
60
118
|
await this.stopListeners(err)
|
|
61
119
|
}
|
|
62
120
|
/* lifecycle */
|
|
121
|
+
/**
|
|
122
|
+
* ${1:Description placeholder}
|
|
123
|
+
*
|
|
124
|
+
* @private
|
|
125
|
+
* @async
|
|
126
|
+
* @returns {Promise<void>}
|
|
127
|
+
*/
|
|
63
128
|
private async startListeners(): Promise<void> {
|
|
64
129
|
return new Promise((resolve) => {
|
|
65
130
|
const port = this.config.port || Globals.Listener_HTTP_DefaultPort
|
|
@@ -77,17 +142,33 @@ export default class Proxy {
|
|
|
77
142
|
})
|
|
78
143
|
})
|
|
79
144
|
}
|
|
80
|
-
|
|
145
|
+
/**
|
|
146
|
+
* ${1:Description placeholder}
|
|
147
|
+
*
|
|
148
|
+
* @private
|
|
149
|
+
* @async
|
|
150
|
+
* @param {?*} [err]
|
|
151
|
+
* @returns {unknown}
|
|
152
|
+
*/
|
|
153
|
+
private async stopListeners(err?: any) {
|
|
81
154
|
if (this.stopping) return
|
|
82
155
|
this.stopping = true
|
|
83
156
|
console.debug("[Proxy] - [STOPPING]")
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
157
|
+
return new Promise((resolve) => {
|
|
158
|
+
this.listener.close((_err) => {
|
|
159
|
+
if (err || _err) console.log("[Proxy] - exit output:", err || _err)
|
|
160
|
+
console.log("[Proxy] - [STOPPED]")
|
|
161
|
+
process.exit(err || _err ? 1 : 0)
|
|
162
|
+
resolve(null)
|
|
163
|
+
})
|
|
88
164
|
})
|
|
89
165
|
}
|
|
90
166
|
/* routing */
|
|
167
|
+
/**
|
|
168
|
+
* ${1:Description placeholder}
|
|
169
|
+
*
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
91
172
|
private installRoutes() {
|
|
92
173
|
//Health check route -- This is a bypass route to only check if
|
|
93
174
|
//runtime proxy is working and responding to calls.
|
|
@@ -1,28 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
10
|
-
for (const key of Object.keys(unflattened)) {
|
|
11
|
-
const item = map.get(key)
|
|
12
|
-
if (item) item.push(unflattened[key])
|
|
13
|
-
else map.set(key, [unflattened[key]])
|
|
14
|
-
}
|
|
15
|
-
return Object.fromEntries(map)
|
|
16
|
-
}
|
|
17
|
-
// https://aws.amazon.com/blogs/compute/support-for-multi-value-parameters-in-amazon-api-gateway/
|
|
18
|
-
// [ [ 'petType', 'dog' ], [ 'petType', 'fish' ] ]
|
|
19
|
-
// => { petType: [ 'dog', 'fish' ] },
|
|
20
|
-
export function parseMultiValueQueryStringParameters(url) {
|
|
1
|
+
/**
|
|
2
|
+
* ${1:Description placeholder}
|
|
3
|
+
*
|
|
4
|
+
* @export
|
|
5
|
+
* @param {string} url
|
|
6
|
+
* @returns {*}
|
|
7
|
+
*/
|
|
8
|
+
export function parseMultiValueQueryStringParameters(url: string) {
|
|
21
9
|
// dummy placeholder url for the WHATWG URL constructor
|
|
22
10
|
// https://github.com/nodejs/node/issues/12682
|
|
23
11
|
const { searchParams } = new URL(url, "http://example")
|
|
24
12
|
//
|
|
25
|
-
if (Array.from(searchParams).length === 0) return
|
|
13
|
+
if (Array.from(searchParams).length === 0) return {}
|
|
26
14
|
const map = new Map()
|
|
27
15
|
// eslint-disable-next-line no-restricted-syntax
|
|
28
16
|
for (const [key, value] of searchParams) {
|
|
@@ -32,6 +20,13 @@ export function parseMultiValueQueryStringParameters(url) {
|
|
|
32
20
|
}
|
|
33
21
|
return Object.fromEntries(map)
|
|
34
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* ${1:Description placeholder}
|
|
25
|
+
*
|
|
26
|
+
* @export
|
|
27
|
+
* @param {*} url
|
|
28
|
+
* @returns {*}
|
|
29
|
+
*/
|
|
35
30
|
export function parseQueryStringParameters(url) {
|
|
36
31
|
// dummy placeholder url for the WHATWG URL constructor
|
|
37
32
|
// https://github.com/nodejs/node/issues/12682
|
|
@@ -39,7 +34,3 @@ export function parseQueryStringParameters(url) {
|
|
|
39
34
|
if (Array.from(searchParams).length === 0) return null
|
|
40
35
|
return Object.fromEntries(searchParams)
|
|
41
36
|
}
|
|
42
|
-
//
|
|
43
|
-
export function nullIfEmpty(obj) {
|
|
44
|
-
return obj && (Object.keys(obj).length > 0 ? obj : null)
|
|
45
|
-
}
|
|
@@ -3,7 +3,23 @@ import { z } from "zod"
|
|
|
3
3
|
import Response, { ResponseErrorType } from "../API/Response"
|
|
4
4
|
import Globals from "../Globals"
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Description placeholder
|
|
8
|
+
*
|
|
9
|
+
* @export
|
|
10
|
+
* @class Validator
|
|
11
|
+
* @typedef {Validator}
|
|
12
|
+
*/
|
|
6
13
|
export default class Validator {
|
|
14
|
+
/**
|
|
15
|
+
* Description placeholder
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
* @static
|
|
19
|
+
* @param {*} data
|
|
20
|
+
* @param {z.ZodObject<any>} schema
|
|
21
|
+
* @returns {(boolean | Response<ResponseErrorType>)}
|
|
22
|
+
*/
|
|
7
23
|
public static validateSchema(
|
|
8
24
|
data: any,
|
|
9
25
|
schema: z.ZodObject<any>,
|
|
@@ -13,8 +29,8 @@ export default class Validator {
|
|
|
13
29
|
try {
|
|
14
30
|
validatedInput = schema.parse(data) as typeof schema
|
|
15
31
|
} catch (err: z.ZodError | any) {
|
|
16
|
-
if (err.
|
|
17
|
-
else error = "Unknown validation error!"
|
|
32
|
+
if (err instanceof z.ZodError) error = JSON.parse(err.message)
|
|
33
|
+
else error = "Unknown validation error!" //unhandled case, hard to test
|
|
18
34
|
}
|
|
19
35
|
// Error validation
|
|
20
36
|
if (!validatedInput || error) {
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { APIGatewayEvent, Context } from "aws-lambda"
|
|
2
|
+
import { expect } from "chai"
|
|
3
|
+
|
|
4
|
+
import Request from "../../src/API/Request"
|
|
5
|
+
import Logger from "../../src/Logger/Logger"
|
|
6
|
+
|
|
7
|
+
function newReq(event: any, context?: any) {
|
|
8
|
+
const logger = new Logger(
|
|
9
|
+
{
|
|
10
|
+
logLevel: "DEBUG",
|
|
11
|
+
},
|
|
12
|
+
"123-456",
|
|
13
|
+
)
|
|
14
|
+
return new Request(
|
|
15
|
+
<APIGatewayEvent>(<unknown>event),
|
|
16
|
+
<Context>context ? context : {},
|
|
17
|
+
logger,
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("Request querystring", () => {
|
|
22
|
+
test("Null query string", () => {
|
|
23
|
+
const r = newReq({
|
|
24
|
+
queryStringParameters: null,
|
|
25
|
+
})
|
|
26
|
+
expect(r.containsQueryParam("123")).to.be.false
|
|
27
|
+
expect(r.getQueryParam("123")).to.be.null
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("Empty query string", () => {
|
|
31
|
+
const r = newReq({
|
|
32
|
+
queryStringParameters: {},
|
|
33
|
+
})
|
|
34
|
+
expect(r.containsQueryParam("123")).to.be.false
|
|
35
|
+
expect(r.getQueryParam("123")).to.be.null
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test("Valid query string", () => {
|
|
39
|
+
const r = newReq({
|
|
40
|
+
queryStringParameters: { "123": "abc" },
|
|
41
|
+
})
|
|
42
|
+
expect(r.containsQueryParam("123")).to.be.true
|
|
43
|
+
expect(r.getQueryParam("123")).to.be.equals("abc")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test("Valid query string number", () => {
|
|
47
|
+
const r = newReq({
|
|
48
|
+
queryStringParameters: { "123": 456 },
|
|
49
|
+
})
|
|
50
|
+
expect(r.containsQueryParam("123")).to.be.true
|
|
51
|
+
expect(r.getQueryParam("123")).to.be.equals(456)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe("Request headers", () => {
|
|
56
|
+
test("Null headers", () => {
|
|
57
|
+
const r = newReq({ headers: null })
|
|
58
|
+
expect(r.getHeader("123")).to.be.null
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test("Empty headers", () => {
|
|
62
|
+
const r = newReq({ headers: {} })
|
|
63
|
+
expect(r.getHeader("123")).to.be.null
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test("Valid headers", () => {
|
|
67
|
+
const r = newReq({ headers: { "123": "abc" } })
|
|
68
|
+
expect(r.getHeader("123")).to.be.equals("abc")
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test("Auth headers", () => {
|
|
72
|
+
const r = newReq({ headers: { Authorization: "abc" } })
|
|
73
|
+
expect(r.getAuthorizationHeader()).to.be.equals("abc")
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe("Request context", () => {
|
|
78
|
+
test("Null context", () => {
|
|
79
|
+
const r = newReq({ requestContext: null })
|
|
80
|
+
expect(r.getContextParam("123")).to.be.null
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test("Empty context", () => {
|
|
84
|
+
const r = newReq({ requestContext: {} })
|
|
85
|
+
expect(r.getContextParam("123")).to.be.null
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test("Valid context", () => {
|
|
89
|
+
const r = newReq({
|
|
90
|
+
requestContext: { "123": "abc" },
|
|
91
|
+
})
|
|
92
|
+
expect(r.getContextParam("123")).to.be.equals("abc")
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe("Request path params", () => {
|
|
97
|
+
test("Null path params", () => {
|
|
98
|
+
const r = newReq({ pathParameters: null })
|
|
99
|
+
expect(r.containsPathParam("123")).to.be.false
|
|
100
|
+
expect(r.getPathParam("123")).to.be.null
|
|
101
|
+
expect(r.getPathParams()).to.be.null
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test("Empty path params", () => {
|
|
105
|
+
const r = newReq({ pathParameters: {} })
|
|
106
|
+
expect(r.containsPathParam("123")).to.be.false
|
|
107
|
+
expect(r.getPathParam("123")).to.be.null
|
|
108
|
+
expect(r.getPathParams()).to.be.deep.equal({})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test("Valid path params", () => {
|
|
112
|
+
const v = { "123": "abc" }
|
|
113
|
+
const r = newReq({
|
|
114
|
+
pathParameters: v,
|
|
115
|
+
})
|
|
116
|
+
expect(r.containsPathParam("123")).to.be.true
|
|
117
|
+
expect(r.getPathParam("123")).to.be.equals("abc")
|
|
118
|
+
expect(r.getPathParams()).to.be.deep.equal(v)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test("Valid path param number", () => {
|
|
122
|
+
const v = { "123": 456 }
|
|
123
|
+
const r = newReq({
|
|
124
|
+
pathParameters: v,
|
|
125
|
+
})
|
|
126
|
+
expect(r.containsPathParam("123")).to.be.true
|
|
127
|
+
expect(r.getPathParam("123")).to.be.equals(456)
|
|
128
|
+
expect(r.getPathParams()).to.be.deep.equal(v)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test("Fix path params", () => {
|
|
132
|
+
const v = { "123": "abc" }
|
|
133
|
+
const r = newReq({ pathParameters: null })
|
|
134
|
+
r.setFixedPathParams(
|
|
135
|
+
Object.keys(v).map((k) => ({ name: k })),
|
|
136
|
+
["/"].concat(Object.values(v)),
|
|
137
|
+
)
|
|
138
|
+
expect(r.containsPathParam("123")).to.be.true
|
|
139
|
+
expect(r.getPathParam("123")).to.be.equals("abc")
|
|
140
|
+
expect(r.getPathParams()).to.be.deep.equal(v)
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
describe("Request body", () => {
|
|
145
|
+
test("Null body", () => {
|
|
146
|
+
const r = newReq({ body: null })
|
|
147
|
+
expect(r.getBody()).to.be.null
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test("Empty body", () => {
|
|
151
|
+
const r = newReq({ body: {} })
|
|
152
|
+
expect(r.getBody()).to.be.deep.equals({})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test("Empty string body", () => {
|
|
156
|
+
const r = newReq({ body: "{}" })
|
|
157
|
+
expect(r.getBody()).to.be.deep.equals({})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test("Broken json string body", () => {
|
|
161
|
+
const r = newReq({ body: '{name": "Joe"}' })
|
|
162
|
+
expect(r.getBody()).to.be.equals('{name": "Joe"}')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
test("Valid string body", () => {
|
|
166
|
+
const r = newReq({ body: '{"name": "Joe"}' })
|
|
167
|
+
expect(r.getBody()).to.be.deep.equals({
|
|
168
|
+
name: "Joe",
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test("Valid json body", () => {
|
|
173
|
+
const r = newReq({ body: { name: "Joe" } })
|
|
174
|
+
expect(r.getBody()).to.be.deep.equals({
|
|
175
|
+
name: "Joe",
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
describe("Request path/method", () => {
|
|
181
|
+
test("Null path/method", () => {
|
|
182
|
+
const r = newReq({
|
|
183
|
+
path: null,
|
|
184
|
+
httpMethod: null,
|
|
185
|
+
})
|
|
186
|
+
expect(r.getPath()).to.be.null
|
|
187
|
+
expect(r.getMethod()).to.be.null
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test("Empty path/method", () => {
|
|
191
|
+
const r = newReq({
|
|
192
|
+
path: "",
|
|
193
|
+
httpMethod: "",
|
|
194
|
+
})
|
|
195
|
+
expect(r.getPath()).to.be.a("string")
|
|
196
|
+
expect(r.getPath()).to.be.equals("")
|
|
197
|
+
expect(r.getMethod()).to.be.a("string")
|
|
198
|
+
expect(r.getMethod()).to.be.equals("")
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test("Valid method/path", () => {
|
|
202
|
+
const r = newReq({
|
|
203
|
+
path: "/root",
|
|
204
|
+
httpMethod: "GET",
|
|
205
|
+
})
|
|
206
|
+
expect(r.getPath()).to.be.a("string")
|
|
207
|
+
expect(r.getPath()).to.be.equals("/root")
|
|
208
|
+
expect(r.getMethod()).to.be.a("string")
|
|
209
|
+
expect(r.getMethod()).to.be.equals("GET")
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
describe("Request ID", () => {
|
|
214
|
+
test("From context", () => {
|
|
215
|
+
const r = newReq({}, { awsRequestId: "123" })
|
|
216
|
+
expect(r.getRequestID()).to.not.be.null
|
|
217
|
+
expect(r.getRequestID()).to.be.equals("123")
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
test("From event", () => {
|
|
221
|
+
const r = newReq({
|
|
222
|
+
requestContext: { requestId: "123" },
|
|
223
|
+
})
|
|
224
|
+
expect(r.getRequestID()).to.not.be.null
|
|
225
|
+
expect(r.getRequestID()).to.be.equals("123")
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test("unknown", () => {
|
|
229
|
+
const r = newReq({})
|
|
230
|
+
expect(r.getRequestID()).to.not.be.null
|
|
231
|
+
expect(r.getRequestID()).to.be.equals("unknown")
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
describe("Request origin IP", () => {
|
|
236
|
+
test("From context", () => {
|
|
237
|
+
const r = newReq({
|
|
238
|
+
requestContext: {
|
|
239
|
+
identity: { sourceIp: "127.0.0.1" },
|
|
240
|
+
},
|
|
241
|
+
})
|
|
242
|
+
expect(r.getOriginIP()).to.not.be.null
|
|
243
|
+
expect(r.getOriginIP()).to.be.equals("127.0.0.1")
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
test("From header", () => {
|
|
247
|
+
const r = newReq({
|
|
248
|
+
headers: {
|
|
249
|
+
"X-Forwarded-For": "127.0.0.1",
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
expect(r.getOriginIP()).to.not.be.null
|
|
253
|
+
expect(r.getOriginIP()).to.be.equals("127.0.0.1")
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test("unknown", () => {
|
|
257
|
+
const r = newReq({})
|
|
258
|
+
expect(r.getOriginIP()).to.not.be.null
|
|
259
|
+
expect(r.getOriginIP()).to.be.equals("unknown")
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
export {}
|