@flowerforce/flowerbase 1.0.1-beta.3

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 (292) hide show
  1. package/CHANGELOG.md +0 -0
  2. package/LICENSE +3 -0
  3. package/README.md +18 -0
  4. package/dist/auth/controller.d.ts +8 -0
  5. package/dist/auth/controller.d.ts.map +1 -0
  6. package/dist/auth/controller.js +76 -0
  7. package/dist/auth/dtos.d.ts +6 -0
  8. package/dist/auth/dtos.d.ts.map +1 -0
  9. package/dist/auth/dtos.js +2 -0
  10. package/dist/auth/plugins/jwt.d.ts +14 -0
  11. package/dist/auth/plugins/jwt.d.ts.map +1 -0
  12. package/dist/auth/plugins/jwt.js +68 -0
  13. package/dist/auth/providers/local-userpass/controller.d.ts +8 -0
  14. package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -0
  15. package/dist/auth/providers/local-userpass/controller.js +184 -0
  16. package/dist/auth/providers/local-userpass/dtos.d.ts +35 -0
  17. package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -0
  18. package/dist/auth/providers/local-userpass/dtos.js +2 -0
  19. package/dist/auth/utils.d.ts +126 -0
  20. package/dist/auth/utils.d.ts.map +1 -0
  21. package/dist/auth/utils.js +122 -0
  22. package/dist/constants.d.ts +18 -0
  23. package/dist/constants.d.ts.map +1 -0
  24. package/dist/constants.js +34 -0
  25. package/dist/features/endpoints/index.d.ts +10 -0
  26. package/dist/features/endpoints/index.d.ts.map +1 -0
  27. package/dist/features/endpoints/index.js +31 -0
  28. package/dist/features/endpoints/interface.d.ts +27 -0
  29. package/dist/features/endpoints/interface.d.ts.map +1 -0
  30. package/dist/features/endpoints/interface.js +2 -0
  31. package/dist/features/endpoints/utils.d.ts +31 -0
  32. package/dist/features/endpoints/utils.d.ts.map +1 -0
  33. package/dist/features/endpoints/utils.js +85 -0
  34. package/dist/features/functions/controller.d.ts +9 -0
  35. package/dist/features/functions/controller.d.ts.map +1 -0
  36. package/dist/features/functions/controller.js +88 -0
  37. package/dist/features/functions/dtos.d.ts +34 -0
  38. package/dist/features/functions/dtos.d.ts.map +1 -0
  39. package/dist/features/functions/dtos.js +2 -0
  40. package/dist/features/functions/index.d.ts +9 -0
  41. package/dist/features/functions/index.d.ts.map +1 -0
  42. package/dist/features/functions/index.js +28 -0
  43. package/dist/features/functions/interface.d.ts +32 -0
  44. package/dist/features/functions/interface.d.ts.map +1 -0
  45. package/dist/features/functions/interface.js +2 -0
  46. package/dist/features/functions/utils.d.ts +23 -0
  47. package/dist/features/functions/utils.d.ts.map +1 -0
  48. package/dist/features/functions/utils.js +75 -0
  49. package/dist/features/rules/index.d.ts +1 -0
  50. package/dist/features/rules/index.d.ts.map +1 -0
  51. package/dist/features/rules/index.js +1 -0
  52. package/dist/features/rules/interface.d.ts +22 -0
  53. package/dist/features/rules/interface.d.ts.map +1 -0
  54. package/dist/features/rules/interface.js +2 -0
  55. package/dist/features/rules/utils.d.ts +3 -0
  56. package/dist/features/rules/utils.d.ts.map +1 -0
  57. package/dist/features/rules/utils.js +31 -0
  58. package/dist/features/triggers/dtos.d.ts +9 -0
  59. package/dist/features/triggers/dtos.d.ts.map +1 -0
  60. package/dist/features/triggers/dtos.js +2 -0
  61. package/dist/features/triggers/index.d.ts +10 -0
  62. package/dist/features/triggers/index.d.ts.map +1 -0
  63. package/dist/features/triggers/index.js +57 -0
  64. package/dist/features/triggers/interface.d.ts +44 -0
  65. package/dist/features/triggers/interface.d.ts.map +1 -0
  66. package/dist/features/triggers/interface.js +2 -0
  67. package/dist/features/triggers/utils.d.ts +16 -0
  68. package/dist/features/triggers/utils.d.ts.map +1 -0
  69. package/dist/features/triggers/utils.js +153 -0
  70. package/dist/index.d.ts +19 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +84 -0
  73. package/dist/model.d.ts +2 -0
  74. package/dist/model.d.ts.map +1 -0
  75. package/dist/model.js +2 -0
  76. package/dist/services/api/index.d.ts +36 -0
  77. package/dist/services/api/index.d.ts.map +1 -0
  78. package/dist/services/api/index.js +36 -0
  79. package/dist/services/api/model.d.ts +33 -0
  80. package/dist/services/api/model.d.ts.map +1 -0
  81. package/dist/services/api/model.js +2 -0
  82. package/dist/services/api/utils.d.ts +16 -0
  83. package/dist/services/api/utils.d.ts.map +1 -0
  84. package/dist/services/api/utils.js +45 -0
  85. package/dist/services/aws/index.d.ts +13 -0
  86. package/dist/services/aws/index.d.ts.map +1 -0
  87. package/dist/services/aws/index.js +50 -0
  88. package/dist/services/index.d.ts +41 -0
  89. package/dist/services/index.d.ts.map +1 -0
  90. package/dist/services/index.js +14 -0
  91. package/dist/services/interface.d.ts +3 -0
  92. package/dist/services/interface.d.ts.map +1 -0
  93. package/dist/services/interface.js +2 -0
  94. package/dist/services/mongodb-atlas/index.d.ts +4 -0
  95. package/dist/services/mongodb-atlas/index.d.ts.map +1 -0
  96. package/dist/services/mongodb-atlas/index.js +483 -0
  97. package/dist/services/mongodb-atlas/model.d.ts +39 -0
  98. package/dist/services/mongodb-atlas/model.d.ts.map +1 -0
  99. package/dist/services/mongodb-atlas/model.js +2 -0
  100. package/dist/services/mongodb-atlas/utils.d.ts +8 -0
  101. package/dist/services/mongodb-atlas/utils.d.ts.map +1 -0
  102. package/dist/services/mongodb-atlas/utils.js +33 -0
  103. package/dist/state.d.ts +6 -0
  104. package/dist/state.d.ts.map +1 -0
  105. package/dist/state.js +18 -0
  106. package/dist/utils/context/helpers.d.ts +74 -0
  107. package/dist/utils/context/helpers.d.ts.map +1 -0
  108. package/dist/utils/context/helpers.js +60 -0
  109. package/dist/utils/context/index.d.ts +14 -0
  110. package/dist/utils/context/index.d.ts.map +1 -0
  111. package/dist/utils/context/index.js +50 -0
  112. package/dist/utils/context/interface.d.ts +18 -0
  113. package/dist/utils/context/interface.d.ts.map +1 -0
  114. package/dist/utils/context/interface.js +2 -0
  115. package/dist/utils/crypto/index.d.ts +19 -0
  116. package/dist/utils/crypto/index.d.ts.map +1 -0
  117. package/dist/utils/crypto/index.js +50 -0
  118. package/dist/utils/helpers/someAsync.d.ts +12 -0
  119. package/dist/utils/helpers/someAsync.d.ts.map +1 -0
  120. package/dist/utils/helpers/someAsync.js +56 -0
  121. package/dist/utils/index.d.ts +3 -0
  122. package/dist/utils/index.d.ts.map +1 -0
  123. package/dist/utils/index.js +11 -0
  124. package/dist/utils/initializer/exposeRoutes.d.ts +8 -0
  125. package/dist/utils/initializer/exposeRoutes.d.ts.map +1 -0
  126. package/dist/utils/initializer/exposeRoutes.js +41 -0
  127. package/dist/utils/initializer/registerPlugins.d.ts +19 -0
  128. package/dist/utils/initializer/registerPlugins.d.ts.map +1 -0
  129. package/dist/utils/initializer/registerPlugins.js +84 -0
  130. package/dist/utils/roles/helpers.d.ts +4 -0
  131. package/dist/utils/roles/helpers.d.ts.map +1 -0
  132. package/dist/utils/roles/helpers.js +47 -0
  133. package/dist/utils/roles/interface.d.ts +33 -0
  134. package/dist/utils/roles/interface.d.ts.map +1 -0
  135. package/dist/utils/roles/interface.js +2 -0
  136. package/dist/utils/roles/machines/commonValidators.d.ts +6 -0
  137. package/dist/utils/roles/machines/commonValidators.d.ts.map +1 -0
  138. package/dist/utils/roles/machines/commonValidators.js +34 -0
  139. package/dist/utils/roles/machines/index.d.ts +14 -0
  140. package/dist/utils/roles/machines/index.d.ts.map +1 -0
  141. package/dist/utils/roles/machines/index.js +27 -0
  142. package/dist/utils/roles/machines/interface.d.ts +46 -0
  143. package/dist/utils/roles/machines/interface.d.ts.map +1 -0
  144. package/dist/utils/roles/machines/interface.js +2 -0
  145. package/dist/utils/roles/machines/machine.d.ts +15 -0
  146. package/dist/utils/roles/machines/machine.d.ts.map +1 -0
  147. package/dist/utils/roles/machines/machine.js +97 -0
  148. package/dist/utils/roles/machines/read/A/index.d.ts +3 -0
  149. package/dist/utils/roles/machines/read/A/index.d.ts.map +1 -0
  150. package/dist/utils/roles/machines/read/A/index.js +27 -0
  151. package/dist/utils/roles/machines/read/B/index.d.ts +3 -0
  152. package/dist/utils/roles/machines/read/B/index.d.ts.map +1 -0
  153. package/dist/utils/roles/machines/read/B/index.js +36 -0
  154. package/dist/utils/roles/machines/read/C/index.d.ts +3 -0
  155. package/dist/utils/roles/machines/read/C/index.d.ts.map +1 -0
  156. package/dist/utils/roles/machines/read/C/index.js +38 -0
  157. package/dist/utils/roles/machines/read/D/index.d.ts +3 -0
  158. package/dist/utils/roles/machines/read/D/index.d.ts.map +1 -0
  159. package/dist/utils/roles/machines/read/D/index.js +26 -0
  160. package/dist/utils/roles/machines/read/D/validators.d.ts +4 -0
  161. package/dist/utils/roles/machines/read/D/validators.d.ts.map +1 -0
  162. package/dist/utils/roles/machines/read/D/validators.js +24 -0
  163. package/dist/utils/roles/machines/read/index.d.ts +2 -0
  164. package/dist/utils/roles/machines/read/index.d.ts.map +1 -0
  165. package/dist/utils/roles/machines/read/index.js +8 -0
  166. package/dist/utils/roles/machines/utils.d.ts +37 -0
  167. package/dist/utils/roles/machines/utils.d.ts.map +1 -0
  168. package/dist/utils/roles/machines/utils.js +54 -0
  169. package/dist/utils/roles/machines/write/A/index.d.ts +3 -0
  170. package/dist/utils/roles/machines/write/A/index.d.ts.map +1 -0
  171. package/dist/utils/roles/machines/write/A/index.js +29 -0
  172. package/dist/utils/roles/machines/write/B/index.d.ts +3 -0
  173. package/dist/utils/roles/machines/write/B/index.d.ts.map +1 -0
  174. package/dist/utils/roles/machines/write/B/index.js +47 -0
  175. package/dist/utils/roles/machines/write/C/index.d.ts +3 -0
  176. package/dist/utils/roles/machines/write/C/index.d.ts.map +1 -0
  177. package/dist/utils/roles/machines/write/C/index.js +26 -0
  178. package/dist/utils/roles/machines/write/C/validators.d.ts +4 -0
  179. package/dist/utils/roles/machines/write/C/validators.d.ts.map +1 -0
  180. package/dist/utils/roles/machines/write/C/validators.js +24 -0
  181. package/dist/utils/roles/machines/write/index.d.ts +2 -0
  182. package/dist/utils/roles/machines/write/index.d.ts.map +1 -0
  183. package/dist/utils/roles/machines/write/index.js +7 -0
  184. package/dist/utils/rules-matcher/interface.d.ts +338 -0
  185. package/dist/utils/rules-matcher/interface.d.ts.map +1 -0
  186. package/dist/utils/rules-matcher/interface.js +26 -0
  187. package/dist/utils/rules-matcher/utils.d.ts +11 -0
  188. package/dist/utils/rules-matcher/utils.d.ts.map +1 -0
  189. package/dist/utils/rules-matcher/utils.js +214 -0
  190. package/dist/utils/rules.d.ts +2 -0
  191. package/dist/utils/rules.d.ts.map +1 -0
  192. package/dist/utils/rules.js +22 -0
  193. package/jest.config.ts +24 -0
  194. package/package.json +63 -0
  195. package/project.json +10 -0
  196. package/rollup.config.js +17 -0
  197. package/src/auth/controller.ts +78 -0
  198. package/src/auth/dtos.ts +6 -0
  199. package/src/auth/plugins/jwt.ts +68 -0
  200. package/src/auth/providers/local-userpass/controller.ts +226 -0
  201. package/src/auth/providers/local-userpass/dtos.ts +40 -0
  202. package/src/auth/utils.ts +165 -0
  203. package/src/babel.config.json +3 -0
  204. package/src/constants.ts +22 -0
  205. package/src/fastify.d.ts +28 -0
  206. package/src/features/endpoints/index.ts +27 -0
  207. package/src/features/endpoints/interface.ts +29 -0
  208. package/src/features/endpoints/utils.ts +72 -0
  209. package/src/features/functions/controller.ts +102 -0
  210. package/src/features/functions/dtos.ts +41 -0
  211. package/src/features/functions/index.ts +21 -0
  212. package/src/features/functions/interface.ts +38 -0
  213. package/src/features/functions/utils.ts +82 -0
  214. package/src/features/rules/index.tsx +0 -0
  215. package/src/features/rules/interface.ts +24 -0
  216. package/src/features/rules/utils.ts +20 -0
  217. package/src/features/triggers/dtos.ts +9 -0
  218. package/src/features/triggers/index.ts +34 -0
  219. package/src/features/triggers/interface.ts +44 -0
  220. package/src/features/triggers/utils.ts +157 -0
  221. package/src/global.d.ts +0 -0
  222. package/src/index.ts +75 -0
  223. package/src/model.ts +1 -0
  224. package/src/services/api/index.ts +50 -0
  225. package/src/services/api/model.ts +38 -0
  226. package/src/services/api/utils.ts +39 -0
  227. package/src/services/aws/index.ts +48 -0
  228. package/src/services/index.ts +9 -0
  229. package/src/services/interface.ts +3 -0
  230. package/src/services/mongodb-atlas/index.ts +569 -0
  231. package/src/services/mongodb-atlas/model.ts +67 -0
  232. package/src/services/mongodb-atlas/utils.ts +44 -0
  233. package/src/state.ts +24 -0
  234. package/src/utils/__tests__/STEP_A_STATES.test.ts +54 -0
  235. package/src/utils/__tests__/STEP_B_STATES.test.ts +113 -0
  236. package/src/utils/__tests__/STEP_C_STATES.test.ts +87 -0
  237. package/src/utils/__tests__/STEP_D_STATES.test.ts +93 -0
  238. package/src/utils/__tests__/checkAdditionalFieldsFn.test.ts +45 -0
  239. package/src/utils/__tests__/checkApplyWhen.test.ts +49 -0
  240. package/src/utils/__tests__/checkFieldsPropertyExists.test.ts +47 -0
  241. package/src/utils/__tests__/checkIsValidFieldNameFn.test.ts +190 -0
  242. package/src/utils/__tests__/comparePassword.test.ts +38 -0
  243. package/src/utils/__tests__/evaluateDocumentsFiltersReadFn.test.ts +57 -0
  244. package/src/utils/__tests__/evaluateDocumentsFiltersWriteFn.test.ts +57 -0
  245. package/src/utils/__tests__/evaluateTopLevelReadFn.test.ts +58 -0
  246. package/src/utils/__tests__/evaluateTopLevelWriteFn.test.ts +66 -0
  247. package/src/utils/__tests__/exposeRoutes.test.ts +65 -0
  248. package/src/utils/__tests__/generateContextData.test.ts +75 -0
  249. package/src/utils/__tests__/getDefaultRule.test.ts +29 -0
  250. package/src/utils/__tests__/getKey.test.ts +12 -0
  251. package/src/utils/__tests__/getKeys.test.ts +11 -0
  252. package/src/utils/__tests__/getWinningRole.test.ts +66 -0
  253. package/src/utils/__tests__/hashPassword.test.ts +28 -0
  254. package/src/utils/__tests__/isEmpty.test.ts +17 -0
  255. package/src/utils/__tests__/logMachineInfo.test.ts +15 -0
  256. package/src/utils/__tests__/operators.test.ts +99 -0
  257. package/src/utils/__tests__/readFileContent.test.ts +35 -0
  258. package/src/utils/__tests__/registerPlugins.test.ts +59 -0
  259. package/src/utils/__tests__/rule.test.ts +51 -0
  260. package/src/utils/__tests__/rulesMatcherInterfaces.test.ts +57 -0
  261. package/src/utils/__tests__/rulesMatcherUtils.test.ts +56 -0
  262. package/src/utils/__tests__/someAsync.test.ts +55 -0
  263. package/src/utils/context/helpers.ts +71 -0
  264. package/src/utils/context/index.ts +52 -0
  265. package/src/utils/context/interface.ts +19 -0
  266. package/src/utils/crypto/index.ts +36 -0
  267. package/src/utils/helpers/someAsync.ts +24 -0
  268. package/src/utils/index.ts +5 -0
  269. package/src/utils/initializer/exposeRoutes.ts +26 -0
  270. package/src/utils/initializer/registerPlugins.ts +97 -0
  271. package/src/utils/roles/helpers.ts +47 -0
  272. package/src/utils/roles/interface.ts +42 -0
  273. package/src/utils/roles/machines/commonValidators.ts +24 -0
  274. package/src/utils/roles/machines/index.ts +20 -0
  275. package/src/utils/roles/machines/interface.ts +46 -0
  276. package/src/utils/roles/machines/machine.ts +85 -0
  277. package/src/utils/roles/machines/read/A/index.ts +19 -0
  278. package/src/utils/roles/machines/read/B/index.ts +31 -0
  279. package/src/utils/roles/machines/read/C/index.ts +30 -0
  280. package/src/utils/roles/machines/read/D/index.ts +20 -0
  281. package/src/utils/roles/machines/read/D/validators.ts +24 -0
  282. package/src/utils/roles/machines/read/index.ts +6 -0
  283. package/src/utils/roles/machines/utils.ts +54 -0
  284. package/src/utils/roles/machines/write/A/index.ts +25 -0
  285. package/src/utils/roles/machines/write/B/index.ts +43 -0
  286. package/src/utils/roles/machines/write/C/index.ts +20 -0
  287. package/src/utils/roles/machines/write/C/validators.ts +24 -0
  288. package/src/utils/roles/machines/write/index.ts +5 -0
  289. package/src/utils/rules-matcher/interface.ts +365 -0
  290. package/src/utils/rules-matcher/utils.ts +281 -0
  291. package/src/utils/rules.ts +19 -0
  292. package/tsconfig.json +28 -0
