@friggframework/core 2.0.0-next.41 → 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
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const catchAsyncError = require('express-async-handler');
|
|
2
|
-
const { User } = require('../../backend-utils');
|
|
3
|
-
|
|
4
|
-
module.exports = catchAsyncError(async (req, res, next) => {
|
|
5
|
-
const authorizationHeader = req.headers.authorization;
|
|
6
|
-
|
|
7
|
-
if (authorizationHeader) {
|
|
8
|
-
// Removes "Bearer " and trims
|
|
9
|
-
const token = authorizationHeader.split(' ')[1].trim();
|
|
10
|
-
// Load user for later middleware/routes to use
|
|
11
|
-
req.user = await User.newUser({ token });
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return next();
|
|
15
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const Boom = require('@hapi/boom');
|
|
2
|
-
|
|
3
|
-
// CheckLoggedIn Middleware
|
|
4
|
-
const requireLoggedInUser = (req, res, next) => {
|
|
5
|
-
if (!req.user || !req.user.isLoggedIn()) {
|
|
6
|
-
throw Boom.unauthorized('Invalid Token');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
next();
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
module.exports = { requireLoggedInUser };
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const {IntegrationFactory, IntegrationHelper} = require('./integration-factory');
|
|
2
|
-
const User = require('./integration-user');
|
|
3
|
-
|
|
4
|
-
function createFriggBackend(appDefinition) {
|
|
5
|
-
const {integrations = [], user=null} = appDefinition
|
|
6
|
-
const integrationFactory = new IntegrationFactory(integrations);
|
|
7
|
-
if (user) {
|
|
8
|
-
if (user.usePassword) {
|
|
9
|
-
User.usePassword = true;
|
|
10
|
-
}
|
|
11
|
-
if (user.primary === 'organization') {
|
|
12
|
-
User.primary = User.OrganizationUser
|
|
13
|
-
}
|
|
14
|
-
if (user.individualUserRequired !== undefined) {
|
|
15
|
-
User.individualUserRequired = user.individualUserRequired
|
|
16
|
-
}
|
|
17
|
-
if (user.organizationUserRequired !== undefined) {
|
|
18
|
-
User.organizationUserRequired = user.organizationUserRequired
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
const backend = {
|
|
23
|
-
integrationFactory,
|
|
24
|
-
moduleFactory: integrationFactory.moduleFactory,
|
|
25
|
-
IntegrationHelper,
|
|
26
|
-
User: User
|
|
27
|
-
}
|
|
28
|
-
return backend
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = { createFriggBackend }
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
const { ModuleFactory, Credential, Entity } = require('../module-plugin');
|
|
2
|
-
const { IntegrationModel } = require('./integration-model');
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
|
|
5
|
-
class IntegrationFactory {
|
|
6
|
-
constructor(integrationClasses = []) {
|
|
7
|
-
this.integrationClasses = integrationClasses;
|
|
8
|
-
this.moduleFactory = new ModuleFactory(...this.getModules());
|
|
9
|
-
this.integrationTypes = this.integrationClasses.map(
|
|
10
|
-
(IntegrationClass) => IntegrationClass.getName()
|
|
11
|
-
);
|
|
12
|
-
this.getIntegrationDefinitions = this.integrationClasses.map(
|
|
13
|
-
(IntegrationClass) => IntegrationClass.Definition
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async getIntegrationOptions() {
|
|
18
|
-
const options = this.integrationClasses.map(
|
|
19
|
-
(IntegrationClass) => IntegrationClass
|
|
20
|
-
);
|
|
21
|
-
return {
|
|
22
|
-
entities: {
|
|
23
|
-
options: options.map((IntegrationClass) =>
|
|
24
|
-
IntegrationClass.getOptionDetails()
|
|
25
|
-
),
|
|
26
|
-
authorized: [],
|
|
27
|
-
},
|
|
28
|
-
integrations: [],
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getModules() {
|
|
33
|
-
return [
|
|
34
|
-
...new Set(
|
|
35
|
-
this.integrationClasses
|
|
36
|
-
.map((integration) =>
|
|
37
|
-
Object.values(integration.Definition.modules).map(
|
|
38
|
-
(module) => module.definition
|
|
39
|
-
)
|
|
40
|
-
)
|
|
41
|
-
.flat()
|
|
42
|
-
),
|
|
43
|
-
];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
getIntegrationClassByType(type) {
|
|
47
|
-
const integrationClassIndex = this.integrationTypes.indexOf(type);
|
|
48
|
-
return this.integrationClasses[integrationClassIndex];
|
|
49
|
-
}
|
|
50
|
-
getModuleTypesAndKeys(integrationClass) {
|
|
51
|
-
const moduleTypesAndKeys = {};
|
|
52
|
-
const moduleTypeCount = {};
|
|
53
|
-
|
|
54
|
-
if (integrationClass && integrationClass.Definition.modules) {
|
|
55
|
-
for (const [key, moduleClass] of Object.entries(
|
|
56
|
-
integrationClass.Definition.modules
|
|
57
|
-
)) {
|
|
58
|
-
if (
|
|
59
|
-
moduleClass &&
|
|
60
|
-
typeof moduleClass.definition.getName === 'function'
|
|
61
|
-
) {
|
|
62
|
-
const moduleType = moduleClass.definition.getName();
|
|
63
|
-
|
|
64
|
-
// Check if this module type has already been seen
|
|
65
|
-
if (moduleType in moduleTypesAndKeys) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
`Duplicate module type "${moduleType}" found in integration class definition.`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Well how baout now
|
|
72
|
-
|
|
73
|
-
moduleTypesAndKeys[moduleType] = key;
|
|
74
|
-
moduleTypeCount[moduleType] =
|
|
75
|
-
(moduleTypeCount[moduleType] || 0) + 1;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Check for any module types with count > 1
|
|
81
|
-
for (const [moduleType, count] of Object.entries(moduleTypeCount)) {
|
|
82
|
-
if (count > 1) {
|
|
83
|
-
throw new Error(
|
|
84
|
-
`Multiple instances of module type "${moduleType}" found in integration class definition.`
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return moduleTypesAndKeys;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async getInstanceFromIntegrationId(params) {
|
|
93
|
-
const integrationRecord = await IntegrationHelper.getIntegrationById(
|
|
94
|
-
params.integrationId
|
|
95
|
-
);
|
|
96
|
-
let { userId } = params;
|
|
97
|
-
if (!integrationRecord) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`No integration found by the ID of ${params.integrationId}`
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (!userId) {
|
|
104
|
-
userId = integrationRecord.user._id.toString();
|
|
105
|
-
} else if (userId.toString() !== integrationRecord.user.toString()) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
`Integration ${
|
|
108
|
-
params.integrationId
|
|
109
|
-
} does not belong to User ${userId}, ${integrationRecord.user.toString()}`
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const integrationClass = this.getIntegrationClassByType(
|
|
114
|
-
integrationRecord.config.type
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const instance = new integrationClass({
|
|
118
|
-
userId,
|
|
119
|
-
integrationId: params.integrationId,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
integrationRecord.entityReference &&
|
|
124
|
-
Object.keys(integrationRecord.entityReference) > 0
|
|
125
|
-
) {
|
|
126
|
-
// Use the specified entityReference to find the modules and load them according to their key
|
|
127
|
-
// entityReference will be a map of entityIds with their corresponding desired key
|
|
128
|
-
for (const [entityId, key] of Object.entries(
|
|
129
|
-
integrationRecord.entityReference
|
|
130
|
-
)) {
|
|
131
|
-
const moduleInstance =
|
|
132
|
-
await this.moduleFactory.getModuleInstanceFromEntityId(
|
|
133
|
-
entityId,
|
|
134
|
-
integrationRecord.user
|
|
135
|
-
);
|
|
136
|
-
instance[key] = moduleInstance;
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
// for each entity, get the moduleinstance and load them according to their keys
|
|
140
|
-
// If it's the first entity, load the moduleinstance into primary as well
|
|
141
|
-
// If it's the second entity, load the moduleinstance into target as well
|
|
142
|
-
const moduleTypesAndKeys =
|
|
143
|
-
this.getModuleTypesAndKeys(integrationClass);
|
|
144
|
-
for (let i = 0; i < integrationRecord.entities.length; i++) {
|
|
145
|
-
const entityId = integrationRecord.entities[i];
|
|
146
|
-
const moduleInstance =
|
|
147
|
-
await this.moduleFactory.getModuleInstanceFromEntityId(
|
|
148
|
-
entityId,
|
|
149
|
-
integrationRecord.user
|
|
150
|
-
);
|
|
151
|
-
const moduleType = moduleInstance.getName();
|
|
152
|
-
const key = moduleTypesAndKeys[moduleType];
|
|
153
|
-
instance[key] = moduleInstance;
|
|
154
|
-
if (i === 0) {
|
|
155
|
-
instance.primary = moduleInstance;
|
|
156
|
-
} else if (i === 1) {
|
|
157
|
-
instance.target = moduleInstance;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
instance.record = integrationRecord;
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
const additionalUserActions =
|
|
165
|
-
await instance.loadDynamicUserActions();
|
|
166
|
-
instance.events = { ...instance.events, ...additionalUserActions };
|
|
167
|
-
} catch (e) {
|
|
168
|
-
instance.record.status = 'ERROR';
|
|
169
|
-
instance.record.messages.errors.push(e);
|
|
170
|
-
await instance.record.save();
|
|
171
|
-
}
|
|
172
|
-
// Register all of the event handlers
|
|
173
|
-
|
|
174
|
-
await instance.registerEventHandlers();
|
|
175
|
-
return instance;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async createIntegration(entities, userId, config) {
|
|
179
|
-
const integrationRecord = await IntegrationModel.create({
|
|
180
|
-
entities: entities,
|
|
181
|
-
user: userId,
|
|
182
|
-
config,
|
|
183
|
-
version: '0.0.0',
|
|
184
|
-
});
|
|
185
|
-
return await this.getInstanceFromIntegrationId({
|
|
186
|
-
integrationId: integrationRecord.id,
|
|
187
|
-
userId,
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const IntegrationHelper = {
|
|
193
|
-
getFormattedIntegration: async function (integrationRecord) {
|
|
194
|
-
const integrationObj = {
|
|
195
|
-
id: integrationRecord.id,
|
|
196
|
-
status: integrationRecord.status,
|
|
197
|
-
config: integrationRecord.config,
|
|
198
|
-
entities: [],
|
|
199
|
-
version: integrationRecord.version,
|
|
200
|
-
messages: integrationRecord.messages,
|
|
201
|
-
};
|
|
202
|
-
for (const entityId of integrationRecord.entities) {
|
|
203
|
-
// Only return non-internal fields. Leverages "select" and "options" to non-excepted fields and a pure object.
|
|
204
|
-
const entity = await Entity.findById(
|
|
205
|
-
entityId,
|
|
206
|
-
'-createdAt -updatedAt -user -credentials -credential -_id -__t -__v',
|
|
207
|
-
{ lean: true }
|
|
208
|
-
);
|
|
209
|
-
integrationObj.entities.push({
|
|
210
|
-
id: entityId,
|
|
211
|
-
...entity,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
return integrationObj;
|
|
215
|
-
},
|
|
216
|
-
|
|
217
|
-
getIntegrationsForUserId: async function (userId) {
|
|
218
|
-
const integrationList = await IntegrationModel.find({ user: userId });
|
|
219
|
-
return await Promise.all(
|
|
220
|
-
integrationList.map(
|
|
221
|
-
async (integrationRecord) =>
|
|
222
|
-
await IntegrationHelper.getFormattedIntegration(
|
|
223
|
-
integrationRecord
|
|
224
|
-
)
|
|
225
|
-
)
|
|
226
|
-
);
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
deleteIntegrationForUserById: async function (userId, integrationId) {
|
|
230
|
-
const integrationList = await IntegrationModel.find({
|
|
231
|
-
user: userId,
|
|
232
|
-
_id: integrationId,
|
|
233
|
-
});
|
|
234
|
-
if (integrationList.length !== 1) {
|
|
235
|
-
throw new Error(
|
|
236
|
-
`Integration with id of ${integrationId} does not exist for this user`
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
await IntegrationModel.deleteOne({ _id: integrationId });
|
|
240
|
-
},
|
|
241
|
-
|
|
242
|
-
getIntegrationById: async function (id) {
|
|
243
|
-
return IntegrationModel.findById(id).populate('entities');
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
listCredentials: async function (options) {
|
|
247
|
-
return Credential.find(options);
|
|
248
|
-
},
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
module.exports = { IntegrationFactory, IntegrationHelper };
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const { mongoose } = require('../database/mongoose');
|
|
2
|
-
const { Encrypt } = require('../encrypt');
|
|
3
|
-
|
|
4
|
-
const schema = new mongoose.Schema(
|
|
5
|
-
{
|
|
6
|
-
integration: {
|
|
7
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
8
|
-
ref: 'Integration',
|
|
9
|
-
required: true,
|
|
10
|
-
},
|
|
11
|
-
sourceId: { type: String }, // Used for lookups
|
|
12
|
-
mapping: {}
|
|
13
|
-
},
|
|
14
|
-
{ timestamps: true }
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
schema.plugin(Encrypt);
|
|
18
|
-
|
|
19
|
-
schema.static({
|
|
20
|
-
findBy: async function (integrationId, sourceId) {
|
|
21
|
-
const mappings = await this.find({ integration: integrationId, sourceId });
|
|
22
|
-
if (mappings.length === 0) {
|
|
23
|
-
return null;
|
|
24
|
-
} else if (mappings.length === 1) {
|
|
25
|
-
return mappings[0].mapping;
|
|
26
|
-
} else {
|
|
27
|
-
throw new Error('multiple integration mappings with same sourceId');
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
upsert: async function (integrationId, sourceId, mapping) {
|
|
31
|
-
return this.findOneAndUpdate(
|
|
32
|
-
{ integration: integrationId, sourceId },
|
|
33
|
-
{ mapping },
|
|
34
|
-
{ new: true, upsert: true, setDefaultsOnInsert: true }
|
|
35
|
-
);
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
schema.index({ integration: 1, sourceId: 1 });
|
|
40
|
-
|
|
41
|
-
const IntegrationMapping =
|
|
42
|
-
mongoose.models.IntegrationMapping || mongoose.model('IntegrationMapping', schema);
|
|
43
|
-
module.exports = { IntegrationMapping };
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const { mongoose } = require('../database/mongoose');
|
|
2
|
-
|
|
3
|
-
const schema = new mongoose.Schema(
|
|
4
|
-
{
|
|
5
|
-
entities: [
|
|
6
|
-
{
|
|
7
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
8
|
-
ref: 'Entity',
|
|
9
|
-
required: true,
|
|
10
|
-
},
|
|
11
|
-
],
|
|
12
|
-
entityReference: {
|
|
13
|
-
type: mongoose.Schema.Types.Map,
|
|
14
|
-
of: String,
|
|
15
|
-
},
|
|
16
|
-
user: {
|
|
17
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
18
|
-
ref: 'User',
|
|
19
|
-
required: false,
|
|
20
|
-
},
|
|
21
|
-
status: {
|
|
22
|
-
type: String,
|
|
23
|
-
enum: [
|
|
24
|
-
'ENABLED',
|
|
25
|
-
'NEEDS_CONFIG',
|
|
26
|
-
'PROCESSING',
|
|
27
|
-
'DISABLED',
|
|
28
|
-
'ERROR',
|
|
29
|
-
],
|
|
30
|
-
default: 'ENABLED',
|
|
31
|
-
},
|
|
32
|
-
config: {},
|
|
33
|
-
version: { type: String },
|
|
34
|
-
messages: {
|
|
35
|
-
errors: [],
|
|
36
|
-
warnings: [],
|
|
37
|
-
info: [],
|
|
38
|
-
logs: [],
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
{ timestamps: true }
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const Integration =
|
|
45
|
-
mongoose.models.Integration || mongoose.model('Integration', schema);
|
|
46
|
-
module.exports = { IntegrationModel: Integration };
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
const bcrypt = require('bcryptjs');
|
|
2
|
-
const crypto = require('crypto');
|
|
3
|
-
const { get } = require('../assertions');
|
|
4
|
-
const { Token } = require('../database/models/Token');
|
|
5
|
-
const { IndividualUser } = require('../database/models/IndividualUser');
|
|
6
|
-
const { OrganizationUser } = require('../database/models/OrganizationUser');
|
|
7
|
-
const Boom = require('@hapi/boom');
|
|
8
|
-
|
|
9
|
-
class User {
|
|
10
|
-
static IndividualUser = IndividualUser;
|
|
11
|
-
static OrganizationUser = OrganizationUser;
|
|
12
|
-
static Token = Token;
|
|
13
|
-
static usePassword = false
|
|
14
|
-
static primary = User.IndividualUser;
|
|
15
|
-
static individualUserRequired = true;
|
|
16
|
-
static organizationUserRequired = false;
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
this.user = null;
|
|
20
|
-
this.individualUser = null;
|
|
21
|
-
this.organizationUser = null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
getPrimaryUser() {
|
|
25
|
-
if (User.primary === User.OrganizationUser) {
|
|
26
|
-
return this.organizationUser;
|
|
27
|
-
}
|
|
28
|
-
return this.individualUser;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
getUserId() {
|
|
32
|
-
return this.getPrimaryUser()?.id;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
isLoggedIn() {
|
|
36
|
-
return Boolean(this.getUserId());
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async createUserToken(minutes) {
|
|
40
|
-
const rawToken = crypto.randomBytes(20).toString('hex');
|
|
41
|
-
const createdToken = await User.Token.createTokenWithExpire(this.getUserId(), rawToken, 120);
|
|
42
|
-
const tokenBuf = User.Token.createBase64BufferToken(createdToken, rawToken);
|
|
43
|
-
return tokenBuf;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
static async newUser(params={}) {
|
|
47
|
-
const user = new User();
|
|
48
|
-
const token = get(params, 'token', null);
|
|
49
|
-
if (token) {
|
|
50
|
-
const jsonToken = this.Token.getJSONTokenFromBase64BufferToken(token);
|
|
51
|
-
const sessionToken = await this.Token.validateAndGetTokenFromJSONToken(jsonToken);
|
|
52
|
-
if (this.primary === User.OrganizationUser) {
|
|
53
|
-
user.organizationUser = await this.OrganizationUser.findById(sessionToken.user);
|
|
54
|
-
} else {
|
|
55
|
-
user.individualUser = await this.IndividualUser.findById(sessionToken.user);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return user;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
static async createIndividualUser(params) {
|
|
62
|
-
const user = await this.newUser(params);
|
|
63
|
-
let hashword;
|
|
64
|
-
if (this.usePassword) {
|
|
65
|
-
hashword = get(params, 'password');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const email = get(params, 'email', null);
|
|
69
|
-
const username = get(params, 'username', null);
|
|
70
|
-
if (!email && !username) {
|
|
71
|
-
throw Boom.badRequest('email or username is required');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const appUserId = get(params, 'appUserId', null);
|
|
75
|
-
const organizationUserId = get(params, 'organizationUserId', null);
|
|
76
|
-
|
|
77
|
-
user.individualUser = await this.IndividualUser.create({
|
|
78
|
-
email,
|
|
79
|
-
username,
|
|
80
|
-
hashword,
|
|
81
|
-
appUserId,
|
|
82
|
-
organizationUser: organizationUserId,
|
|
83
|
-
});
|
|
84
|
-
return user;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
static async createOrganizationUser(params) {
|
|
88
|
-
const user = await this.newUser(params);
|
|
89
|
-
const name = get(params, 'name');
|
|
90
|
-
const appOrgId = get(params, 'appOrgId');
|
|
91
|
-
user.organizationUser = await this.OrganizationUser.create({
|
|
92
|
-
name,
|
|
93
|
-
appOrgId,
|
|
94
|
-
});
|
|
95
|
-
return user;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
static async loginUser(params) {
|
|
99
|
-
const user = await this.newUser(params);
|
|
100
|
-
|
|
101
|
-
if (this.usePassword){
|
|
102
|
-
const username = get(params, 'username');
|
|
103
|
-
const password = get(params, 'password');
|
|
104
|
-
|
|
105
|
-
const individualUser = await this.IndividualUser.findOne({username});
|
|
106
|
-
|
|
107
|
-
if (!individualUser) {
|
|
108
|
-
throw Boom.unauthorized('incorrect username or password');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const isValid = await bcrypt.compareSync(password, individualUser.hashword);
|
|
112
|
-
if (!isValid) {
|
|
113
|
-
throw Boom.unauthorized('incorrect username or password');
|
|
114
|
-
}
|
|
115
|
-
user.individualUser = individualUser;
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
const appUserId = get(params, 'appUserId', null);
|
|
119
|
-
user.individualUser = await this.IndividualUser.getUserByAppUserId(
|
|
120
|
-
appUserId
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const appOrgId = get(params, 'appOrgId', null);
|
|
125
|
-
user.organizationUser = await this.OrganizationUser.getUserByAppOrgId(
|
|
126
|
-
appOrgId
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
if (this.individualUserRequired) {
|
|
130
|
-
if (!user.individualUser) {
|
|
131
|
-
throw Boom.unauthorized('user not found');
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (this.organizationUserRequired) {
|
|
136
|
-
if (!user.organizationUser) {
|
|
137
|
-
throw Boom.unauthorized(`org user ${appOrgId} not found`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return user;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
module.exports = User;
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const { mongoose } = require('../../database/mongoose');
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const { IntegrationBase } = require("../integration-base");
|
|
5
|
-
const {Credential} = require('../../module-plugin/credential');
|
|
6
|
-
const {Entity} = require('../../module-plugin/entity');
|
|
7
|
-
const { IntegrationMapping } = require('../integration-mapping')
|
|
8
|
-
const {IntegrationModel} = require("../integration-model");
|
|
9
|
-
|
|
10
|
-
describe(`Should fully test the IntegrationBase Class`, () => {
|
|
11
|
-
let integrationRecord;
|
|
12
|
-
let userId;
|
|
13
|
-
const integration = new IntegrationBase;
|
|
14
|
-
|
|
15
|
-
beforeAll(async () => {
|
|
16
|
-
await mongoose.connect(process.env.MONGO_URI);
|
|
17
|
-
userId = new mongoose.Types.ObjectId();
|
|
18
|
-
const credential = await Credential.findOneAndUpdate(
|
|
19
|
-
{
|
|
20
|
-
user: this.userId,
|
|
21
|
-
},
|
|
22
|
-
{ $set: { user: this.userId } },
|
|
23
|
-
{
|
|
24
|
-
new: true,
|
|
25
|
-
upsert: true,
|
|
26
|
-
setDefaultsOnInsert: true,
|
|
27
|
-
}
|
|
28
|
-
);
|
|
29
|
-
const entity1 = await Entity.findOneAndUpdate(
|
|
30
|
-
{
|
|
31
|
-
user: this.userId,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
$set: {
|
|
35
|
-
credential: credential.id,
|
|
36
|
-
user: userId,
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
new: true,
|
|
41
|
-
upsert: true,
|
|
42
|
-
setDefaultsOnInsert: true,
|
|
43
|
-
}
|
|
44
|
-
);
|
|
45
|
-
const entity2 = await Entity.findOneAndUpdate(
|
|
46
|
-
{
|
|
47
|
-
user: userId,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
$set: {
|
|
51
|
-
credential: credential.id,
|
|
52
|
-
user: userId,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
new: true,
|
|
57
|
-
upsert: true,
|
|
58
|
-
setDefaultsOnInsert: true,
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
|
-
integrationRecord = await IntegrationModel.create({
|
|
62
|
-
entities: [entity1, entity2],
|
|
63
|
-
user: userId
|
|
64
|
-
});
|
|
65
|
-
integration.record = integrationRecord;
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
afterAll(async () => {
|
|
69
|
-
await Entity.deleteMany();
|
|
70
|
-
await Credential.deleteMany();
|
|
71
|
-
await IntegrationMapping.deleteMany();
|
|
72
|
-
await IntegrationModel.deleteMany();
|
|
73
|
-
await mongoose.disconnect();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
beforeEach(() => {
|
|
77
|
-
integration.record = integrationRecord;
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
describe('getIntegrationMapping()', () => {
|
|
81
|
-
it('should return null if not found', async () => {
|
|
82
|
-
const mappings = await integration.getMapping('badId');
|
|
83
|
-
expect(mappings).to.be.null;
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should return if valid ids', async () => {
|
|
87
|
-
await integration.upsertMapping('validId', {});
|
|
88
|
-
const mapping = await integration.getMapping('validId');
|
|
89
|
-
expect(mapping).to.eql({})
|
|
90
|
-
});
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
describe('upsertIntegrationMapping()', () => {
|
|
94
|
-
it('should throw error if sourceId is null', async () => {
|
|
95
|
-
try {
|
|
96
|
-
await integration.upsertMapping( null, {});
|
|
97
|
-
fail('should have thrown error')
|
|
98
|
-
} catch(err) {
|
|
99
|
-
expect(err.message).to.contain('sourceId must be set');
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should return for empty mapping', async () => {
|
|
104
|
-
const mapping = await integration.upsertMapping( 'validId2', {});
|
|
105
|
-
expect(_.pick(mapping, ['integration', 'sourceId', 'mapping'])).to.eql({
|
|
106
|
-
integration: integrationRecord._id,
|
|
107
|
-
sourceId: 'validId2',
|
|
108
|
-
mapping: {}
|
|
109
|
-
})
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should return for filled mapping', async () => {
|
|
113
|
-
const mapping = await integration.upsertMapping('validId3', {
|
|
114
|
-
name: 'someName',
|
|
115
|
-
value: 5
|
|
116
|
-
});
|
|
117
|
-
expect(_.pick(mapping, ['integration', 'sourceId', 'mapping'])).to.eql({
|
|
118
|
-
integration: integrationRecord._id,
|
|
119
|
-
sourceId: 'validId3',
|
|
120
|
-
mapping: {
|
|
121
|
-
name: 'someName',
|
|
122
|
-
value: 5
|
|
123
|
-
}
|
|
124
|
-
})
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should allow upserting to same id', async () => {
|
|
128
|
-
await integration.upsertMapping('validId4', {});
|
|
129
|
-
const mapping = await integration.upsertMapping('validId4', {
|
|
130
|
-
name: 'trustMe',
|
|
131
|
-
thisWorks: true,
|
|
132
|
-
});
|
|
133
|
-
expect(_.pick(mapping, ['integration', 'sourceId', 'mapping'])).to.eql({
|
|
134
|
-
integration: integrationRecord._id,
|
|
135
|
-
sourceId: 'validId4',
|
|
136
|
-
mapping: {
|
|
137
|
-
name: 'trustMe',
|
|
138
|
-
thisWorks: true,
|
|
139
|
-
}
|
|
140
|
-
})
|
|
141
|
-
});
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
});
|