@ikonintegration/ikapi 4.0.1 → 5.0.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.
Files changed (236) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +81 -0
  3. package/.github/workflows/npmpublish.yml +8 -19
  4. package/.github/workflows/prs.yml +12 -0
  5. package/README.md +89 -99
  6. package/dist/index.d.ts +16 -0
  7. package/dist/index.js +27 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/package-lock.json +11881 -0
  10. package/dist/package.json +81 -0
  11. package/dist/src/API/Request.d.ts +125 -0
  12. package/dist/src/API/Request.js +185 -0
  13. package/dist/src/API/Request.js.map +1 -0
  14. package/dist/src/API/Response.d.ts +188 -0
  15. package/dist/src/API/Response.js +270 -0
  16. package/dist/src/API/Response.js.map +1 -0
  17. package/dist/src/BaseEvent/DynamoTransaction.d.ts +70 -0
  18. package/dist/src/BaseEvent/DynamoTransaction.js +104 -0
  19. package/dist/src/BaseEvent/DynamoTransaction.js.map +1 -0
  20. package/dist/src/BaseEvent/EventProcessor.d.ts +58 -0
  21. package/dist/src/BaseEvent/EventProcessor.js +101 -0
  22. package/dist/src/BaseEvent/EventProcessor.js.map +1 -0
  23. package/dist/src/BaseEvent/Process.d.ts +50 -0
  24. package/dist/src/BaseEvent/Process.js +64 -0
  25. package/dist/src/BaseEvent/Process.js.map +1 -0
  26. package/dist/src/BaseEvent/StepTransaction.d.ts +23 -0
  27. package/dist/src/BaseEvent/StepTransaction.js +27 -0
  28. package/dist/src/BaseEvent/StepTransaction.js.map +1 -0
  29. package/dist/src/BaseEvent/Transaction.d.ts +149 -0
  30. package/dist/src/BaseEvent/Transaction.js +224 -0
  31. package/dist/src/BaseEvent/Transaction.js.map +1 -0
  32. package/dist/src/Cache/Redis.d.ts +29 -0
  33. package/dist/src/Cache/Redis.js +80 -0
  34. package/dist/src/Cache/Redis.js.map +1 -0
  35. package/dist/src/Cache/types.d.ts +31 -0
  36. package/dist/src/Cache/types.js +2 -0
  37. package/dist/src/Cache/types.js.map +1 -0
  38. package/dist/src/Config/Configuration.d.ts +123 -0
  39. package/dist/src/Config/Configuration.js +109 -0
  40. package/dist/src/Config/Configuration.js.map +1 -0
  41. package/dist/src/Config/EnvironmentVar.d.ts +74 -0
  42. package/dist/src/Config/EnvironmentVar.js +138 -0
  43. package/dist/src/Config/EnvironmentVar.js.map +1 -0
  44. package/dist/src/Crypto/Crypto.d.ts +45 -0
  45. package/dist/src/Crypto/Crypto.js +72 -0
  46. package/dist/src/Crypto/Crypto.js.map +1 -0
  47. package/dist/src/Database/Database.d.ts +21 -0
  48. package/dist/src/Database/Database.js +15 -0
  49. package/dist/src/Database/Database.js.map +1 -0
  50. package/dist/src/Database/DatabaseManager.d.ts +47 -0
  51. package/dist/src/Database/DatabaseManager.js +60 -0
  52. package/dist/src/Database/DatabaseManager.js.map +1 -0
  53. package/dist/src/Database/DatabaseTransaction.d.ts +101 -0
  54. package/dist/src/Database/DatabaseTransaction.js +126 -0
  55. package/dist/src/Database/DatabaseTransaction.js.map +1 -0
  56. package/dist/src/Database/index.d.ts +10 -0
  57. package/dist/src/Database/index.js +15 -0
  58. package/dist/src/Database/index.js.map +1 -0
  59. package/dist/src/Database/integrations/dynamo/DynamoDatabase.d.ts +35 -0
  60. package/dist/src/Database/integrations/dynamo/DynamoDatabase.js +59 -0
  61. package/dist/src/Database/integrations/dynamo/DynamoDatabase.js.map +1 -0
  62. package/dist/src/Database/integrations/kysely/KyselyDatabase.d.ts +66 -0
  63. package/dist/src/Database/integrations/kysely/KyselyDatabase.js +86 -0
  64. package/dist/src/Database/integrations/kysely/KyselyDatabase.js.map +1 -0
  65. package/dist/src/Database/integrations/kysely/KyselyTransaction.d.ts +70 -0
  66. package/dist/src/Database/integrations/kysely/KyselyTransaction.js +118 -0
  67. package/dist/src/Database/integrations/kysely/KyselyTransaction.js.map +1 -0
  68. package/dist/src/Database/integrations/pgsql/PostgresDatabase.d.ts +36 -0
  69. package/dist/src/Database/integrations/pgsql/PostgresDatabase.js +54 -0
  70. package/dist/src/Database/integrations/pgsql/PostgresDatabase.js.map +1 -0
  71. package/dist/src/Database/integrations/pgsql/PostgresTransaction.d.ts +63 -0
  72. package/dist/src/Database/integrations/pgsql/PostgresTransaction.js +61 -0
  73. package/dist/src/Database/integrations/pgsql/PostgresTransaction.js.map +1 -0
  74. package/dist/src/Database/types.d.ts +76 -0
  75. package/dist/src/Database/types.js +2 -0
  76. package/dist/src/Database/types.js.map +1 -0
  77. package/dist/src/Globals.d.ts +93 -0
  78. package/dist/src/Globals.js +99 -0
  79. package/dist/src/Globals.js.map +1 -0
  80. package/dist/src/Logger/Logger.d.ts +161 -0
  81. package/dist/src/Logger/Logger.js +299 -0
  82. package/dist/src/Logger/Logger.js.map +1 -0
  83. package/dist/src/Mailer/Mailer.d.ts +78 -0
  84. package/dist/src/Mailer/Mailer.js +182 -0
  85. package/dist/src/Mailer/Mailer.js.map +1 -0
  86. package/dist/src/Publisher/Publisher.d.ts +39 -0
  87. package/dist/src/Publisher/Publisher.js +77 -0
  88. package/dist/src/Publisher/Publisher.js.map +1 -0
  89. package/dist/src/Server/RouteResolver.d.ts +33 -0
  90. package/dist/src/Server/RouteResolver.js +100 -0
  91. package/dist/src/Server/RouteResolver.js.map +1 -0
  92. package/dist/src/Server/Router.d.ts +157 -0
  93. package/dist/src/Server/Router.js +32 -0
  94. package/dist/src/Server/Router.js.map +1 -0
  95. package/dist/src/Server/lib/ContainerServer.d.ts +42 -0
  96. package/dist/src/Server/lib/ContainerServer.js +66 -0
  97. package/dist/src/Server/lib/ContainerServer.js.map +1 -0
  98. package/dist/src/Server/lib/Server.d.ts +45 -0
  99. package/dist/src/Server/lib/Server.js +93 -0
  100. package/dist/src/Server/lib/Server.js.map +1 -0
  101. package/dist/src/Server/lib/container/GenericHandler.d.ts +9 -0
  102. package/dist/src/Server/lib/container/GenericHandler.js +82 -0
  103. package/dist/src/Server/lib/container/GenericHandler.js.map +1 -0
  104. package/dist/src/Server/lib/container/GenericHandlerEvent.d.ts +52 -0
  105. package/dist/src/Server/lib/container/GenericHandlerEvent.js +132 -0
  106. package/dist/src/Server/lib/container/GenericHandlerEvent.js.map +1 -0
  107. package/dist/src/Server/lib/container/HealthHandler.d.ts +9 -0
  108. package/dist/src/Server/lib/container/HealthHandler.js +19 -0
  109. package/dist/src/Server/lib/container/HealthHandler.js.map +1 -0
  110. package/dist/src/Server/lib/container/Proxy.d.ts +67 -0
  111. package/dist/src/Server/lib/container/Proxy.js +143 -0
  112. package/dist/src/Server/lib/container/Proxy.js.map +1 -0
  113. package/dist/src/Server/lib/container/Utils.d.ts +14 -0
  114. package/dist/src/Server/lib/container/Utils.js +37 -0
  115. package/dist/src/Server/lib/container/Utils.js.map +1 -0
  116. package/dist/src/Util/AsyncSingleton.d.ts +31 -0
  117. package/dist/src/Util/AsyncSingleton.js +83 -0
  118. package/dist/src/Util/AsyncSingleton.js.map +1 -0
  119. package/dist/src/Util/Utils.d.ts +61 -0
  120. package/dist/src/Util/Utils.js +147 -0
  121. package/dist/src/Util/Utils.js.map +1 -0
  122. package/dist/src/Validation/Validator.d.ts +17 -0
  123. package/dist/src/Validation/Validator.js +39 -0
  124. package/dist/src/Validation/Validator.js.map +1 -0
  125. package/dist/tsconfig.tsbuildinfo +1 -0
  126. package/index.ts +41 -0
  127. package/jest.config.ts +37 -0
  128. package/jest.smoke.config.ts +34 -0
  129. package/package.json +66 -22
  130. package/src/API/Request.ts +214 -0
  131. package/src/API/Response.ts +370 -0
  132. package/src/BaseEvent/DynamoTransaction.ts +175 -0
  133. package/src/BaseEvent/EventProcessor.ts +140 -0
  134. package/src/BaseEvent/Process.ts +78 -0
  135. package/src/BaseEvent/StepTransaction.ts +35 -0
  136. package/src/BaseEvent/Transaction.ts +323 -0
  137. package/src/Cache/Redis.ts +89 -0
  138. package/src/Cache/types.ts +33 -0
  139. package/src/Config/Configuration.ts +199 -0
  140. package/src/Config/EnvironmentVar.ts +142 -0
  141. package/src/Crypto/Crypto.ts +89 -0
  142. package/src/Database/Database.ts +22 -0
  143. package/src/Database/DatabaseManager.ts +67 -0
  144. package/src/Database/DatabaseTransaction.ts +170 -0
  145. package/src/Database/index.ts +27 -0
  146. package/src/Database/integrations/dynamo/DynamoDatabase.ts +58 -0
  147. package/src/Database/integrations/kysely/KyselyDatabase.ts +99 -0
  148. package/src/Database/integrations/kysely/KyselyTransaction.ts +172 -0
  149. package/src/Database/integrations/pgsql/PostgresDatabase.ts +56 -0
  150. package/src/Database/integrations/pgsql/PostgresTransaction.ts +87 -0
  151. package/src/Database/types.ts +85 -0
  152. package/src/Globals.ts +103 -0
  153. package/src/Logger/Logger.ts +363 -0
  154. package/src/Mailer/Mailer.ts +217 -0
  155. package/src/Publisher/Publisher.ts +96 -0
  156. package/src/Server/RouteResolver.ts +124 -0
  157. package/src/Server/Router.ts +200 -0
  158. package/src/Server/lib/ContainerServer.ts +65 -0
  159. package/src/Server/lib/Server.ts +109 -0
  160. package/src/Server/lib/container/GenericHandler.ts +76 -0
  161. package/src/Server/lib/container/GenericHandlerEvent.ts +154 -0
  162. package/src/Server/lib/container/HealthHandler.ts +11 -0
  163. package/src/Server/lib/container/Proxy.ts +172 -0
  164. package/src/Server/lib/container/Utils.ts +33 -0
  165. package/src/Util/AsyncSingleton.ts +86 -0
  166. package/src/Util/Utils.ts +131 -0
  167. package/src/Validation/Validator.ts +45 -0
  168. package/tests/API/Request.test.ts +273 -0
  169. package/tests/API/Response.test.ts +367 -0
  170. package/tests/BaseEvent/DynamoTransaction.test.ts +272 -0
  171. package/tests/BaseEvent/EventProcessor.test.ts +263 -0
  172. package/tests/BaseEvent/Process.test.ts +47 -0
  173. package/tests/BaseEvent/StepTransaction.test.ts +44 -0
  174. package/tests/BaseEvent/Transaction.test.ts +402 -0
  175. package/tests/Cache/Redis-client.test.ts +90 -0
  176. package/tests/Cache/Redis-cluster.test.ts +100 -0
  177. package/tests/Config/Config.test.ts +205 -0
  178. package/tests/Config/EnvironmentVar.test.ts +251 -0
  179. package/tests/Crypto/Crypto.test.ts +88 -0
  180. package/tests/Database/DatabaseManager.test.ts +79 -0
  181. package/tests/Database/integrations/dynamo/DynamoDatabase.test.ts +44 -0
  182. package/tests/Database/integrations/kysely/KyselyDatabase.test.ts +113 -0
  183. package/tests/Database/integrations/kysely/KyselyTransaction.test.ts +119 -0
  184. package/tests/Database/integrations/pg/PostgresDatabase.test.ts +76 -0
  185. package/tests/Database/integrations/pg/PostgresTransaction.test.ts +118 -0
  186. package/tests/Logger/Logger.test.ts +215 -0
  187. package/tests/Mailer/Mailer.test.ts +59 -0
  188. package/tests/Publisher/Publisher.test.ts +60 -0
  189. package/tests/Server/RouteResolver.test.ts +116 -0
  190. package/tests/Server/Router.test.ts +39 -0
  191. package/tests/Server/lib/ContainerServer.test.ts +531 -0
  192. package/tests/Server/lib/Server.test.ts +12 -0
  193. package/tests/Server/lib/container/GenericHandler.test.ts +131 -0
  194. package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
  195. package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
  196. package/tests/Server/lib/container/Proxy.test.ts +268 -0
  197. package/tests/Server/lib/container/Utils.test.ts +47 -0
  198. package/tests/Test.utils.ts +78 -0
  199. package/tests/Utils/Utils.test.ts +229 -0
  200. package/tests/Validation/Validator.test.ts +82 -0
  201. package/tsconfig.json +26 -0
  202. package/tsconfig.smoke.json +26 -0
  203. package/index.js +0 -88
  204. package/src/API/IKRequest.js +0 -52
  205. package/src/API/IKResponse.js +0 -119
  206. package/src/API/IKUtils.js +0 -51
  207. package/src/BaseEvent/IKProcess.js +0 -77
  208. package/src/BaseEvent/IKTransaction.js +0 -139
  209. package/src/Cache/Prototype/IKCache.js +0 -17
  210. package/src/Cache/Redis/IKRedis.js +0 -148
  211. package/src/Database/DDB/IKDB.js +0 -56
  212. package/src/Database/DDB/IKDBBaseExpression.js +0 -130
  213. package/src/Database/DDB/IKDBBaseQuery.js +0 -151
  214. package/src/Database/DDB/IKDBQueryBatchGet.js +0 -37
  215. package/src/Database/DDB/IKDBQueryBatchWrite.js +0 -64
  216. package/src/Database/DDB/IKDBQueryDelete.js +0 -34
  217. package/src/Database/DDB/IKDBQueryGet.js +0 -48
  218. package/src/Database/DDB/IKDBQueryPut.js +0 -87
  219. package/src/Database/DDB/IKDBQueryScan.js +0 -45
  220. package/src/Database/DDB/IKDBQueryTransactionalWrite.js +0 -69
  221. package/src/Database/DDB/IKDBQueryUpdate.js +0 -221
  222. package/src/Database/DDB/_IKDBQueryTransactionalRead.js +0 -46
  223. package/src/Database/PSQL/IKDB.js +0 -41
  224. package/src/Database/PSQL/IKDBBaseQuery.js +0 -26
  225. package/src/Database/Prototype/IKDB.js +0 -21
  226. package/src/Database/Prototype/IKDBBaseQuery.js +0 -14
  227. package/src/IKDynamoStream.js +0 -42
  228. package/src/IKEventProcessor.js +0 -42
  229. package/src/IKGlobals.js +0 -24
  230. package/src/IKRouter.js +0 -47
  231. package/src/IKStepTransaction.js +0 -14
  232. package/src/Logger/IKLogger.js +0 -136
  233. package/src/Mailer/IKMailer.js +0 -69
  234. package/src/Publisher/IKPublisher.js +0 -44
  235. package/src/Tracker/IKExecutionTracker.js +0 -79
  236. package/src/Validation/IKValidation.js +0 -76
