node-server-engine 1.4.9 → 1.5.1

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 (277) hide show
  1. package/.commitlintrc.js +27 -0
  2. package/.releaserc.js +56 -0
  3. package/README.md +154 -2
  4. package/dist/const/tokenIssuer.js +1 -0
  5. package/dist/const/tokenIssuer.js.map +1 -1
  6. package/dist/db/models/SequelizeMeta.js +3 -1
  7. package/dist/db/models/SequelizeMeta.js.map +1 -1
  8. package/dist/entities/ElasticSearch/ElasticSearch.js +1 -2
  9. package/dist/entities/ElasticSearch/ElasticSearch.js.map +1 -1
  10. package/dist/entities/Sequelize/Sequelize.js +4 -1
  11. package/dist/entities/Sequelize/Sequelize.js.map +1 -1
  12. package/dist/entities/Server/Server.js +8 -11
  13. package/dist/entities/Server/Server.js.map +1 -1
  14. package/dist/entities/SocketClient/SocketClient.d.ts +4 -1
  15. package/dist/entities/SocketClient/SocketClient.js +5 -1
  16. package/dist/entities/SocketClient/SocketClient.js.map +1 -1
  17. package/dist/index.js +1 -2
  18. package/dist/index.js.map +1 -1
  19. package/dist/middleware/checkPermission/checkPermission.d.ts +1 -1
  20. package/dist/middleware/checkPermission/checkPermission.js +4 -2
  21. package/dist/middleware/checkPermission/checkPermission.js.map +1 -1
  22. package/dist/utils/createSortedObject/createSortedObject.d.ts +5 -1
  23. package/dist/utils/createSortedObject/createSortedObject.js +13 -4
  24. package/dist/utils/createSortedObject/createSortedObject.js.map +1 -1
  25. package/dist/utils/dbMigration/dbMigration.js +34 -15
  26. package/dist/utils/dbMigration/dbMigration.js.map +1 -1
  27. package/dist/utils/geminiFileUpload/geminiFileUpload.js +2 -1
  28. package/dist/utils/geminiFileUpload/geminiFileUpload.js.map +1 -1
  29. package/dist/utils/jwt/jwt.js +1 -1
  30. package/dist/utils/jwt/jwt.js.map +1 -1
  31. package/dist/utils/report/report.d.ts +9 -3
  32. package/dist/utils/report/report.js +29 -12
  33. package/dist/utils/report/report.js.map +1 -1
  34. package/dist/utils/secretManager/secretLoader.js +5 -1
  35. package/dist/utils/secretManager/secretLoader.js.map +1 -1
  36. package/dist/utils/semverCompare/semverCompare.d.ts +4 -4
  37. package/dist/utils/semverCompare/semverCompare.js +16 -18
  38. package/dist/utils/semverCompare/semverCompare.js.map +1 -1
  39. package/package.json +30 -19
  40. package/.env.example +0 -186
  41. package/.github/workflows/publish.yml +0 -41
  42. package/.prettierrc +0 -7
  43. package/Dockerfile +0 -31
  44. package/babel.config.json +0 -34
  45. package/eslint.config.mjs +0 -11
  46. package/src/const/DebeziumEvent.ts +0 -6
  47. package/src/const/LogSeverity.ts +0 -7
  48. package/src/const/index.ts +0 -5
  49. package/src/const/metrics.ts +0 -6
  50. package/src/const/mime.ts +0 -37
  51. package/src/const/tokenIssuer.ts +0 -7
  52. package/src/db/migrations/20241030121500-create-sequelize-meta-table.ts +0 -29
  53. package/src/db/models/SequelizeMeta.ts +0 -16
  54. package/src/db/models/index.ts +0 -2
  55. package/src/entities/ElasticSearch/ElasticSearch.ts +0 -103
  56. package/src/entities/ElasticSearch/ElasticSearch.types.ts +0 -7
  57. package/src/entities/ElasticSearch/index.ts +0 -2
  58. package/src/entities/Endpoint/Endpoint.test.ts +0 -248
  59. package/src/entities/Endpoint/Endpoint.ts +0 -221
  60. package/src/entities/Endpoint/Endpoint.types.ts +0 -181
  61. package/src/entities/Endpoint/index.ts +0 -2
  62. package/src/entities/EngineError/EngineError.ts +0 -28
  63. package/src/entities/EngineError/EngineError.types.ts +0 -15
  64. package/src/entities/EngineError/index.ts +0 -2
  65. package/src/entities/KeySet/KeySet.test.ts +0 -126
  66. package/src/entities/KeySet/KeySet.ts +0 -172
  67. package/src/entities/KeySet/KeySet.types.ts +0 -28
  68. package/src/entities/KeySet/index.ts +0 -2
  69. package/src/entities/LifecycleController/LifecycleController.test.ts +0 -48
  70. package/src/entities/LifecycleController/LifecycleController.ts +0 -24
  71. package/src/entities/LifecycleController/LifecycleController.types.ts +0 -5
  72. package/src/entities/LifecycleController/index.ts +0 -2
  73. package/src/entities/Localizator/Localizator.ts +0 -139
  74. package/src/entities/Localizator/Localizator.types.ts +0 -52
  75. package/src/entities/Localizator/guessNamingConvention/guessNamingConvention.test.ts +0 -165
  76. package/src/entities/Localizator/guessNamingConvention/guessNamingConvention.ts +0 -58
  77. package/src/entities/Localizator/guessNamingConvention/index.ts +0 -1
  78. package/src/entities/Localizator/index.ts +0 -2
  79. package/src/entities/MessageHandler/MessageHandler.test.ts +0 -130
  80. package/src/entities/MessageHandler/MessageHandler.ts +0 -72
  81. package/src/entities/MessageHandler/MessageHandler.types.ts +0 -15
  82. package/src/entities/MessageHandler/index.ts +0 -2
  83. package/src/entities/PubSub/PubSub.test.ts +0 -297
  84. package/src/entities/PubSub/PubSub.ts +0 -202
  85. package/src/entities/PubSub/PubSub.types.ts +0 -76
  86. package/src/entities/PubSub/handleMessage/handleMessage.test.ts +0 -102
  87. package/src/entities/PubSub/handleMessage/handleMessage.ts +0 -65
  88. package/src/entities/PubSub/handleMessage/index.ts +0 -1
  89. package/src/entities/PubSub/index.ts +0 -2
  90. package/src/entities/PushNotification/PushNotification.test.ts +0 -35
  91. package/src/entities/PushNotification/PushNotification.ts +0 -44
  92. package/src/entities/PushNotification/PushNotification.types.ts +0 -25
  93. package/src/entities/PushNotification/index.ts +0 -2
  94. package/src/entities/Redis/Redis.ts +0 -71
  95. package/src/entities/Redis/Redis.types.ts +0 -19
  96. package/src/entities/Redis/Redis.validate.ts +0 -17
  97. package/src/entities/Redis/index.ts +0 -2
  98. package/src/entities/Sequelize/Sequelize.config.ts +0 -59
  99. package/src/entities/Sequelize/Sequelize.test.ts +0 -21
  100. package/src/entities/Sequelize/Sequelize.ts +0 -75
  101. package/src/entities/Sequelize/Sequelize.types.ts +0 -10
  102. package/src/entities/Sequelize/Sequelize.validate.ts +0 -15
  103. package/src/entities/Sequelize/index.ts +0 -2
  104. package/src/entities/Server/Server.const.ts +0 -56
  105. package/src/entities/Server/Server.test.ts +0 -272
  106. package/src/entities/Server/Server.ts +0 -471
  107. package/src/entities/Server/Server.types.ts +0 -88
  108. package/src/entities/Server/index.ts +0 -2
  109. package/src/entities/SocketClient/SocketClient.test.ts +0 -392
  110. package/src/entities/SocketClient/SocketClient.ts +0 -401
  111. package/src/entities/SocketClient/SocketClient.types.ts +0 -62
  112. package/src/entities/SocketClient/index.ts +0 -2
  113. package/src/entities/SocketClient/parseMessage/index.ts +0 -1
  114. package/src/entities/SocketClient/parseMessage/parseMessage.test.ts +0 -20
  115. package/src/entities/SocketClient/parseMessage/parseMessage.ts +0 -26
  116. package/src/entities/Storage/Storage.test.ts +0 -58
  117. package/src/entities/Storage/Storage.ts +0 -219
  118. package/src/entities/Storage/Storage.types.ts +0 -57
  119. package/src/entities/Storage/index.ts +0 -2
  120. package/src/entities/ValidationError/ValidationError.ts +0 -34
  121. package/src/entities/ValidationError/index.ts +0 -1
  122. package/src/entities/WebError/WebError.test.ts +0 -18
  123. package/src/entities/WebError/WebError.ts +0 -23
  124. package/src/entities/WebError/WebError.types.ts +0 -9
  125. package/src/entities/WebError/index.ts +0 -2
  126. package/src/entities/index.ts +0 -16
  127. package/src/index.ts +0 -6
  128. package/src/metrics/debug.ts +0 -10
  129. package/src/metrics/index.ts +0 -2
  130. package/src/metrics/request.ts +0 -17
  131. package/src/middleware/authHmac/authHmac.test.ts +0 -86
  132. package/src/middleware/authHmac/authHmac.ts +0 -56
  133. package/src/middleware/authHmac/authHmac.types.ts +0 -7
  134. package/src/middleware/authHmac/index.ts +0 -2
  135. package/src/middleware/authJwt/authJwt.test.ts +0 -54
  136. package/src/middleware/authJwt/authJwt.ts +0 -55
  137. package/src/middleware/authJwt/authJwt.types.ts +0 -14
  138. package/src/middleware/authJwt/index.ts +0 -2
  139. package/src/middleware/authStatic/authStatic.test.ts +0 -36
  140. package/src/middleware/authStatic/authStatic.ts +0 -45
  141. package/src/middleware/authStatic/index.ts +0 -1
  142. package/src/middleware/authTls/authTls.test.ts +0 -138
  143. package/src/middleware/authTls/authTls.ts +0 -119
  144. package/src/middleware/authTls/authTls.types.ts +0 -13
  145. package/src/middleware/authTls/index.ts +0 -2
  146. package/src/middleware/checkPermission/checkPermission.ts +0 -33
  147. package/src/middleware/checkPermission/index.ts +0 -1
  148. package/src/middleware/error/error.test.ts +0 -60
  149. package/src/middleware/error/error.ts +0 -25
  150. package/src/middleware/error/index.ts +0 -1
  151. package/src/middleware/fileUploader/fileUploader.ts +0 -154
  152. package/src/middleware/fileUploader/fileUploader.types.ts +0 -35
  153. package/src/middleware/fileUploader/index.ts +0 -2
  154. package/src/middleware/fileUploader/isMultiPart/index.ts +0 -1
  155. package/src/middleware/fileUploader/isMultiPart/isMultipart.ts +0 -9
  156. package/src/middleware/fileUploader/validate/index.ts +0 -1
  157. package/src/middleware/fileUploader/validate/validate.ts +0 -27
  158. package/src/middleware/fourOhFour/fourOhFour.test.ts +0 -14
  159. package/src/middleware/fourOhFour/fourOhFour.ts +0 -13
  160. package/src/middleware/fourOhFour/index.ts +0 -1
  161. package/src/middleware/geminiFileUploader/geminiFileUploader.ts +0 -68
  162. package/src/middleware/geminiFileUploader/index.ts +0 -1
  163. package/src/middleware/healthCheck/healthCheck.test.ts +0 -12
  164. package/src/middleware/healthCheck/healthCheck.ts +0 -14
  165. package/src/middleware/healthCheck/index.ts +0 -1
  166. package/src/middleware/index.ts +0 -17
  167. package/src/middleware/metrics/index.ts +0 -1
  168. package/src/middleware/metrics/metrics.test.ts +0 -46
  169. package/src/middleware/metrics/metrics.ts +0 -24
  170. package/src/middleware/multiPartFileUploader/index.ts +0 -2
  171. package/src/middleware/multiPartFileUploader/multiPartFileUploader.ts +0 -249
  172. package/src/middleware/multiPartFileUploader/multiPartFileUploader.types.ts +0 -23
  173. package/src/middleware/multiPartFileUploader/validate/index.ts +0 -1
  174. package/src/middleware/multiPartFileUploader/validate/validate.ts +0 -27
  175. package/src/middleware/requestMetrics/index.ts +0 -1
  176. package/src/middleware/requestMetrics/requestMetrics.test.ts +0 -77
  177. package/src/middleware/requestMetrics/requestMetrics.ts +0 -20
  178. package/src/middleware/swaggerDocs/index.ts +0 -2
  179. package/src/middleware/swaggerDocs/swaggerDocs.ts +0 -114
  180. package/src/middleware/swaggerDocs/swaggerDocs.types.ts +0 -41
  181. package/src/middleware/userResolver/index.ts +0 -1
  182. package/src/middleware/userResolver/userResolver.ts +0 -53
  183. package/src/middleware/validate/index.ts +0 -1
  184. package/src/middleware/validate/validate.test.ts +0 -43
  185. package/src/middleware/validate/validate.ts +0 -25
  186. package/src/middleware/version/index.ts +0 -1
  187. package/src/middleware/version/version.test.ts +0 -23
  188. package/src/middleware/version/version.ts +0 -12
  189. package/src/utils/checkEnvironment/index.ts +0 -49
  190. package/src/utils/createHttpServer/createHttpServer.test.ts +0 -24
  191. package/src/utils/createHttpServer/createHttpServer.ts +0 -34
  192. package/src/utils/createHttpServer/createHttpServer.validate.ts +0 -17
  193. package/src/utils/createHttpServer/index.ts +0 -1
  194. package/src/utils/createSortedObject/createSortedObject.ts +0 -20
  195. package/src/utils/createSortedObject/createSortedObjet.test.ts +0 -47
  196. package/src/utils/createSortedObject/index.ts +0 -1
  197. package/src/utils/dbMigration/dbMigration.test.ts +0 -209
  198. package/src/utils/dbMigration/dbMigration.ts +0 -199
  199. package/src/utils/dbMigration/dbMigration.types.ts +0 -11
  200. package/src/utils/dbMigration/index.ts +0 -2
  201. package/src/utils/dbMigration/migrateMetadataTable/index.ts +0 -1
  202. package/src/utils/dbMigration/migrateMetadataTable/migrateMetadataTable.test.ts +0 -59
  203. package/src/utils/dbMigration/migrateMetadataTable/migrateMetadataTable.ts +0 -102
  204. package/src/utils/envAssert/envAssert.ts +0 -31
  205. package/src/utils/envAssert/index.ts +0 -1
  206. package/src/utils/envAssert/validators/hostList.ts +0 -17
  207. package/src/utils/envAssert/validators/index.ts +0 -5
  208. package/src/utils/envAssert/validators/ipList.ts +0 -13
  209. package/src/utils/envAssert/validators/path.ts +0 -13
  210. package/src/utils/envAssert/validators/string.ts +0 -9
  211. package/src/utils/envAssert/validators/stringList.ts +0 -13
  212. package/src/utils/filter/filter.ts +0 -17
  213. package/src/utils/filter/index.ts +0 -1
  214. package/src/utils/geminiFileUpload/geminiFileUpload.ts +0 -93
  215. package/src/utils/geminiFileUpload/index.ts +0 -1
  216. package/src/utils/hmac/checkEnv/checkEnv.test.ts +0 -18
  217. package/src/utils/hmac/checkEnv/checkEnv.ts +0 -9
  218. package/src/utils/hmac/checkEnv/index.ts +0 -1
  219. package/src/utils/hmac/createHmac/createHmac.ts +0 -33
  220. package/src/utils/hmac/createHmac/index.ts +0 -1
  221. package/src/utils/hmac/index.ts +0 -4
  222. package/src/utils/hmac/index.types.ts +0 -13
  223. package/src/utils/hmac/signPayload/index.ts +0 -1
  224. package/src/utils/hmac/signPayload/signPayload.ts +0 -16
  225. package/src/utils/hmac/verifySignature/index.ts +0 -1
  226. package/src/utils/hmac/verifySignature/verifySignature.ts +0 -24
  227. package/src/utils/index.ts +0 -29
  228. package/src/utils/jwt/index.ts +0 -2
  229. package/src/utils/jwt/jwt.test.ts +0 -28
  230. package/src/utils/jwt/jwt.ts +0 -182
  231. package/src/utils/jwt/jwt.types.ts +0 -21
  232. package/src/utils/report/index.ts +0 -3
  233. package/src/utils/report/output.ts +0 -45
  234. package/src/utils/report/output.types.ts +0 -70
  235. package/src/utils/report/report.test.ts +0 -133
  236. package/src/utils/report/report.ts +0 -133
  237. package/src/utils/report/report.types.ts +0 -13
  238. package/src/utils/request/index.ts +0 -1
  239. package/src/utils/request/request.test.ts +0 -26
  240. package/src/utils/request/request.ts +0 -48
  241. package/src/utils/secretManager/index.ts +0 -2
  242. package/src/utils/secretManager/secretLoader.ts +0 -62
  243. package/src/utils/secretManager/secretResolver.ts +0 -35
  244. package/src/utils/semverCompare/index.ts +0 -1
  245. package/src/utils/semverCompare/semverCompare.test.ts +0 -49
  246. package/src/utils/semverCompare/semverCompare.ts +0 -60
  247. package/src/utils/sendEmail/index.ts +0 -2
  248. package/src/utils/sendEmail/sendEmail.ts +0 -61
  249. package/src/utils/sendEmail/sendEmail.types.ts +0 -61
  250. package/src/utils/sendPush/index.ts +0 -2
  251. package/src/utils/sendPush/sendPush.ts +0 -20
  252. package/src/utils/sendPush/sendPush.types.ts +0 -25
  253. package/src/utils/tlsConfig/index.ts +0 -2
  254. package/src/utils/tlsConfig/tlsConfig.test.ts +0 -32
  255. package/src/utils/tlsConfig/tlsConfig.ts +0 -47
  256. package/src/utils/tlsConfig/tlsConfig.types.ts +0 -11
  257. package/src/utils/tlsConfig/tlsConfig.validate.ts +0 -25
  258. package/src/utils/tlsRequest/index.ts +0 -1
  259. package/src/utils/tlsRequest/tlsRequest.test.ts +0 -63
  260. package/src/utils/tlsRequest/tlsRequest.ts +0 -12
  261. package/src/utils/trimPathSlash/index.ts +0 -1
  262. package/src/utils/trimPathSlash/trimPathSlash.test.ts +0 -26
  263. package/src/utils/trimPathSlash/trimPathSlash.ts +0 -8
  264. package/test/env.ts +0 -53
  265. package/test/factory/index.ts +0 -8
  266. package/test/factory/sequelizeMeta.ts +0 -12
  267. package/test/hooks.ts +0 -35
  268. package/test/index.ts +0 -3
  269. package/test/stub/index.ts +0 -3
  270. package/test/stub/stubLocales.ts +0 -68
  271. package/test/stub/stubPubSub.ts +0 -7
  272. package/test/stub/stubStorage.ts +0 -77
  273. package/test/stub/stubStorage.types.ts +0 -11
  274. package/test/utils/app.ts +0 -31
  275. package/test/utils/index.ts +0 -1
  276. package/tsconfig.build.json +0 -5
  277. package/tsconfig.json +0 -32
