@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,363 @@
1
+ import abind from 'abind'
2
+ import stringify from 'json-stringify-safe'
3
+ import stackTrace from 'stack-trace'
4
+
5
+ import Utils from '../Util/Utils.js'
6
+
7
+ /**
8
+ * Enumeration of log levels.
9
+ * @enum {string}
10
+ * @property {string} DEBUG - Debug log level.
11
+ * @property {string} INFO - Info log level.
12
+ * @property {string} WARN - Warning log level.
13
+ * @property {string} ERROR - Error log level.
14
+ */
15
+ export enum LOG_LEVELS {
16
+ DEBUG = 'DEBUG',
17
+ INFO = 'INFO',
18
+ WARN = 'WARN',
19
+ ERROR = 'ERROR',
20
+ }
21
+ /**
22
+ * A constant that represents the console object to be used for logging.
23
+ * If the console object has a property named 'notGlobalLogger', it is assumed
24
+ * to be a custom logger and the 'origin' property is used as the console object.
25
+ * Otherwise, the global console object is used.
26
+ * @type {Console}
27
+ */
28
+ const PURE_CONSOLE = console['notGlobalLogger'] ? console['origin'] : console
29
+ /**
30
+ * The default log function that is used for logging messages.
31
+ * @type {Function}
32
+ */
33
+ const DEFAULT_LOG_FUNCTION = PURE_CONSOLE.log.bind(PURE_CONSOLE)
34
+
35
+ /**
36
+ * Creates a blacklist array by mapping each string in the given array to its lowercase form.
37
+ * The resulting blacklist array is used to filter out sensitive information.
38
+ * @type {string[]} blacklist - An array of strings to be converted to lowercase and used as a blacklist.
39
+ * @returns {string[]} - An array of lowercase strings representing the blacklist.
40
+ */
41
+ const blacklist = ['password', 'token', 'accounts', 'rawBody'].map(s => s.toLowerCase())
42
+
43
+ /* Used to have a clean log */
44
+ const defaultSuppress = ['rawBody'].map(s => s.toLowerCase())
45
+
46
+ /**
47
+ * Configuration options for the logger.
48
+ * @typedef {Object} LoggerConfig
49
+ * @property {boolean | Array<string>} [sensitiveFilteringKeywords] - Specifies whether to filter sensitive keywords in log messages. Can be a boolean value or an array of strings.
50
+ * @property {LOG_LEVELS | string} [logLevel] - The log level to use for logging. Can be one of the predefined log levels or a custom string value.
51
+ */
52
+ export type LoggerConfig = {
53
+ sensitiveFilteringKeywords?: boolean | Array<string>
54
+ logLevel?: LOG_LEVELS | string
55
+ }
56
+
57
+ type SupressableItem = {
58
+ value: any
59
+ parent: any
60
+ key: string | number
61
+ }
62
+
63
+ /**
64
+ * Logger class for logging messages with different log levels.
65
+ */
66
+ export default class Logger {
67
+ /**
68
+ * The optional configuration object for the logger.
69
+ */
70
+ private config?: LoggerConfig
71
+ /**
72
+ * Private property representing the transaction ID.
73
+ * @type {string}
74
+ * @private
75
+ */
76
+ private transactionID: string
77
+ /**
78
+ * An array of strings representing a blacklist of filters
79
+ */
80
+ private filterBlacklist: string[]
81
+ /**
82
+ * The current log level for the application.
83
+ * @private
84
+ * @type {LOG_LEVELS}
85
+ */
86
+ private _LOG_LEVEL: LOG_LEVELS
87
+ /**
88
+ * The origin of the object.
89
+ * @private
90
+ * @type {any}
91
+ */
92
+ private origin: any
93
+
94
+ /**
95
+ * Constructs a Logger object with the given configuration and transaction ID.
96
+ * @param {config} config - The configuration object for the logger. Can be undefined.
97
+ * @param {string} transactionID - The ID of the transaction associated with the logger.
98
+ * @returns None
99
+ */
100
+ constructor(config: LoggerConfig | undefined, transactionID: string) {
101
+ abind(this)
102
+ //
103
+ this.origin = PURE_CONSOLE
104
+ this._LOG_LEVEL = config?.logLevel
105
+ ? LOG_LEVELS[config?.logLevel] || LOG_LEVELS.DEBUG
106
+ : LOG_LEVELS.DEBUG
107
+ this.config = config || {}
108
+ this.transactionID = transactionID
109
+ this.filterBlacklist = this.config.sensitiveFilteringKeywords
110
+ ? Array.isArray(this.config.sensitiveFilteringKeywords)
111
+ ? this.config.sensitiveFilteringKeywords
112
+ : blacklist
113
+ : defaultSuppress
114
+ //
115
+ this.setupBindings()
116
+ //
117
+ this.log('Using logger with level: ' + this._LOG_LEVEL.toString())
118
+ this.debug('logger config: ', this.config)
119
+ }
120
+
121
+ /**
122
+ * Returns a boolean value indicating whether the notGlobalLogger function is executed successfully.
123
+ * @returns {boolean} - true if the function is executed successfully, false otherwise.
124
+ */
125
+ public notGlobalLogger() {
126
+ return true
127
+ }
128
+
129
+ /**
130
+ * Logs the given arguments with the debug log level.
131
+ * @param {...any} args - The arguments to be logged.
132
+ * @returns None
133
+ */
134
+ public debug(...args) {
135
+ this.processLog(LOG_LEVELS.DEBUG, args)
136
+ }
137
+
138
+ /**
139
+ * Logs the given arguments with the INFO log level.
140
+ * @param {...any} args - The arguments to be logged.
141
+ * @returns None
142
+ */
143
+ public log(...args) {
144
+ this.processLog(LOG_LEVELS.INFO, args)
145
+ }
146
+
147
+ /**
148
+ * Logs an informational message.
149
+ * @param {...any} args - The message(s) to log.
150
+ * @returns None
151
+ */
152
+ public info(...args) {
153
+ this.processLog(LOG_LEVELS.INFO, args)
154
+ }
155
+
156
+ /**
157
+ * Logs a warning message with the provided arguments.
158
+ * @param {...any} args - The arguments to be logged as a warning message.
159
+ * @returns None
160
+ */
161
+ public warning(...args) {
162
+ this.processLog(LOG_LEVELS.WARN, args)
163
+ }
164
+
165
+ /**
166
+ * Logs a warning message to the console.
167
+ * @param {...any} args - The arguments to be logged.
168
+ * @returns None
169
+ */
170
+ public warn(...args) {
171
+ this.processLog(LOG_LEVELS.WARN, args)
172
+ }
173
+
174
+ /**
175
+ * Logs an error message with the given arguments.
176
+ * @param {...any} args - The arguments to log as an error message.
177
+ * @returns None
178
+ */
179
+ public error(...args) {
180
+ this.processLog(LOG_LEVELS.ERROR, args)
181
+ }
182
+
183
+ /**
184
+ * Logs an exception with optional additional arguments.
185
+ * @param {any} exception - The exception to log.
186
+ * @param {...any} args - Additional arguments to include in the log.
187
+ * @returns None
188
+ */
189
+ public exception(exception, ...args) {
190
+ this.iexception(exception, args)
191
+ }
192
+
193
+ /**
194
+ * Sets up the console bindings for logging purposes.
195
+ * @private
196
+ * @returns None
197
+ */
198
+ private setupBindings(): void {
199
+ global.console = {
200
+ debug: (...args) => this.processLog(LOG_LEVELS.DEBUG, args),
201
+ log: (...args) => this.processLog(LOG_LEVELS.INFO, args),
202
+ info: (...args) => this.processLog(LOG_LEVELS.INFO, args),
203
+ warn: (...args) => this.processLog(LOG_LEVELS.WARN, args),
204
+ error: (...args) => this.processLog(LOG_LEVELS.ERROR, args),
205
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
206
+ // @ts-ignore
207
+ warning: (...args) => this.processLog(LOG_LEVELS.WARN, args),
208
+ exception: (exception, ...args) => this.iexception(exception, args),
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Formats a log message with the specified log level, message, and caller.
214
+ * @param {LOG_LEVELS} level - The log level of the message.
215
+ * @param {Array<string>} msg - An array of strings representing the message.
216
+ * @param {string} caller - The name of the caller function.
217
+ * @returns {string} - The formatted log message.
218
+ */
219
+ private formattedLog(level: LOG_LEVELS, msg: Array<string>, caller: string): string {
220
+ if (Utils.isHybridlessContainer() && this.transactionID) {
221
+ return `${this.transactionID}` + ` [${level.toString()}] [${caller}] ${msg.join(' ')}`
222
+ } else {
223
+ return `[${level.toString()}] [${caller}] ${msg.join(' ')}`
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Retrieves the name of the caller function at the specified index in the stack trace.
229
+ * @param {number} index - The index of the caller function in the stack trace.
230
+ * @returns {string} The name of the caller function along with the file path and line number.
231
+ */
232
+ private callerName(index: number): string {
233
+ const safeIndex = Math.min(index, stackTrace.get().length)
234
+ if (stackTrace.get()[safeIndex]) {
235
+ let callerName = stackTrace?.get()?.[safeIndex]?.getFileName()?.split('/')
236
+ callerName = callerName?.slice(callerName?.indexOf('src'))?.join('/')
237
+ return callerName + ':' + stackTrace?.get()?.[safeIndex]?.getLineNumber()
238
+ }
239
+ return ''
240
+ }
241
+
242
+ /**
243
+ * Processes log messages based on the specified log level.
244
+ * @param {LOG_LEVELS} level - The level of the log message.
245
+ * @param {any} args - The arguments to be logged.
246
+ * @returns None
247
+ */
248
+ private processLog(level: LOG_LEVELS, args: any): void {
249
+ if (level < this._LOG_LEVEL) return
250
+ //get args
251
+ const msg: string[] = []
252
+ for (const arg of args) {
253
+ // Deep clone object so we dont modify source
254
+ const fMsg = this.formatArgument(arg)
255
+ msg.push(fMsg)
256
+ }
257
+ //push into logs stack
258
+ // todo: improve error stack
259
+ this.pushLog(level, this.formattedLog(level, msg, this.callerName(3)))
260
+ }
261
+
262
+ private formatArgument(arg: any): string {
263
+ if (arg instanceof Error) {
264
+ return arg.message + '\n' + arg.stack
265
+ }
266
+
267
+ if (arg && typeof arg === 'object') {
268
+ return stringify(this.suppressSensitiveInfo(JSON.parse(stringify(arg))), null, 2)
269
+ }
270
+
271
+ return `${this.suppressSensitiveInfo(arg)}`
272
+ }
273
+
274
+ /**
275
+ * Logs an exception along with additional arguments and the stack trace.
276
+ * @param {Error} exception - The exception object to log.
277
+ * @param {...any} args - Additional arguments to include in the log.
278
+ * @returns None
279
+ */
280
+ private iexception(exception: Error, ...args): void {
281
+ //format message
282
+ const msg: Array<string> = []
283
+ //push exeception
284
+ msg.push(exception.toString() + ' -')
285
+ //get args
286
+ for (const arg of args) if (arg != exception) msg.push(arg)
287
+ if (exception.stack) msg.push(exception.stack) //push Exeception stack at the end
288
+ //push into logs stack
289
+ this.pushLog(LOG_LEVELS.ERROR, this.formattedLog(LOG_LEVELS.ERROR, msg, this.callerName(3)))
290
+ }
291
+
292
+ /**
293
+ * Pushes a log message to the console with the specified log level.
294
+ * @param {LOG_LEVELS} level - The log level of the message.
295
+ * @param {string} fMsg - The formatted log message.
296
+ * @returns None
297
+ */
298
+ private pushLog(level: LOG_LEVELS, fMsg: string): void {
299
+ DEFAULT_LOG_FUNCTION.apply(PURE_CONSOLE, [fMsg])
300
+ }
301
+
302
+ /**
303
+ * Suppresses sensitive information in the given value based on the filter blacklist.
304
+ * @param {any} value - The value to suppress sensitive information from.
305
+ * @returns {string} - The value with sensitive information suppressed.
306
+ */
307
+ private suppressSensitiveInfo(value: any): string | any[] {
308
+ if (!value || !this.filterBlacklist.length) return value
309
+
310
+ const parent = [value]
311
+ const stack: SupressableItem[] = [{ value, parent, key: 0 }]
312
+
313
+ while (stack.length > 0) {
314
+ this.suppressSensitiveInfoItem(stack.pop()!, stack.push.bind(stack))
315
+ }
316
+
317
+ return parent[0]
318
+ }
319
+
320
+ private suppressSensitiveInfoItem(
321
+ { value, parent, key }: SupressableItem,
322
+ push: (e: SupressableItem) => void
323
+ ) {
324
+ if (!value) return
325
+
326
+ if (typeof value === 'string') {
327
+ this.suppressSensitiveString({ value, parent, key }, push)
328
+ } else if (Array.isArray(value)) {
329
+ value.forEach((v, index) => push({ value: v, parent: value, key: index }))
330
+ } else if (typeof value === 'object') {
331
+ this.suppressSensitiveObject(value, push)
332
+ }
333
+ }
334
+
335
+ private suppressSensitiveString(
336
+ { value, parent, key }: SupressableItem,
337
+ push: (e: SupressableItem) => void
338
+ ) {
339
+ let modifiedValue = value
340
+ try {
341
+ // Try to parse json string
342
+ modifiedValue = JSON.parse(value)
343
+ push({ value: modifiedValue, parent, key })
344
+ } catch {
345
+ const lower = value.toLowerCase()
346
+ if (this.filterBlacklist.some(f => lower == f))
347
+ modifiedValue = `**SUPPRESSED_SENSITIVE_DATA** (${String(modifiedValue)?.length || 0} len)`
348
+ }
349
+ parent[key] = modifiedValue
350
+ }
351
+
352
+ private suppressSensitiveObject(value: object, push: (e: SupressableItem) => void) {
353
+ Object.entries(value).forEach(([k, v]) => {
354
+ const lower = k.toLowerCase()
355
+ const match = v && this.filterBlacklist.some(f => lower == f)
356
+ if (match) {
357
+ value[k] = `**SUPPRESSED_SENSITIVE_DATA** (${String(v)?.length || 0} len)`
358
+ } else {
359
+ push({ value: v, parent: value, key: k })
360
+ }
361
+ })
362
+ }
363
+ }
@@ -0,0 +1,217 @@
1
+ import * as SES from '@aws-sdk/client-ses'
2
+ import { defaultProvider } from '@aws-sdk/credential-provider-node'
3
+ import Email from 'email-templates'
4
+ import nodemailer from 'nodemailer'
5
+ import type SESTransport from 'nodemailer/lib/ses-transport'
6
+
7
+ export default class Mailer {
8
+ /**
9
+ * The starting point of a range.
10
+ * @type {string}
11
+ */
12
+ private readonly from: string
13
+ /**
14
+ * The default file template for the application.
15
+ * @type {string}
16
+ */
17
+ private readonly templateDefaultFile: string = 'html'
18
+ /**
19
+ * The transporter object used for sending emails using the AWS SES service.
20
+ * @type {ReturnType<typeof nodemailer.createTransport<SESTransport.SentMessageInfo>>}
21
+ */
22
+ private readonly transporter: ReturnType<
23
+ typeof nodemailer.createTransport<SESTransport.SentMessageInfo>
24
+ >
25
+
26
+ /**
27
+ * Constructs a new instance of the EmailSender class.
28
+ * @param {string} defaultFrom - The default "from" email address.
29
+ * @param {string} region - The AWS region to use for sending emails.
30
+ * @returns None
31
+ */
32
+ constructor(defaultFrom: string, region: string) {
33
+ this.from = defaultFrom
34
+ this.transporter = nodemailer.createTransport({
35
+ SES: {
36
+ ses: new SES.SESClient({
37
+ credentials: defaultProvider(),
38
+ apiVersion: '2010-12-01',
39
+ region,
40
+ }),
41
+ aws: SES,
42
+ },
43
+ })
44
+ }
45
+
46
+ /**
47
+ * Sends a raw email with the specified parameters.
48
+ * @param {string | Array<string>} to - The recipient(s) of the email.
49
+ * @param {string} htmlMessage - The HTML content of the email.
50
+ * @param {string} subject - The subject of the email.
51
+ * @param {string | Array<string>} [optionalCC] - The optional CC recipient(s) of the email.
52
+ * @param {string} [optionalFrom] - The optional sender of the email. If not provided, the default sender will be used.
53
+ * @param {string} [optionalReplyTo] - The optional reply-to address for the email.
54
+ * @param {any[]} [optionalAttachments] - The optional attachments to include
55
+ */
56
+ public async sendRawEmail(
57
+ to: string | Array<string>,
58
+ htmlMessage: string,
59
+ subject: string,
60
+ optionalCC?: string | Array<string>,
61
+ optionalFrom?: string,
62
+ optionalReplyTo?: string,
63
+ // TODO: improve attachment type -> Attachment
64
+ optionalAttachments?: any[],
65
+ optionalTransport?: Email.NodeMailerTransportOptions
66
+ ) {
67
+ //Generate emails
68
+ const email = new Email({
69
+ message: {
70
+ from: optionalFrom || this.from,
71
+ to: to,
72
+ html: htmlMessage,
73
+ subject,
74
+ ...(optionalAttachments ? { attachments: optionalAttachments } : {}),
75
+ ...(optionalCC ? { cc: optionalCC } : {}),
76
+ ...(optionalReplyTo ? { replyTo: optionalReplyTo } : {}),
77
+ },
78
+ transport: optionalTransport || this.transporter,
79
+ send: true,
80
+ })
81
+ //
82
+ let resp = null
83
+ try {
84
+ resp = await email.send()
85
+ console.debug('Mailer resp:', resp)
86
+ } catch (e) {
87
+ console.error('Mailer error:', e)
88
+ throw e
89
+ }
90
+ return resp
91
+ }
92
+
93
+ /**
94
+ * Sends a templated email to the specified recipients.
95
+ * @param {string | Array<string>} to - The email address(es) of the recipient(s).
96
+ * @param {string | Array<string>} templates - The template(s) to use for the email.
97
+ * @param {object} data - The data to be used in the email template.
98
+ * @param {string | Array<string>} [optionalCC] - The email address(es) to CC.
99
+ * @param {string} [optionalFrom] - The email address to send the email from.
100
+ * @param {string} [optionalReplyTo] - The email address to set as the reply-to address.
101
+ * @param {any[]} [optionalAttachments] - An array
102
+ */
103
+ public async sendTemplatedEmail(
104
+ to: string | Array<string>,
105
+ templates: string | Array<string>,
106
+ data: object,
107
+ optionalCC?: string | Array<string>,
108
+ optionalFrom?: string,
109
+ optionalReplyTo?: string,
110
+ // TODO: improve attachment type -> Attachment
111
+ optionalAttachments?: any[],
112
+ optionalTransport?: Email.NodeMailerTransportOptions
113
+ ) {
114
+ //Generate emails
115
+ const email = new Email({
116
+ message: {
117
+ from: optionalFrom || this.from,
118
+ to: to,
119
+ ...(optionalAttachments ? { attachments: optionalAttachments } : {}),
120
+ ...(optionalCC ? { cc: optionalCC } : {}),
121
+ ...(optionalReplyTo ? { replyTo: optionalReplyTo } : {}),
122
+ },
123
+ transport: optionalTransport || this.transporter,
124
+ send: true,
125
+ })
126
+ //
127
+ let resp = null
128
+ try {
129
+ const chosenTemplate = await this.chooseTemplate(templates, data)
130
+ resp = await email.send({ template: chosenTemplate, locals: data })
131
+ console.debug('Mailer resp:', resp)
132
+ } catch (e) {
133
+ console.error('Mailer error:', e)
134
+ throw e
135
+ }
136
+ return resp
137
+ }
138
+
139
+ /**
140
+ * Creates a new SMTP transporter for sending emails using NodeMailer.
141
+ * @param {string} host - The SMTP server host.
142
+ * @param {number} portNumber - The port number to connect to the SMTP server.
143
+ * @param {string} user - The username for authentication with the SMTP server.
144
+ * @param {string} password - The password for authentication with the SMTP server.
145
+ * @returns {Email.NodeMailerTransportOptions} - The SMTP transporter object.
146
+ */
147
+ public newSMTPTransporter(
148
+ host: string,
149
+ portNumber: number,
150
+ user: string,
151
+ password: string
152
+ ): Email.NodeMailerTransportOptions {
153
+ const smtpTransporter = nodemailer.createTransport({
154
+ host: host,
155
+ port: portNumber,
156
+ connectionTimeout: 2000,
157
+ auth: {
158
+ user: user,
159
+ pass: password,
160
+ },
161
+ })
162
+ return smtpTransporter
163
+ }
164
+
165
+ /**
166
+ * Chooses a template from the given array of templates or a single template string based on whether it can be rendered with the provided data.
167
+ * @param {string | Array<string>} templates - The template(s) to choose from.
168
+ * @param {object} data - The data to be used for rendering the template.
169
+ * @returns {Promise<string>} - The chosen template.
170
+ * @throws {Error} - If no template can be rendered with the provided data.
171
+ */
172
+ private async chooseTemplate(templates: string | Array<string>, data: object): Promise<string> {
173
+ if (Array.isArray(templates)) {
174
+ //For each template check if can render it
175
+ for (const template of templates) {
176
+ if (await this.canRenderTemplate(template, data)) return template
177
+ }
178
+ } else if (templates) {
179
+ if (await this.canRenderTemplate(templates, data)) return templates
180
+ }
181
+ throw new Error(
182
+ `Could not render email with template ${templates} and following data. Please, check logs above! ` +
183
+ JSON.stringify(data, null, 2)
184
+ )
185
+ }
186
+
187
+ /**
188
+ * Checks if a given email template can be rendered with the provided data.
189
+ * @param {string} template - The name of the email template.
190
+ * @param {object} data - The data to be used for rendering the template.
191
+ * @returns {Promise<boolean>} - A promise that resolves to true if the template can be rendered, false otherwise.
192
+ */
193
+ private async canRenderTemplate(template: string, data: object): Promise<boolean> {
194
+ const validRenderResp = await Mailer.renderTemplate(
195
+ `${template}/${this.templateDefaultFile}`,
196
+ data
197
+ )
198
+ return validRenderResp != null
199
+ }
200
+
201
+ /**
202
+ * Renders the given template with the provided data using the Email class.
203
+ * @param {string} template - The name or path of the template to render.
204
+ * @param {object} data - The data object to pass to the template.
205
+ * @returns {Promise<boolean>} - A promise that resolves to true if the template was rendered successfully, false otherwise.
206
+ */
207
+ public static async renderTemplate(template: string, data: object): Promise<any> {
208
+ let validRenderResp: string | null = null
209
+ try {
210
+ const email = new Email()
211
+ validRenderResp = await email.render(`${template}`, data)
212
+ } catch (e) {
213
+ console.log(`Error while checking renderability of email template ${template}`, e)
214
+ }
215
+ return validRenderResp
216
+ }
217
+ }
@@ -0,0 +1,96 @@
1
+ import {
2
+ SNSClient,
3
+ PublishCommand,
4
+ PublishCommandOutput,
5
+ PublishCommandInput,
6
+ } from '@aws-sdk/client-sns'
7
+ import sha1 from 'sha1'
8
+ //reusable client
9
+ /**
10
+ * A variable that holds the connection to the SNS client for publishing messages.
11
+ * @type {SNSClient | null}
12
+ */
13
+ // eslint-disable-next-line no-var
14
+ let PUBLISHER_CONN: SNSClient | null = null
15
+ /**
16
+ * The connection hash for the publisher. It is initially set to null.
17
+ * @type {string | null}
18
+ */
19
+ // eslint-disable-next-line no-var
20
+ let PUBLISHER_CONN_HASH: string | null = null
21
+
22
+ /**
23
+ * Represents the configuration options for a publisher.
24
+ * @typedef {Object} PublisherConfig
25
+ * @property {string} [region] - The region where the publisher is located.
26
+ */
27
+ export type PublisherConfig = {
28
+ region?: string
29
+ }
30
+
31
+ /**
32
+ * Represents a publisher that can publish messages to an SNS topic.
33
+ */
34
+ export default class Publisher {
35
+ /**
36
+ * The region of the object or entity.
37
+ * @private
38
+ * @type {string}
39
+ */
40
+ private region: string
41
+
42
+ /**
43
+ * Constructs a new instance of the Publisher class.
44
+ * @param {PublisherConfig} [config] - The configuration object for the Publisher.
45
+ * @returns None
46
+ */
47
+ constructor(config?: PublisherConfig) {
48
+ if (config && config.region) {
49
+ this.region = config.region
50
+ // console.debug(`Using region: ${this.region}`);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Publishes a message on a specified topic.
56
+ * @param {any} messageObject - The message object to be published.
57
+ * @param {string} topic - The ARN of the topic to publish the message to.
58
+ * @param {object} [additionalProps] - Additional properties to include in the publish command.
59
+ * @returns {Promise<PublishCommandOutput>} - A promise that resolves to the response from the publish command.
60
+ */
61
+ public async publishOnTopic(
62
+ messageObject: any,
63
+ topic: string,
64
+ additionalProps?: object
65
+ ): Promise<PublishCommandOutput | undefined> {
66
+ let resp: undefined | PublishCommandOutput = undefined
67
+ try {
68
+ this.connect()
69
+ //Send to SNS
70
+ const params: PublishCommandInput = {
71
+ Message: JSON.stringify(messageObject),
72
+ TopicArn: topic,
73
+ ...(additionalProps ? additionalProps : {}),
74
+ }
75
+ resp = await PUBLISHER_CONN!.send(new PublishCommand(params))
76
+ } catch (e) {
77
+ console.error(`Error while publishing into topic ${topic} with error: ${e}`)
78
+ }
79
+ console.debug('Publisher resp', resp)
80
+ return resp
81
+ }
82
+
83
+ /**
84
+ * Establishes a connection to the SNS client if it does not already exist or if the region has changed.
85
+ * @returns None
86
+ */
87
+ private connect(): void {
88
+ if ((!PUBLISHER_CONN && !PUBLISHER_CONN_HASH) || PUBLISHER_CONN_HASH != sha1(this.region)) {
89
+ PUBLISHER_CONN = new SNSClient({
90
+ apiVersion: '2010-03-31',
91
+ region: this.region,
92
+ })
93
+ PUBLISHER_CONN_HASH = sha1(this.region).toString()
94
+ }
95
+ }
96
+ }