@@ -0,0 +1,370 @@
1
+ import type { Context } from 'aws-lambda'
2
+
3
+ import Transaction from '../BaseEvent/Transaction.js'
4
+ import Globals from '../Globals.js'
5
+
6
+ /**
7
+ * CustomError class that extends the built-in Error class.
8
+ * @class
9
+ * @extends Error
10
+ * @param {any} body - The body of the error, which can be an object or a string.
11
+ * @constructor
12
+ * @property {string} name - The name of the error.
13
+ */
14
+ class CustomError extends Error {
15
+ constructor(body: any) {
16
+ super(body.message || 'Unknown error!')
17
+ this.name = body.error || 'UnknownError'
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Represents an error response from an API.
23
+ * @typedef {Object} ResponseErrorType
24
+ * @property {string} err - The error message.
25
+ * @property {string} [errCode] - The error code, if available.
26
+ */
27
+ export type ResponseErrorType = {
28
+ err: string
29
+ errCode?: string
30
+ }
31
+
32
+ /**
33
+ * Represents a response object with various methods for building and manipulating the response.
34
+ * @template BodyType - The type of the response body.
35
+ */
36
+ export default class Response<BodyType = null> {
37
+ /**
38
+ * The private property that stores the status code.
39
+ * @type {number}
40
+ * @private
41
+ */
42
+ private statusCode: number
43
+ /**
44
+ * Private property representing the body of an object.
45
+ * @type {any}
46
+ * @private
47
+ */
48
+ private body: any
49
+ /**
50
+ * Indicates whether the object is currently piping out.
51
+ * @type {boolean}
52
+ */
53
+ private isPipingOut: boolean
54
+ /**
55
+ * Private property that stores the headers as an object.
56
+ */
57
+ private headers: object
58
+ /**
59
+ * Determines whether streaming is enabled or not.
60
+ * @returns {boolean} - True if streaming is enabled, false otherwise.
61
+ */
62
+ public readonly shouldStream: boolean
63
+ /**
64
+ * Indicates whether the request body should be treated as raw data.
65
+ * @type {boolean}
66
+ */
67
+ public readonly rawBody: boolean
68
+ /**
69
+ * A boolean flag indicating whether to throw an error when encountering errors.
70
+ * If set to true, any errors encountered will result in an exception being thrown.
71
+ * If set to false, errors will be logged but the program will continue execution.
72
+ */
73
+ public readonly throwOnErrors: boolean
74
+ /**
75
+ * Indicates whether the transaction ID is disabled.
76
+ * @type {boolean}
77
+ */
78
+ public readonly disableTransactionID: boolean
79
+
80
+ /**
81
+ * Constructs a new Response object with the given status code, body, and optional behavior.
82
+ * @param {number} statusCode - The HTTP status code of the response.
83
+ * @param {BodyType} body - The body of the response.
84
+ * @param {Object} [optBehaviour] - Optional behavior configuration for the response.
85
+ * @param {boolean} [optBehaviour.shouldStream] - Indicates whether the response should be streamed.
86
+ * @param {boolean} [optBehaviour.rawBody] - Indicates whether the response body should be treated as raw data.
87
+ * @param {boolean} [optBehaviour.throwOnErrors] - Indicates whether errors should be thrown for non-successful status codes.
88
+ * @param {boolean} [optBehaviour
89
+ */
90
+ constructor(
91
+ statusCode: number,
92
+ body: BodyType,
93
+ optBehaviour?:
94
+ | {
95
+ shouldStream?: boolean
96
+ rawBody?: boolean
97
+ throwOnErrors?: boolean
98
+ disableTransactionID?: boolean
99
+ }
100
+ | undefined
101
+ ) {
102
+ // response
103
+ this.statusCode = statusCode
104
+ this.body = body
105
+ this.headers = {
106
+ 'Access-Control-Allow-Origin': '*',
107
+ 'Access-Control-Allow-Credentials': true,
108
+ 'Content-Type': 'application/json',
109
+ }
110
+ // behaviour
111
+ this.isPipingOut = false //internal -- flag to indicate streaming out has started and avoid double stream call
112
+ // options
113
+ this.shouldStream = !!optBehaviour?.shouldStream //
114
+ this.rawBody = !!optBehaviour?.rawBody
115
+ this.throwOnErrors = !!optBehaviour?.throwOnErrors
116
+ this.disableTransactionID = !!optBehaviour?.disableTransactionID
117
+ }
118
+
119
+ /**
120
+ * Get the status code of the response.
121
+ * @returns {number} The status code.
122
+ */
123
+ public getCode(): number {
124
+ return this.statusCode
125
+ }
126
+
127
+ /**
128
+ * Get the body of the object.
129
+ * @returns {BodyType} The body of the object.
130
+ */
131
+ public getBody(): BodyType {
132
+ return this.body
133
+ }
134
+
135
+ /**
136
+ * Appends a key-value pair into the body object.
137
+ * @param {string} key - The key to append.
138
+ * @param {any} value - The value to append.
139
+ * @returns None
140
+ */
141
+ public appendIntoBody(key: string, value: any): void {
142
+ this.body[key] = value
143
+ }
144
+
145
+ /**
146
+ * Appends a header to the existing headers object.
147
+ * @param {string} key - The key of the header.
148
+ * @param {any} value - The value of the header.
149
+ * @returns None
150
+ */
151
+ public appendHeader(key: string, value: any): void {
152
+ this.headers[key] = value
153
+ }
154
+
155
+ /**
156
+ * Builds the response for the given context and transaction.
157
+ * @param {Context} context - The context object.
158
+ * @param {Transaction<any, any, any>} transaction - The transaction object.
159
+ * @param {boolean} optDoNotCallContext - Optional flag to indicate whether to call the context or not.
160
+ * @returns {Promise<void>} - A promise that resolves when the response is built.
161
+ */
162
+ public async build(
163
+ context: Context,
164
+ transaction: Transaction<any, any, any, any, any>,
165
+ optDoNotCallContext: boolean
166
+ ): Promise<void> {
167
+ //Stream support
168
+ if (this.isPipingOut) return
169
+ if (this.shouldStream) return this.pipe(context)
170
+
171
+ //append default fields
172
+ if (transaction.request.getRequestID() && this.body && !this.disableTransactionID)
173
+ this.appendIntoBody('transactionID', transaction.request.getRequestID()) //append transaction ID
174
+ //Raw response support
175
+ if (this.rawBody) return this.rawContext(context, transaction)
176
+
177
+ //build response
178
+ const b = {
179
+ statusCode: this.statusCode,
180
+ headers: this.headers,
181
+ ...(this.body ? { body: JSON.stringify(this.body) } : {}),
182
+ }
183
+ //log response and respond to context
184
+ transaction.logger.debug(b)
185
+ //Check for transaction response proxy
186
+ if (transaction.responseProxy) await transaction.responseProxy(this)
187
+ //Batch does not succeed directly just on upper transaction (which will should be a batch)
188
+ if (!optDoNotCallContext) context.succeed(b)
189
+ }
190
+
191
+ /**
192
+ * Private method that pipes the response to the given context.
193
+ * @param {Context} context - The context object provided by AWS Lambda.
194
+ * @returns None
195
+ */
196
+ private pipe(context: Context): void {
197
+ //Check if not streaming
198
+ this.isPipingOut = true
199
+ //build response
200
+ const b = {
201
+ statusCode: this.statusCode,
202
+ body: this.body,
203
+ headers: this.headers,
204
+ }
205
+ //log response and respond to context
206
+ context.succeed(b)
207
+ }
208
+
209
+ /**
210
+ * Private method that handles the raw context of a transaction.
211
+ * @param {Context} context - The context object.
212
+ * @param {Transaction<null, BodyType>} transaction - The transaction object.
213
+ * @returns None
214
+ */
215
+ private rawContext(context: Context, transaction: Transaction<null, BodyType, any>): void {
216
+ //log response and respond to context
217
+ transaction.logger.debug(this.body)
218
+ if (this.getCode() <= 200 && this.getCode() <= 299) context.succeed(this.body)
219
+ else {
220
+ if (!this.throwOnErrors) context.fail(new CustomError(this.body))
221
+ else throw new CustomError(this.body)
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Generates a response object for a missing path parameter error.
227
+ * @param {string} paramName - The name of the missing path parameter.
228
+ * @returns {Response<ResponseErrorType>} - The response object with error details.
229
+ */
230
+ public static MissingParamResponse(paramName: string): Response<ResponseErrorType> {
231
+ console.warn(`Invalid request - Path parameter ${paramName} is missing.`)
232
+ return new Response<ResponseErrorType>(400, {
233
+ err: `Invalid request. Path parameter ${paramName} is missing.`,
234
+ errCode: Globals.ErrorCode_MissingParam,
235
+ })
236
+ }
237
+
238
+ /**
239
+ * Creates a response object for a missing query parameter error.
240
+ * @param {string} paramName - The name of the missing query parameter.
241
+ * @returns {Response<ResponseErrorType>} - The response object with error details.
242
+ */
243
+ public static MissingQueryResponse(paramName: string): Response<ResponseErrorType> {
244
+ console.warn(`Invalid request - Query parameter ${paramName} is missing.`)
245
+ return new Response<ResponseErrorType>(400, {
246
+ err: `Invalid request. Query parameter ${paramName} is missing.`,
247
+ errCode: Globals.ErrorCode_MissingParam,
248
+ })
249
+ }
250
+
251
+ /**
252
+ * Creates a BadRequestResponse object with the given parameters.
253
+ * @param {string} [msg] - The error message.
254
+ * @param {string} [errCode] - The error code.
255
+ * @param {any} [optBody] - Optional additional body data.
256
+ * @returns {Response<ResponseErrorType>} - The BadRequestResponse object.
257
+ */
258
+ public static BadRequestResponse(
259
+ msg?: string,
260
+ errCode?: string,
261
+ optBody?: any
262
+ ): Response<ResponseErrorType> {
263
+ console.warn(`Bad request - ${msg}`)
264
+ return new Response<ResponseErrorType>(400, {
265
+ err: msg,
266
+ ...(errCode ? { errCode: errCode } : {}),
267
+ ...(optBody || {}),
268
+ })
269
+ }
270
+
271
+ /**
272
+ * Creates a BadRequestResponse object with rollback option.
273
+ * @param {string} msg - The error message.
274
+ * @param {string} [errCode] - The error code.
275
+ * @param {any} [optBody] - Optional body to include in the response.
276
+ * @returns {Response<ResponseErrorType>} - The BadRequestResponse object.
277
+ */
278
+ public static BadRequestResponseWithRollback(
279
+ msg: string,
280
+ errCode?: string,
281
+ optBody?: any
282
+ ): Response<ResponseErrorType> {
283
+ console.warn(`Bad request - ${msg}`)
284
+ return new Response<ResponseErrorType>(400, {
285
+ err: msg,
286
+ rollback: true,
287
+ ...(errCode ? { errCode: errCode } : {}),
288
+ ...(optBody || {}),
289
+ })
290
+ }
291
+
292
+ /**
293
+ * Creates an unauthorized response with the given error message and error code.
294
+ * @param {string} msg - The error message.
295
+ * @param {string} [errCode] - The error code (optional).
296
+ * @returns {Response<ResponseErrorType>} - The unauthorized response.
297
+ */
298
+ public static UnauthorizedResponse(msg: string, errCode?: string): Response<ResponseErrorType> {
299
+ console.warn(`Denying request - ${msg}`)
300
+ return new Response<ResponseErrorType>(401, {
301
+ err: msg,
302
+ ...(errCode ? { errCode: errCode } : {}),
303
+ })
304
+ }
305
+
306
+ /**
307
+ * Creates a success response object with the given body.
308
+ * @param {BodyType} body - The body of the response.
309
+ * @returns {Response<BodyType>} - The success response object.
310
+ */
311
+ public static SuccessResponse<BodyType>(body: BodyType): Response<BodyType> {
312
+ return new Response<BodyType>(200, (body ? body : {}) as BodyType)
313
+ }
314
+
315
+ /**
316
+ * Creates a redirect response with the specified URL.
317
+ * @param {string} url - The URL to redirect to.
318
+ * @returns {Response<null>} - The redirect response.
319
+ */
320
+ public static RedirectResponse(url: string): Response<null> {
321
+ const resp = new Response<null>(302, null)
322
+ resp.appendHeader('Location', url)
323
+ return resp
324
+ }
325
+
326
+ /**
327
+ * Creates a success response with no content.
328
+ * @returns {Response<null>} A response object with a status code of 204 and no content.
329
+ */
330
+ public static SuccessNoContentResponse(): Response<null> {
331
+ return new Response<null>(204, null)
332
+ }
333
+
334
+ /**
335
+ * Creates a success response object with a streaming body and specified content type.
336
+ * @param {any} stream - The stream object to be used as the response body.
337
+ * @param {string} contentType - The content type of the response.
338
+ * @returns {Response} - The success response object.
339
+ */
340
+ public static SuccessStreamResponse(stream: any, contentType: string): Response {
341
+ const resp = new Response(200, stream, {
342
+ shouldStream: true,
343
+ })
344
+ resp.appendHeader('Connection', 'keep-alive')
345
+ if (contentType) resp.appendHeader('Content-Type', contentType)
346
+ return resp
347
+ }
348
+
349
+ /**
350
+ * Creates a simple HTTP response with the given body and optional status code.
351
+ * @param {BodyType} body - The body of the response.
352
+ * @param {number} [optionalCode] - The optional status code of the response. Defaults to 200.
353
+ * @returns {Response<BodyType>} - The created response object.
354
+ */
355
+ public static SimpleResponse<BodyType>(
356
+ body: BodyType,
357
+ optionalCode?: number
358
+ ): Response<BodyType> {
359
+ const resp = new Response<BodyType>(optionalCode || 200, body)
360
+ return resp
361
+ }
362
+
363
+ public static StepFunctionResponse<BodyType>(body: BodyType, optionalCode?: number) {
364
+ const resp = new Response<BodyType>(optionalCode || 200, body, {
365
+ rawBody: true,
366
+ throwOnErrors: true,
367
+ })
368
+ return resp
369
+ }
370
+ }
@@ -0,0 +1,175 @@
1
+ import type {
2
+ Context,
3
+ DynamoDBBatchResponse,
4
+ DynamoDBRecord,
5
+ DynamoDBStreamEvent,
6
+ } from 'aws-lambda'
7
+
8
+ import Transaction, { TransactionConfig } from './Transaction.js'
9
+ import Response, { ResponseErrorType } from '../API/Response.js'
10
+ import Globals from '../Globals.js'
11
+ import Utils from '../Util/Utils.js'
12
+
13
+ /**
14
+ * Interface representing a DynamoDB record with marshalled data.
15
+ * Extends the DynamoDBRecord interface.
16
+ * @property {object} Keys - The keys of the record.
17
+ * @property {object} OldImage - The old image of the record.
18
+ * @property {object} NewImage - The new image of the record.
19
+ */
20
+ export interface DynamoDBMarshalledRecord extends DynamoDBRecord {
21
+ marshalled: {
22
+ Keys?: object
23
+ OldImage?: object
24
+ NewImage?: object
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Defines a type for executing a transaction on DynamoDB.
30
+ * @param {Transaction<null, ResponseInnerType | ResponseErrorType, DynamoDBBatchResponse | null>} transaction - The transaction to execute.
31
+ * @param {DynamoDBMarshalledRecord} recordContent - The content of the DynamoDB record.
32
+ * @returns A promise that resolves to a response or a DynamoDB batch response.
33
+ */
34
+ export type DynamoTransactionExecution<ResponseInnerType> = (
35
+ transaction: Transaction<
36
+ null,
37
+ ResponseInnerType | ResponseErrorType,
38
+ DynamoDBBatchResponse | null
39
+ >,
40
+ recordContent: DynamoDBMarshalledRecord
41
+ ) => Promise<Response<ResponseInnerType | ResponseErrorType> | DynamoDBBatchResponse | null>
42
+
43
+ /**
44
+ * Represents a DynamoDB transaction handler that processes events from a DynamoDB stream.
45
+ * @template ResponseInnerType - The inner type of the response.
46
+ */
47
+ export default class DynamoTransaction<ResponseInnerType> {
48
+ /**
49
+ * A boolean flag indicating whether failures are allowed.
50
+ */
51
+ private readonly allowFailure: boolean
52
+
53
+ /**
54
+ * Readonly property that holds the transaction configuration.
55
+ */
56
+ private readonly config: TransactionConfig
57
+
58
+ /**
59
+ * The context object that provides information about the current execution context.
60
+ * @type {Context}
61
+ */
62
+ private readonly context: Context
63
+
64
+ /**
65
+ * Represents an event from a DynamoDB stream.
66
+ * @type {DynamoDBStreamEvent}
67
+ */
68
+ private readonly event: DynamoDBStreamEvent
69
+
70
+ /**
71
+ * Constructor for a TransactionHandler object.
72
+ * @param {DynamoDBStreamEvent} event - The DynamoDB stream event that triggered the transaction.
73
+ * @param {Context} context - The AWS Lambda context object.
74
+ * @param {TransactionConfig} [config] - Optional configuration for the transaction.
75
+ * @param {boolean} [allowFailure] - Flag to indicate whether to allow transaction failure.
76
+ * @returns None
77
+ */
78
+ constructor(
79
+ event: DynamoDBStreamEvent,
80
+ context: Context,
81
+ config?: TransactionConfig,
82
+ allowFailure?: boolean
83
+ ) {
84
+ this.event = event
85
+ this.context = context
86
+ this.config = config || {}
87
+ this.allowFailure = !!allowFailure
88
+ }
89
+
90
+ /**
91
+ * Processes the event execution and returns a response based on the outcome.
92
+ * @param {DynamoTransactionExecution<ResponseInnerType>} execution - The execution object to process.
93
+ * @returns {Promise<Response<ResponseErrorType | null> | null | DynamoDBBatchResponse>} A promise that resolves to a response object or null.
94
+ * @throws {Error} If the response code is not within the success range and failure is not allowed.
95
+ */
96
+ public async processEvent(
97
+ execution: DynamoTransactionExecution<ResponseInnerType>
98
+ ): Promise<Response<ResponseErrorType | null> | null | DynamoDBBatchResponse> {
99
+ const resp = await this.processRawEvent(execution)
100
+ if (
101
+ !this.allowFailure &&
102
+ resp &&
103
+ resp instanceof Response &&
104
+ !(resp.getCode() >= 200 && resp.getCode() < 300)
105
+ )
106
+ throw new Error(JSON.stringify(resp.getBody() || {}))
107
+ else if (resp) return resp
108
+ return null
109
+ }
110
+
111
+ /**
112
+ * Processes a raw event by executing a transaction on each record in the event.
113
+ * @param {DynamoTransactionExecution<ResponseInnerType>} execution - The transaction execution function.
114
+ * @returns {Promise<Response<ResponseErrorType | any> | null | DynamoDBBatchResponse>} A promise that resolves to a response object, null, or a DynamoDB batch response.
115
+ */
116
+ private async processRawEvent(
117
+ execution: DynamoTransactionExecution<ResponseInnerType>
118
+ ): Promise<Response<ResponseErrorType | any> | null | DynamoDBBatchResponse> {
119
+ // safe check for empty events?
120
+ if (this.event.Records && this.event.Records.length > 0) {
121
+ // Regular cleanup of tmp disk to avoid tmp overflow on reused lambdas
122
+ if (!this.config?.skipCleanTmp) await Utils.cleanTemporaryFolder()
123
+ // init transaction for all records
124
+ return await new Transaction<null, any, DynamoDBBatchResponse | null>(
125
+ this.event,
126
+ this.context,
127
+ {
128
+ ...this.config,
129
+ syncReturn: true,
130
+ }
131
+ ).execute(async transaction => {
132
+ // for each available event
133
+ const failureIDs: Array<string> = []
134
+ for (const eventRecordIdx in this.event.Records) {
135
+ const eventRecord = this.event.Records[eventRecordIdx]
136
+ const record = {
137
+ ...eventRecord,
138
+ marshalled: {
139
+ ...(eventRecord.dynamodb?.Keys
140
+ ? { Keys: Utils.ddbUnmarshall(eventRecord.dynamodb?.Keys) }
141
+ : {}),
142
+ ...(eventRecord.dynamodb?.OldImage
143
+ ? { OldImage: Utils.ddbUnmarshall(eventRecord.dynamodb?.OldImage) }
144
+ : {}),
145
+ ...(eventRecord.dynamodb?.NewImage
146
+ ? { NewImage: Utils.ddbUnmarshall(eventRecord.dynamodb?.NewImage) }
147
+ : {}),
148
+ },
149
+ }
150
+ // Call execution with marshalled item
151
+ const resp = await execution(transaction, record)
152
+ // check for failure
153
+ if (
154
+ !resp ||
155
+ (resp instanceof Response && !(resp?.getCode() >= 200 && resp?.getCode() < 300))
156
+ ) {
157
+ // response with failures or fail hard at first
158
+ if (this.allowFailure) failureIDs.push(eventRecord.eventID!)
159
+ else return resp
160
+ }
161
+ }
162
+ // not errored and loop ended - succeeded (might have failures)
163
+ if (this.allowFailure)
164
+ return {
165
+ batchItemFailures: failureIDs.map(id => ({ itemIdentifier: id })),
166
+ }
167
+ return Response.SuccessResponse(null)
168
+ })
169
+ } else
170
+ return Response.BadRequestResponse(
171
+ Globals.ErrorResponseNoRecords,
172
+ Globals.ErrorCode_NoRecords
173
+ )
174
+ }
175
+ }
@@ -0,0 +1,140 @@
1
+ import type { Context, SQSBatchResponse, SQSEvent } from 'aws-lambda'
2
+
3
+ import Transaction, { TransactionConfig } from './Transaction.js'
4
+ import Response, { ResponseErrorType } from '../API/Response.js'
5
+ import Globals from '../Globals.js'
6
+ import Utils from '../Util/Utils.js'
7
+
8
+ /**
9
+ * Type definition for an event processor execution function.
10
+ * @param {Transaction<null, ResponseInnerType | ResponseErrorType, SQSBatchResponse>} transaction - The transaction object.
11
+ * @param {string | object} recordContent - The content of the record being processed.
12
+ * @returns {Promise<Response<ResponseInnerType | ResponseErrorType> | SQSBatchResponse>} - A promise that resolves to the response or batch response.
13
+ */
14
+ export type EventProcessorExecution<ResponseInnerType> = (
15
+ transaction: Transaction<null, ResponseInnerType | ResponseErrorType, SQSBatchResponse | null>,
16
+ recordContent: string | object
17
+ ) => Promise<Response<ResponseInnerType | ResponseErrorType> | SQSBatchResponse | null>
18
+
19
+ /**
20
+ * EventProcessor class that processes events from an SQS queue.
21
+ * @template ResponseInnerType - The type of the inner response object.
22
+ */
23
+ export default class EventProcessor<ResponseInnerType> {
24
+ /**
25
+ * A boolean flag indicating whether failures are allowed or not.
26
+ * @readonly
27
+ */
28
+ private readonly allowFailure: boolean
29
+ /**
30
+ * The configuration object for the API transaction.
31
+ */
32
+ private readonly config: TransactionConfig
33
+ /**
34
+ * The private readonly context property of the class.
35
+ * @type {Context}
36
+ */
37
+ private readonly context: Context
38
+ /**
39
+ * The SQS event object that triggered the Lambda function.
40
+ */
41
+ private readonly event: SQSEvent
42
+
43
+ /**
44
+ * Constructs a new instance of the class.
45
+ * @param {SQSEvent} event - The event object representing the incoming SQS message.
46
+ * @param {Context} context - The context object representing the AWS Lambda execution context.
47
+ * @param {TransactionConfig} [config] - Optional configuration object for the transaction.
48
+ * @param {boolean} [allowFailure] - Optional flag indicating whether to allow failure for the transaction.
49
+ * @returns None
50
+ */
51
+ constructor(
52
+ event: SQSEvent,
53
+ context: Context,
54
+ config?: TransactionConfig,
55
+ allowFailure?: boolean
56
+ ) {
57
+ this.event = event
58
+ this.context = context
59
+ this.config = config || {}
60
+ this.allowFailure = !!allowFailure
61
+ }
62
+
63
+ /**
64
+ * Processes an event using the provided execution object and returns a response.
65
+ * @param {EventProcessorExecution<ResponseInnerType>} execution - The execution object containing the event to process.
66
+ * @param {boolean} [doNotDecodeMessage] - Optional flag indicating whether to decode the message.
67
+ * @returns {Promise<Response<ResponseErrorType> | null | SQSBatchResponse>} - A promise that resolves to the response object, or null if no response is available.
68
+ * @throws {Error} - Throws an error if the response code is not within the range of 200 to 299 and failure is not allowed.
69
+ */
70
+ public async processEvent(
71
+ execution: EventProcessorExecution<ResponseInnerType>,
72
+ doNotDecodeMessage?: boolean
73
+ ): Promise<Response<ResponseErrorType | null> | null | SQSBatchResponse> {
74
+ const resp = await this.processRawEvent(execution, !!doNotDecodeMessage)
75
+ if (
76
+ !this.allowFailure &&
77
+ resp &&
78
+ resp instanceof Response &&
79
+ !(resp.getCode() >= 200 && resp.getCode() < 300)
80
+ )
81
+ throw new Error(JSON.stringify(resp.getBody() || {}))
82
+ else if (resp) return resp
83
+ return null
84
+ }
85
+
86
+ /**
87
+ * Processes a raw event by executing the provided execution function and handling any errors or failures.
88
+ * @param {EventProcessorExecution<ResponseInnerType>} execution - The execution function to process the event.
89
+ * @param {boolean} doNotDecodeMessage - Flag indicating whether to decode the message or not.
90
+ * @returns {Promise<Response<ResponseErrorType> | null | SQSBatchResponse>} - A promise that resolves to a response object, null, or a SQS batch response.
91
+ */
92
+ private async processRawEvent(
93
+ execution: EventProcessorExecution<ResponseInnerType>,
94
+ doNotDecodeMessage: boolean
95
+ ): Promise<Response<ResponseErrorType | any> | null | SQSBatchResponse> {
96
+ // safe check for empty events?
97
+ if (this.event.Records && this.event.Records.length > 0) {
98
+ // Regular cleanup of tmp disk to avoid tmp overflow on reused lambdas
99
+ if (!this.config?.skipCleanTmp) await Utils.cleanTemporaryFolder()
100
+ // init transaction for all records
101
+ return await new Transaction<null, any, SQSBatchResponse | null>(this.event, this.context, {
102
+ ...this.config,
103
+ syncReturn: true,
104
+ }).execute(async transaction => {
105
+ // Map records with decoded message when required
106
+ const decodedRecords: string[] | object[] = this.event.Records.map(eventRecord =>
107
+ doNotDecodeMessage ? eventRecord.body : JSON.parse(eventRecord.body)
108
+ )
109
+
110
+ // for each available event
111
+ const failureIDs: Array<string> = []
112
+ for (const eventRecordIdx in decodedRecords) {
113
+ const eventRecord = decodedRecords[eventRecordIdx]
114
+ const message = this.event.Records[eventRecordIdx]
115
+ // Call execution
116
+ const resp = await execution(transaction, eventRecord)
117
+ // check for failure
118
+ if (
119
+ !resp ||
120
+ (resp instanceof Response && !(resp?.getCode() >= 200 && resp?.getCode() < 300))
121
+ ) {
122
+ // response with failures or fail hard at first
123
+ if (this.allowFailure) failureIDs.push(message.messageId)
124
+ else return resp
125
+ }
126
+ }
127
+ // not errored and loop ended - succeeded (might have failures)
128
+ if (this.allowFailure)
129
+ return {
130
+ batchItemFailures: failureIDs.map(id => ({ itemIdentifier: id })),
131
+ }
132
+ return Response.SuccessResponse(null)
133
+ })
134
+ } else
135
+ return Response.BadRequestResponse(
136
+ Globals.ErrorResponseNoRecords,
137
+ Globals.ErrorCode_NoRecords
138
+ ) // no event to be processed?
139
+ }
140
+ }