@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
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { ModuleRepositoryInterface } = require('./module-repository-interface');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Prisma-based Module Repository
|
|
6
|
+
* Handles Entity model operations for external service entities
|
|
7
|
+
*
|
|
8
|
+
* Works identically for both MongoDB and PostgreSQL:
|
|
9
|
+
* - MongoDB: String IDs with @db.ObjectId
|
|
10
|
+
* - PostgreSQL: Integer IDs with auto-increment
|
|
11
|
+
* - Both use same query patterns (no many-to-many differences)
|
|
12
|
+
*
|
|
13
|
+
* Migration from Mongoose:
|
|
14
|
+
* - Constructor injection of Prisma client
|
|
15
|
+
* - populate('credential') → include: { credential: true }
|
|
16
|
+
* - entity.__t (discriminator) → entity.subType
|
|
17
|
+
* - _id → id conversion automatic in Prisma
|
|
18
|
+
*/
|
|
19
|
+
class ModuleRepository extends ModuleRepositoryInterface {
|
|
20
|
+
constructor(prismaClient = prisma) {
|
|
21
|
+
super();
|
|
22
|
+
this.prisma = prismaClient; // Allow injection for testing
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Find entity by ID with credential
|
|
27
|
+
* Replaces: Entity.findById(entityId).populate('credential')
|
|
28
|
+
*
|
|
29
|
+
* @param {string} entityId - Entity ID
|
|
30
|
+
* @returns {Promise<Object>} Entity object
|
|
31
|
+
* @throws {Error} If entity not found
|
|
32
|
+
*/
|
|
33
|
+
async findEntityById(entityId) {
|
|
34
|
+
const entity = await this.prisma.entity.findUnique({
|
|
35
|
+
where: { id: entityId },
|
|
36
|
+
include: { credential: true },
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!entity) {
|
|
40
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
id: entity.id,
|
|
45
|
+
accountId: entity.accountId,
|
|
46
|
+
credential: entity.credential,
|
|
47
|
+
userId: entity.userId,
|
|
48
|
+
name: entity.name,
|
|
49
|
+
externalId: entity.externalId,
|
|
50
|
+
type: entity.subType,
|
|
51
|
+
moduleName: entity.moduleName,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Find all entities for a user
|
|
57
|
+
* Replaces: Entity.find({ user: userId }).populate('credential')
|
|
58
|
+
*
|
|
59
|
+
* @param {string} userId - User ID
|
|
60
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
61
|
+
*/
|
|
62
|
+
async findEntitiesByUserId(userId) {
|
|
63
|
+
const entities = await this.prisma.entity.findMany({
|
|
64
|
+
where: { userId },
|
|
65
|
+
include: { credential: true },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return entities.map((e) => ({
|
|
69
|
+
id: e.id,
|
|
70
|
+
accountId: e.accountId,
|
|
71
|
+
credential: e.credential,
|
|
72
|
+
userId: e.userId,
|
|
73
|
+
name: e.name,
|
|
74
|
+
externalId: e.externalId,
|
|
75
|
+
type: e.subType,
|
|
76
|
+
moduleName: e.moduleName,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Find entities by array of IDs
|
|
82
|
+
* Replaces: Entity.find({ _id: { $in: entitiesIds } }).populate('credential')
|
|
83
|
+
*
|
|
84
|
+
* @param {Array<string>} entitiesIds - Array of entity IDs
|
|
85
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
86
|
+
*/
|
|
87
|
+
async findEntitiesByIds(entitiesIds) {
|
|
88
|
+
const entities = await this.prisma.entity.findMany({
|
|
89
|
+
where: { id: { in: entitiesIds } },
|
|
90
|
+
include: { credential: true },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return entities.map((e) => ({
|
|
94
|
+
id: e.id,
|
|
95
|
+
accountId: e.accountId,
|
|
96
|
+
credential: e.credential,
|
|
97
|
+
userId: e.userId,
|
|
98
|
+
name: e.name,
|
|
99
|
+
externalId: e.externalId,
|
|
100
|
+
type: e.subType,
|
|
101
|
+
moduleName: e.moduleName,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Find entities by user ID and module name
|
|
107
|
+
* Replaces: Entity.find({ user: userId, moduleName: moduleName }).populate('credential')
|
|
108
|
+
*
|
|
109
|
+
* @param {string} userId - User ID
|
|
110
|
+
* @param {string} moduleName - Module name
|
|
111
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
112
|
+
*/
|
|
113
|
+
async findEntitiesByUserIdAndModuleName(userId, moduleName) {
|
|
114
|
+
const entities = await this.prisma.entity.findMany({
|
|
115
|
+
where: {
|
|
116
|
+
userId,
|
|
117
|
+
moduleName,
|
|
118
|
+
},
|
|
119
|
+
include: { credential: true },
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return entities.map((e) => ({
|
|
123
|
+
id: e.id,
|
|
124
|
+
accountId: e.accountId,
|
|
125
|
+
credential: e.credential,
|
|
126
|
+
userId: e.userId,
|
|
127
|
+
name: e.name,
|
|
128
|
+
externalId: e.externalId,
|
|
129
|
+
type: e.subType,
|
|
130
|
+
moduleName: e.moduleName,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Remove credential reference from entity
|
|
136
|
+
* Replaces: Entity.updateOne({ _id: entityId }, { $unset: { credential: "" } })
|
|
137
|
+
*
|
|
138
|
+
* @param {string} entityId - Entity ID
|
|
139
|
+
* @returns {Promise<boolean>} Success indicator
|
|
140
|
+
*/
|
|
141
|
+
async unsetCredential(entityId) {
|
|
142
|
+
await this.prisma.entity.update({
|
|
143
|
+
where: { id: entityId },
|
|
144
|
+
data: { credentialId: null },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Find entity by filter criteria
|
|
152
|
+
* Replaces: Entity.findOne(filter).populate('credential')
|
|
153
|
+
*
|
|
154
|
+
* @param {Object} filter - Filter criteria
|
|
155
|
+
* @returns {Promise<Object|null>} Entity object or null
|
|
156
|
+
*/
|
|
157
|
+
async findEntity(filter) {
|
|
158
|
+
const where = this._convertFilterToWhere(filter);
|
|
159
|
+
const entity = await this.prisma.entity.findFirst({
|
|
160
|
+
where,
|
|
161
|
+
include: { credential: true },
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (!entity) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
id: entity.id,
|
|
170
|
+
accountId: entity.accountId,
|
|
171
|
+
credential: entity.credential,
|
|
172
|
+
userId: entity.userId,
|
|
173
|
+
name: entity.name,
|
|
174
|
+
externalId: entity.externalId,
|
|
175
|
+
type: entity.subType,
|
|
176
|
+
moduleName: entity.moduleName,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Create a new entity
|
|
182
|
+
* Replaces: Entity.create(entityData)
|
|
183
|
+
*
|
|
184
|
+
* @param {Object} entityData - Entity data
|
|
185
|
+
* @returns {Promise<Object>} Created entity object
|
|
186
|
+
*/
|
|
187
|
+
async createEntity(entityData) {
|
|
188
|
+
// Convert Mongoose-style fields to Prisma
|
|
189
|
+
const data = {
|
|
190
|
+
userId: entityData.user || entityData.userId,
|
|
191
|
+
credentialId: entityData.credential || entityData.credentialId,
|
|
192
|
+
subType: entityData.type || entityData.subType,
|
|
193
|
+
name: entityData.name,
|
|
194
|
+
moduleName: entityData.moduleName,
|
|
195
|
+
externalId: entityData.externalId,
|
|
196
|
+
accountId: entityData.accountId,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const entity = await this.prisma.entity.create({
|
|
200
|
+
data,
|
|
201
|
+
include: { credential: true },
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
id: entity.id,
|
|
206
|
+
accountId: entity.accountId,
|
|
207
|
+
credential: entity.credential,
|
|
208
|
+
userId: entity.userId,
|
|
209
|
+
name: entity.name,
|
|
210
|
+
externalId: entity.externalId,
|
|
211
|
+
type: entity.subType,
|
|
212
|
+
moduleName: entity.moduleName,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Update an entity by ID
|
|
218
|
+
* Replaces: Entity.findByIdAndUpdate(entityId, updates, { new: true })
|
|
219
|
+
*
|
|
220
|
+
* @param {string} entityId - Entity ID to update
|
|
221
|
+
* @param {Object} updates - Fields to update
|
|
222
|
+
* @returns {Promise<Object|null>} Updated entity object or null if not found
|
|
223
|
+
*/
|
|
224
|
+
async updateEntity(entityId, updates) {
|
|
225
|
+
// Convert Mongoose-style fields to Prisma
|
|
226
|
+
const data = {};
|
|
227
|
+
if (updates.user !== undefined) data.userId = updates.user;
|
|
228
|
+
if (updates.userId !== undefined) data.userId = updates.userId;
|
|
229
|
+
if (updates.credential !== undefined)
|
|
230
|
+
data.credentialId = updates.credential;
|
|
231
|
+
if (updates.credentialId !== undefined)
|
|
232
|
+
data.credentialId = updates.credentialId;
|
|
233
|
+
if (updates.type !== undefined) data.subType = updates.type;
|
|
234
|
+
if (updates.subType !== undefined) data.subType = updates.subType;
|
|
235
|
+
if (updates.name !== undefined) data.name = updates.name;
|
|
236
|
+
if (updates.moduleName !== undefined)
|
|
237
|
+
data.moduleName = updates.moduleName;
|
|
238
|
+
if (updates.externalId !== undefined)
|
|
239
|
+
data.externalId = updates.externalId;
|
|
240
|
+
if (updates.accountId !== undefined) data.accountId = updates.accountId;
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const entity = await this.prisma.entity.update({
|
|
244
|
+
where: { id: entityId },
|
|
245
|
+
data,
|
|
246
|
+
include: { credential: true },
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
id: entity.id,
|
|
251
|
+
accountId: entity.accountId,
|
|
252
|
+
credential: entity.credential,
|
|
253
|
+
userId: entity.userId,
|
|
254
|
+
name: entity.name,
|
|
255
|
+
externalId: entity.externalId,
|
|
256
|
+
type: entity.subType,
|
|
257
|
+
moduleName: entity.moduleName,
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error.code === 'P2025') {
|
|
261
|
+
// Record not found
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Delete an entity by ID
|
|
270
|
+
* Replaces: Entity.deleteOne({ _id: entityId })
|
|
271
|
+
*
|
|
272
|
+
* @param {string} entityId - Entity ID to delete
|
|
273
|
+
* @returns {Promise<boolean>} True if deleted successfully
|
|
274
|
+
*/
|
|
275
|
+
async deleteEntity(entityId) {
|
|
276
|
+
try {
|
|
277
|
+
await this.prisma.entity.delete({
|
|
278
|
+
where: { id: entityId },
|
|
279
|
+
});
|
|
280
|
+
return true;
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (error.code === 'P2025') {
|
|
283
|
+
// Record not found
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Convert Mongoose-style filter to Prisma where clause
|
|
292
|
+
* @private
|
|
293
|
+
* @param {Object} filter - Mongoose filter
|
|
294
|
+
* @returns {Object} Prisma where clause
|
|
295
|
+
*/
|
|
296
|
+
_convertFilterToWhere(filter) {
|
|
297
|
+
const where = {};
|
|
298
|
+
|
|
299
|
+
// Handle _id field (Mongoose uses _id, Prisma uses id)
|
|
300
|
+
if (filter._id) {
|
|
301
|
+
where.id = filter._id;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Handle user field (Mongoose uses user, Prisma uses userId)
|
|
305
|
+
if (filter.user) {
|
|
306
|
+
where.userId = filter.user;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Handle credential field (Mongoose uses credential, Prisma uses credentialId)
|
|
310
|
+
if (filter.credential) {
|
|
311
|
+
where.credentialId = filter.credential;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Copy other fields directly
|
|
315
|
+
if (filter.id) where.id = filter.id;
|
|
316
|
+
if (filter.userId) where.userId = filter.userId;
|
|
317
|
+
if (filter.credentialId) where.credentialId = filter.credentialId;
|
|
318
|
+
if (filter.name) where.name = filter.name;
|
|
319
|
+
if (filter.moduleName) where.moduleName = filter.moduleName;
|
|
320
|
+
if (filter.externalId) where.externalId = filter.externalId;
|
|
321
|
+
if (filter.subType) where.subType = filter.subType;
|
|
322
|
+
|
|
323
|
+
return where;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
module.exports = { ModuleRepository };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { get } = require('
|
|
2
|
-
const { OAuth2Requester } = require('
|
|
1
|
+
const { get } = require('../../../assertions');
|
|
2
|
+
const { OAuth2Requester } = require('../..');
|
|
3
3
|
|
|
4
4
|
class Api extends OAuth2Requester {
|
|
5
5
|
constructor(params) {
|
|
@@ -23,7 +23,12 @@ class Api extends OAuth2Requester {
|
|
|
23
23
|
return this.authorizationUri;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
getAuthorizationRequirements() {
|
|
27
|
+
return {
|
|
28
|
+
url: this.getAuthUri(),
|
|
29
|
+
type: 'oauth2',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
module.exports = { Api };
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
require('dotenv').config();
|
|
2
|
-
const {Api} = require('./api');
|
|
3
|
-
const {get} = require('
|
|
4
|
-
const config = {name: 'anapi'}
|
|
2
|
+
const { Api } = require('./api');
|
|
3
|
+
const { get } = require('../../../assertions');
|
|
4
|
+
const config = { name: 'anapi' }
|
|
5
5
|
|
|
6
6
|
const Definition = {
|
|
7
7
|
API: Api,
|
|
8
|
-
|
|
8
|
+
getAuthorizationRequirements: () => ({
|
|
9
|
+
url: 'http://localhost:3000/redirect/anapi',
|
|
10
|
+
type: 'oauth2',
|
|
11
|
+
}),
|
|
12
|
+
getName: function () { return config.name },
|
|
9
13
|
moduleName: config.name,
|
|
10
14
|
modelName: 'AnApi',
|
|
11
15
|
requiredAuthMethods: {
|
|
12
|
-
getToken: async function(api, params){
|
|
16
|
+
getToken: async function (api, params) {
|
|
13
17
|
const code = get(params.data, 'code');
|
|
14
18
|
return api.getTokenFromCode(code);
|
|
15
19
|
},
|
|
16
|
-
getEntityDetails: async function(api, callbackParams, tokenResponse, userId) {
|
|
20
|
+
getEntityDetails: async function (api, callbackParams, tokenResponse, userId) {
|
|
17
21
|
const userDetails = await api.getUserDetails();
|
|
18
22
|
return {
|
|
19
23
|
identifiers: { externalId: userDetails.portalId, user: userId },
|
|
@@ -26,14 +30,14 @@ const Definition = {
|
|
|
26
30
|
],
|
|
27
31
|
entity: [],
|
|
28
32
|
},
|
|
29
|
-
getCredentialDetails: async function(api, userId) {
|
|
33
|
+
getCredentialDetails: async function (api, userId) {
|
|
30
34
|
const userDetails = await api.getUserDetails();
|
|
31
35
|
return {
|
|
32
36
|
identifiers: { externalId: userDetails.portalId, user: userId },
|
|
33
37
|
details: {}
|
|
34
38
|
};
|
|
35
39
|
},
|
|
36
|
-
testAuthRequest: async function(api){
|
|
40
|
+
testAuthRequest: async function (api) {
|
|
37
41
|
return api.getUserDetails()
|
|
38
42
|
},
|
|
39
43
|
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class TestModuleFactory {
|
|
2
|
+
constructor() { }
|
|
3
|
+
|
|
4
|
+
async getModuleInstance(entityId, userId) {
|
|
5
|
+
// return minimal stub module with getName and api property
|
|
6
|
+
return {
|
|
7
|
+
getName() { return 'stubModule'; },
|
|
8
|
+
api: {},
|
|
9
|
+
entityId,
|
|
10
|
+
userId,
|
|
11
|
+
testAuth: async () => true,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { TestModuleFactory };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class TestModuleRepository {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.entities = new Map();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
addEntity(entity) {
|
|
7
|
+
this.entities.set(entity.id, entity);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async findEntityById(id) {
|
|
11
|
+
return this.entities.get(id);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async findEntitiesByIds(ids) {
|
|
15
|
+
return ids.map((id) => this.entities.get(id));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async findEntity(filter) {
|
|
19
|
+
if (!filter || typeof filter !== 'object') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (filter.id && this.entities.has(filter.id)) {
|
|
24
|
+
return this.entities.get(filter.id);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (filter.externalId) {
|
|
28
|
+
for (const entity of this.entities.values()) {
|
|
29
|
+
if (entity.externalId === filter.externalId) {
|
|
30
|
+
return entity;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { TestModuleRepository };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
const { mapModuleClassToModuleDTO } = require('../utils/map-module-dto');
|
|
3
|
+
|
|
4
|
+
class GetEntitiesForUser {
|
|
5
|
+
constructor({ moduleRepository, moduleDefinitions }) {
|
|
6
|
+
this.moduleRepository = moduleRepository;
|
|
7
|
+
|
|
8
|
+
this.definitionMap = new Map();
|
|
9
|
+
for (const definition of moduleDefinitions) {
|
|
10
|
+
this.definitionMap.set(definition.moduleName, definition);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async execute(userId) {
|
|
15
|
+
const entities = await this.moduleRepository.findEntitiesByUserId(
|
|
16
|
+
userId
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return entities.map((entity) => {
|
|
20
|
+
const definition = this.definitionMap.get(entity.moduleName);
|
|
21
|
+
|
|
22
|
+
const moduleInstance = new Module({
|
|
23
|
+
userId,
|
|
24
|
+
definition: definition,
|
|
25
|
+
entity: entity,
|
|
26
|
+
});
|
|
27
|
+
return mapModuleClassToModuleDTO(moduleInstance);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { GetEntitiesForUser };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetEntityOptionsById {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {import('../repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository
|
|
7
|
+
* @param {} params.moduleDefinitions
|
|
8
|
+
*/
|
|
9
|
+
constructor({ moduleRepository, moduleDefinitions }) {
|
|
10
|
+
this.moduleRepository = moduleRepository;
|
|
11
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Retrieve a Module instance for a given user and entity/module type.
|
|
16
|
+
* @param {string} userId
|
|
17
|
+
* @param {string} entityId
|
|
18
|
+
*/
|
|
19
|
+
async execute(entityId, userId) {
|
|
20
|
+
const entity = await this.moduleRepository.findEntityById(
|
|
21
|
+
entityId,
|
|
22
|
+
userId
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (!entity) {
|
|
26
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (entity.userId !== userId) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Entity ${entityId} does not belong to user ${userId}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const entityType = entity.type;
|
|
36
|
+
const moduleDefinition = this.moduleDefinitions.find((def) => {
|
|
37
|
+
const modelName =
|
|
38
|
+
Module.getEntityModelFromDefinition(def).modelName;
|
|
39
|
+
return entityType === modelName;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!moduleDefinition) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Module definition not found for entity type: ${entityType}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const module = new Module({
|
|
49
|
+
userId,
|
|
50
|
+
entity,
|
|
51
|
+
definition: moduleDefinition,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const entityOptions = await module.getEntityOptions();
|
|
55
|
+
return entityOptions;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { GetEntityOptionsById };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetEntityOptionsByType {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {} params.moduleDefinitions
|
|
7
|
+
*/
|
|
8
|
+
constructor({ moduleDefinitions }) {
|
|
9
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Retrieve a Module instance for a given user and entity/module type.
|
|
14
|
+
* @param {string} userId
|
|
15
|
+
* @param {string} type – human-readable module/entity type (e.g. "Hubspot")
|
|
16
|
+
*/
|
|
17
|
+
async execute(userId, type) {
|
|
18
|
+
const moduleDefinition = this.moduleDefinitions.find(
|
|
19
|
+
(def) => def.getName() === type
|
|
20
|
+
);
|
|
21
|
+
if (!moduleDefinition) {
|
|
22
|
+
throw new Error(`Module definition not found for type: ${type}`);
|
|
23
|
+
}
|
|
24
|
+
const moduleInstance = new Module({
|
|
25
|
+
userId,
|
|
26
|
+
definition: moduleDefinition,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const entityOptions = await moduleInstance.getEntityOptions();
|
|
30
|
+
return entityOptions;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { GetEntityOptionsByType };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetModuleInstanceFromType {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {} params.moduleDefinitions
|
|
7
|
+
*/
|
|
8
|
+
constructor({ moduleDefinitions }) {
|
|
9
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Retrieve a Module instance for a given user and entity/module type.
|
|
14
|
+
* @param {string} userId
|
|
15
|
+
* @param {string} type – human-readable module/entity type (e.g. "Hubspot")
|
|
16
|
+
*/
|
|
17
|
+
async execute(userId, type) {
|
|
18
|
+
const moduleDefinition = this.moduleDefinitions.find(
|
|
19
|
+
(def) => def.getName() === type
|
|
20
|
+
);
|
|
21
|
+
if (!moduleDefinition) {
|
|
22
|
+
throw new Error(`Module definition not found for type: ${type}`);
|
|
23
|
+
}
|
|
24
|
+
return new Module({
|
|
25
|
+
userId,
|
|
26
|
+
definition: moduleDefinition,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { GetModuleInstanceFromType };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetModule {
|
|
4
|
+
constructor({ moduleRepository, moduleDefinitions }) {
|
|
5
|
+
this.moduleRepository = moduleRepository;
|
|
6
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async execute(entityId, userId) {
|
|
10
|
+
const entity = await this.moduleRepository.findEntityById(
|
|
11
|
+
entityId,
|
|
12
|
+
userId
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
if (!entity) {
|
|
16
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (entity.userId !== userId) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Entity ${entityId} does not belong to user ${userId}`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const entityType = entity.type;
|
|
26
|
+
const moduleDefinition = this.moduleDefinitions.find((def) => {
|
|
27
|
+
const modelName = Module.getEntityModelFromDefinition(def).modelName;
|
|
28
|
+
return entityType === modelName;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!moduleDefinition) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Module definition not found for entity type: ${entityType}`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const module = new Module({
|
|
38
|
+
userId,
|
|
39
|
+
entity,
|
|
40
|
+
definition: moduleDefinition,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// todo: this properties should be methods in the Module class
|
|
44
|
+
return {
|
|
45
|
+
id: module.entity.id,
|
|
46
|
+
name: module.entity.name,
|
|
47
|
+
type: module.entity.moduleName,
|
|
48
|
+
moduleName: module.entity.moduleName,
|
|
49
|
+
credential: module.credential,
|
|
50
|
+
externalId: module.entity.externalId,
|
|
51
|
+
userId: module.entity.user.toString(),
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { GetModule };
|