@@ -0,0 +1,102 @@
1
+ import { ObjectId } from 'bson'
2
+
3
+
4
+ import { services } from '../../services'
5
+ import { StateManager } from '../../state'
6
+ import { GenerateContext } from '../../utils/context'
7
+ import { Base64Function, FunctionCallBase64Dto, FunctionCallDto } from './dtos'
8
+ import { FunctionController } from './interface'
9
+ import { executeQuery } from './utils'
10
+
11
+ /**
12
+ * > Creates a pre handler for every query
13
+ * @param app -> the fastify instance
14
+ * @param functionsList -> the list of all functions
15
+ * @param rules -> all the rules
16
+ */
17
+ export const functionsController: FunctionController = async (
18
+ app,
19
+ { functionsList, rules }
20
+ ) => {
21
+ app.addHook('preHandler', app.jwtAuthentication)
22
+
23
+ app.post<{ Body: FunctionCallDto }>('/call', async (req, res) => {
24
+ const { user } = req
25
+ const { name: method, arguments: args } = req.body
26
+
27
+ if ('service' in req.body) {
28
+ const serviceFn = services[req.body.service]
29
+ if (!serviceFn) {
30
+ throw new Error(`Service "${req.body.service}" does not exist`)
31
+ }
32
+ const [{ database, collection, query, update, document }] = args
33
+
34
+ const currentMethod = serviceFn(app, { rules, user })
35
+ .db(database)
36
+ .collection(collection)[method]
37
+
38
+ const operatorsByType = await executeQuery({
39
+ currentMethod,
40
+ query,
41
+ update,
42
+ document
43
+ })
44
+ return operatorsByType[method as keyof typeof operatorsByType]()
45
+ }
46
+
47
+ const currentFunction = functionsList[method]
48
+
49
+ if (!currentFunction) {
50
+ throw new Error(`Function "${req.body.name}" does not exist`)
51
+ }
52
+
53
+ if (currentFunction.private) {
54
+ throw new Error(`Function "${req.body.name}" is private`)
55
+ }
56
+
57
+
58
+ const result = await GenerateContext({
59
+ args: req.body.arguments,
60
+ app,
61
+ rules,
62
+ user: { ...user, _id: new ObjectId(user.id) },
63
+ currentFunction,
64
+ functionsList,
65
+ services
66
+ })
67
+ res.type("application/json")
68
+ return JSON.stringify(result)
69
+ })
70
+ app.get<{
71
+ Querystring: FunctionCallBase64Dto,
72
+ }>('/call', async (req, res) => {
73
+ const { query, user } = req
74
+ const { baas_request, stitch_request } = query
75
+
76
+ const config: Base64Function = JSON.parse(Buffer.from(baas_request || stitch_request || "", "base64").toString("utf8"));
77
+ const [{ database, collection }] = config.arguments
78
+ const app = StateManager.select("app")
79
+ const services = StateManager.select("services")
80
+
81
+ const changeStream = await services["mongodb-atlas"](app, {
82
+ user,
83
+ rules
84
+ }).db(database).collection(collection).watch([], { fullDocument: "whenAvailable" })
85
+
86
+ res.header('Content-Type', 'text/event-stream');
87
+ res.header('Cache-Control', 'no-cache');
88
+ res.header('Connection', 'keep-alive');
89
+ res.raw.flushHeaders();
90
+
91
+
92
+ changeStream.on('change', (change) => {
93
+ res.raw.write(`data: ${JSON.stringify(change)}\n\n`);
94
+ });
95
+
96
+ req.raw.on('close', () => {
97
+ changeStream.close();
98
+ });
99
+ })
100
+
101
+
102
+ }
@@ -0,0 +1,41 @@
1
+ import { Document } from "mongodb"
2
+ import { Arguments } from '../../auth/dtos'
3
+ import { GetOperatorsFunction } from '../../services/mongodb-atlas/model'
4
+
5
+ type MethodName = Exclude<keyof ReturnType<GetOperatorsFunction>, 'match'>
6
+
7
+ export type FunctionCallDto =
8
+ | {
9
+ name: MethodName
10
+ arguments: ArgumentsData
11
+ }
12
+ | {
13
+ arguments: ArgumentsData
14
+ name: MethodName
15
+ service: 'mongodb-atlas'
16
+ }
17
+
18
+ export type FunctionCallBase64Dto = {
19
+ baas_request?: string
20
+ stitch_request?: string
21
+ }
22
+
23
+ type ArgumentsData = Arguments<{
24
+ database: string
25
+ collection: string
26
+ query: Parameters<GetOperatorsFunction>
27
+ update: Document
28
+ document: Document
29
+ }>
30
+
31
+
32
+ export type Base64Function = {
33
+ name: string
34
+ arguments: Argument[]
35
+ service: string
36
+ }
37
+
38
+ type Argument = {
39
+ database: string
40
+ collection: string
41
+ }
@@ -0,0 +1,21 @@
1
+ import { API_VERSION } from '../../constants'
2
+ import { functionsController } from './controller'
3
+ import { RegisterFunctionsParams } from './interface'
4
+
5
+ /**
6
+ * > Registers the functions controller
7
+ * @param app -> the fastify instance
8
+ * @param functionsList -> the list of all functions
9
+ * @param rulesList -> the list of all rules
10
+ */
11
+ export const registerFunctions = async ({
12
+ app,
13
+ rulesList,
14
+ functionsList
15
+ }: RegisterFunctionsParams) => {
16
+ await app.register(functionsController, {
17
+ functionsList,
18
+ rules: rulesList,
19
+ prefix: `${API_VERSION}/app/:appId/functions`
20
+ })
21
+ }
@@ -0,0 +1,38 @@
1
+ import { FastifyInstance } from 'fastify'
2
+ import { Document } from "mongodb"
3
+ import { GetOperatorsFunction } from '../../services/mongodb-atlas/model'
4
+ import { Rules } from '../rules/interface'
5
+
6
+ export interface FunctionConfig {
7
+ name: string
8
+ private?: boolean
9
+ run_as_system?: boolean
10
+ disable_arg_logs?: boolean
11
+ }
12
+
13
+ export type Function = Omit<FunctionConfig, 'name'> & { code: string }
14
+
15
+ export type Functions = Record<string, Function>
16
+
17
+ export type RegisterFunctionsParams = {
18
+ app: FastifyInstance
19
+ functionsList: Functions
20
+ rulesList: Rules
21
+ }
22
+
23
+ export type ExecuteQueryParams = {
24
+ currentMethod: ReturnType<GetOperatorsFunction>[keyof ReturnType<GetOperatorsFunction>]
25
+ query: Parameters<GetOperatorsFunction>
26
+ update: Document
27
+ document: Document
28
+ }
29
+
30
+ type FunctionsControllerOptions = {
31
+ functionsList: Functions
32
+ rules: Rules
33
+ }
34
+
35
+ export type FunctionController = (
36
+ app: FastifyInstance,
37
+ { functionsList, rules }: FunctionsControllerOptions
38
+ ) => Promise<void>
@@ -0,0 +1,82 @@
1
+ import fs from 'fs'
2
+ import path from 'node:path'
3
+ import { EJSON } from 'bson'
4
+ import { GetOperatorsFunction } from '../../services/mongodb-atlas/model'
5
+ import { ExecuteQueryParams, Functions } from './interface'
6
+
7
+ /**
8
+ * > Loads the functions config json file
9
+ * @testable
10
+ */
11
+ export const loadFunctions = async (rootDir = process.cwd()): Promise<Functions> => {
12
+ const fnDir = 'functions'
13
+ const configFile = path.join(rootDir, fnDir, 'config.json')
14
+ const config = JSON.parse(fs.readFileSync(configFile, 'utf-8')) as {
15
+ name: string
16
+ }[]
17
+
18
+ const functions = config.reduce((acc, { name, ...opts }) => {
19
+ const extensions = ['.js', '.ts'];
20
+ let code = '';
21
+ const fnPath = extensions
22
+ .map(ext => path.join(rootDir, fnDir, `${name}${ext}`))
23
+ .find(fs.existsSync);
24
+
25
+ if (!fnPath) {
26
+ throw new Error(`File ${name}.js or ${name}.ts not found`);
27
+ }
28
+ code = fs.readFileSync(fnPath, 'utf-8');
29
+ acc[name] = { code, ...opts }
30
+
31
+ return acc
32
+ }, {} as Functions)
33
+
34
+ return functions
35
+ }
36
+
37
+ /**
38
+ * > Executes a single query
39
+ * @param currentMethod -> the method that should be called
40
+ * @param query -> the query data
41
+ * @param update -> the update Document that should be deserialized
42
+ */
43
+ export const executeQuery = async ({
44
+ currentMethod,
45
+ query,
46
+ update,
47
+ document
48
+ }: ExecuteQueryParams) => {
49
+ return {
50
+ find: async () =>
51
+ await (currentMethod as ReturnType<GetOperatorsFunction>['find'])(
52
+ EJSON.deserialize(query)
53
+ ).toArray(),
54
+ findOne: () =>
55
+ (currentMethod as ReturnType<GetOperatorsFunction>['findOne'])(
56
+ EJSON.deserialize(query)
57
+ ),
58
+ deleteOne: () =>
59
+ (currentMethod as ReturnType<GetOperatorsFunction>['deleteOne'])(
60
+ EJSON.deserialize(query)
61
+ ),
62
+ insertOne: () =>
63
+ (currentMethod as ReturnType<GetOperatorsFunction>['insertOne'])(
64
+ EJSON.deserialize(document)
65
+ ),
66
+ updateOne: () => currentMethod(EJSON.deserialize(query), EJSON.deserialize(update)),
67
+ aggregate: () =>
68
+ (currentMethod as ReturnType<GetOperatorsFunction>['aggregate'])(
69
+ EJSON.deserialize(query)
70
+ ),
71
+ insertMany: () =>
72
+ (currentMethod as ReturnType<GetOperatorsFunction>['insertMany'])(
73
+ EJSON.deserialize(query)
74
+ ),
75
+ updateMany: () =>
76
+ (currentMethod as ReturnType<GetOperatorsFunction>['updateMany'])(
77
+ EJSON.deserialize(query), EJSON.deserialize(update)
78
+ ),
79
+
80
+
81
+ }
82
+ }
File without changes
@@ -0,0 +1,24 @@
1
+ export interface Filter {
2
+ name: string
3
+ query: Record<string, unknown>
4
+ apply_when: Record<string, unknown>
5
+ }
6
+
7
+ export interface Role {
8
+ name: string
9
+ apply_when: Record<string, unknown>
10
+ insert: boolean
11
+ delete: boolean
12
+ search: boolean
13
+ read: boolean
14
+ write: boolean
15
+ }
16
+
17
+ export interface RulesConfig {
18
+ database: string
19
+ collection: string
20
+ filters: Filter[]
21
+ roles: Role[]
22
+ }
23
+
24
+ export type Rules = Record<string, RulesConfig>
@@ -0,0 +1,20 @@
1
+ import fs from 'fs'
2
+ import path from 'node:path'
3
+ import { readJsonContent } from '../../utils'
4
+ import { Rules, RulesConfig } from './interface'
5
+
6
+ export const loadRules = async (rootDir = process.cwd()): Promise<Rules> => {
7
+ const rulesRoot = path.join(rootDir, 'data_sources', 'mongodb-atlas')
8
+ const files = fs.readdirSync(rulesRoot, { recursive: true }) as string[]
9
+ const rulesFiles = files.filter((x) => (x as string).endsWith('rules.json'))
10
+
11
+ const rulesByCollection = rulesFiles.reduce((acc, rulesFile) => {
12
+ const filePath = path.join(rulesRoot, rulesFile)
13
+ const collectionRules = readJsonContent(filePath) as RulesConfig
14
+ acc[collectionRules.collection] = collectionRules
15
+
16
+ return acc
17
+ }, {} as Rules)
18
+
19
+ return rulesByCollection
20
+ }
@@ -0,0 +1,9 @@
1
+ import { FastifyInstance } from 'fastify'
2
+ import { Functions } from '../functions/interface'
3
+ import { Triggers } from './interface'
4
+
5
+ export type ActivateTriggersParams = {
6
+ fastify: FastifyInstance
7
+ triggersList: Triggers
8
+ functionsList: Functions
9
+ }
@@ -0,0 +1,34 @@
1
+ import { services } from '../../services'
2
+ import { Function, Functions } from '../functions/interface'
3
+ import { ActivateTriggersParams } from './dtos'
4
+ import { TRIGGER_HANDLERS } from './utils'
5
+
6
+ /**
7
+ * > Used to activate all app triggers
8
+ * @testable
9
+ * @param fastify -> the fastify instance
10
+ * @param triggersList -> the list of all triggers
11
+ * @param functionsList -> the list of all functions
12
+ */
13
+ export const activateTriggers = async ({
14
+ fastify,
15
+ triggersList,
16
+ functionsList
17
+ }: ActivateTriggersParams) => {
18
+ try {
19
+ for await (const trigger of triggersList) {
20
+ const { content } = trigger
21
+ const { type, config, event_processors } = content
22
+
23
+ const functionName: keyof Functions = event_processors.FUNCTION.config.function_name
24
+ const triggerHandler = functionsList[functionName] as Function
25
+
26
+ await TRIGGER_HANDLERS[type]({ config, triggerHandler, app: fastify, services, functionsList })
27
+
28
+ }
29
+ } catch (e) {
30
+ console.error('Error while activating triggers', (e as Error).message)
31
+ }
32
+ }
33
+
34
+
@@ -0,0 +1,44 @@
1
+ import { FastifyInstance } from "fastify"
2
+ import { Services } from "../../services/interface"
3
+ import { Function, Functions } from "../functions/interface"
4
+
5
+ export interface Trigger {
6
+ name: string
7
+ type: TriggerType
8
+ disabled: boolean
9
+ config: Config
10
+ event_processors: {
11
+ FUNCTION: {
12
+ config: {
13
+ function_name: string
14
+ }
15
+ }
16
+ }
17
+ }
18
+
19
+ type Config = {
20
+ collection: string
21
+ database: string
22
+ full_document: boolean
23
+ full_document_before_change: boolean
24
+ match: Record<string, unknown>
25
+ operation_types: string[]
26
+ project: Record<string, unknown>
27
+ service_name: string
28
+ skip_catchup_events: boolean
29
+ tolerate_resume_errors: boolean
30
+ unordered: boolean
31
+ schedule: string
32
+ }
33
+
34
+ export type TriggerType = "SCHEDULED" | "DATABASE" | "AUTHENTICATION"
35
+ export type Triggers = { fileName: string; content: Trigger }[]
36
+
37
+
38
+ export type HandlerParams = {
39
+ config: Config,
40
+ triggerHandler: Function,
41
+ app: FastifyInstance,
42
+ services: Services,
43
+ functionsList: Functions
44
+ }
@@ -0,0 +1,157 @@
1
+ import fs from 'fs'
2
+ import path from 'node:path'
3
+ import cron from "node-cron"
4
+ import { AUTH_CONFIG } from '../../constants'
5
+ import { readJsonContent } from '../../utils'
6
+ import { GenerateContext } from '../../utils/context'
7
+ import { HandlerParams, Trigger, Triggers } from './interface'
8
+
9
+ /**
10
+ * Loads trigger files from the specified directory and returns them as an array of objects.
11
+ * Each object contains the file name and the parsed JSON content.
12
+ *
13
+ * @testable
14
+ * @param {string} [rootDir=process.cwd()] - The root directory from which to load the triggers. Defaults to the current working directory.
15
+ * @returns {Promise<Triggers>} A promise that resolves to an array of trigger objects.
16
+ */
17
+ export const loadTriggers = async (rootDir = process.cwd()): Promise<Triggers> => {
18
+ const triggersPath = path.join(rootDir, 'triggers')
19
+ const files = fs.readdirSync(triggersPath)
20
+
21
+ const triggers = files
22
+ .filter((fileName) => fileName.endsWith('.json'))
23
+ .map((fileName) => ({
24
+ fileName,
25
+ content: readJsonContent(path.join(triggersPath, fileName)) as Trigger
26
+ }))
27
+
28
+ return triggers
29
+ }
30
+
31
+ /**
32
+ * Handles the scheduling of a cron job and triggers the appropriate function.
33
+ *
34
+ * @testable
35
+ * @param {Object} params - The parameters for the handler.
36
+ * @param {Object} params.config - Configuration object for the cron trigger.
37
+ * @param {string} params.config.schedule - Cron schedule string (e.g., "* * * * *" for every minute).
38
+ * @param {Function} params.triggerHandler - The function to be triggered when the cron job executes.
39
+ * @param {Array<Function>} params.functionsList - List of available functions.
40
+ * @param {Object} params.services - Services available to the handler.
41
+ * @param {Object} params.app - The app instance for context.
42
+ */
43
+ const handleCronTrigger = async ({ config, triggerHandler, functionsList, services, app }: HandlerParams) => {
44
+ cron.schedule(config.schedule, async () => {
45
+ await GenerateContext({
46
+ args: [],
47
+ app,
48
+ rules: {},
49
+ user: {},
50
+ currentFunction: triggerHandler,
51
+ functionsList,
52
+ services
53
+ })
54
+
55
+ });
56
+ }
57
+
58
+ const handleAuthenticationTrigger = async ({ config, triggerHandler, functionsList, services, app }: HandlerParams) => {
59
+ const { database } = config
60
+ const pipeline = [{
61
+ $match: {
62
+ operationType: { $in: ["INSERT"] }
63
+ }
64
+ }]
65
+ const changeStream = app.mongo.client.db(database).collection(AUTH_CONFIG.authCollection).watch(pipeline, {
66
+ fullDocument: 'whenAvailable'
67
+ })
68
+ changeStream.on('change', async function (change) {
69
+ const document = change["fullDocument" as keyof typeof change] as Record<string, string> //TODO -> define user type
70
+
71
+ if (document) {
72
+ delete document.password
73
+
74
+ const currentUser = { ...document }
75
+ delete currentUser.password
76
+ await GenerateContext({
77
+ args: [{ user: currentUser }],
78
+ app,
79
+ rules: {},
80
+ user: {},
81
+ currentFunction: triggerHandler,
82
+ functionsList,
83
+ services
84
+ })
85
+
86
+ }
87
+
88
+ })
89
+
90
+ }
91
+
92
+ /**
93
+ * Handles a database trigger by watching changes in a specified collection and triggering the appropriate handler.
94
+ *
95
+ * @testable
96
+ * @param {Object} params - The parameters for the handler.
97
+ * @param {Object} params.config - Configuration object for the database trigger.
98
+ * @param {string} params.config.database - The name of the database to watch.
99
+ * @param {string} params.config.collection - The name of the collection to watch.
100
+ * @param {Array<string>} [params.config.operation_types=[]] - List of operation types to watch (e.g., "insert", "update").
101
+ * @param {Object} [params.config.match={}] - Additional match criteria for the change stream.
102
+ * @param {Object} [params.config.project={}] - Projection to apply to the change stream results.
103
+ * @param {boolean} [params.config.full_document] - Whether to include the full document in the change stream results.
104
+ * @param {boolean} [params.config.full_document_before_change] - Whether to include the full document before the change.
105
+ * @param {Function} params.triggerHandler - The function to be triggered on database changes.
106
+ * @param {Array<Function>} params.functionsList - List of available functions.
107
+ * @param {Object} params.services - Services available to the handler.
108
+ * @param {Object} params.app - The app instance for context.
109
+ */
110
+ const handleDataBaseTrigger = async ({ config, triggerHandler, functionsList, services, app }: HandlerParams) => {
111
+ const {
112
+ database,
113
+ collection: collectionName,
114
+ operation_types = [],
115
+ match = {},
116
+ project = {}
117
+ } = config
118
+
119
+ const collection = app.mongo.client.db(database).collection(collectionName)
120
+ const pipeline = [
121
+ {
122
+ $match: {
123
+ operationType: { $in: operation_types.map((op: string) => op.toLowerCase()) },
124
+ ...match
125
+ }
126
+ },
127
+ Object.keys(project).length
128
+ ? {
129
+ $project: project
130
+ }
131
+ : undefined
132
+ ].filter(Boolean) as Parameters<typeof collection.watch>[0]
133
+ const changeStream = collection.watch(pipeline, {
134
+ fullDocument: config.full_document ? 'whenAvailable' : undefined,
135
+ fullDocumentBeforeChange: config.full_document_before_change
136
+ ? 'whenAvailable'
137
+ : undefined
138
+ })
139
+ changeStream.on('change', async function (change) {
140
+ await GenerateContext({
141
+ args: [change],
142
+ app,
143
+ rules: {},
144
+ user: {},
145
+ currentFunction: triggerHandler,
146
+ functionsList,
147
+ services
148
+ })
149
+ })
150
+ // TODO -> gestire close dello stream
151
+ }
152
+
153
+ export const TRIGGER_HANDLERS = {
154
+ SCHEDULED: handleCronTrigger,
155
+ DATABASE: handleDataBaseTrigger,
156
+ AUTHENTICATION: handleAuthenticationTrigger
157
+ }
File without changes
package/src/index.ts ADDED
@@ -0,0 +1,75 @@
1
+ import 'dotenv/config'
2
+ import Fastify from 'fastify'
3
+ import { DEFAULT_CONFIG } from './constants'
4
+ import { generateEndpoints } from './features/endpoints'
5
+ import { loadEndpoints } from './features/endpoints/utils'
6
+ import { registerFunctions } from './features/functions'
7
+ import { loadFunctions } from './features/functions/utils'
8
+ import { loadRules } from './features/rules/utils'
9
+ import { activateTriggers } from './features/triggers'
10
+ import { loadTriggers } from './features/triggers/utils'
11
+ import { services } from './services'
12
+ import { StateManager } from './state'
13
+ import { exposeRoutes } from './utils/initializer/exposeRoutes'
14
+ import { registerPlugins } from './utils/initializer/registerPlugins'
15
+ export * from "./model";
16
+
17
+ export type InitializeConfig = {
18
+ projectId: string
19
+ mongodbUrl?: string
20
+ jwtSecret?: string
21
+ port?: number
22
+ host?: string
23
+ }
24
+
25
+ /**
26
+ * > Used to initialize fastify app
27
+ * @param projectId -> the project id string
28
+ * @param host -> the host string
29
+ * @param jwtSecret -> connection jwt
30
+ * @param port -> the serve port number
31
+ * @param mongodbUrl -> the database connection string
32
+ */
33
+ export async function initialize({
34
+ projectId,
35
+ host,
36
+ jwtSecret = DEFAULT_CONFIG.JWT_SECRET,
37
+ port = DEFAULT_CONFIG.PORT,
38
+ mongodbUrl = DEFAULT_CONFIG.MONGODB_URL
39
+ }: InitializeConfig) {
40
+ const fastify = Fastify({
41
+ logger: false
42
+ })
43
+
44
+
45
+ const functionsList = await loadFunctions()
46
+ const triggersList = await loadTriggers()
47
+ const endpointsList = await loadEndpoints()
48
+ const rulesList = await loadRules()
49
+ const stateConfig = {
50
+ functions: functionsList,
51
+ triggers: triggersList,
52
+ endpoints: endpointsList,
53
+ rules: rulesList,
54
+ app: fastify,
55
+ services
56
+ }
57
+
58
+ Object.entries(stateConfig).forEach(([key, value]) => StateManager.setData(key as Parameters<typeof StateManager.setData>[0], value))
59
+
60
+ await registerPlugins({
61
+ register: fastify.register,
62
+ mongodbUrl,
63
+ jwtSecret,
64
+ functionsList
65
+ })
66
+ await exposeRoutes(fastify)
67
+ await registerFunctions({ app: fastify, functionsList, rulesList })
68
+
69
+ await generateEndpoints({ app: fastify, functionsList, endpointsList })
70
+
71
+ fastify.ready(() => activateTriggers({ fastify, triggersList, functionsList }))
72
+ await fastify.listen({ port, host })
73
+
74
+ fastify.log.info(`[${projectId}] Server listening on port ${port}`)
75
+ }
package/src/model.ts ADDED
@@ -0,0 +1 @@
1
+ export { Document, WithId } from 'mongodb';