@@ -1,202 +0,0 @@
1
- import {
2
- Attributes,
3
- PubSub as PubsubClient,
4
- Message
5
- } from '@google-cloud/pubsub';
6
- import { handleMessage } from './handleMessage';
7
- import {
8
- PubSubTopicsMap,
9
- PubSubSubscriptionsMap,
10
- PubSubAddSubscriberOptions,
11
- PubSubMessageHandler
12
- } from './PubSub.types';
13
- import { EngineError } from 'entities/EngineError';
14
- import { LifecycleController } from 'entities/LifecycleController';
15
- import { reportDebug, reportError } from 'utils';
16
-
17
- const namespace = 'engine:PubSub';
18
-
19
- /**
20
- * Associative containing all the subscriptions sorted by topic registered for publishing
21
- */
22
- const subscriptions: PubSubSubscriptionsMap = {};
23
-
24
- /**
25
- * Associative containing all the topics registered for publishing
26
- */
27
- const topics: PubSubTopicsMap = {};
28
-
29
- const pubSubClient = new PubsubClient();
30
-
31
- /**
32
- * This is a singleton service who's role is to manage pub/sub connections
33
- * It is the preferred way to interact with Pub/Sub as it manages the underlying subscription and handler objects
34
- * It avoids potential subscription splitting on an instance
35
- */
36
- export const PubSub = {
37
- /**
38
- * Initialize the registered publishers and subscribers
39
- */
40
- async init(): Promise<void> {
41
- reportDebug({ namespace, message: `Initializing Pub/Sub` });
42
- // Check that all topics/permissions exist, and that we have permission on them
43
- await Promise.all([
44
- // Test that all topics actually exist and that we have permissions
45
- ...Object.values(topics).map(async (topic) => {
46
- const [exists] = await topic.exists();
47
- if (!exists)
48
- throw new EngineError(`Topic ${topic.name} does not exist`);
49
- // Test permission ton publish to topic
50
- const [permissions] = await topic.iam.testPermissions([
51
- 'pubsub.topics.publish'
52
- ]);
53
- const hasPermissions = Object.values(permissions).every(
54
- (success) => success
55
- );
56
- if (!hasPermissions)
57
- throw new EngineError(
58
- `Instance does not have appropriate permissions to publish to topic ${topic.name}`
59
- );
60
- }),
61
- // Test that all subscriptions actually exist and that we have permissions
62
- ...Object.values(subscriptions).map(async ({ subscription }) => {
63
- const [exists] = await subscription.exists();
64
- if (!exists)
65
- throw new EngineError(
66
- `Subscription ${subscription.name} does not exist`
67
- );
68
- // Test permission ton consume subscription
69
- const [permissions] = await subscription.iam.testPermissions([
70
- 'pubsub.subscriptions.consume'
71
- ]);
72
- const hasPermissions = Object.values(permissions).every(
73
- (success) => success
74
- );
75
- if (!hasPermissions)
76
- throw new EngineError(
77
- `Instance does not have appropriate permissions to subscribe to subscription ${subscription.name}`
78
- );
79
- })
80
- ]);
81
-
82
- // Register handlers on the subscriptions to activate them
83
- for (const { subscription, handlers, isDebezium } of Object.values(
84
- subscriptions
85
- )) {
86
- subscription.on('message', (message) => {
87
- handleMessage(
88
- message as Message,
89
- subscription,
90
- handlers,
91
- isDebezium
92
- ).catch((error) => {
93
- reportError(error);
94
- });
95
- });
96
- }
97
-
98
- LifecycleController.register(PubSub);
99
- },
100
-
101
- /**
102
- * Shutdown the registered publishers and subscribers
103
- */
104
- async shutdown(): Promise<void> {
105
- reportDebug({ namespace, message: `Shutting down Pub/Sub` });
106
- // Flush all pending messages and shut down connections
107
- await Promise.all([
108
- ...Object.values(topics).map(async (topic) => topic.flush()),
109
- ...Object.values(subscriptions).map(async ({ subscription }) =>
110
- subscription.close()
111
- )
112
- ]);
113
- // Delete the stored topics/subscriptions
114
- Object.keys(topics).forEach((topic) => delete topics[topic]);
115
- Object.keys(subscriptions).forEach(
116
- (subscription) => delete subscriptions[subscription]
117
- );
118
- // Close the client
119
- await pubSubClient.close();
120
- },
121
-
122
- /**
123
- * Creates a publisher if it does not exist yet
124
- */
125
- addPublisher(topic: string | Array<string>): void {
126
- const topicArray = topic instanceof Array ? topic : [topic];
127
- reportDebug({
128
- namespace,
129
- message: `Registering publisher for topics '${topicArray.join("', '")}'`
130
- });
131
- // Register a new topic if it does not exist yet
132
- for (const topic of topicArray) {
133
- if (!Object.keys(topics).includes(topic)) {
134
- topics[topic] = pubSubClient.topic(topic, {
135
- enableOpenTelemetryTracing: true
136
- });
137
- }
138
- }
139
- },
140
-
141
- /**
142
- * Add message handlers to a subscription
143
- * @param subscription - Name of the Pub/Sub subscription
144
- * @param handler - Function or list of functions to set as message handlers
145
- * @param options - Indicates that the additional options need to handle on Subscriber
146
- */
147
- addSubscriber<T>(
148
- subscription: string,
149
- handler: PubSubMessageHandler<T> | Array<PubSubMessageHandler<T>>,
150
- options: PubSubAddSubscriberOptions = {}
151
- ): void {
152
- const handlers = handler instanceof Array ? handler : [handler];
153
- const isDebezium = options?.isDebezium ?? false;
154
- const first = options?.first ?? false;
155
-
156
- reportDebug({
157
- namespace,
158
- message: `Registering subscriber for subscription '${subscription}'`,
159
- data: { first, handlersCount: handlers.length, isDebezium }
160
- });
161
- if (!Object.keys(subscriptions).includes(subscription)) {
162
- subscriptions[subscription] = {
163
- subscription: pubSubClient.subscription(subscription, {
164
- enableOpenTelemetryTracing: true
165
- }),
166
- handlers: []
167
- };
168
- }
169
-
170
- if (isDebezium) subscriptions[subscription].isDebezium = true;
171
-
172
- if (first) subscriptions[subscription].handlers.push(...handlers);
173
- else subscriptions[subscription].handlers.unshift(...handlers);
174
- },
175
-
176
- /**
177
- * Send a message to a Pub/Sub topic
178
- * @param topic - Name of the topic
179
- * @param message - Content of the message, must be an object that can be JSON stringified
180
- * @param attributes - Attributes for this message, can be used to filter messages on the subscription
181
- * @param orderingKey - Enforce order guarantee of messages sent with the same key
182
- */
183
- async publish(
184
- topic: string,
185
- message: unknown,
186
- attributes?: Attributes,
187
- orderingKey?: string
188
- ): Promise<void> {
189
- if (!topics[topic])
190
- throw new EngineError(`No publisher was registered for topic '${topic}'`);
191
- reportDebug({
192
- namespace,
193
- message: `Sending message to topic '${topic}'`,
194
- data: { message, attributes, orderingKey }
195
- });
196
- await topics[topic].publishMessage({
197
- json: message,
198
- attributes,
199
- orderingKey
200
- });
201
- }
202
- };
@@ -1,76 +0,0 @@
1
- import { Topic, Subscription, Attributes } from '@google-cloud/pubsub';
2
- import { DebeziumEvent } from 'const/DebeziumEvent';
3
-
4
- /** List of all active topics */
5
- export interface PubSubTopicsMap {
6
- [topic: string]: Topic;
7
- }
8
-
9
- /** List of subscriptions handled for each topic */
10
- export interface PubSubSubscriptionsMap {
11
- [topic: string]: {
12
- /** The Pub/Sub subscription object */
13
- subscription: Subscription;
14
- /** The message handling functions */
15
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
- handlers: Array<PubSubMessageHandler<any>>;
17
- /** Indicates that the subscription from Debezium events */
18
- isDebezium?: boolean;
19
- };
20
- }
21
-
22
- /** Options to setup a subscription */
23
- export interface PubSubAddSubscriberOptions {
24
- /** Should the handlers declared here be added to the top of the queue, and called first when an new message arrives */
25
- first?: boolean;
26
- /** Indicates that the topic is a Debezium CDC topic, additional processing is done on the message */
27
- isDebezium?: boolean;
28
- }
29
-
30
- export type PubSubMessageHandler<T> = (
31
- message: T,
32
- attributes: Attributes,
33
- publishedAt: Date
34
- ) => void | Promise<void>;
35
-
36
- /** Debezium create event payload */
37
- interface PubSubDebeziumCreatePayload<T> {
38
- /** The instance properties before the operation */
39
- before: null;
40
- /** The instance properties after the operation */
41
- after: T;
42
- /** The operation that was executed */
43
- op: DebeziumEvent.Create;
44
- /** List of fields that have changed */
45
- diff: Array<string>;
46
- }
47
-
48
- /** Debezium update event payload */
49
- interface PubSubDebeziumUpdatePayload<T> {
50
- /** The instance properties before the operation */
51
- before: T;
52
- /** The instance properties after the operation */
53
- after: T;
54
- /** The operation that was executed */
55
- op: DebeziumEvent.Update;
56
- /** List of fields that have changed */
57
- diff: Array<string>;
58
- }
59
-
60
- /** Debezium delete event payload */
61
- interface PubSubDebeziumDeletePayload<T> {
62
- /** The instance properties before the operation */
63
- before: T;
64
- /** The instance properties after the operation */
65
- after: null;
66
- /** The operation that was executed */
67
- op: DebeziumEvent.Delete;
68
- /** List of fields that have changed */
69
- diff: Array<string>;
70
- }
71
-
72
- /** Payload of Debezium messages */
73
- export type PubSubDebeziumPayload<T> =
74
- | PubSubDebeziumCreatePayload<T>
75
- | PubSubDebeziumUpdatePayload<T>
76
- | PubSubDebeziumDeletePayload<T>;
@@ -1,102 +0,0 @@
1
- import { PreciseDate } from '@google-cloud/precise-date';
2
- import {
3
- Subscription,
4
- PubSub,
5
- Attributes,
6
- Message
7
- } from '@google-cloud/pubsub';
8
- import { expect } from 'chai';
9
- import { faker } from '@faker-js/faker';
10
- import { fake, SinonSpy } from 'sinon';
11
- import { handleMessage } from './handleMessage';
12
-
13
- describe('Entities - Pub/Sub - Handle Message', () => {
14
- let subscription: Subscription;
15
- let payload: Record<string, unknown>;
16
- let attributes: Attributes;
17
- let ack: SinonSpy;
18
- let message: Message;
19
- let publishTime: PreciseDate;
20
-
21
- beforeEach(() => {
22
- subscription = new Subscription(new PubSub(), faker.lorem.slug());
23
-
24
- payload = { [faker.lorem.word()]: faker.lorem.word() };
25
-
26
- attributes = { [faker.lorem.word()]: faker.lorem.word() };
27
- const data = Buffer.from(JSON.stringify(payload));
28
-
29
- publishTime = new PreciseDate(faker.date.recent());
30
-
31
- ack = fake();
32
- message = {
33
- id: faker.string.uuid(),
34
- ack,
35
- ackId: faker.string.uuid(),
36
- data,
37
- attributes,
38
- publishTime,
39
- deliveryAttempt: 0,
40
- received: 0
41
- } as unknown as Message;
42
- });
43
-
44
- it('should parse the message and forward it to all the handlers', async () => {
45
- const handlers = [fake(), fake(), fake()];
46
-
47
- await handleMessage(message, subscription, handlers);
48
-
49
- for (const handler of handlers) {
50
- expect(handler).to.have.been.calledOnceWithExactly(
51
- payload,
52
- attributes,
53
- publishTime
54
- );
55
- }
56
- });
57
-
58
- it('should parse a debezium message [create]', async () => {
59
- const handler = fake();
60
- payload = { before: null, after: { a: 'a', b: 'b', c: 'c' } };
61
- message.data = Buffer.from(JSON.stringify(payload), 'utf-8');
62
-
63
- await handleMessage(message, subscription, [handler], true);
64
-
65
- expect(handler).to.have.been.calledOnceWithExactly(
66
- { ...payload, diff: ['a', 'b', 'c'] },
67
- attributes,
68
- publishTime
69
- );
70
- });
71
-
72
- it('should parse a debezium message [destroy]', async () => {
73
- const handler = fake();
74
- payload = { before: { a: 'a', b: 'b', c: 'c' }, after: null };
75
- message.data = Buffer.from(JSON.stringify(payload), 'utf-8');
76
-
77
- await handleMessage(message, subscription, [handler], true);
78
-
79
- expect(handler).to.have.been.calledOnceWithExactly(
80
- { ...payload, diff: ['a', 'b', 'c'] },
81
- attributes,
82
- publishTime
83
- );
84
- });
85
-
86
- it('should parse a debezium message [update]', async () => {
87
- const handler = fake();
88
- payload = {
89
- before: { a: 'a', b: 'b', c: 'c' },
90
- after: { a: 'a', b: 'y', c: 'c' }
91
- };
92
- message.data = Buffer.from(JSON.stringify(payload), 'utf-8');
93
-
94
- await handleMessage(message, subscription, [handler], true);
95
-
96
- expect(handler).to.have.been.calledOnceWithExactly(
97
- { ...payload, diff: ['b'] },
98
- attributes,
99
- publishTime
100
- );
101
- });
102
- });
@@ -1,65 +0,0 @@
1
- import { Subscription, Message } from '@google-cloud/pubsub';
2
- import { PubSubMessageHandler, PubSubDebeziumPayload } from '../PubSub.types';
3
- import { LogSeverity } from 'const';
4
- import { EngineError } from 'entities/EngineError';
5
- import { reportDebug, reportError } from 'utils';
6
-
7
- const namespace = 'engine:PubSub:handleMessage';
8
-
9
- /**
10
- * handle the pubSubClients on message event
11
- * @param message - Message to parse and handle
12
- * @param subscription - The related Pub/Sub subscription object
13
- * @param handlers - Message handlers
14
- * @param isDebezium - Indicates that the subscription from Debezium events
15
- */
16
- export async function handleMessage(
17
- message: Message,
18
- subscription: Subscription,
19
- handlers: Array<PubSubMessageHandler<unknown>>,
20
- isDebezium = false
21
- ): Promise<void> {
22
- reportDebug({
23
- namespace,
24
- message: `Received message from Pub/Sub [${subscription.name}]`
25
- });
26
-
27
- message.ack();
28
-
29
- let payload;
30
- try {
31
- payload = JSON.parse(message.data.toString()) as unknown;
32
-
33
- // When handling Debezium message, we calculate automatically the keys that have changed between the previous and current state
34
- if (isDebezium) {
35
- payload = payload as PubSubDebeziumPayload<Record<string, unknown>>;
36
- const { before, after } = payload;
37
- if (before && after)
38
- payload.diff = Object.keys(after).filter(
39
- (key) => after[key] !== before[key]
40
- );
41
- else if (before) payload.diff = Object.keys(before);
42
- else if (after) payload.diff = Object.keys(after);
43
- else
44
- throw new EngineError({
45
- severity: LogSeverity.WARNING,
46
- message: `Debezium Pub/Sub message did not include before nor after value [${subscription.name}]`,
47
- data: { subscription, message: message.data.toString() }
48
- });
49
- }
50
- } catch {
51
- throw new EngineError({
52
- severity: LogSeverity.WARNING,
53
- message: `Pub/Sub message is not valid JSON ${subscription.name}`,
54
- data: { subscription, message: message.data.toString() }
55
- });
56
- }
57
-
58
- for (const handler of handlers) {
59
- try {
60
- await handler(payload, message.attributes, message.publishTime);
61
- } catch (error) {
62
- reportError(error);
63
- }
64
- }
65
- }
@@ -1 +0,0 @@
1
- export * from './handleMessage';
@@ -1,2 +0,0 @@
1
- export * from './PubSub';
2
- export * from './PubSub.types';
@@ -1,35 +0,0 @@
1
- import { expect } from 'chai';
2
- import { faker } from '@faker-js/faker';
3
- import { stub, SinonStub } from 'sinon';
4
- import { PushNotification } from './PushNotification';
5
- import { PushNotificationNotification } from './PushNotification.types';
6
- import { PubSub } from 'entities/PubSub/PubSub';
7
-
8
- describe('Entities - PushNotification', function () {
9
- let notification: PushNotificationNotification;
10
- let publishStub: SinonStub;
11
- let userId: string;
12
-
13
- beforeEach(() => {
14
- process.env.CHART = 'test';
15
- process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC = 'notification_queue';
16
- userId = faker.string.uuid();
17
- notification = {
18
- title: faker.lorem.word(),
19
- body: faker.lorem.word()
20
- };
21
-
22
- publishStub = stub(PubSub, 'publish');
23
- });
24
-
25
- it('should send push notification to the user', async () => {
26
- await PushNotification.sendPush(userId, notification);
27
- expect(publishStub).to.have.been.calledOnceWithExactly(
28
- 'notification_queue',
29
- {
30
- ...notification,
31
- userId
32
- }
33
- );
34
- });
35
- });
@@ -1,44 +0,0 @@
1
- import { PushNotificationNotification } from './PushNotification.types';
2
- import { EngineError } from 'entities';
3
- import { PubSub } from 'entities/PubSub';
4
- import { reportDebug } from 'utils';
5
-
6
- const namespace = 'engine:PushNotification';
7
-
8
- export const PushNotification = {
9
- /** Initialize the push notification entity */
10
- init(): void {
11
- reportDebug({ namespace, message: 'Initializing PushNotification' });
12
- if (!process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC)
13
- throw new EngineError({
14
- message:
15
- 'Environment variable PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC is not defined'
16
- });
17
- // Register publisher
18
- PubSub.addPublisher(process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC);
19
- },
20
-
21
- /**
22
- * Send a push notification through the push service
23
- * @param userId - ID of the user that should receive the notification
24
- * @param notification - Notification that should be sent
25
- */
26
- async sendPush(
27
- userId: string,
28
- notification: PushNotificationNotification
29
- ): Promise<void> {
30
- reportDebug({
31
- namespace,
32
- message: `Received data to send notification to user with Id '${userId}'`,
33
- data: { userId, notification }
34
- });
35
-
36
- await PubSub.publish(
37
- process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC as string,
38
- {
39
- ...notification,
40
- userId
41
- }
42
- );
43
- }
44
- };
@@ -1,25 +0,0 @@
1
- /** Notification to send to the user's device */
2
- export interface PushNotificationNotification {
3
- /** Title of the notification */
4
- title?: string;
5
- /** Body of the notification */
6
- body?: string;
7
- /** Data payload of the notification */
8
- payload?: unknown;
9
- /** Indicates that the notification is a VOIP notification */
10
- voip?: boolean;
11
- /** Indicate that it is a background notification (data only, low priority) */
12
- background?: boolean;
13
- /** Push token can be specified here is we want to send to a given user */
14
- token?: string;
15
- /** Indicates that the data content can be mutated by the client (iOS) */
16
- mutable?: boolean;
17
- /** Indicates that the notification requires some client side processing to be displayed (iOS) */
18
- contentAvailable?: boolean;
19
- /** Time to live of the notification in seconds. After this the notification will not be distributed */
20
- ttl?: number;
21
- /** Indicate that it is a high priority notification */
22
- priority?: boolean;
23
- /** Notifications with the same string here will be collapsed in the system tray */
24
- collapseId?: string;
25
- }
@@ -1,2 +0,0 @@
1
- export * from './PushNotification';
2
- export * from './PushNotification.types';
@@ -1,71 +0,0 @@
1
- import { Redis as RedisClient } from 'ioredis';
2
- import { RedisInterface, RedisCreateOptions } from './Redis.types';
3
- import { validateRedisEnvironment } from './Redis.validate';
4
- import { EngineError } from 'entities/EngineError';
5
- import { LifecycleController } from 'entities/LifecycleController';
6
-
7
- export let redisClient: undefined | RedisClient;
8
-
9
- /**
10
- * We use a proxy to extend the Redis behavior and allow initialization of the client only when needed while keeping the same interface
11
- */
12
- export const Redis = new Proxy<RedisInterface>(
13
- // Due to how we do the proxying, we are forced to ignore ts-errors, we could never create an object that respects the interface properly
14
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
15
- // @ts-ignore
16
- {},
17
- {
18
- get: function (target, property): unknown {
19
- if (property === 'init') return init;
20
- if (property === 'shutdown') return shutdown;
21
- // Create the redis client if it does not exist
22
- if (!redisClient)
23
- throw new EngineError({ message: 'Redis client was not initialized' });
24
- // Call the function on the redis client
25
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
26
- // @ts-ignore
27
- return redisClient[property] as unknown;
28
- }
29
- }
30
- );
31
-
32
- /**
33
- * On init create client anc connect
34
- */
35
- export function init(): void {
36
- // Ignore if already initialized
37
- if (redisClient) return;
38
- redisClient = createRedisClient();
39
- LifecycleController.register(Redis);
40
- }
41
-
42
- /**
43
- * On shutdown close connection and clear client
44
- */
45
- export async function shutdown(): Promise<void> {
46
- if (redisClient) await redisClient.quit();
47
- redisClient = undefined;
48
- }
49
-
50
- /**
51
- * Create Redis client
52
- */
53
- export function createRedisClient(
54
- options: RedisCreateOptions = {}
55
- ): RedisClient {
56
- // Check that environment variables are correctly set
57
- validateRedisEnvironment(options);
58
- // Throw an error if there is no host initial
59
- if (!process.env.REDIS_HOST)
60
- throw new EngineError({
61
- message: 'Host required to initialize Redis connection'
62
- });
63
-
64
- return new RedisClient({
65
- port: process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : undefined,
66
- host: process.env.REDIS_HOST,
67
- username: process.env.REDIS_USERNAME,
68
- password: process.env.REDIS_PASSWORD,
69
- db: 0
70
- });
71
- }
@@ -1,19 +0,0 @@
1
- import { Redis as IoRedis, Cluster, RedisOptions } from 'ioredis';
2
-
3
- export type RedisInterface = IoRedis &
4
- Cluster & {
5
- /** Start the redis service */
6
- init: () => void;
7
- /** Stop the redis service */
8
- shutdown: () => Promise<void>;
9
- };
10
-
11
- /** Options to initialize a Redis client */
12
- export interface RedisCreateOptions {
13
- /** Redis client settings */
14
- redis?: RedisOptions;
15
- /** Should the client perform readiness check when establishing the connection */
16
- enableReadyCheck?: boolean;
17
- /** Cluster mode read operation settings, can make reads on masters, slaves, or both */
18
- scaleReads?: string;
19
- }
@@ -1,17 +0,0 @@
1
- import { RedisCreateOptions } from './Redis.types';
2
- import { EngineError } from 'entities/EngineError';
3
-
4
- /**
5
- * Verify that the environment variables to connect to a Redis server are properly set
6
- */
7
- export function validateRedisEnvironment(options: RedisCreateOptions): void {
8
- const host = process.env.REDIS_HOST ?? options.redis?.host;
9
- if (typeof host !== 'string')
10
- throw new EngineError({ message: 'No host specified for Redis instance' });
11
-
12
- const port = process.env.REDIS_PORT
13
- ? parseInt(process.env.REDIS_PORT)
14
- : options.redis?.port;
15
- if (typeof port !== 'number')
16
- throw new EngineError({ message: 'No port specified for Redis instance' });
17
- }
@@ -1,2 +0,0 @@
1
- export { Redis, createRedisClient } from './Redis';
2
- export * from './Redis.types';