@friggframework/core 2.0.0-next.5 → 2.0.0-next.50

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 (267) hide show
  1. package/CLAUDE.md +693 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +421 -0
  4. package/application/commands/credential-commands.js +224 -0
  5. package/application/commands/entity-commands.js +315 -0
  6. package/application/commands/integration-commands.js +179 -0
  7. package/application/commands/user-commands.js +213 -0
  8. package/application/index.js +69 -0
  9. package/core/CLAUDE.md +690 -0
  10. package/core/Worker.js +8 -21
  11. package/core/create-handler.js +2 -7
  12. package/credential/repositories/credential-repository-factory.js +47 -0
  13. package/credential/repositories/credential-repository-interface.js +98 -0
  14. package/credential/repositories/credential-repository-mongo.js +307 -0
  15. package/credential/repositories/credential-repository-postgres.js +313 -0
  16. package/credential/repositories/credential-repository.js +302 -0
  17. package/credential/use-cases/get-credential-for-user.js +21 -0
  18. package/credential/use-cases/update-authentication-status.js +15 -0
  19. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  20. package/database/adapters/lambda-invoker.js +97 -0
  21. package/database/config.js +154 -0
  22. package/database/encryption/README.md +684 -0
  23. package/database/encryption/encryption-schema-registry.js +141 -0
  24. package/database/encryption/field-encryption-service.js +226 -0
  25. package/database/encryption/logger.js +79 -0
  26. package/database/encryption/prisma-encryption-extension.js +222 -0
  27. package/database/index.js +25 -12
  28. package/database/models/WebsocketConnection.js +16 -10
  29. package/database/models/readme.md +1 -0
  30. package/database/prisma.js +222 -0
  31. package/database/repositories/health-check-repository-factory.js +43 -0
  32. package/database/repositories/health-check-repository-interface.js +87 -0
  33. package/database/repositories/health-check-repository-mongodb.js +91 -0
  34. package/database/repositories/health-check-repository-postgres.js +82 -0
  35. package/database/repositories/health-check-repository.js +108 -0
  36. package/database/repositories/migration-status-repository-s3.js +137 -0
  37. package/database/use-cases/check-database-health-use-case.js +29 -0
  38. package/database/use-cases/check-database-state-use-case.js +81 -0
  39. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  40. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  41. package/database/use-cases/get-migration-status-use-case.js +93 -0
  42. package/database/use-cases/run-database-migration-use-case.js +137 -0
  43. package/database/use-cases/test-encryption-use-case.js +253 -0
  44. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  45. package/database/utils/mongodb-collection-utils.js +91 -0
  46. package/database/utils/mongodb-schema-init.js +106 -0
  47. package/database/utils/prisma-runner.js +400 -0
  48. package/database/utils/prisma-schema-parser.js +182 -0
  49. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  50. package/encrypt/Cryptor.js +34 -168
  51. package/encrypt/index.js +1 -2
  52. package/encrypt/test-encrypt.js +0 -2
  53. package/generated/prisma-mongodb/client.d.ts +1 -0
  54. package/generated/prisma-mongodb/client.js +4 -0
  55. package/generated/prisma-mongodb/default.d.ts +1 -0
  56. package/generated/prisma-mongodb/default.js +4 -0
  57. package/generated/prisma-mongodb/edge.d.ts +1 -0
  58. package/generated/prisma-mongodb/edge.js +334 -0
  59. package/generated/prisma-mongodb/index-browser.js +316 -0
  60. package/generated/prisma-mongodb/index.d.ts +22898 -0
  61. package/generated/prisma-mongodb/index.js +359 -0
  62. package/generated/prisma-mongodb/package.json +183 -0
  63. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  64. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  65. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  66. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  67. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  68. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  69. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  70. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  71. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  72. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  73. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  74. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  75. package/generated/prisma-mongodb/schema.prisma +362 -0
  76. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  77. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  78. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  79. package/generated/prisma-mongodb/wasm.js +341 -0
  80. package/generated/prisma-postgresql/client.d.ts +1 -0
  81. package/generated/prisma-postgresql/client.js +4 -0
  82. package/generated/prisma-postgresql/default.d.ts +1 -0
  83. package/generated/prisma-postgresql/default.js +4 -0
  84. package/generated/prisma-postgresql/edge.d.ts +1 -0
  85. package/generated/prisma-postgresql/edge.js +356 -0
  86. package/generated/prisma-postgresql/index-browser.js +338 -0
  87. package/generated/prisma-postgresql/index.d.ts +25072 -0
  88. package/generated/prisma-postgresql/index.js +381 -0
  89. package/generated/prisma-postgresql/package.json +183 -0
  90. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  91. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  92. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  93. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  94. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  95. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  96. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  97. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  98. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  99. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  100. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  101. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  102. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  103. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  104. package/generated/prisma-postgresql/schema.prisma +345 -0
  105. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  106. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  107. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  108. package/generated/prisma-postgresql/wasm.js +363 -0
  109. package/handlers/WEBHOOKS.md +653 -0
  110. package/handlers/app-definition-loader.js +38 -0
  111. package/handlers/app-handler-helpers.js +56 -0
  112. package/handlers/backend-utils.js +180 -0
  113. package/handlers/database-migration-handler.js +227 -0
  114. package/handlers/integration-event-dispatcher.js +54 -0
  115. package/handlers/routers/HEALTHCHECK.md +342 -0
  116. package/handlers/routers/auth.js +15 -0
  117. package/handlers/routers/db-migration.handler.js +29 -0
  118. package/handlers/routers/db-migration.js +256 -0
  119. package/handlers/routers/health.js +519 -0
  120. package/handlers/routers/integration-defined-routers.js +45 -0
  121. package/handlers/routers/integration-webhook-routers.js +67 -0
  122. package/handlers/routers/user.js +63 -0
  123. package/handlers/routers/websocket.js +57 -0
  124. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  125. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  126. package/handlers/workers/db-migration.js +352 -0
  127. package/handlers/workers/integration-defined-workers.js +27 -0
  128. package/index.js +77 -22
  129. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  130. package/integrations/index.js +12 -10
  131. package/integrations/integration-base.js +296 -54
  132. package/integrations/integration-router.js +381 -182
  133. package/integrations/options.js +1 -1
  134. package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
  135. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  136. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  137. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  138. package/integrations/repositories/integration-mapping-repository.js +156 -0
  139. package/integrations/repositories/integration-repository-factory.js +44 -0
  140. package/integrations/repositories/integration-repository-interface.js +127 -0
  141. package/integrations/repositories/integration-repository-mongo.js +303 -0
  142. package/integrations/repositories/integration-repository-postgres.js +352 -0
  143. package/integrations/repositories/process-repository-factory.js +46 -0
  144. package/integrations/repositories/process-repository-interface.js +90 -0
  145. package/integrations/repositories/process-repository-mongo.js +190 -0
  146. package/integrations/repositories/process-repository-postgres.js +217 -0
  147. package/integrations/tests/doubles/dummy-integration-class.js +83 -0
  148. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  149. package/integrations/use-cases/create-integration.js +83 -0
  150. package/integrations/use-cases/create-process.js +128 -0
  151. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  152. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  153. package/integrations/use-cases/get-integration-for-user.js +78 -0
  154. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  155. package/integrations/use-cases/get-integration-instance.js +83 -0
  156. package/integrations/use-cases/get-integrations-for-user.js +87 -0
  157. package/integrations/use-cases/get-possible-integrations.js +27 -0
  158. package/integrations/use-cases/get-process.js +87 -0
  159. package/integrations/use-cases/index.js +19 -0
  160. package/integrations/use-cases/load-integration-context.js +71 -0
  161. package/integrations/use-cases/update-integration-messages.js +44 -0
  162. package/integrations/use-cases/update-integration-status.js +32 -0
  163. package/integrations/use-cases/update-integration.js +93 -0
  164. package/integrations/use-cases/update-process-metrics.js +201 -0
  165. package/integrations/use-cases/update-process-state.js +119 -0
  166. package/integrations/utils/map-integration-dto.js +36 -0
  167. package/jest-global-setup-noop.js +3 -0
  168. package/jest-global-teardown-noop.js +3 -0
  169. package/logs/logger.js +0 -4
  170. package/{module-plugin → modules}/entity.js +1 -1
  171. package/{module-plugin → modules}/index.js +0 -8
  172. package/modules/module-factory.js +56 -0
  173. package/modules/module.js +221 -0
  174. package/modules/repositories/module-repository-factory.js +33 -0
  175. package/modules/repositories/module-repository-interface.js +129 -0
  176. package/modules/repositories/module-repository-mongo.js +377 -0
  177. package/modules/repositories/module-repository-postgres.js +426 -0
  178. package/modules/repositories/module-repository.js +316 -0
  179. package/{module-plugin → modules}/requester/requester.js +1 -0
  180. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  181. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  182. package/modules/tests/doubles/test-module-factory.js +16 -0
  183. package/modules/tests/doubles/test-module-repository.js +39 -0
  184. package/modules/use-cases/get-entities-for-user.js +32 -0
  185. package/modules/use-cases/get-entity-options-by-id.js +59 -0
  186. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  187. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  188. package/modules/use-cases/get-module.js +55 -0
  189. package/modules/use-cases/process-authorization-callback.js +122 -0
  190. package/modules/use-cases/refresh-entity-options.js +59 -0
  191. package/modules/use-cases/test-module-auth.js +55 -0
  192. package/modules/utils/map-module-dto.js +18 -0
  193. package/package.json +82 -50
  194. package/prisma-mongodb/schema.prisma +362 -0
  195. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  196. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  197. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  198. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  199. package/prisma-postgresql/schema.prisma +345 -0
  200. package/queues/queuer-util.js +28 -15
  201. package/syncs/manager.js +468 -443
  202. package/syncs/repositories/sync-repository-factory.js +38 -0
  203. package/syncs/repositories/sync-repository-interface.js +109 -0
  204. package/syncs/repositories/sync-repository-mongo.js +239 -0
  205. package/syncs/repositories/sync-repository-postgres.js +319 -0
  206. package/syncs/sync.js +0 -1
  207. package/token/repositories/token-repository-factory.js +33 -0
  208. package/token/repositories/token-repository-interface.js +131 -0
  209. package/token/repositories/token-repository-mongo.js +212 -0
  210. package/token/repositories/token-repository-postgres.js +257 -0
  211. package/token/repositories/token-repository.js +219 -0
  212. package/types/core/index.d.ts +2 -2
  213. package/types/integrations/index.d.ts +2 -6
  214. package/types/module-plugin/index.d.ts +5 -59
  215. package/types/syncs/index.d.ts +0 -2
  216. package/user/repositories/user-repository-factory.js +46 -0
  217. package/user/repositories/user-repository-interface.js +198 -0
  218. package/user/repositories/user-repository-mongo.js +291 -0
  219. package/user/repositories/user-repository-postgres.js +350 -0
  220. package/user/tests/doubles/test-user-repository.js +72 -0
  221. package/user/use-cases/authenticate-user.js +127 -0
  222. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  223. package/user/use-cases/create-individual-user.js +61 -0
  224. package/user/use-cases/create-organization-user.js +47 -0
  225. package/user/use-cases/create-token-for-user-id.js +30 -0
  226. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  227. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  228. package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
  229. package/user/use-cases/login-user.js +122 -0
  230. package/user/user.js +93 -0
  231. package/utils/backend-path.js +38 -0
  232. package/utils/index.js +6 -0
  233. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  234. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  235. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  236. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  237. package/websocket/repositories/websocket-connection-repository.js +161 -0
  238. package/database/models/State.js +0 -9
  239. package/database/models/Token.js +0 -70
  240. package/database/mongo.js +0 -45
  241. package/encrypt/Cryptor.test.js +0 -32
  242. package/encrypt/encrypt.js +0 -132
  243. package/encrypt/encrypt.test.js +0 -1069
  244. package/errors/base-error.test.js +0 -32
  245. package/errors/fetch-error.test.js +0 -79
  246. package/errors/halt-error.test.js +0 -11
  247. package/errors/validation-errors.test.js +0 -120
  248. package/integrations/create-frigg-backend.js +0 -31
  249. package/integrations/integration-factory.js +0 -251
  250. package/integrations/integration-mapping.js +0 -43
  251. package/integrations/integration-model.js +0 -46
  252. package/integrations/integration-user.js +0 -144
  253. package/integrations/test/integration-base.test.js +0 -144
  254. package/lambda/TimeoutCatcher.test.js +0 -68
  255. package/logs/logger.test.js +0 -76
  256. package/module-plugin/auther.js +0 -393
  257. package/module-plugin/credential.js +0 -22
  258. package/module-plugin/entity-manager.js +0 -70
  259. package/module-plugin/manager.js +0 -169
  260. package/module-plugin/module-factory.js +0 -61
  261. package/module-plugin/requester/requester.test.js +0 -28
  262. package/module-plugin/test/auther.test.js +0 -97
  263. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  264. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  265. /package/{module-plugin → modules}/requester/basic.js +0 -0
  266. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  267. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
