@creator.co/wapi 1.2.1-beta5 → 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.
Files changed (127) hide show
  1. package/.github/workflows/npmpublish.yml +11 -19
  2. package/.github/workflows/prs.yml +13 -0
  3. package/jest.config.ts +39 -0
  4. package/package.json +15 -4
  5. package/src/API/Request.ts +120 -10
  6. package/src/API/Response.ts +236 -8
  7. package/src/API/Utils.ts +56 -1
  8. package/src/BaseEvent/EventProcessor.ts +84 -11
  9. package/src/BaseEvent/Process.ts +62 -1
  10. package/src/BaseEvent/Transaction.ts +5 -8
  11. package/src/Config/Configuration.ts +111 -5
  12. package/src/Config/EnvironmentVar.ts +90 -1
  13. package/src/Crypto/Crypto.ts +77 -18
  14. package/src/Crypto/JWT.ts +49 -3
  15. package/src/Globals.ts +141 -3
  16. package/src/Logger/Logger.ts +173 -19
  17. package/src/Mailer/Mailer.ts +95 -0
  18. package/src/Publisher/Publisher.ts +50 -4
  19. package/src/Server/RouteResolver.ts +142 -0
  20. package/src/Server/Router.ts +77 -0
  21. package/src/Server/lib/ContainerServer.ts +48 -1
  22. package/src/Server/lib/Server.ts +78 -38
  23. package/src/Server/lib/container/GenericHandler.ts +7 -5
  24. package/src/Server/lib/container/GenericHandlerEvent.ts +59 -17
  25. package/src/Server/lib/container/HealthHandler.ts +1 -1
  26. package/src/Server/lib/container/Proxy.ts +92 -11
  27. package/src/Server/lib/container/Utils.ts +16 -25
  28. package/src/Validation/Validator.ts +18 -2
  29. package/tests/API/Request.test.ts +263 -0
  30. package/tests/API/Response.test.ts +372 -0
  31. package/tests/API/Utils.test.ts +157 -0
  32. package/tests/BaseEvent/EventProcessor.test.ts +278 -0
  33. package/tests/BaseEvent/Process.test.ts +49 -0
  34. package/tests/BaseEvent/Transaction.test.ts +231 -0
  35. package/tests/Config/Config.test.ts +193 -0
  36. package/tests/Config/EnvironmentVar.test.ts +223 -0
  37. package/tests/Crypto/Crypto.test.ts +90 -0
  38. package/tests/Crypto/JWT.test.ts +92 -0
  39. package/tests/Logger/Logger.test.ts +108 -0
  40. package/tests/Mailer/Mailer.test.ts +67 -0
  41. package/tests/Publisher/Publisher.test.ts +60 -0
  42. package/tests/Server/RouteResolver.test.ts +106 -0
  43. package/tests/Server/Router.test.ts +38 -0
  44. package/tests/Server/lib/ContainerServer.test.ts +329 -0
  45. package/tests/Server/lib/Server.test.ts +12 -0
  46. package/tests/Server/lib/container/GenericHandler.test.ts +141 -0
  47. package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
  48. package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
  49. package/tests/Server/lib/container/Proxy.test.ts +278 -0
  50. package/tests/Server/lib/container/Utils.test.ts +48 -0
  51. package/tests/Test.utils.ts +95 -0
  52. package/tests/Validation/Validator.test.ts +88 -0
  53. package/tests/main.test.ts +15 -0
  54. package/tsconfig.json +1 -0
  55. package/dist/index.d.ts +0 -11
  56. package/dist/index.js +0 -24
  57. package/dist/index.js.map +0 -1
  58. package/dist/package.json +0 -53
  59. package/dist/src/API/Request.d.ts +0 -21
  60. package/dist/src/API/Request.js +0 -86
  61. package/dist/src/API/Request.js.map +0 -1
  62. package/dist/src/API/Response.d.ts +0 -39
  63. package/dist/src/API/Response.js +0 -232
  64. package/dist/src/API/Response.js.map +0 -1
  65. package/dist/src/API/Utils.d.ts +0 -8
  66. package/dist/src/API/Utils.js +0 -49
  67. package/dist/src/API/Utils.js.map +0 -1
  68. package/dist/src/BaseEvent/EventProcessor.d.ts +0 -13
  69. package/dist/src/BaseEvent/EventProcessor.js +0 -151
  70. package/dist/src/BaseEvent/EventProcessor.js.map +0 -1
  71. package/dist/src/BaseEvent/Process.d.ts +0 -12
  72. package/dist/src/BaseEvent/Process.js +0 -114
  73. package/dist/src/BaseEvent/Process.js.map +0 -1
  74. package/dist/src/BaseEvent/Transaction.d.ts +0 -29
  75. package/dist/src/BaseEvent/Transaction.js +0 -248
  76. package/dist/src/BaseEvent/Transaction.js.map +0 -1
  77. package/dist/src/Config/Configuration.d.ts +0 -34
  78. package/dist/src/Config/Configuration.js +0 -93
  79. package/dist/src/Config/Configuration.js.map +0 -1
  80. package/dist/src/Config/EnvironmentVar.d.ts +0 -17
  81. package/dist/src/Config/EnvironmentVar.js +0 -152
  82. package/dist/src/Config/EnvironmentVar.js.map +0 -1
  83. package/dist/src/Crypto/Crypto.d.ts +0 -8
  84. package/dist/src/Crypto/Crypto.js +0 -84
  85. package/dist/src/Crypto/Crypto.js.map +0 -1
  86. package/dist/src/Crypto/JWT.d.ts +0 -16
  87. package/dist/src/Crypto/JWT.js +0 -49
  88. package/dist/src/Crypto/JWT.js.map +0 -1
  89. package/dist/src/Globals.d.ts +0 -21
  90. package/dist/src/Globals.js +0 -35
  91. package/dist/src/Globals.js.map +0 -1
  92. package/dist/src/Logger/Logger.d.ts +0 -34
  93. package/dist/src/Logger/Logger.js +0 -345
  94. package/dist/src/Logger/Logger.js.map +0 -1
  95. package/dist/src/Mailer/Mailer.d.ts +0 -12
  96. package/dist/src/Mailer/Mailer.js +0 -234
  97. package/dist/src/Mailer/Mailer.js.map +0 -1
  98. package/dist/src/Publisher/Publisher.d.ts +0 -10
  99. package/dist/src/Publisher/Publisher.js +0 -109
  100. package/dist/src/Publisher/Publisher.js.map +0 -1
  101. package/dist/src/Server/Router.d.ts +0 -27
  102. package/dist/src/Server/Router.js +0 -22
  103. package/dist/src/Server/Router.js.map +0 -1
  104. package/dist/src/Server/lib/ContainerServer.d.ts +0 -11
  105. package/dist/src/Server/lib/ContainerServer.js +0 -103
  106. package/dist/src/Server/lib/ContainerServer.js.map +0 -1
  107. package/dist/src/Server/lib/Server.d.ts +0 -9
  108. package/dist/src/Server/lib/Server.js +0 -141
  109. package/dist/src/Server/lib/Server.js.map +0 -1
  110. package/dist/src/Server/lib/container/GenericHandler.d.ts +0 -4
  111. package/dist/src/Server/lib/container/GenericHandler.js +0 -136
  112. package/dist/src/Server/lib/container/GenericHandler.js.map +0 -1
  113. package/dist/src/Server/lib/container/GenericHandlerEvent.d.ts +0 -14
  114. package/dist/src/Server/lib/container/GenericHandlerEvent.js +0 -164
  115. package/dist/src/Server/lib/container/GenericHandlerEvent.js.map +0 -1
  116. package/dist/src/Server/lib/container/HealthHandler.d.ts +0 -3
  117. package/dist/src/Server/lib/container/HealthHandler.js +0 -44
  118. package/dist/src/Server/lib/container/HealthHandler.js.map +0 -1
  119. package/dist/src/Server/lib/container/Proxy.d.ts +0 -15
  120. package/dist/src/Server/lib/container/Proxy.js +0 -157
  121. package/dist/src/Server/lib/container/Proxy.js.map +0 -1
  122. package/dist/src/Server/lib/container/Utils.d.ts +0 -6
  123. package/dist/src/Server/lib/container/Utils.js +0 -109
  124. package/dist/src/Server/lib/container/Utils.js.map +0 -1
  125. package/dist/src/Validation/Validator.d.ts +0 -5
  126. package/dist/src/Validation/Validator.js +0 -31
  127. 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
