@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,531 @@
1
+ import { jest } from '@jest/globals'
2
+ import { expect as c_expect } from 'chai'
3
+ import request from 'supertest'
4
+ import { z } from 'zod'
5
+
6
+ import { HttpMethod } from '../../../src/API/Request.js'
7
+ import Response from '../../../src/API/Response.js'
8
+ import Globals from '../../../src/Globals.js'
9
+ import ContainerServer from '../../../src/Server/lib/ContainerServer.js'
10
+ import { defaultUrl } from '../../Test.utils.js'
11
+
12
+ export const ViewSchema = z.object({
13
+ id: z.string(),
14
+ content: z.string(),
15
+ createdAt: z.string(),
16
+ updatedAt: z.string(),
17
+ })
18
+
19
+ export const QuerySchema = z.object({
20
+ id: z.string(),
21
+ order: z.string().refine(
22
+ v => {
23
+ const n = Number(v)
24
+ return !isNaN(n) && v?.length > 0
25
+ },
26
+ { message: 'Invalid number' }
27
+ ),
28
+ })
29
+
30
+ describe('Container server routing', () => {
31
+ // @ts-ignore
32
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
33
+ beforeAll(() => {
34
+ // @ts-ignore
35
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
36
+ })
37
+ afterAll(() => {
38
+ mockExit.mockRestore()
39
+ })
40
+ beforeEach(() => {
41
+ mockExit.mockReset()
42
+ })
43
+
44
+ test('Health & 404 handler', async () => {
45
+ const server = new ContainerServer({ routes: [] })
46
+ await server.start()
47
+ // Health check
48
+ const res = await request(defaultUrl)
49
+ .get(Globals.Listener_HTTP_DefaultHealthCheckRoute)
50
+ .expect('Content-Type', 'text/html; charset=utf-8')
51
+ .expect(200)
52
+ c_expect(res.text).to.be.equals('Healthy!')
53
+ // Not handled
54
+ await request(defaultUrl)
55
+ .get(`/abc`)
56
+ .expect('Content-Type', 'application/json; charset=utf-8')
57
+ .expect(404)
58
+
59
+ // Unload
60
+ await server.stop('Error')
61
+ expect(mockExit).toHaveBeenCalledTimes(1)
62
+ expect(mockExit).toBeCalledWith(1)
63
+ })
64
+
65
+ test('Simple route', async () => {
66
+ const server = new ContainerServer({
67
+ routes: [
68
+ {
69
+ path: '/abc',
70
+ method: HttpMethod.GET,
71
+ handler: async () => {
72
+ return Response.SimpleResponse({ name: 'abc' })
73
+ },
74
+ } as any,
75
+ ],
76
+ })
77
+ await server.start()
78
+ // Health check
79
+ const res = await request(defaultUrl)
80
+ .get(Globals.Listener_HTTP_DefaultHealthCheckRoute)
81
+ .expect('Content-Type', 'text/html; charset=utf-8')
82
+ .expect(200)
83
+ c_expect(res.text).to.be.equals('Healthy!')
84
+ // Found route
85
+ const resG = await request(defaultUrl)
86
+ .get(`/abc`)
87
+ .expect('Content-Type', 'application/json; charset=utf-8')
88
+ .expect(200)
89
+ c_expect(resG.body['name']).to.be.deep.equals('abc')
90
+ c_expect(resG.body['transactionID']).to.not.be.null
91
+ // Found route, but not method
92
+ await request(defaultUrl)
93
+ .post(`/abc`)
94
+ .expect('Content-Type', 'application/json; charset=utf-8')
95
+ .expect(404)
96
+ // Not found route
97
+ await request(defaultUrl)
98
+ .get(`/abc/123`)
99
+ .expect('Content-Type', 'application/json; charset=utf-8')
100
+ .expect(404)
101
+ // Unload
102
+ await server.stop('Error')
103
+ expect(mockExit).toHaveBeenCalledTimes(1)
104
+ expect(mockExit).toBeCalledWith(1)
105
+ })
106
+ })
107
+
108
+ describe('Container server basics', () => {
109
+ // @ts-ignore
110
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
111
+ beforeAll(() => {
112
+ // @ts-ignore
113
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
114
+ })
115
+ afterAll(() => {
116
+ mockExit.mockRestore()
117
+ })
118
+ beforeEach(() => {
119
+ mockExit.mockReset()
120
+ })
121
+
122
+ test('Starts with exports', async () => {
123
+ const server = new ContainerServer({ routes: [] })
124
+ server.getExport()
125
+ return new Promise(resolve => {
126
+ setTimeout(async () => {
127
+ // Health check
128
+ const res = await request(defaultUrl)
129
+ .get(Globals.Listener_HTTP_DefaultHealthCheckRoute)
130
+ .expect('Content-Type', 'text/html; charset=utf-8')
131
+ .expect(200)
132
+ c_expect(res.text).to.be.equals('Healthy!')
133
+ // Unload
134
+ await server.stop()
135
+ expect(mockExit).toHaveBeenCalledTimes(1)
136
+ expect(mockExit).toBeCalledWith(0)
137
+ resolve(null)
138
+ }, 2000)
139
+ })
140
+ })
141
+
142
+ test('Stops if process sends message SIGINT', async () => {
143
+ const server = new ContainerServer({ routes: [] })
144
+ await server.start()
145
+ process.emit('SIGINT')
146
+ return new Promise(resolve => {
147
+ setTimeout(async () => {
148
+ expect(mockExit).toHaveBeenCalledTimes(1)
149
+ expect(mockExit).toBeCalledWith(0)
150
+ resolve(null)
151
+ }, 2000)
152
+ })
153
+ }, 10000)
154
+
155
+ test('Stops if process sends message unhandledRejection', async () => {
156
+ const server = new ContainerServer({ routes: [] })
157
+ await server.start()
158
+ // @ts-ignore
159
+ process.emit('unhandledRejection')
160
+ return new Promise(resolve => {
161
+ setTimeout(async () => {
162
+ expect(mockExit).toHaveBeenCalledTimes(1)
163
+ expect(mockExit).toBeCalledWith(0)
164
+ resolve(null)
165
+ }, 2000)
166
+ })
167
+ }, 10000)
168
+ })
169
+
170
+ describe('Container server validation (body)', () => {
171
+ // @ts-ignore
172
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
173
+ beforeAll(() => {
174
+ // @ts-ignore
175
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
176
+ })
177
+ afterAll(() => {
178
+ mockExit.mockRestore()
179
+ })
180
+ beforeEach(() => {
181
+ mockExit.mockReset()
182
+ })
183
+
184
+ function validateValidationFailure(res: any, failureCount?: number) {
185
+ c_expect(res.body['err']).to.be.equals(Globals.ErrorResponseValidationFail)
186
+ c_expect(res.body['errCode']).to.be.equals(Globals.ErrorCode_InvalidInput)
187
+ c_expect(res.body['transactionID']).to.not.be.null
188
+ c_expect(res.body['transactionID']).to.be.an('string')
189
+ c_expect(res.body['validationFailure']?.length).to.be.equals(failureCount || 4)
190
+ }
191
+
192
+ test('Validates empty body', async () => {
193
+ const server = new ContainerServer({
194
+ routes: [
195
+ {
196
+ path: '/abc',
197
+ method: HttpMethod.POST,
198
+ inputSchema: ViewSchema,
199
+ handler: async () => {
200
+ return Response.SimpleResponse({ name: 'abc' })
201
+ },
202
+ } as any,
203
+ ],
204
+ })
205
+ await server.start()
206
+ // Validation fails, empty body
207
+ const resG = await request(defaultUrl)
208
+ .post(`/abc`)
209
+ .expect('Content-Type', 'application/json; charset=utf-8')
210
+ .expect(400)
211
+ validateValidationFailure(resG)
212
+ await server.stop()
213
+ })
214
+
215
+ test('Validates empty string body', async () => {
216
+ const server = new ContainerServer({
217
+ routes: [
218
+ {
219
+ path: '/abc',
220
+ method: HttpMethod.POST,
221
+ inputSchema: ViewSchema,
222
+ handler: async () => {
223
+ return Response.SimpleResponse({ name: 'abc' })
224
+ },
225
+ } as any,
226
+ ],
227
+ })
228
+ await server.start()
229
+ // Validation fails, empty body
230
+ const resG = await request(defaultUrl)
231
+ .post(`/abc`)
232
+ .send('')
233
+ .expect('Content-Type', 'application/json; charset=utf-8')
234
+ .expect(400)
235
+ validateValidationFailure(resG)
236
+ await server.stop()
237
+ })
238
+
239
+ test('Validates empty object body', async () => {
240
+ const server = new ContainerServer({
241
+ routes: [
242
+ {
243
+ path: '/abc',
244
+ method: HttpMethod.POST,
245
+ inputSchema: ViewSchema,
246
+ handler: async () => {
247
+ return Response.SimpleResponse({ name: 'abc' })
248
+ },
249
+ } as any,
250
+ ],
251
+ })
252
+ await server.start()
253
+ // Validation fails, empty body
254
+ const resG = await request(defaultUrl)
255
+ .post(`/abc`)
256
+ .send({})
257
+ .expect('Content-Type', 'application/json; charset=utf-8')
258
+ .expect(400)
259
+ validateValidationFailure(resG)
260
+ await server.stop()
261
+ })
262
+
263
+ test('Validates missing props body', async () => {
264
+ const server = new ContainerServer({
265
+ routes: [
266
+ {
267
+ path: '/abc',
268
+ method: HttpMethod.POST,
269
+ inputSchema: ViewSchema,
270
+ handler: async () => {
271
+ return Response.SimpleResponse({ name: 'abc' })
272
+ },
273
+ } as any,
274
+ ],
275
+ })
276
+ await server.start()
277
+ // Validation fails, empty body
278
+ const resG = await request(defaultUrl)
279
+ .post(`/abc`)
280
+ .send({ id: '123' })
281
+ .expect('Content-Type', 'application/json; charset=utf-8')
282
+ .expect(400)
283
+ validateValidationFailure(resG, 3)
284
+ await server.stop()
285
+ })
286
+
287
+ test('Validates wrong type props body', async () => {
288
+ const server = new ContainerServer({
289
+ routes: [
290
+ {
291
+ path: '/abc',
292
+ method: HttpMethod.POST,
293
+ inputSchema: ViewSchema,
294
+ handler: async () => {
295
+ return Response.SimpleResponse({ name: 'abc' })
296
+ },
297
+ } as any,
298
+ ],
299
+ })
300
+ await server.start()
301
+ // Validation fails, empty body
302
+ const resG = await request(defaultUrl)
303
+ .post(`/abc`)
304
+ .send({ id: 123 })
305
+ .expect('Content-Type', 'application/json; charset=utf-8')
306
+ .expect(400)
307
+ validateValidationFailure(resG)
308
+ await server.stop()
309
+ })
310
+
311
+ test('Validates successfully', async () => {
312
+ const server = new ContainerServer({
313
+ routes: [
314
+ {
315
+ path: '/abc',
316
+ method: HttpMethod.POST,
317
+ inputSchema: ViewSchema,
318
+ handler: async () => {
319
+ return Response.SimpleResponse({ name: 'abc' })
320
+ },
321
+ } as any,
322
+ ],
323
+ })
324
+ await server.start()
325
+ // Validation fails, empty body
326
+ await request(defaultUrl)
327
+ .post(`/abc`)
328
+ .send({
329
+ id: '123',
330
+ content: '123',
331
+ createdAt: new Date(),
332
+ updatedAt: new Date(),
333
+ })
334
+ .expect('Content-Type', 'application/json; charset=utf-8')
335
+ .expect(200)
336
+ await server.stop()
337
+ })
338
+ })
339
+
340
+ describe('Container server validation (query)', () => {
341
+ let server: ContainerServer
342
+ // @ts-ignore
343
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
344
+ beforeAll(() => {
345
+ // @ts-ignore
346
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
347
+ })
348
+ afterAll(() => {
349
+ mockExit.mockRestore()
350
+ })
351
+ beforeEach(async () => {
352
+ mockExit.mockReset()
353
+ server = new ContainerServer({
354
+ routes: [
355
+ {
356
+ path: '/abc',
357
+ method: HttpMethod.POST,
358
+ querySchema: QuerySchema,
359
+ handler: async () => {
360
+ return Response.SimpleResponse({ name: 'abc' })
361
+ },
362
+ } as any,
363
+ ],
364
+ })
365
+ await server.start()
366
+ })
367
+
368
+ afterEach(async () => {
369
+ await server.stop()
370
+ })
371
+
372
+ function validateValidationFailure(res: any, failureCount?: number) {
373
+ c_expect(res.body['err']).to.be.equals(Globals.ErrorResponseValidationFail)
374
+ c_expect(res.body['errCode']).to.be.equals(Globals.ErrorCode_InvalidInput)
375
+ c_expect(res.body['transactionID']).to.not.be.null
376
+ c_expect(res.body['transactionID']).to.be.an('string')
377
+ c_expect(res.body['validationFailure']?.length).to.be.equals(failureCount || 2)
378
+ }
379
+
380
+ test('Validates empty query', async () => {
381
+ // Validation fails, empty body
382
+ const resG = await request(defaultUrl)
383
+ .post(`/abc`)
384
+ .expect('Content-Type', 'application/json; charset=utf-8')
385
+ .expect(400)
386
+ validateValidationFailure(resG, 2)
387
+ })
388
+
389
+ test('Validates empty query differently', async () => {
390
+ // Validation fails, empty body
391
+ const resG = await request(defaultUrl)
392
+ .post(`/abc?`)
393
+ .expect('Content-Type', 'application/json; charset=utf-8')
394
+ .expect(400)
395
+ validateValidationFailure(resG, 2)
396
+ })
397
+
398
+ test('Validates missing props body', async () => {
399
+ // Validation fails, empty body
400
+ const resG = await request(defaultUrl)
401
+ .post(`/abc?id=myname`)
402
+ .expect('Content-Type', 'application/json; charset=utf-8')
403
+ .expect(400)
404
+ validateValidationFailure(resG, 1)
405
+ })
406
+
407
+ test('Validates wrong type props body', async () => {
408
+ // Validation fails, empty body
409
+ const resG = await request(defaultUrl)
410
+ .post(`/abc?id=name&order=myname`)
411
+ .expect('Content-Type', 'application/json; charset=utf-8')
412
+ .expect(400)
413
+ validateValidationFailure(resG, 1)
414
+ })
415
+
416
+ test('Validates successfully2', async () => {
417
+ // Validation fails, empty body
418
+ await request(defaultUrl)
419
+ .post(`/abc`)
420
+ .query({ id: 'name', order: 123 })
421
+ .expect('Content-Type', 'application/json; charset=utf-8')
422
+ .expect(200)
423
+ })
424
+ })
425
+
426
+ describe('Container server validation (path)', () => {
427
+ let server: ContainerServer
428
+ // @ts-ignore
429
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
430
+ beforeAll(() => {
431
+ // @ts-ignore
432
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
433
+ })
434
+ afterAll(() => {
435
+ mockExit.mockRestore()
436
+ })
437
+ beforeEach(async () => {
438
+ mockExit.mockReset()
439
+ server = new ContainerServer({
440
+ routes: [
441
+ {
442
+ path: '/abc/:id/:order',
443
+ method: HttpMethod.POST,
444
+ pathSchema: QuerySchema,
445
+ handler: async () => {
446
+ return Response.SimpleResponse({ name: 'abc' })
447
+ },
448
+ } as any,
449
+ ],
450
+ })
451
+ await server.start()
452
+ })
453
+
454
+ afterEach(async () => {
455
+ await server.stop()
456
+ })
457
+
458
+ function validateValidationFailure(res: any, failureCount?: number) {
459
+ c_expect(res.body['err']).to.be.equals(Globals.ErrorResponseValidationFail)
460
+ c_expect(res.body['errCode']).to.be.equals(Globals.ErrorCode_InvalidInput)
461
+ c_expect(res.body['transactionID']).to.not.be.null
462
+ c_expect(res.body['transactionID']).to.be.an('string')
463
+ c_expect(res.body['validationFailure']?.length).to.be.equals(failureCount || 2)
464
+ }
465
+
466
+ test('Validates wrong type props body', async () => {
467
+ // Validation fails, empty body
468
+ const resG = await request(defaultUrl)
469
+ .post(`/abc/name/myname`)
470
+ .expect('Content-Type', 'application/json; charset=utf-8')
471
+ .expect(400)
472
+ validateValidationFailure(resG, 1)
473
+ })
474
+
475
+ test('Validates successfully4', async () => {
476
+ // Validation fails, empty body
477
+ await request(defaultUrl)
478
+ .post(`/abc/name/123`)
479
+ .expect('Content-Type', 'application/json; charset=utf-8')
480
+ .expect(200)
481
+ })
482
+ })
483
+
484
+ describe('Container server raw body', () => {
485
+ // @ts-ignore
486
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
487
+ beforeAll(() => {
488
+ // @ts-ignore
489
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
490
+ process.env['HYBRIDLESS_RUNTIME'] = 'true'
491
+ })
492
+ afterAll(() => {
493
+ mockExit.mockRestore()
494
+ process.env['HYBRIDLESS_RUNTIME'] = undefined
495
+ })
496
+ beforeEach(() => {
497
+ mockExit.mockReset()
498
+ })
499
+
500
+ test('Raw body is available', async () => {
501
+ let raw = null
502
+ const server = new ContainerServer({
503
+ routes: [
504
+ {
505
+ path: '/abc',
506
+ method: HttpMethod.POST,
507
+ handler: async t => {
508
+ raw = t.request.getBody(true)
509
+ return Response.SimpleResponse(null)
510
+ },
511
+ } as any,
512
+ ],
513
+ })
514
+ await server.start()
515
+
516
+ // it's formatted funky, because we're testing for exact match
517
+ const data = ' { "name" : "abc" }'
518
+ await request(defaultUrl)
519
+ .post(`/abc`)
520
+ .send(data)
521
+ .set('Content-Type', 'application/json')
522
+ .expect(200)
523
+
524
+ const s = new TextDecoder('utf-8').decode(raw!)
525
+ c_expect(s).to.be.equals(data)
526
+
527
+ await server.stop()
528
+ })
529
+ })
530
+
531
+ export {}
@@ -0,0 +1,12 @@
1
+ import { expect } from 'chai'
2
+
3
+ import Server from '../../../src/Server/lib/Server.js'
4
+
5
+ describe('Server basics', () => {
6
+ test('Works', async () => {
7
+ const server = new Server({ routes: [] })
8
+ expect(server.getExport()).to.not.be.undefined
9
+ })
10
+ })
11
+
12
+ export {}
@@ -0,0 +1,131 @@
1
+ import { jest } from '@jest/globals'
2
+ import { APIGatewayProxyEvent, Context } from 'aws-lambda'
3
+ import { expect as c_expect } from 'chai'
4
+ import { Request, Response } from 'express'
5
+
6
+ import Globals from '../../../../src/Globals.js'
7
+ import GenericHandler from '../../../../src/Server/lib/container/GenericHandler.js'
8
+
9
+ function observableResponse(): Response {
10
+ const resp = <Response>(<unknown>{
11
+ send: jest.fn(() => resp),
12
+ json: jest.fn(() => resp),
13
+ status: jest.fn(() => resp),
14
+ header: jest.fn(() => resp),
15
+ })
16
+ return resp
17
+ }
18
+
19
+ describe('GenericHandler success invocation path', () => {
20
+ test('Simple success', async () => {
21
+ const v = {
22
+ statusCode: 200,
23
+ body: JSON.stringify({ id: 123 }),
24
+ }
25
+ const handler = GenericHandler(async (event: APIGatewayProxyEvent, context: Context) => {
26
+ context.succeed(v)
27
+ })
28
+ const resp = observableResponse()
29
+ const handlerResp = await handler(<Request>(<unknown>{}), resp)
30
+ //
31
+ c_expect(handlerResp).to.be.undefined
32
+ expect(resp.send).not.toBeCalled()
33
+ expect(resp.json).toBeCalledWith(JSON.parse(v.body))
34
+ expect(resp.status).toBeCalledWith(v.statusCode)
35
+ expect(resp.header).not.toBeCalled()
36
+ })
37
+
38
+ test('Simple success w/ headers', async () => {
39
+ const v = {
40
+ statusCode: 200,
41
+ body: JSON.stringify({ id: 123 }),
42
+ headers: { Authorization: 123 },
43
+ }
44
+ const handler = GenericHandler(async (event: APIGatewayProxyEvent, context: Context) => {
45
+ context.succeed(v)
46
+ })
47
+ const resp = observableResponse()
48
+ const handlerResp = await handler(<Request>(<unknown>{}), resp)
49
+ //
50
+ c_expect(handlerResp).to.be.undefined
51
+ expect(resp.send).not.toBeCalled()
52
+ expect(resp.json).toBeCalledWith(JSON.parse(v.body))
53
+ expect(resp.status).toBeCalledWith(v.statusCode)
54
+ expect(resp.header).toBeCalledTimes(Object.keys(v.headers).length)
55
+ })
56
+
57
+ test('Simple success w/ null body', async () => {
58
+ const v = {
59
+ statusCode: 200,
60
+ headers: { Authorization: 123 },
61
+ }
62
+ const handler = GenericHandler(async (event: APIGatewayProxyEvent, context: Context) => {
63
+ context.succeed(v)
64
+ })
65
+ const resp = observableResponse()
66
+ const handlerResp = await handler(<Request>(<unknown>{}), resp)
67
+ //
68
+ c_expect(handlerResp).to.be.undefined
69
+ expect(resp.send).not.toBeCalled()
70
+ expect(resp.json).toBeCalledWith({})
71
+ expect(resp.status).toBeCalledWith(v.statusCode)
72
+ expect(resp.header).toBeCalledTimes(Object.keys(v.headers).length)
73
+ })
74
+ })
75
+
76
+ describe('GenericHandler failure invocation path', () => {
77
+ test('Invalid response', async () => {
78
+ const handler = GenericHandler(async (event: APIGatewayProxyEvent, context: Context) => {
79
+ context.succeed(null)
80
+ })
81
+ const resp = observableResponse()
82
+ const handlerResp = await handler(<Request>(<unknown>{}), resp)
83
+ //
84
+ c_expect(handlerResp).to.be.undefined
85
+ expect(resp.send).not.toBeCalled()
86
+ expect(resp.header).not.toBeCalled()
87
+ expect(resp.json).toBeCalledWith({
88
+ err: Globals.Resp_MSG_INVALIDRESP,
89
+ errCode: Globals.Resp_CODE_INVALIDRESP,
90
+ })
91
+ expect(resp.status).toBeCalledWith(Globals.Resp_STATUSCODE_INVALIDRESP)
92
+ })
93
+
94
+ test('Error response', async () => {
95
+ const err = new Error('Failed!')
96
+ const handler = GenericHandler(async (event: APIGatewayProxyEvent, context: Context) => {
97
+ context.fail(err)
98
+ })
99
+ const resp = observableResponse()
100
+ const handlerResp = await handler(<Request>(<unknown>{}), resp)
101
+ //
102
+ c_expect(handlerResp).to.be.undefined
103
+ expect(resp.send).not.toBeCalled()
104
+ expect(resp.header).not.toBeCalled()
105
+ expect(resp.json).toBeCalledWith({
106
+ err,
107
+ })
108
+ expect(resp.status).toBeCalledWith(400)
109
+ })
110
+
111
+ test('Exception response', async () => {
112
+ const err = new Error('Failed!')
113
+ const handler = GenericHandler(async () => {
114
+ throw err
115
+ })
116
+ const resp = observableResponse()
117
+ const handlerResp = await handler(<Request>(<unknown>{}), resp)
118
+ //
119
+ c_expect(handlerResp).to.be.undefined
120
+ expect(resp.send).not.toBeCalled()
121
+ expect(resp.header).not.toBeCalled()
122
+ expect(resp.json).toBeCalledWith({
123
+ ...err,
124
+ err: Globals.Resp_MSG_EXCEPTION,
125
+ errCode: Globals.Resp_CODE_EXCEPTION,
126
+ })
127
+ expect(resp.status).toBeCalledWith(Globals.Resp_STATUSCODE_EXCEPTION)
128
+ })
129
+ })
130
+
131
+ export {}