@friggframework/core 2.0.0-next.40 → 2.0.0-next.42
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.
- package/CLAUDE.md +693 -0
- package/README.md +931 -50
- package/application/commands/README.md +421 -0
- package/application/commands/credential-commands.js +224 -0
- package/application/commands/entity-commands.js +315 -0
- package/application/commands/integration-commands.js +160 -0
- package/application/commands/integration-commands.test.js +123 -0
- package/application/commands/user-commands.js +213 -0
- package/application/index.js +69 -0
- package/core/CLAUDE.md +690 -0
- package/core/create-handler.js +0 -6
- package/credential/repositories/credential-repository-factory.js +47 -0
- package/credential/repositories/credential-repository-interface.js +98 -0
- package/credential/repositories/credential-repository-mongo.js +301 -0
- package/credential/repositories/credential-repository-postgres.js +307 -0
- package/credential/repositories/credential-repository.js +307 -0
- package/credential/use-cases/get-credential-for-user.js +21 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/config.js +117 -0
- package/database/encryption/README.md +683 -0
- package/database/encryption/encryption-integration.test.js +553 -0
- package/database/encryption/encryption-schema-registry.js +141 -0
- package/database/encryption/encryption-schema-registry.test.js +392 -0
- package/database/encryption/field-encryption-service.js +226 -0
- package/database/encryption/field-encryption-service.test.js +525 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/mongo-decryption-fix-verification.test.js +348 -0
- package/database/encryption/postgres-decryption-fix-verification.test.js +371 -0
- package/database/encryption/postgres-relation-decryption.test.js +245 -0
- package/database/encryption/prisma-encryption-extension.js +222 -0
- package/database/encryption/prisma-encryption-extension.test.js +439 -0
- package/database/index.js +25 -12
- package/database/models/readme.md +1 -0
- package/database/prisma.js +162 -0
- package/database/repositories/health-check-repository-factory.js +38 -0
- package/database/repositories/health-check-repository-interface.js +86 -0
- package/database/repositories/health-check-repository-mongodb.js +72 -0
- package/database/repositories/health-check-repository-postgres.js +75 -0
- package/database/repositories/health-check-repository.js +108 -0
- package/database/use-cases/check-database-health-use-case.js +34 -0
- package/database/use-cases/check-encryption-health-use-case.js +82 -0
- package/database/use-cases/test-encryption-use-case.js +252 -0
- package/encrypt/Cryptor.js +20 -152
- package/encrypt/index.js +1 -2
- package/encrypt/test-encrypt.js +0 -2
- package/handlers/app-definition-loader.js +38 -0
- package/handlers/app-handler-helpers.js +0 -3
- package/handlers/auth-flow.integration.test.js +147 -0
- package/handlers/backend-utils.js +25 -45
- package/handlers/integration-event-dispatcher.js +54 -0
- package/handlers/integration-event-dispatcher.test.js +141 -0
- package/handlers/routers/HEALTHCHECK.md +103 -1
- package/handlers/routers/auth.js +3 -14
- package/handlers/routers/health.js +63 -424
- package/handlers/routers/health.test.js +7 -0
- package/handlers/routers/integration-defined-routers.js +8 -5
- package/handlers/routers/user.js +25 -5
- package/handlers/routers/websocket.js +5 -3
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +32 -0
- package/handlers/workers/integration-defined-workers.js +6 -3
- package/index.js +45 -22
- package/integrations/index.js +12 -10
- package/integrations/integration-base.js +224 -53
- package/integrations/integration-router.js +386 -178
- package/integrations/options.js +1 -1
- package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-factory.js +44 -0
- package/integrations/repositories/integration-repository-interface.js +115 -0
- package/integrations/repositories/integration-repository-mongo.js +271 -0
- package/integrations/repositories/integration-repository-postgres.js +319 -0
- package/integrations/tests/doubles/dummy-integration-class.js +90 -0
- package/integrations/tests/doubles/test-integration-repository.js +99 -0
- package/integrations/tests/use-cases/create-integration.test.js +131 -0
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +150 -0
- package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +92 -0
- package/integrations/tests/use-cases/get-integration-for-user.test.js +150 -0
- package/integrations/tests/use-cases/get-integration-instance.test.js +176 -0
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +176 -0
- package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
- package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
- package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
- package/integrations/tests/use-cases/update-integration.test.js +141 -0
- package/integrations/use-cases/create-integration.js +83 -0
- package/integrations/use-cases/delete-integration-for-user.js +73 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +87 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/index.js +11 -0
- package/integrations/use-cases/load-integration-context-full.test.js +329 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/load-integration-context.test.js +114 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +93 -0
- package/integrations/utils/map-integration-dto.js +36 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/{module-plugin → modules}/entity.js +1 -0
- package/{module-plugin → modules}/index.js +0 -8
- package/modules/module-factory.js +56 -0
- package/modules/module-hydration.test.js +205 -0
- package/modules/module.js +221 -0
- package/modules/repositories/module-repository-factory.js +33 -0
- package/modules/repositories/module-repository-interface.js +129 -0
- package/modules/repositories/module-repository-mongo.js +386 -0
- package/modules/repositories/module-repository-postgres.js +437 -0
- package/modules/repositories/module-repository.js +327 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +59 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +31 -0
- package/modules/use-cases/get-module.js +56 -0
- package/modules/use-cases/process-authorization-callback.js +121 -0
- package/modules/use-cases/refresh-entity-options.js +59 -0
- package/modules/use-cases/test-module-auth.js +55 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +14 -6
- package/prisma-mongodb/schema.prisma +321 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +303 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-factory.js +38 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/token/repositories/token-repository-factory.js +33 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +212 -0
- package/token/repositories/token-repository-postgres.js +257 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/integrations/index.d.ts +2 -6
- package/types/module-plugin/index.d.ts +5 -57
- package/types/syncs/index.d.ts +0 -2
- package/user/repositories/user-repository-factory.js +46 -0
- package/user/repositories/user-repository-interface.js +198 -0
- package/user/repositories/user-repository-mongo.js +250 -0
- package/user/repositories/user-repository-postgres.js +311 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/tests/use-cases/create-individual-user.test.js +24 -0
- package/user/tests/use-cases/create-organization-user.test.js +28 -0
- package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
- package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
- package/user/tests/use-cases/login-user.test.js +140 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +77 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +155 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +195 -0
- package/websocket/repositories/websocket-connection-repository.js +160 -0
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/mongo.js +0 -171
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -104
- package/encrypt/encrypt.test.js +0 -1069
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/test/auther.test.js +0 -97
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/api-key.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
- /package/{module-plugin → modules}/requester/requester.js +0 -0
- /package/{module-plugin → modules}/requester/requester.test.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
package/syncs/manager.js
CHANGED
|
@@ -1,464 +1,489 @@
|
|
|
1
|
-
const _ = require(
|
|
2
|
-
const moment = require(
|
|
3
|
-
const mongoose = require(
|
|
4
|
-
const SyncObject = require(
|
|
5
|
-
const { debug } = require(
|
|
6
|
-
const { get } = require(
|
|
7
|
-
const {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
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
|
-
|
|
398
|
-
);
|
|
399
|
-
syncObj = await this.createSyncDBObject(
|
|
400
|
-
[primaryObj],
|
|
401
|
-
[primaryEntityId, secondaryEntityId]
|
|
476
|
+
'Successfully updated secondaryObject. Updating the hash in the DB'
|
|
402
477
|
);
|
|
403
|
-
|
|
404
|
-
|
|
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;
|