- resp = processServerlessResponse(invokationResp, res)
19
+ processServerlessResponse(invokationResp, res)
21
20
  } catch (e) {
22
21
  console.error("[Proxy] - Exception during execution!", e)
23
22
  console.error(e.stack)
24
- resp = res.status(Globals.Resp_STATUSCODE_EXCEPTION).json({
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?.data?.headers) {
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 ? unflatten(this.request.headers, 2) : [],
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: parseMultiValueHeaders(
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: this.request.params
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.remoteAddress,
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,
@@ -1,5 +1,5 @@
1
1
  import { Request, Response } from "express"
2
2
 
3
3
  export default async (_request: Request, res: Response) => {
4
- return res.send("Healthy!")
4
+ res.send("Healthy!")
5
5
  }
@@ -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
- options: {
38
- origin: corsConfig?.origin,
39
- allowedHeaders: corsConfig?.headers,
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
- async unload(err) {
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
- private async stopListeners(err) {
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
- this.listener.close((_err) => {
85
- if (err || _err) console.log("[Proxy] - exit output:", err || _err)
86
- console.log("[Proxy] - [STOPPED]")
87
- process.exit(err || _err ? 1 : 0)
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
- import * as unflatten from "unflatten"
2
- // https://aws.amazon.com/blogs/compute/support-for-multi-value-parameters-in-amazon-api-gateway/
3
- // (rawHeaders: Array<string>): { [string]: Array<string> }
4
- export function parseMultiValueHeaders(rawHeaders) {
5
- if (rawHeaders.length === 0) return null
6
- //
7
- const map = new Map()
8
- const unflattened = unflatten(rawHeaders, 2)
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 null
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.message) error = err.message
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 {}