package/syncs/manager.js CHANGED
@@ -1,464 +1,489 @@
1
- const _ = require("lodash");
2
- const moment = require("moment");
3
- const mongoose = require("mongoose");
4
- const SyncObject = require("./sync");
5
- const { debug } = require("packages/logs");
6
- const { get } = require("../assertions");
7
- const { Sync } = require("./model");
1
+ const _ = require('lodash');
2
+ const moment = require('moment');
3
+ const mongoose = require('mongoose');
4
+ const SyncObject = require('./sync');
5
+ const { debug } = require('packages/logs');
6
+ const { get } = require('../assertions');
7
+ const { createSyncRepository } = require('./sync-repository-factory');
8
8
 
9
9
  class SyncManager {
10
- constructor(params) {
11
- // TODO verify type????????
12
- // this.primaryModule = getAndVerifyType(params, 'primary', ModuleManager);
13
- // this.secondaryModule = getAndVerifyType(
14
- // params,
15
- // 'secondary',
16
- // ModuleManager
17
- // );
18
- this.SyncObjectClass = getAndVerifyType(
19
- params,
20
- "syncObjectClass",
21
- SyncObject
22
- );
23
- this.ignoreEmptyMatchValues = get(params, "ignoreEmptyMatchValues", true);
24
- this.isUnidirectionalSync = get(params, "isUnidirectionalSync", false);
25
- this.useFirstMatchingDuplicate = get(
26
- params,
27
- "useFirstMatchingDuplicate",
28
- true
29
- );
30
- this.omitEmptyStringsFromData = get(
31
- params,
32
- "omitEmptyStringsFromData",
33
- true
34
- );
35
-
36
- this.integration = get(params, "integration", null); // TODO Change to type validation
37
-
38
- }
39
-
40
- // calls getAllSyncObjects() on the modules and then finds the difference between each. The Primary Module
41
- // takes precedence unless the field is an empty string or null
42
- async initialSync() {
43
- const time0 = parseInt(moment().format("x"));
44
- const primaryEntityId = await this.primaryModule.entity.id;
45
- const secondaryEntityId = await this.secondaryModule.entity.id;
46
-
47
- // get array of sync objects
48
- let primaryArr = await this.primaryModule.getAllSyncObjects(
49
- this.SyncObjectClass
50
- );
51
- const primaryArrayInitialCount = primaryArr.length;
52
- const time1 = parseInt(moment().format("x"));
53
- debug(
54
- `${primaryArr.length} number of ${
55
- this.SyncObjectClass.name
56
- } retrieved from ${this.primaryModule.constructor.getName()} in ${
57
- time1 - time0
58
- } ms`
59
- );
60
- let secondaryArr = await this.secondaryModule.getAllSyncObjects(
61
- this.SyncObjectClass
62
- );
63
- const secondaryArrayInitialCount = secondaryArr.length;
64
- const time2 = parseInt(moment().format("x"));
65
- debug(
66
- `${secondaryArr.length} number of ${
67
- this.SyncObjectClass.name
68
- } retrieved from ${this.secondaryModule.constructor.getName()} in ${
69
- time2 - time1
70
- } ms`
71
- );
72
-
73
- // ignore the empty match values
74
- if (this.ignoreEmptyMatchValues) {
75
- const primaryCountBefore = primaryArr.length;
76
- primaryArr = primaryArr.filter((obj) => !obj.missingMatchData);
77
- const primaryCountAfter = primaryArr.length;
78
- const secondaryCountBefore = secondaryArr.length;
79
- secondaryArr = secondaryArr.filter((obj) => !obj.missingMatchData);
80
- const secondaryCountAfter = secondaryArr.length;
81
- debug(
82
- `Ignoring ${primaryCountBefore - primaryCountAfter} ${
83
- this.SyncObjectClass.name
84
- } objects from ${this.primaryModule.constructor.getName()}`
85
- );
86
- debug(
87
- `Ignoring ${secondaryCountBefore - secondaryCountAfter} ${
88
- this.SyncObjectClass.name
89
- } objects from ${this.secondaryModule.constructor.getName()}`
90
- );
91
- }
92
- if (this.useFirstMatchingDuplicate) {
93
- primaryArr = _.uniqBy(primaryArr, "matchHash");
94
- debug(
95
- `${primaryArr.length} Objects remaining after removing duplicates from Primary Array`
96
- );
97
- secondaryArr = _.uniqBy(secondaryArr, "matchHash");
98
- debug(
99
- `${secondaryArr.length} Objects remaining after removing duplicates from Secondary Array`
100
- );
10
+ constructor(params) {
11
+ this.SyncObjectClass = getAndVerifyType(
12
+ params,
13
+ 'syncObjectClass',
14
+ SyncObject
15
+ );
16
+ this.ignoreEmptyMatchValues = get(
17
+ params,
18
+ 'ignoreEmptyMatchValues',
19
+ true
20
+ );
21
+ this.isUnidirectionalSync = get(params, 'isUnidirectionalSync', false);
22
+ this.useFirstMatchingDuplicate = get(
23
+ params,
24
+ 'useFirstMatchingDuplicate',
25
+ true
26
+ );
27
+ this.omitEmptyStringsFromData = get(
28
+ params,
29
+ 'omitEmptyStringsFromData',
30
+ true
31
+ );
32
+
33
+ this.integration = get(params, 'integration', null); // TODO Change to type validation
34
+ this.syncRepository = createSyncRepository();
101
35
  }
102
- const primaryUpdate = [];
103
- const secondaryUpdate = [];
104
- // PrimaryIntersection is an array where at least one matching object was found inside
105
- // SecondaryArray that matched the inspected object from Primary.
106
- // The only catch is, there will definitely be duplicates unless self filtered
107
- const primaryIntersection = primaryArr.filter((e1) =>
108
- secondaryArr.some((e2) => e1.equals(e2))
109
- );
110
- // SecondaryIntersection is an array where at least one matching object was found inside
111
- // primaryIntersection that matched the inspected object from secondaryArray.
112
- // The only catch is, there will definitely be duplicates unless self filtered
113
- const secondaryIntersection = secondaryArr.filter((e1) =>
114
- primaryIntersection.some((e2) => e1.equals(e2))
115
- );
116
- const secondaryCreate = primaryArr.filter(
117
- (e1) => !secondaryArr.some((e2) => e1.equals(e2))
118
- );
119
- const primaryCreate = secondaryArr.filter(
120
- (e1) => !primaryArr.some((e2) => e1.equals(e2))
121
- );
122
-
123
- // process the intersections and see which ones need to be updated.
124
- for (const primaryObj of primaryIntersection) {
125
- const secondaryObj = secondaryIntersection.find((e1) =>
126
- e1.equals(primaryObj)
127
- );
128
-
129
- let primaryUpdated = false;
130
- let secondaryUpdated = false;
131
-
132
- for (const key in primaryObj.data) {
133
- let valuesAreNotEquivalent = true; // Default to this just to be safe
134
- // Make sure we're not comparing a number 0 to a empty string/null/undefined.
135
- if (_.isEqual(primaryObj.data[key], secondaryObj.data[key])) {
136
- // This should basically tell us if both values are falsy, in which case we're good
137
- valuesAreNotEquivalent = false;
138
- } else if (
139
- typeof primaryObj.data[key] === "number" ||
140
- typeof secondaryObj.data[key] === "number"
141
- ) {
142
- // This should try comparing if at least one of the two are numbers
143
- valuesAreNotEquivalent =
144
- primaryObj.data[key] !== secondaryObj.data[key];
145
- } else if (!primaryObj.data[key] && !secondaryObj.data[key]) {
146
- valuesAreNotEquivalent = false;
36
+
37
+ // calls getAllSyncObjects() on the modules and then finds the difference between each. The Primary Module
38
+ // takes precedence unless the field is an empty string or null
39
+ async initialSync() {
40
+ const time0 = parseInt(moment().format('x'));
41
+ const primaryEntityId = await this.primaryModule.entity.id;
42
+ const secondaryEntityId = await this.secondaryModule.entity.id;
43
+
44
+ // get array of sync objects
45
+ let primaryArr = await this.primaryModule.getAllSyncObjects(
46
+ this.SyncObjectClass
47
+ );
48
+ const primaryArrayInitialCount = primaryArr.length;
49
+ const time1 = parseInt(moment().format('x'));
50
+ debug(
51
+ `${primaryArr.length} number of ${
52
+ this.SyncObjectClass.name
53
+ } retrieved from ${this.primaryModule.constructor.getName()} in ${
54
+ time1 - time0
55
+ } ms`
56
+ );
57
+ let secondaryArr = await this.secondaryModule.getAllSyncObjects(
58
+ this.SyncObjectClass
59
+ );
60
+ const secondaryArrayInitialCount = secondaryArr.length;
61
+ const time2 = parseInt(moment().format('x'));
62
+ debug(
63
+ `${secondaryArr.length} number of ${
64
+ this.SyncObjectClass.name
65
+ } retrieved from ${this.secondaryModule.constructor.getName()} in ${
66
+ time2 - time1
67
+ } ms`
68
+ );
69
+
70
+ // ignore the empty match values
71
+ if (this.ignoreEmptyMatchValues) {
72
+ const primaryCountBefore = primaryArr.length;
73
+ primaryArr = primaryArr.filter((obj) => !obj.missingMatchData);
74
+ const primaryCountAfter = primaryArr.length;
75
+ const secondaryCountBefore = secondaryArr.length;
76
+ secondaryArr = secondaryArr.filter((obj) => !obj.missingMatchData);
77
+ const secondaryCountAfter = secondaryArr.length;
78
+ debug(
79
+ `Ignoring ${primaryCountBefore - primaryCountAfter} ${
80
+ this.SyncObjectClass.name
81
+ } objects from ${this.primaryModule.constructor.getName()}`
82
+ );
83
+ debug(
84
+ `Ignoring ${secondaryCountBefore - secondaryCountAfter} ${
85
+ this.SyncObjectClass.name
86
+ } objects from ${this.secondaryModule.constructor.getName()}`
87
+ );
88
+ }
89
+ if (this.useFirstMatchingDuplicate) {
90
+ primaryArr = _.uniqBy(primaryArr, 'matchHash');
91
+ debug(
92
+ `${primaryArr.length} Objects remaining after removing duplicates from Primary Array`
93
+ );
94
+ secondaryArr = _.uniqBy(secondaryArr, 'matchHash');
95
+ debug(
96
+ `${secondaryArr.length} Objects remaining after removing duplicates from Secondary Array`
97
+ );
147
98
  }
99
+ const primaryUpdate = [];
100
+ const secondaryUpdate = [];
101
+ // PrimaryIntersection is an array where at least one matching object was found inside
102
+ // SecondaryArray that matched the inspected object from Primary.
103
+ // The only catch is, there will definitely be duplicates unless self filtered
104
+ const primaryIntersection = primaryArr.filter((e1) =>
105
+ secondaryArr.some((e2) => e1.equals(e2))
106
+ );
107
+ // SecondaryIntersection is an array where at least one matching object was found inside
108
+ // primaryIntersection that matched the inspected object from secondaryArray.
109
+ // The only catch is, there will definitely be duplicates unless self filtered
110
+ const secondaryIntersection = secondaryArr.filter((e1) =>
111
+ primaryIntersection.some((e2) => e1.equals(e2))
112
+ );
113
+ const secondaryCreate = primaryArr.filter(
114
+ (e1) => !secondaryArr.some((e2) => e1.equals(e2))
115
+ );
116
+ const primaryCreate = secondaryArr.filter(
117
+ (e1) => !primaryArr.some((e2) => e1.equals(e2))
118
+ );
148
119
 
149
- if (valuesAreNotEquivalent) {
150
- if (
151
- primaryObj.dataKeyIsReplaceable(key) &&
152
- !secondaryObj.dataKeyIsReplaceable(key) &&
153
- !this.isUnidirectionalSync
154
- ) {
155
- primaryObj.data[key] = secondaryObj.data[key];
156
- primaryUpdated = true;
157
- } else if (!primaryObj.dataKeyIsReplaceable(key)) {
158
- secondaryObj.data[key] = primaryObj.data[key];
159
- secondaryUpdated = true;
160
- }
120
+ // process the intersections and see which ones need to be updated.
121
+ for (const primaryObj of primaryIntersection) {
122
+ const secondaryObj = secondaryIntersection.find((e1) =>
123
+ e1.equals(primaryObj)
124
+ );
125
+
126
+ let primaryUpdated = false;
127
+ let secondaryUpdated = false;
128
+
129
+ for (const key in primaryObj.data) {
130
+ let valuesAreNotEquivalent = true; // Default to this just to be safe
131
+ // Make sure we're not comparing a number 0 to a empty string/null/undefined.
132
+ if (_.isEqual(primaryObj.data[key], secondaryObj.data[key])) {
133
+ // This should basically tell us if both values are falsy, in which case we're good
134
+ valuesAreNotEquivalent = false;
135
+ } else if (
136
+ typeof primaryObj.data[key] === 'number' ||
137
+ typeof secondaryObj.data[key] === 'number'
138
+ ) {
139
+ // This should try comparing if at least one of the two are numbers
140
+ valuesAreNotEquivalent =
141
+ primaryObj.data[key] !== secondaryObj.data[key];
142
+ } else if (!primaryObj.data[key] && !secondaryObj.data[key]) {
143
+ valuesAreNotEquivalent = false;
144
+ }
145
+
146
+ if (valuesAreNotEquivalent) {
147
+ if (
148
+ primaryObj.dataKeyIsReplaceable(key) &&
149
+ !secondaryObj.dataKeyIsReplaceable(key) &&
150
+ !this.isUnidirectionalSync
151
+ ) {
152
+ primaryObj.data[key] = secondaryObj.data[key];
153
+ primaryUpdated = true;
154
+ } else if (!primaryObj.dataKeyIsReplaceable(key)) {
155
+ secondaryObj.data[key] = primaryObj.data[key];
156
+ secondaryUpdated = true;
157
+ }
158
+ }
159
+ }
160
+ if (primaryUpdated && !this.isUnidirectionalSync) {
161
+ primaryUpdate.push(primaryObj);
162
+ }
163
+ if (secondaryUpdated) {
164
+ secondaryUpdate.push(secondaryObj);
165
+ }
166
+
167
+ const createdObj = await this.createSyncDBObject(
168
+ [primaryObj, secondaryObj],
169
+ [primaryEntityId, secondaryEntityId]
170
+ );
171
+
172
+ primaryObj.setSyncId(createdObj.id);
173
+ secondaryObj.setSyncId(createdObj.id);
161
174
  }
162
- }
163
- if (primaryUpdated && !this.isUnidirectionalSync) {
164
- primaryUpdate.push(primaryObj);
165
- }
166
- if (secondaryUpdated) {
167
- secondaryUpdate.push(secondaryObj);
168
- }
169
-
170
- const createdObj = await this.createSyncDBObject(
171
- [primaryObj, secondaryObj],
172
- [primaryEntityId, secondaryEntityId]
173
- );
174
-
175
- primaryObj.setSyncId(createdObj.id);
176
- secondaryObj.setSyncId(createdObj.id);
177
- }
178
- debug(
179
- `Found ${
180
- primaryUpdate.length
181
- } for updating in ${this.primaryModule.constructor.getName()}`
182
- );
183
- debug(
184
- `Found ${
185
- primaryCreate.length
186
- } for creating in ${this.primaryModule.constructor.getName()}`
187
- );
188
- debug(
189
- `Found ${
190
- secondaryUpdate.length
191
- } for updating in ${this.secondaryModule.constructor.getName()}`
192
- );
193
- debug(
194
- `Found ${
195
- secondaryCreate.length
196
- } for creating in ${this.secondaryModule.constructor.getName()}`
197
- );
198
-
199
- const time3 = parseInt(moment().format("x"));
200
- debug(`Sorting complete in ${time3 - time2} ms`);
201
-
202
- // create the database entries for the
203
- if (!this.isUnidirectionalSync) {
204
- for (const secondaryObj of primaryCreate) {
205
- const createdObj = await this.createSyncDBObject(
206
- [secondaryObj],
207
- [secondaryEntityId, primaryEntityId]
175
+ debug(
176
+ `Found ${
177
+ primaryUpdate.length
178
+ } for updating in ${this.primaryModule.constructor.getName()}`
179
+ );
180
+ debug(
181
+ `Found ${
182
+ primaryCreate.length
183
+ } for creating in ${this.primaryModule.constructor.getName()}`
184
+ );
185
+ debug(
186
+ `Found ${
187
+ secondaryUpdate.length
188
+ } for updating in ${this.secondaryModule.constructor.getName()}`
189
+ );
190
+ debug(
191
+ `Found ${
192
+ secondaryCreate.length
193
+ } for creating in ${this.secondaryModule.constructor.getName()}`
208
194
  );
209
195
 
210
- secondaryObj.setSyncId(createdObj.id);
211
- }
196
+ const time3 = parseInt(moment().format('x'));
197
+ debug(`Sorting complete in ${time3 - time2} ms`);
198
+
199
+ // create the database entries for the
200
+ if (!this.isUnidirectionalSync) {
201
+ for (const secondaryObj of primaryCreate) {
202
+ const createdObj = await this.createSyncDBObject(
203
+ [secondaryObj],
204
+ [secondaryEntityId, primaryEntityId]
205
+ );
206
+
207
+ secondaryObj.setSyncId(createdObj.id);
208
+ }
209
+ }
210
+
211
+ for (const primaryObj of secondaryCreate) {
212
+ const createdObj = await this.createSyncDBObject(
213
+ [primaryObj],
214
+ [primaryEntityId, secondaryEntityId]
215
+ );
216
+ primaryObj.setSyncId(createdObj.id);
217
+ }
218
+ const time4 = parseInt(moment().format('x'));
219
+ debug(`Sync objects create in DB in ${time4 - time3} ms`);
220
+
221
+ // call the batch update/creates
222
+ let time5 = parseInt(moment().format('x'));
223
+ let time6 = parseInt(moment().format('x'));
224
+ if (!this.isUnidirectionalSync) {
225
+ await this.primaryModule.batchUpdateSyncObjects(
226
+ primaryUpdate,
227
+ this
228
+ );
229
+ time5 = parseInt(moment().format('x'));
230
+ debug(
231
+ `Updated ${primaryUpdate.length} ${
232
+ this.SyncObjectClass.name
233
+ }s in ${this.primaryModule.constructor.getName()} in ${
234
+ time5 - time4
235
+ } ms`
236
+ );
237
+ await this.primaryModule.batchCreateSyncObjects(
238
+ primaryCreate,
239
+ this
240
+ );
241
+ time6 = parseInt(moment().format('x'));
242
+ debug(
243
+ `Created ${primaryCreate.length} ${
244
+ this.SyncObjectClass.name
245
+ }s in ${this.primaryModule.constructor.getName()} in ${
246
+ time6 - time5
247
+ } ms`
248
+ );
249
+ }
250
+
251
+ await this.secondaryModule.batchUpdateSyncObjects(
252
+ secondaryUpdate,
253
+ this
254
+ );
255
+ const time7 = parseInt(moment().format('x'));
256
+ debug(
257
+ `Updated ${secondaryUpdate.length} ${
258
+ this.SyncObjectClass.name
259
+ }s in ${this.secondaryModule.constructor.getName()} in ${
260
+ time7 - time6
261
+ } ms`
262
+ );
263
+
264
+ await this.secondaryModule.batchCreateSyncObjects(
265
+ secondaryCreate,
266
+ this
267
+ );
268
+ const time8 = parseInt(moment().format('x'));
269
+ debug(
270
+ `${primaryArrayInitialCount} number of ${
271
+ this.SyncObjectClass.name
272
+ } objects retrieved from ${this.primaryModule.constructor.getName()} in ${
273
+ time1 - time0
274
+ } ms`
275
+ );
276
+ debug(
277
+ `${secondaryArrayInitialCount} number of ${
278
+ this.SyncObjectClass.name
279
+ } objects retrieved from ${this.secondaryModule.constructor.getName()} in ${
280
+ time2 - time1
281
+ } ms`
282
+ );
283
+ debug(`Sorting complete in ${time3 - time2} ms`);
284
+ debug(`Sync objects create in DB in ${time4 - time3} ms`);
285
+ debug(
286
+ `Updated ${primaryUpdate.length} ${
287
+ this.SyncObjectClass.name
288
+ }s in ${this.primaryModule.constructor.getName()} in ${
289
+ time5 - time4
290
+ } ms`
291
+ );
292
+ debug(
293
+ `Created ${primaryCreate.length} ${
294
+ this.SyncObjectClass.name
295
+ }s in ${this.primaryModule.constructor.getName()} in ${
296
+ time6 - time5
297
+ } ms`
298
+ );
299
+ debug(
300
+ `Updated ${secondaryUpdate.length} ${
301
+ this.SyncObjectClass.name
302
+ }s in ${this.secondaryModule.constructor.getName()} in ${
303
+ time7 - time6
304
+ } ms`
305
+ );
306
+ debug(
307
+ `Created ${secondaryCreate.length} ${
308
+ this.SyncObjectClass.name
309
+ }s in ${this.secondaryModule.constructor.getName()} in ${
310
+ time8 - time7
311
+ } ms`
312
+ );
212
313
  }
213
314
 
214
- for (const primaryObj of secondaryCreate) {
215
- const createdObj = await this.createSyncDBObject(
216
- [primaryObj],
217
- [primaryEntityId, secondaryEntityId]
218
- );
219
- primaryObj.setSyncId(createdObj.id);
315
+ async createSyncDBObject(objArr, entities) {
316
+ const entityIds = entities.map(
317
+ (ent) => ({ $elemMatch: { $eq: mongoose.Types.ObjectId(ent) } })
318
+ // return {"$elemMatch": {"$eq": ent}};
319
+ );
320
+ const dataIdentifiers = [];
321
+ for (const index in objArr) {
322
+ dataIdentifiers.push({
323
+ entity: entities[index],
324
+ id: objArr[index].dataIdentifier,
325
+ hash: objArr[index].dataIdentifierHash,
326
+ });
327
+ }
328
+ const primaryObj = objArr[0];
329
+
330
+ const createSyncObj = {
331
+ name: primaryObj.getName(),
332
+ entities,
333
+ hash: primaryObj.getHashData({
334
+ omitEmptyStringsFromData: this.omitEmptyStringsFromData,
335
+ }),
336
+ dataIdentifiers,
337
+ };
338
+ const filter = {
339
+ name: primaryObj.getName(),
340
+ dataIdentifiers: {
341
+ $elemMatch: {
342
+ id: primaryObj.dataIdentifier,
343
+ entity: entities[0],
344
+ },
345
+ },
346
+ entities: { $all: entityIds },
347
+ // entities
348
+ };
349
+
350
+ return await this.syncRepository.upsertSync(filter, createSyncObj);
220
351
  }
221
- const time4 = parseInt(moment().format("x"));
222
- debug(`Sync objects create in DB in ${time4 - time3} ms`);
223
-
224
- // call the batch update/creates
225
- let time5 = parseInt(moment().format("x"));
226
- let time6 = parseInt(moment().format("x"));
227
- if (!this.isUnidirectionalSync) {
228
- await this.primaryModule.batchUpdateSyncObjects(primaryUpdate, this);
229
- time5 = parseInt(moment().format("x"));
230
- debug(
231
- `Updated ${primaryUpdate.length} ${
232
- this.SyncObjectClass.name
233
- }s in ${this.primaryModule.constructor.getName()} in ${
234
- time5 - time4
235
- } ms`
236
- );
237
- await this.primaryModule.batchCreateSyncObjects(primaryCreate, this);
238
- time6 = parseInt(moment().format("x"));
239
- debug(
240
- `Created ${primaryCreate.length} ${
241
- this.SyncObjectClass.name
242
- }s in ${this.primaryModule.constructor.getName()} in ${
243
- time6 - time5
244
- } ms`
245
- );
352
+
353
+ // Automatically syncs the objects with the secondary module if the object was updated
354
+ async sync(syncObjects) {
355
+ const batchUpdates = [];
356
+ const batchCreates = [];
357
+ const noChange = [];
358
+ const primaryEntityId = await this.primaryModule.entity.id;
359
+ const secondaryEntityId = await this.secondaryModule.entity.id;
360
+
361
+ const secondaryModuleName = this.secondaryModule.constructor.getName();
362
+ for (const primaryObj of syncObjects) {
363
+ const dataHash = primaryObj.getHashData({
364
+ omitEmptyStringsFromData: this.omitEmptyStringsFromData,
365
+ });
366
+
367
+ // get the sync object in the database if it exists
368
+ let syncObj = await this.syncRepository.getSyncObject(
369
+ primaryObj.getName(),
370
+ primaryObj.dataIdentifier,
371
+ primaryEntityId
372
+ );
373
+
374
+ if (syncObj) {
375
+ debug('Sync object found, evaluating...');
376
+ const hashMatch = syncObj.hash === dataHash;
377
+ const dataIdentifierLength = syncObj.dataIdentifiers.length;
378
+
379
+ if (!hashMatch && dataIdentifierLength > 1) {
380
+ debug(
381
+ "Previously successful sync, but hashes don't match. Updating."
382
+ );
383
+ const secondaryObj = new this.SyncObjectClass({
384
+ data: primaryObj.data,
385
+ dataIdentifier:
386
+ this.syncRepository.getEntityObjIdForEntityIdFromObject(
387
+ syncObj,
388
+ secondaryEntityId
389
+ ),
390
+ moduleName: secondaryModuleName,
391
+ useMapping: false,
392
+ });
393
+ secondaryObj.setSyncId(syncObj.id);
394
+ batchUpdates.push(secondaryObj);
395
+ } else if (hashMatch && dataIdentifierLength > 1) {
396
+ debug(
397
+ 'Data hashes match, no updates or creates needed for this one.'
398
+ );
399
+ noChange.push(syncObj);
400
+ }
401
+
402
+ if (dataIdentifierLength === 1) {
403
+ debug(
404
+ "We have only one data Identifier, which means we don't have a record in the secondary app for whatever reason (failure or filter). So, creating."
405
+ );
406
+ primaryObj.setSyncId(syncObj.id);
407
+ batchCreates.push(primaryObj);
408
+ }
409
+ } else {
410
+ debug(
411
+ "No sync object, so we'll try creating, first creating an object"
412
+ );
413
+ syncObj = await this.createSyncDBObject(
414
+ [primaryObj],
415
+ [primaryEntityId, secondaryEntityId]
416
+ );
417
+ primaryObj.setSyncId(syncObj.id);
418
+ batchCreates.push(primaryObj);
419
+ }
420
+ }
421
+ const updateRes =
422
+ batchUpdates.length > 0
423
+ ? await this.secondaryModule.batchUpdateSyncObjects(
424
+ batchUpdates,
425
+ this
426
+ )
427
+ : [];
428
+ const createRes =
429
+ batchCreates.length > 0
430
+ ? await this.secondaryModule.batchCreateSyncObjects(
431
+ batchCreates,
432
+ this
433
+ )
434
+ : [];
435
+ return updateRes.concat(createRes).concat(noChange);
246
436
  }
247
437
 
248
- await this.secondaryModule.batchUpdateSyncObjects(secondaryUpdate, this);
249
- const time7 = parseInt(moment().format("x"));
250
- debug(
251
- `Updated ${secondaryUpdate.length} ${
252
- this.SyncObjectClass.name
253
- }s in ${this.secondaryModule.constructor.getName()} in ${
254
- time7 - time6
255
- } ms`
256
- );
257
-
258
- await this.secondaryModule.batchCreateSyncObjects(secondaryCreate, this);
259
- const time8 = parseInt(moment().format("x"));
260
- debug(
261
- `${primaryArrayInitialCount} number of ${
262
- this.SyncObjectClass.name
263
- } objects retrieved from ${this.primaryModule.constructor.getName()} in ${
264
- time1 - time0
265
- } ms`
266
- );
267
- debug(
268
- `${secondaryArrayInitialCount} number of ${
269
- this.SyncObjectClass.name
270
- } objects retrieved from ${this.secondaryModule.constructor.getName()} in ${
271
- time2 - time1
272
- } ms`
273
- );
274
- debug(`Sorting complete in ${time3 - time2} ms`);
275
- debug(`Sync objects create in DB in ${time4 - time3} ms`);
276
- debug(
277
- `Updated ${primaryUpdate.length} ${
278
- this.SyncObjectClass.name
279
- }s in ${this.primaryModule.constructor.getName()} in ${time5 - time4} ms`
280
- );
281
- debug(
282
- `Created ${primaryCreate.length} ${
283
- this.SyncObjectClass.name
284
- }s in ${this.primaryModule.constructor.getName()} in ${time6 - time5} ms`
285
- );
286
- debug(
287
- `Updated ${secondaryUpdate.length} ${
288
- this.SyncObjectClass.name
289
- }s in ${this.secondaryModule.constructor.getName()} in ${
290
- time7 - time6
291
- } ms`
292
- );
293
- debug(
294
- `Created ${secondaryCreate.length} ${
295
- this.SyncObjectClass.name
296
- }s in ${this.secondaryModule.constructor.getName()} in ${
297
- time8 - time7
298
- } ms`
299
- );
300
- }
301
-
302
- async createSyncDBObject(objArr, entities) {
303
- const entityIds = entities.map(
304
- (ent) => ({ $elemMatch: { $eq: mongoose.Types.ObjectId(ent) } })
305
- // return {"$elemMatch": {"$eq": ent}};
306
- );
307
- const dataIdentifiers = [];
308
- for (const index in objArr) {
309
- dataIdentifiers.push({
310
- entity: entities[index],
311
- id: objArr[index].dataIdentifier,
312
- hash: objArr[index].dataIdentifierHash,
313
- });
438
+ // takes in:
439
+ // 1. the Sync Id of an object in our database
440
+ // 2. the object Id in the form of a json object for example:
441
+ // {
442
+ // companyId: 12,
443
+ // saleId:524
444
+ // }
445
+ // 3. the module manager calling the function
446
+ async confirmCreate(syncObj, createdId, moduleManager) {
447
+ const dataIdentifier = {
448
+ entity: await moduleManager.entity.id,
449
+ id: createdId,
450
+ hash: this.SyncObjectClass.hashJSON(createdId),
451
+ };
452
+ // No matter what, save the hash because why not?
453
+ // TODO this is suboptimal because it does 2 DB requests where only 1 is needed
454
+ // TODO If you want to get even more optimized, batch any/all updates together.
455
+ // Also this is only needed because of the case where an "update" becomes a "create" when we find only
456
+ // 1 data identifier. So, during `sync()`, if we see that the hashes don't match, we check for DataIDs and
457
+ // decide to create in the "target" or "secondary" because we know it failed for some reason. We also want
458
+ // to hold off on updating the hash in case the create fails for some reason again.
459
+
460
+ await this.syncRepository.updateSync(syncObj.syncId, {
461
+ hash: syncObj.getHashData({
462
+ omitEmptyStringsFromData: this.omitEmptyStringsFromData,
463
+ }),
464
+ });
465
+
466
+ const result = await this.syncRepository.addDataIdentifier(
467
+ syncObj.syncId,
468
+ dataIdentifier
469
+ );
470
+
471
+ return result;
314
472
  }
315
- const primaryObj = objArr[0];
316
-
317
- const createSyncObj = {
318
- name: primaryObj.getName(),
319
- entities,
320
- hash: primaryObj.getHashData({
321
- omitEmptyStringsFromData: this.omitEmptyStringsFromData,
322
- }),
323
- dataIdentifiers,
324
- };
325
- const filter = {
326
- name: primaryObj.getName(),
327
- dataIdentifiers: {
328
- $elemMatch: {
329
- id: primaryObj.dataIdentifier,
330
- entity: entities[0],
331
- },
332
- },
333
- entities: { $all: entityIds },
334
- // entities
335
- };
336
-
337
- return await Sync.upsert(filter, createSyncObj);
338
- }
339
-
340
- // Automatically syncs the objects with the secondary module if the object was updated
341
- async sync(syncObjects) {
342
- const batchUpdates = [];
343
- const batchCreates = [];
344
- const noChange = [];
345
- const primaryEntityId = await this.primaryModule.entity.id;
346
- const secondaryEntityId = await this.secondaryModule.entity.id;
347
-
348
- const secondaryModuleName = this.secondaryModule.constructor.getName();
349
- for (const primaryObj of syncObjects) {
350
- const dataHash = primaryObj.getHashData({
351
- omitEmptyStringsFromData: this.omitEmptyStringsFromData,
352
- });
353
-
354
- // get the sync object in the database if it exists
355
- let syncObj = await Sync.getSyncObject(
356
- primaryObj.getName(),
357
- primaryObj.dataIdentifier,
358
- primaryEntityId
359
- );
360
-
361
- if (syncObj) {
362
- debug("Sync object found, evaluating...");
363
- const hashMatch = syncObj.hash === dataHash;
364
- const dataIdentifierLength = syncObj.dataIdentifiers.length;
365
-
366
- if (!hashMatch && dataIdentifierLength > 1) {
367
- debug(
368
- "Previously successful sync, but hashes don't match. Updating."
369
- );
370
- const secondaryObj = new this.SyncObjectClass({
371
- data: primaryObj.data,
372
- dataIdentifier: Sync.getEntityObjIdForEntityIdFromObject(
373
- syncObj,
374
- secondaryEntityId
375
- ),
376
- moduleName: secondaryModuleName,
377
- useMapping: false,
378
- });
379
- secondaryObj.setSyncId(syncObj.id);
380
- batchUpdates.push(secondaryObj);
381
- } else if (hashMatch && dataIdentifierLength > 1) {
382
- debug(
383
- "Data hashes match, no updates or creates needed for this one."
384
- );
385
- noChange.push(syncObj);
386
- }
387
473
 
388
- if (dataIdentifierLength === 1) {
389
- debug(
390
- "We have only one data Identifier, which means we don't have a record in the secondary app for whatever reason (failure or filter). So, creating."
391
- );
392
- primaryObj.setSyncId(syncObj.id);
393
- batchCreates.push(primaryObj);
394
- }
395
- } else {
474
+ async confirmUpdate(syncObj) {
396
475
  debug(
397
- "No sync object, so we'll try creating, first creating an object"
398
- );
399
- syncObj = await this.createSyncDBObject(
400
- [primaryObj],
401
- [primaryEntityId, secondaryEntityId]
476
+ 'Successfully updated secondaryObject. Updating the hash in the DB'
402
477
  );
403
- primaryObj.setSyncId(syncObj.id);
404
- batchCreates.push(primaryObj);
405
- }
478
+ const result = await this.syncRepository.updateSync(syncObj.syncId, {
479
+ hash: syncObj.getHashData({
480
+ omitEmptyStringsFromData: this.omitEmptyStringsFromData,
481
+ }),
482
+ });
483
+ debug('Success');
484
+
485
+ return result;
406
486
  }
407
- const updateRes =
408
- batchUpdates.length > 0
409
- ? await this.secondaryModule.batchUpdateSyncObjects(batchUpdates, this)
410
- : [];
411
- const createRes =
412
- batchCreates.length > 0
413
- ? await this.secondaryModule.batchCreateSyncObjects(batchCreates, this)
414
- : [];
415
- return updateRes.concat(createRes).concat(noChange);
416
- }
417
-
418
- // takes in:
419
- // 1. the Sync Id of an object in our database
420
- // 2. the object Id in the form of a json object for example:
421
- // {
422
- // companyId: 12,
423
- // saleId:524
424
- // }
425
- // 3. the module manager calling the function
426
- async confirmCreate(syncObj, createdId, moduleManager) {
427
- const dataIdentifier = {
428
- entity: await moduleManager.entity.id,
429
- id: createdId,
430
- hash: this.SyncObjectClass.hashJSON(createdId),
431
- };
432
- // No matter what, save the hash because why not?
433
- // TODO this is suboptimal because it does 2 DB requests where only 1 is needed
434
- // TODO If you want to get even more optimized, batch any/all updates together.
435
- // Also this is only needed because of the case where an "update" becomes a "create" when we find only
436
- // 1 data identifier. So, during `sync()`, if we see that the hashes don't match, we check for DataIDs and
437
- // decide to create in the "target" or "secondary" because we know it failed for some reason. We also want
438
- // to hold off on updating the hash in case the create fails for some reason again.
439
-
440
- await Sync.update(syncObj.syncId, {
441
- hash: syncObj.getHashData({
442
- omitEmptyStringsFromData: this.omitEmptyStringsFromData,
443
- }),
444
- });
445
-
446
- const result = await Sync.addDataIdentifier(syncObj.syncId, dataIdentifier);
447
-
448
- return result;
449
- }
450
-
451
- async confirmUpdate(syncObj) {
452
- debug("Successfully updated secondaryObject. Updating the hash in the DB");
453
- const result = await Sync.update(syncObj.syncId, {
454
- hash: syncObj.getHashData({
455
- omitEmptyStringsFromData: this.omitEmptyStringsFromData,
456
- }),
457
- });
458
- debug("Success");
459
-
460
- return result;
461
- }
462
487
  }
463
488
 
464
489
  module.exports = SyncManager;