@paralect/hive 0.1.49 → 0.1.50-alpha.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 (145) hide show
  1. package/.hive/.babelrc +3 -0
  2. package/.hive/.cursor/commands/add-endpoint.md +262 -0
  3. package/.hive/.cursor/commands/add-handler.md +137 -0
  4. package/.hive/.cursor/commands/add-middleware.md +95 -0
  5. package/.hive/.cursor/commands/add-resource.md +71 -0
  6. package/.hive/.cursor/commands/add-scheduler.md +138 -0
  7. package/.hive/.cursor/commands/add-service.md +188 -0
  8. package/.hive/.cursor/skills/hive-auth/SKILL.md +134 -0
  9. package/.hive/.cursor/skills/hive-database/SKILL.md +103 -0
  10. package/.hive/.cursor/skills/hive-endpoint/SKILL.md +103 -0
  11. package/.hive/.cursor/skills/hive-handler/SKILL.md +88 -0
  12. package/.hive/.cursor/skills/hive-mapping/SKILL.md +85 -0
  13. package/.hive/.cursor/skills/hive-middleware/SKILL.md +104 -0
  14. package/.hive/.cursor/skills/hive-overview/SKILL.md +50 -0
  15. package/.hive/.cursor/skills/hive-scheduler/SKILL.md +94 -0
  16. package/.hive/.cursor/skills/hive-schema/SKILL.md +73 -0
  17. package/.hive/.cursor/skills/hive-service/SKILL.md +90 -0
  18. package/.hive/.dockerignore +1 -0
  19. package/.hive/Dockerfile +22 -0
  20. package/.hive/Dockerfile.dev +33 -0
  21. package/.hive/Dockerfile.prod +29 -0
  22. package/.hive/README.md +11 -0
  23. package/.hive/bin/deploy.sh +5 -0
  24. package/.hive/bin/start.sh +2 -0
  25. package/.hive/bootstrap-hive.js +118 -0
  26. package/.hive/deploy/api/Chart.yaml +6 -0
  27. package/.hive/deploy/api/staging.yaml +3 -0
  28. package/.hive/deploy/api/templates/deployment.yaml +44 -0
  29. package/.hive/deploy/api/templates/ingress.yaml +26 -0
  30. package/.hive/deploy/api/templates/service.yaml +14 -0
  31. package/.hive/deploy/script/Dockerfile +39 -0
  32. package/.hive/deploy/script/package-lock.json +1499 -0
  33. package/.hive/deploy/script/package.json +12 -0
  34. package/.hive/deploy/script/src/config.js +48 -0
  35. package/.hive/deploy/script/src/index.js +108 -0
  36. package/.hive/deploy/script/src/util.js +19 -0
  37. package/.hive/initial-data.json +176 -0
  38. package/.hive/package-lock.json +10242 -0
  39. package/.hive/package.json +98 -0
  40. package/.hive/ship_logo.png +0 -0
  41. package/.hive/src/app-config/app.js +3 -0
  42. package/.hive/src/app-config/assertEnv.js +15 -0
  43. package/.hive/src/app-config/index.js +62 -0
  44. package/.hive/src/app.js +69 -0
  45. package/.hive/src/assets/emails/components/header.mjml +13 -0
  46. package/.hive/src/assets/emails/dist/.gitkeep +0 -0
  47. package/.hive/src/assets/emails/signup-welcome.mjml +34 -0
  48. package/.hive/src/assets/emails/styles/index.mjml +77 -0
  49. package/.hive/src/autoMap/addHandlers.js +142 -0
  50. package/.hive/src/autoMap/getDependentFields.js +37 -0
  51. package/.hive/src/autoMap/mapSchema.js +99 -0
  52. package/.hive/src/autoMap/schemaMappings.js +13 -0
  53. package/.hive/src/autoMap/schemaMappings.json +3 -0
  54. package/.hive/src/bullMqBus.js +21 -0
  55. package/.hive/src/bullMqWrapper.js +23 -0
  56. package/.hive/src/db.js +52 -0
  57. package/.hive/src/emails/MyEmailComponent.jsx +14 -0
  58. package/.hive/src/emails/compiled/MyEmailComponent.js +18 -0
  59. package/.hive/src/emails/compiled/compiled/MyEmailComponent.js +18 -0
  60. package/.hive/src/helpers/db/ifUpdated.js +22 -0
  61. package/.hive/src/helpers/getMiddlewares.js +38 -0
  62. package/.hive/src/helpers/getResourceEndpoints.js +28 -0
  63. package/.hive/src/helpers/getResources.js +32 -0
  64. package/.hive/src/helpers/getSchemas.js +50 -0
  65. package/.hive/src/helpers/importHandlers.js +29 -0
  66. package/.hive/src/helpers/isZodArray.js +13 -0
  67. package/.hive/src/helpers/prettierFormat.js +8 -0
  68. package/.hive/src/helpers/schema/db.schema.js +9 -0
  69. package/.hive/src/helpers/schema/pagination.schema.js +14 -0
  70. package/.hive/src/ioEmitter.js +9 -0
  71. package/.hive/src/jsconfig.json +5 -0
  72. package/.hive/src/lib/node-mongo/.github/workflows/npm-publish.yml +32 -0
  73. package/.hive/src/lib/node-mongo/API.md +654 -0
  74. package/.hive/src/lib/node-mongo/CHANGELOG.md +98 -0
  75. package/.hive/src/lib/node-mongo/README.md +97 -0
  76. package/.hive/src/lib/node-mongo/package-lock.json +3682 -0
  77. package/.hive/src/lib/node-mongo/package.json +74 -0
  78. package/.hive/src/lib/node-mongo/src/index.js +64 -0
  79. package/.hive/src/lib/node-mongo/src/mongo-query-service.js +78 -0
  80. package/.hive/src/lib/node-mongo/src/mongo-service-error.js +15 -0
  81. package/.hive/src/lib/node-mongo/src/mongo-service.js +303 -0
  82. package/.hive/src/logger.js +43 -0
  83. package/.hive/src/middlewares/allowNoAuth.js +9 -0
  84. package/.hive/src/middlewares/attachUser.js +41 -0
  85. package/.hive/src/middlewares/global/extractUserTokens.js +15 -0
  86. package/.hive/src/middlewares/global/tryToAttachUser.js +33 -0
  87. package/.hive/src/middlewares/isAuthorized.js +18 -0
  88. package/.hive/src/middlewares/shouldExist.js +37 -0
  89. package/.hive/src/middlewares/shouldNotExist.js +19 -0
  90. package/.hive/src/middlewares/uploadFile.js +5 -0
  91. package/.hive/src/middlewares/validate.js +32 -0
  92. package/.hive/src/migrations/migration.js +8 -0
  93. package/.hive/src/migrations/migration.service.js +73 -0
  94. package/.hive/src/migrations/migrations/1.js +22 -0
  95. package/.hive/src/migrations/migrations-log/migration-log.schema.js +13 -0
  96. package/.hive/src/migrations/migrations-log/migration-log.service.js +50 -0
  97. package/.hive/src/migrations/migrations.schema.js +6 -0
  98. package/.hive/src/migrations/migrator.js +75 -0
  99. package/.hive/src/migrator.js +4 -0
  100. package/.hive/src/resources/_dev/endpoints/triggerSchedulerHandler.js +32 -0
  101. package/.hive/src/resources/health/endpoints/get.js +19 -0
  102. package/.hive/src/resources/schemaMappings/schemaMappings.schema.js +6 -0
  103. package/.hive/src/resources/tokens/methods/generateSecureToken.js +9 -0
  104. package/.hive/src/resources/tokens/methods/setToken.js +8 -0
  105. package/.hive/src/resources/tokens/methods/storeToken.js +35 -0
  106. package/.hive/src/resources/tokens/tokens.schema.js +11 -0
  107. package/.hive/src/resources/users/endpoints/getCurrentUser.js +14 -0
  108. package/.hive/src/resources/users/endpoints/getUserProfile.js +19 -0
  109. package/.hive/src/resources/users/handlers/test.js +1 -0
  110. package/.hive/src/resources/users/methods/ensureUserCreated.js +68 -0
  111. package/.hive/src/resources/users/users.schema.js +16 -0
  112. package/.hive/src/routes/index.js +172 -0
  113. package/.hive/src/routes/middlewares/attachCustomErrors.js +28 -0
  114. package/.hive/src/routes/middlewares/routeErrorHandler.js +27 -0
  115. package/.hive/src/scheduler/handlers/sendDailyReport.example.js +7 -0
  116. package/.hive/src/scheduler.js +32 -0
  117. package/.hive/src/security.util.js +38 -0
  118. package/.hive/src/services/emailService.js +15 -0
  119. package/.hive/src/services/globalTest.js +0 -0
  120. package/.hive/src/services/setCookie.js +21 -0
  121. package/.hive/src/socketIo.js +99 -0
  122. package/.hive/tsconfig.json +31 -0
  123. package/cli/helpers/docker.js +59 -0
  124. package/cli/helpers/envCheck.js +123 -0
  125. package/cli/helpers/findPort.js +32 -0
  126. package/cli/hive.js +84 -12
  127. package/package.json +1 -1
  128. package/test-app/.cursor/commands/add-endpoint.md +262 -0
  129. package/test-app/.cursor/commands/add-handler.md +137 -0
  130. package/test-app/.cursor/commands/add-middleware.md +95 -0
  131. package/test-app/.cursor/commands/add-resource.md +71 -0
  132. package/test-app/.cursor/commands/add-scheduler.md +138 -0
  133. package/test-app/.cursor/commands/add-service.md +188 -0
  134. package/test-app/.cursor/skills/hive-auth/SKILL.md +134 -0
  135. package/test-app/.cursor/skills/hive-database/SKILL.md +103 -0
  136. package/test-app/.cursor/skills/hive-endpoint/SKILL.md +103 -0
  137. package/test-app/.cursor/skills/hive-handler/SKILL.md +88 -0
  138. package/test-app/.cursor/skills/hive-mapping/SKILL.md +85 -0
  139. package/test-app/.cursor/skills/hive-middleware/SKILL.md +104 -0
  140. package/test-app/.cursor/skills/hive-overview/SKILL.md +50 -0
  141. package/test-app/.cursor/skills/hive-scheduler/SKILL.md +94 -0
  142. package/test-app/.cursor/skills/hive-schema/SKILL.md +73 -0
  143. package/test-app/.cursor/skills/hive-service/SKILL.md +90 -0
  144. package/test-app/package-lock.json +8684 -0
  145. package/test-app/package.json +21 -0
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "koa-api-starter",
3
+ "version": "2.3.0",
4
+ "description": "Koa api",
5
+ "private": false,
6
+ "main": "src/app.js",
7
+ "author": "Paralect",
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "koa",
11
+ "rest api",
12
+ "paralect"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/paralect/koa-api-starter.git"
17
+ },
18
+ "scripts": {
19
+ "build-assets": "mjml ./src/assets/emails/*.mjml -o ./src/assets/emails/dist/",
20
+ "build-emails": "babel src/emails --out-dir src/emails/compiled",
21
+ "dev": "tsx --watch src/app.js",
22
+ "init-project": "./bin/init-project.sh",
23
+ "start": "tsx src/app.js",
24
+ "migrate": "node ./src/migrator.js",
25
+ "schedule-dev": "nodemon --watch ./src ./src/scheduler ./src/scheduler.js",
26
+ "schedule": "node ./src/scheduler.js",
27
+ "precommit": "lint-staged",
28
+ "prepare": "husky install"
29
+ },
30
+ "dependencies": {
31
+ "@koa/cors": "5.0.0",
32
+ "@koa/multer": "3.0.2",
33
+ "@koa/router": "12.0.1",
34
+ "@paralect/node-mongo": "3.2.0",
35
+ "@react-email/components": "^0.0.21",
36
+ "@react-email/render": "^0.0.16",
37
+ "@sendgrid/mail": "8.1.3",
38
+ "@socket.io/redis-adapter": "8.3.0",
39
+ "@socket.io/redis-emitter": "5.1.0",
40
+ "app-module-path": "2.2.0",
41
+ "aws-sdk": "2.1661.0",
42
+ "bcryptjs": "2.4.3",
43
+ "bullmq": "^5.10.3",
44
+ "dotenv": "16.4.5",
45
+ "eslint-config-prettier": "9.1.0",
46
+ "handlebars": "4.7.8",
47
+ "joi": "^17.13.3",
48
+ "koa": "2.15.3",
49
+ "koa-bodyparser": "4.4.1",
50
+ "koa-helmet": "7.0.2",
51
+ "koa-logger": "3.2.1",
52
+ "koa-mount": "4.0.0",
53
+ "koa-qs": "3.0.0",
54
+ "lodash": "4.17.21",
55
+ "mjml": "4.15.3",
56
+ "mkdirp": "3.0.1",
57
+ "moment": "2.30.1",
58
+ "moment-duration-format": "2.3.2",
59
+ "monk": "^7.3.4",
60
+ "multer": "1.4.4",
61
+ "node-schedule": "2.1.1",
62
+ "nodemailer": "^6.9.14",
63
+ "prettier": "3.3.3",
64
+ "psl": "1.9.0",
65
+ "redis": "^3.1.2",
66
+ "require-dir": "1.2.0",
67
+ "slug": "^9.1.0",
68
+ "socket.io": "4.7.5",
69
+ "socket.io-emitter": "3.2.0",
70
+ "tail": "2.2.6",
71
+ "winston": "3.13.1",
72
+ "zod": "^3.23.8"
73
+ },
74
+ "devDependencies": {
75
+ "@babel/cli": "^7.24.8",
76
+ "@babel/core": "^7.24.9",
77
+ "@babel/preset-env": "^7.24.8",
78
+ "@babel/preset-react": "^7.24.7",
79
+ "@types/node": "^20.14.11",
80
+ "eslint": "^8.2.0",
81
+ "eslint-config-airbnb-base": "^15.0.0",
82
+ "eslint-plugin-import": "^2.25.2",
83
+ "husky": "9.1.1",
84
+ "lint-staged": "15.2.7",
85
+ "tsx": "^4.16.2",
86
+ "typescript": "^5.5.4"
87
+ },
88
+ "lint-staged": {
89
+ "*.js": [
90
+ "prettier --write",
91
+ "git add"
92
+ ],
93
+ ".js": [
94
+ "npm run lint:fix",
95
+ "npm run lint"
96
+ ]
97
+ }
98
+ }
Binary file
@@ -0,0 +1,3 @@
1
+ export default {
2
+ // myVar: process.env.MY_VAR
3
+ };
@@ -0,0 +1,15 @@
1
+ import _ from 'lodash';
2
+
3
+ export default (envs = []) => {
4
+ let missingEnvs = [];
5
+
6
+ _.each(envs, envName => {
7
+ if (!process.env[envName]) {
8
+ missingEnvs.push(envName);
9
+ }
10
+ });
11
+
12
+ if (!_.isEmpty(missingEnvs)) {
13
+ throw Error(`Missing env variables: ${missingEnvs.join(', ')}`)
14
+ }
15
+ };
@@ -0,0 +1,62 @@
1
+ import _ from 'lodash';
2
+ import fs from "fs";
3
+ import dotenv from "dotenv";
4
+ import appConfig from "./app.js";
5
+
6
+ dotenv.config({ path: `${__dirname}/.env` });
7
+ dotenv.config({ path: `${__dirname}/.env.app` });
8
+
9
+ console.log('process.env.HIVE_SRC', process.env.HIVE_SRC)
10
+
11
+ if (process.env.HIVE_SRC) {
12
+ dotenv.config({
13
+ path: `${process.env.HIVE_SRC}/app-config/.env`,
14
+ });
15
+
16
+ dotenv.config({
17
+ path: `${process.env.HIVE_SRC}/app-config/.env.app`,
18
+ });
19
+ }
20
+
21
+ const env = process.env.APP_ENV || "development";
22
+
23
+ let config = {
24
+ async init() {
25
+ if (process.env.HIVE_SRC) {
26
+ let pluginConfigPath = `${process.env.HIVE_SRC}/app-config/app.js`;
27
+
28
+ if (fs.existsSync(pluginConfigPath)) {
29
+ const { default: pluginConfig } = await (import(pluginConfigPath))
30
+ _.extend(config, pluginConfig);
31
+ }
32
+ }
33
+ },
34
+
35
+ _hive: {},
36
+ env,
37
+ port: process.env.PORT || 3001,
38
+ domain:
39
+ env === "production" ? "https://api.yourapp.com" : process.env.DOMAIN || "",
40
+ isDev: env === "development",
41
+ mongoUri: process.env.MONGODB_URI,
42
+ redis: {
43
+ url: process.env.REDIS_URI,
44
+ },
45
+ smtp: {
46
+ host: process.env.SMTP_SERVER,
47
+ port: process.env.SMTP_PORT,
48
+ secure: true,
49
+ auth: {
50
+ user: process.env.SMTP_USER,
51
+ pass: process.env.SMTP_KEY,
52
+ },
53
+ },
54
+ ...appConfig,
55
+ assert(configKey) {
56
+ if (!config[configKey]) {
57
+ throw Error(`Config [${configKey}] is missing`);
58
+ }
59
+ },
60
+ };
61
+
62
+ export default config;
@@ -0,0 +1,69 @@
1
+
2
+ import appModulePath from "app-module-path";
3
+ appModulePath.addPath(__dirname);
4
+
5
+ if (process.env.HIVE_SRC) {
6
+ appModulePath.addPath(process.env.HIVE_SRC);
7
+ }
8
+
9
+ import Koa from "koa";
10
+ import http from "http";
11
+ import config from "app-config";
12
+ import logger from "logger";
13
+ import cors from "@koa/cors";
14
+ import helmet from "koa-helmet";
15
+ import qs from "koa-qs";
16
+ import bodyParser from "koa-bodyparser";
17
+
18
+ import requestLogger from "koa-logger";
19
+ import db from "db";
20
+ import socketIo from "socketIo";
21
+ import mount from "koa-mount";
22
+ import get from "resources/health/endpoints/get";
23
+
24
+ const main = async () => {
25
+ await config.init();
26
+ await db.init();
27
+
28
+ const routes = (await import("routes")).default;
29
+ const scheduler = (await import("./scheduler")).default;
30
+
31
+ process.on("unhandledRejection", (reason, p) => {
32
+ console.trace(reason.stack);
33
+ logger.error(reason.stack);
34
+ });
35
+
36
+ const app = new Koa();
37
+
38
+ app.use(cors({ credentials: true }));
39
+ app.use(helmet());
40
+
41
+ qs(app);
42
+
43
+ app.use(bodyParser({
44
+ enableTypes: ["json", "form", "text"],
45
+
46
+ formLimit: config._hive.bodyParser?.formLimit || "10mb",
47
+ textLimit: config._hive.bodyParser?.textLimit || "10mb",
48
+ jsonLimit: config._hive.bodyParser?.jsonLimit || "10mb",
49
+ }));
50
+
51
+ app.use(mount("/health", get.handler));
52
+ app.use(requestLogger());
53
+
54
+ await routes(app);
55
+
56
+ const server = http.createServer(app.callback());
57
+
58
+ server.listen(config.port, () => {
59
+ console.log(`Api server listening on ${config.port}, in ${process.env.NODE_ENV} mode and ${process.env.APP_ENV} environment`);
60
+ });
61
+
62
+ scheduler();
63
+
64
+ await socketIo(server);
65
+ };
66
+
67
+ main();
68
+
69
+ export default main;
@@ -0,0 +1,13 @@
1
+ <mj-section background-color="#ffffff" padding-bottom="0px" padding-top="0">
2
+ <mj-column vertical-align="top" width="100%">
3
+ <mj-text
4
+ align="center"
5
+ font-size="30px"
6
+ font-weight="bold"
7
+ font-family="Trebuchet MS, Arial, sans-serif"
8
+ color="#55299a"
9
+ >
10
+ Ship
11
+ </mj-text>
12
+ </mj-column>
13
+ </mj-section>
File without changes
@@ -0,0 +1,34 @@
1
+ <mjml>
2
+ <mj-head>
3
+ <mj-include path="./styles/index.mjml" />
4
+ </mj-head>
5
+
6
+ <mj-body>
7
+ <mj-include path="./components/header.mjml" />
8
+
9
+ <mj-section mj-class="main-section">
10
+ <mj-column vertical-align="middle" width="100%">
11
+ <mj-text mj-class="big-text"> Hi, there. </mj-text>
12
+
13
+ <mj-text mj-class="text">
14
+ Thanks for checking out Ship, we hope our products can make your
15
+ routine life a little more enjoyable.
16
+ </mj-text>
17
+
18
+ <mj-button mj-class="btn" href="{{verifyEmailUrl}}">
19
+ Verify Your Email
20
+ </mj-button>
21
+ </mj-column>
22
+ </mj-section>
23
+
24
+ <mj-section mj-class="bottom-section">
25
+ <mj-column vertical-align="middle" width="70%">
26
+ <mj-text mj-class="small-text"> INTERESTING FACT </mj-text>
27
+
28
+ <mj-text mj-class="tiny-text">
29
+ It took more than three years to build the Titanic.
30
+ </mj-text>
31
+ </mj-column>
32
+ </mj-section>
33
+ </mj-body>
34
+ </mjml>
@@ -0,0 +1,77 @@
1
+ <mj-attributes>
2
+ <mj-class
3
+ name="big-text"
4
+ align="center"
5
+ font-weight="bold"
6
+ font-family="Trebuchet MS, Arial, sans-serif"
7
+ color="#3b1d6c"
8
+ font-size="30px"
9
+ padding-left="25px"
10
+ padding-right="25px"
11
+ />
12
+
13
+ <mj-class
14
+ name="text"
15
+ align="center"
16
+ color="#000"
17
+ font-size="18px"
18
+ font-family="Century Gothic, Arial, sans-serif"
19
+ padding-left="25px"
20
+ padding-right="25px"
21
+ line-height="26px"
22
+ />
23
+
24
+ <mj-class
25
+ name="small-text"
26
+ align="center"
27
+ font-weight="bold"
28
+ font-family="Trebuchet MS, Arial, sans-serif"
29
+ color="#3b1d6c"
30
+ font-size="15px"
31
+ padding-left="25px"
32
+ padding-right="25px"
33
+ />
34
+
35
+ <mj-class
36
+ name="tiny-text"
37
+ align="center"
38
+ color="#000"
39
+ font-size="15px"
40
+ font-family="Verdana, Arial, sans-serif"
41
+ line-height="20px"
42
+ />
43
+
44
+ <mj-class
45
+ name="btn"
46
+ align="center"
47
+ font-size="22px"
48
+ background-color="#4c258b"
49
+ border-radius="4px"
50
+ color="#fff"
51
+ font-family="open Sans Helvetica, Arial, sans-serif"
52
+ />
53
+
54
+ <mj-class
55
+ name="main-section"
56
+ background-color="#cccccc"
57
+ vertical-align="top"
58
+ padding-bottom="20px"
59
+ padding-top="0"
60
+ />
61
+
62
+ <mj-class
63
+ name="main-section-image"
64
+ background-color="#cccccc"
65
+ vertical-align="top"
66
+ padding-bottom="0"
67
+ padding-top="0"
68
+ />
69
+
70
+ <mj-class
71
+ name="bottom-section"
72
+ background-color="#ffffff"
73
+ vertical-align="top"
74
+ padding-bottom="20px"
75
+ padding-top="20px"
76
+ />
77
+ </mj-attributes>
@@ -0,0 +1,142 @@
1
+ import _ from "lodash";
2
+ import db from "db";
3
+ import ifUpdated from "helpers/db/ifUpdated";
4
+ import schemaMappings from "./schemaMappings";
5
+ import getDependentFields from './getDependentFields';
6
+ import isZodArray from "helpers/isZodArray";
7
+
8
+ const updatedSchemaMappings = (() => {
9
+ const result = {};
10
+ const schemaNames = Object.keys(schemaMappings);
11
+ schemaNames.forEach((schemaName) => {
12
+ const dependentFieldNames = Object.keys(schemaMappings[schemaName]);
13
+ const schema = db.schemas[schemaName];
14
+ dependentFieldNames.forEach((dependentFieldName) => {
15
+ const dependentFields = getDependentFields(schema, dependentFieldName);
16
+ if (!_.isEmpty(dependentFields)) {
17
+ result[schemaName] = result[schemaName] || {};
18
+ result[schemaName][dependentFieldName] = {
19
+ schema: schemaMappings[schemaName][dependentFieldName].schema,
20
+ dependentFields,
21
+ };
22
+ }
23
+ });
24
+
25
+ });
26
+ return result;
27
+ })();
28
+
29
+ const getToUpdate = async ({ doc, schemaName }) => {
30
+ const dependentFieldNames = Object.keys(updatedSchemaMappings[schemaName]);
31
+ const toUpdate = {};
32
+ await Promise.all(
33
+ dependentFieldNames.map(async (dependentFieldName) => {
34
+ const { schema: dependentSchemaName } =
35
+ updatedSchemaMappings[schemaName][dependentFieldName];
36
+ const { dependentFields } =
37
+ updatedSchemaMappings[schemaName][dependentFieldName];
38
+ if (!_.isEmpty(doc[dependentFieldName])) {
39
+ if (_.isArray(doc[dependentFieldName])) {
40
+ toUpdate[dependentFieldName] = (
41
+ await db.services[dependentSchemaName].find(
42
+ {
43
+ _id: { $in: _.uniq(_.map(doc[dependentFieldName], "_id")) },
44
+ },
45
+ {
46
+ fields: dependentFields,
47
+ }
48
+ )
49
+ ).results;
50
+ } else {
51
+ toUpdate[dependentFieldName] = await db.services[
52
+ dependentSchemaName
53
+ ].findOne(
54
+ {
55
+ _id: doc[dependentFieldName]._id,
56
+ },
57
+ {
58
+ fields: dependentFields,
59
+ }
60
+ );
61
+ }
62
+ }
63
+ })
64
+ );
65
+ return toUpdate;
66
+ };
67
+
68
+ const populateOnCreate = ({ schemaName }) => {
69
+ db.services[schemaName]._options.onBeforeCreated = async ({ docs }) => {
70
+ const res = await Promise.all(
71
+ docs.map(async (doc) => {
72
+ const toUpdate = await getToUpdate({ schemaName, doc });
73
+ return {
74
+ ...doc,
75
+ ...toUpdate,
76
+ };
77
+ })
78
+ );
79
+ return res;
80
+ };
81
+ };
82
+
83
+ // const addOnEntityCreatedHandler = ({ schemaName }) => {
84
+ // db.services[schemaName].on('created', async ({ doc }) => {
85
+ // const toUpdate = await getToUpdate({ schemaName, doc });
86
+ // if (!_.isEmpty(toUpdate)) {
87
+ // await db.services[schemaName].atomic.update(
88
+ // { _id: doc._id },
89
+ // { $set: toUpdate }
90
+ // );
91
+ // }
92
+ // });
93
+ // };
94
+
95
+ const addOnDependentEntitiesUpdatedHandlers = ({ schemaName }) => {
96
+ const dependentFieldNames = Object.keys(schemaMappings[schemaName]);
97
+ dependentFieldNames.forEach((dependentFieldName) => {
98
+ const dependentFieldSchemaName =
99
+ schemaMappings[schemaName][dependentFieldName].schema;
100
+ const schema = db.schemas[schemaName];
101
+ const dependentFields = getDependentFields(schema, dependentFieldName);
102
+ db.services[dependentFieldSchemaName].on(
103
+ "updated",
104
+ ifUpdated(dependentFields, async ({ doc }) => {
105
+ const toUpdate = _.pick(doc, ["_id", ...dependentFields]);
106
+ if (isZodArray(schema.shape[dependentFieldName])) {
107
+ db.services[schemaName].atomic.update(
108
+ { [`${dependentFieldName}._id`]: doc._id },
109
+ {
110
+ $set: {
111
+ [`${dependentFieldName}.$`]: toUpdate,
112
+ },
113
+ },
114
+ {
115
+ multi: true,
116
+ }
117
+ );
118
+ } else {
119
+ db.services[schemaName].atomic.update(
120
+ { [`${dependentFieldName}._id`]: doc._id },
121
+ {
122
+ $set: {
123
+ [`${dependentFieldName}`]: toUpdate,
124
+ },
125
+ },
126
+ {
127
+ multi: true,
128
+ }
129
+ );
130
+ }
131
+ })
132
+ );
133
+ });
134
+ };
135
+ export default async () => {
136
+ const schemaNames = Object.keys(updatedSchemaMappings);
137
+ schemaNames.forEach((schemaName) => {
138
+ // addOnEntityCreatedHandler({ schemaName });
139
+ populateOnCreate({ schemaName });
140
+ addOnDependentEntitiesUpdatedHandlers({ schemaName });
141
+ });
142
+ };
@@ -0,0 +1,37 @@
1
+ import _ from 'lodash';
2
+ import { z } from 'zod';
3
+ import isZodArray from "helpers/isZodArray";
4
+
5
+ const getZodKeys = schema => {
6
+ // make sure schema is not null or undefined
7
+ if (schema === null || schema === undefined) return [];
8
+ // check if schema is nullable or optional
9
+ if (schema instanceof z.ZodNullable || schema instanceof z.ZodOptional) return getZodKeys(schema.unwrap());
10
+ // check if schema is an array
11
+ if (isZodArray(schema)) return getZodKeys(schema.element);
12
+ // check if schema is an object
13
+ if (schema instanceof z.ZodObject) {
14
+ // get key/value pairs from schema
15
+ const entries = Object.entries(schema.shape);
16
+ // loop through key/value pairs
17
+ return entries.flatMap(([key, value]) => {
18
+ // get nested keys
19
+ const nested = value instanceof z.ZodType ? getZodKeys(value).map(subKey => `${key}.${subKey}`) : [];
20
+ // return nested keys
21
+ return nested.length ? nested : key;
22
+ });
23
+ }
24
+ // return empty array
25
+ return [];
26
+ };
27
+
28
+ export default (schema, dependentFieldName) => {
29
+ let targetSchema = schema.shape[dependentFieldName];
30
+ let zodKeys = getZodKeys(targetSchema);
31
+
32
+ zodKeys = _.uniq(zodKeys.map(key => key.split('.')[0]));
33
+
34
+ return zodKeys.filter(
35
+ (key) => !_.includes(['_id', 'createdOn', 'updatedOn'], key)
36
+ );
37
+ };
@@ -0,0 +1,99 @@
1
+ import _ from "lodash";
2
+ import db from "db";
3
+
4
+ import schemaMappings from "./schemaMappings";
5
+ import getDependentFields from './getDependentFields';
6
+ import isZodArray from "helpers/isZodArray";
7
+
8
+ const schemaMappingService = db.services.schemaMappings;
9
+
10
+ const zodSchemaToSchemaMappings = () => {
11
+ const newSchemaMappings = {};
12
+
13
+ Object.keys(schemaMappings).forEach((schemaName) => {
14
+ const schema = db.schemas[schemaName];
15
+
16
+ newSchemaMappings[schemaName] = {};
17
+
18
+ Object.keys(schemaMappings[schemaName]).forEach((fieldName) => {
19
+ newSchemaMappings[schemaName][fieldName] = {
20
+ schema: schemaMappings[schemaName][fieldName].schema,
21
+ fields: getDependentFields(schema, fieldName),
22
+ };
23
+ });
24
+ });
25
+ console.log('newSchemaMappings', newSchemaMappings)
26
+ return newSchemaMappings;
27
+ };
28
+
29
+
30
+ export default async () => {
31
+ const prevSchema = await schemaMappingService.findOne({});
32
+ const prevSchemaMappings = prevSchema?.mappings;
33
+ if (!prevSchemaMappings) {
34
+ const initialSchemaMappings = zodSchemaToSchemaMappings();
35
+ schemaMappingService.create({ mappings: initialSchemaMappings });
36
+ } else {
37
+ const schemaNames = Object.keys(schemaMappings);
38
+ await Promise.all(
39
+ schemaNames.map(async (schemaName) => {
40
+ const schema = db.schemas[schemaName];
41
+
42
+ const fieldNames = Object.keys(schemaMappings[schemaName]);
43
+ await Promise.all(
44
+ fieldNames.map(async (fieldName) => {
45
+ const dependentFields = getDependentFields(schema, fieldName);
46
+ const prevSchema = (prevSchemaMappings[schemaName] &&
47
+ prevSchemaMappings[schemaName][fieldName]) || { fields: [] };
48
+ const { fields: prevDependentFields } = prevSchema;
49
+ if (
50
+ _.difference(dependentFields, prevDependentFields).length !== 0
51
+ ) {
52
+ console.log(`Mapping schema changes: ${schemaName}.${fieldName}`);
53
+ const uniqueDependentEntityIds = await db.services[
54
+ schemaName
55
+ ].distinct(`${fieldName}._id`);
56
+ const { results: uniqueDependentEntities } = await db.services[
57
+ schemaMappings[schemaName][fieldName].schema
58
+ ].find(
59
+ {
60
+ _id: { $in: uniqueDependentEntityIds },
61
+ },
62
+ { fields: ["_id", ...dependentFields] }
63
+ );
64
+ await Promise.all(
65
+ uniqueDependentEntities.map(async (entity) => {
66
+ if (isZodArray(schema.shape[fieldName])) {
67
+ await db.services[schemaName].atomic.update(
68
+ { [`${fieldName}._id`]: entity._id },
69
+ {
70
+ $set: { [`${fieldName}.$`]: entity },
71
+ },
72
+ {
73
+ multi: true,
74
+ }
75
+ );
76
+ } else {
77
+ await db.services[schemaName].atomic.update(
78
+ { [`${fieldName}._id`]: entity._id },
79
+ {
80
+ $set: { [`${fieldName}`]: entity },
81
+ },
82
+ {
83
+ multi: true,
84
+ }
85
+ );
86
+ }
87
+ })
88
+ );
89
+ }
90
+ })
91
+ );
92
+ })
93
+ );
94
+ schemaMappingService.atomic.update(
95
+ { _id: prevSchema._id },
96
+ { $set: { mappings: zodSchemaToSchemaMappings() } }
97
+ );
98
+ }
99
+ };
@@ -0,0 +1,13 @@
1
+ import fs from 'fs';
2
+
3
+ let schemaMappings = {};
4
+
5
+ if (fs.existsSync('./schemaMappings.json')) {
6
+ schemaMappings = JSON.parse(fs.readFileSync('./schemaMappings.json', 'utf8') || '{}');
7
+ }
8
+
9
+ if (process.env.HIVE_SRC && fs.existsSync(`${process.env.HIVE_SRC}/autoMap/schemaMappings.json`)) {
10
+ schemaMappings = JSON.parse(fs.readFileSync(`${process.env.HIVE_SRC}/autoMap/schemaMappings.json`, 'utf8') || '{}');
11
+ }
12
+
13
+ export default schemaMappings;
@@ -0,0 +1,3 @@
1
+ {
2
+
3
+ }