@friggframework/core 2.0.0-next.41 → 2.0.0-next.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/CLAUDE.md +693 -0
  2. package/README.md +931 -50
  3. package/application/commands/README.md +421 -0
  4. package/application/commands/credential-commands.js +224 -0
  5. package/application/commands/entity-commands.js +315 -0
  6. package/application/commands/integration-commands.js +160 -0
  7. package/application/commands/integration-commands.test.js +123 -0
  8. package/application/commands/user-commands.js +213 -0
  9. package/application/index.js +69 -0
  10. package/core/CLAUDE.md +690 -0
  11. package/core/create-handler.js +0 -6
  12. package/credential/repositories/credential-repository-factory.js +47 -0
  13. package/credential/repositories/credential-repository-interface.js +98 -0
  14. package/credential/repositories/credential-repository-mongo.js +301 -0
  15. package/credential/repositories/credential-repository-postgres.js +307 -0
  16. package/credential/repositories/credential-repository.js +307 -0
  17. package/credential/use-cases/get-credential-for-user.js +21 -0
  18. package/credential/use-cases/update-authentication-status.js +15 -0
  19. package/database/config.js +117 -0
  20. package/database/encryption/README.md +683 -0
  21. package/database/encryption/encryption-integration.test.js +553 -0
  22. package/database/encryption/encryption-schema-registry.js +141 -0
  23. package/database/encryption/encryption-schema-registry.test.js +392 -0
  24. package/database/encryption/field-encryption-service.js +226 -0
  25. package/database/encryption/field-encryption-service.test.js +525 -0
  26. package/database/encryption/logger.js +79 -0
  27. package/database/encryption/mongo-decryption-fix-verification.test.js +348 -0
  28. package/database/encryption/postgres-decryption-fix-verification.test.js +371 -0
  29. package/database/encryption/postgres-relation-decryption.test.js +245 -0
  30. package/database/encryption/prisma-encryption-extension.js +222 -0
  31. package/database/encryption/prisma-encryption-extension.test.js +439 -0
  32. package/database/index.js +25 -12
  33. package/database/models/readme.md +1 -0
  34. package/database/prisma.js +162 -0
  35. package/database/repositories/health-check-repository-factory.js +38 -0
  36. package/database/repositories/health-check-repository-interface.js +86 -0
  37. package/database/repositories/health-check-repository-mongodb.js +72 -0
  38. package/database/repositories/health-check-repository-postgres.js +75 -0
  39. package/database/repositories/health-check-repository.js +108 -0
  40. package/database/use-cases/check-database-health-use-case.js +34 -0
  41. package/database/use-cases/check-encryption-health-use-case.js +82 -0
  42. package/database/use-cases/test-encryption-use-case.js +252 -0
  43. package/encrypt/Cryptor.js +20 -152
  44. package/encrypt/index.js +1 -2
  45. package/encrypt/test-encrypt.js +0 -2
  46. package/handlers/app-definition-loader.js +38 -0
  47. package/handlers/app-handler-helpers.js +0 -3
  48. package/handlers/auth-flow.integration.test.js +147 -0
  49. package/handlers/backend-utils.js +25 -45
  50. package/handlers/integration-event-dispatcher.js +54 -0
  51. package/handlers/integration-event-dispatcher.test.js +141 -0
  52. package/handlers/routers/HEALTHCHECK.md +103 -1
  53. package/handlers/routers/auth.js +3 -14
  54. package/handlers/routers/health.js +63 -424
  55. package/handlers/routers/health.test.js +7 -0
  56. package/handlers/routers/integration-defined-routers.js +8 -5
  57. package/handlers/routers/user.js +27 -5
  58. package/handlers/routers/websocket.js +5 -3
  59. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  60. package/handlers/use-cases/check-integrations-health-use-case.js +32 -0
  61. package/handlers/workers/integration-defined-workers.js +6 -3
  62. package/index.js +45 -22
  63. package/integrations/index.js +12 -10
  64. package/integrations/integration-base.js +224 -53
  65. package/integrations/integration-router.js +386 -178
  66. package/integrations/options.js +1 -1
  67. package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
  68. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  69. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  70. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  71. package/integrations/repositories/integration-mapping-repository.js +156 -0
  72. package/integrations/repositories/integration-repository-factory.js +44 -0
  73. package/integrations/repositories/integration-repository-interface.js +115 -0
  74. package/integrations/repositories/integration-repository-mongo.js +271 -0
  75. package/integrations/repositories/integration-repository-postgres.js +319 -0
  76. package/integrations/tests/doubles/dummy-integration-class.js +90 -0
  77. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  78. package/integrations/tests/use-cases/create-integration.test.js +131 -0
  79. package/integrations/tests/use-cases/delete-integration-for-user.test.js +150 -0
  80. package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +92 -0
  81. package/integrations/tests/use-cases/get-integration-for-user.test.js +150 -0
  82. package/integrations/tests/use-cases/get-integration-instance.test.js +176 -0
  83. package/integrations/tests/use-cases/get-integrations-for-user.test.js +176 -0
  84. package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
  85. package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
  86. package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
  87. package/integrations/tests/use-cases/update-integration.test.js +141 -0
  88. package/integrations/use-cases/create-integration.js +83 -0
  89. package/integrations/use-cases/delete-integration-for-user.js +73 -0
  90. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  91. package/integrations/use-cases/get-integration-for-user.js +78 -0
  92. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  93. package/integrations/use-cases/get-integration-instance.js +83 -0
  94. package/integrations/use-cases/get-integrations-for-user.js +87 -0
  95. package/integrations/use-cases/get-possible-integrations.js +27 -0
  96. package/integrations/use-cases/index.js +11 -0
  97. package/integrations/use-cases/load-integration-context-full.test.js +329 -0
  98. package/integrations/use-cases/load-integration-context.js +71 -0
  99. package/integrations/use-cases/load-integration-context.test.js +114 -0
  100. package/integrations/use-cases/update-integration-messages.js +44 -0
  101. package/integrations/use-cases/update-integration-status.js +32 -0
  102. package/integrations/use-cases/update-integration.js +93 -0
  103. package/integrations/utils/map-integration-dto.js +36 -0
  104. package/jest-global-setup-noop.js +3 -0
  105. package/jest-global-teardown-noop.js +3 -0
  106. package/{module-plugin → modules}/entity.js +1 -0
  107. package/{module-plugin → modules}/index.js +0 -8
  108. package/modules/module-factory.js +56 -0
  109. package/modules/module-hydration.test.js +205 -0
  110. package/modules/module.js +221 -0
  111. package/modules/repositories/module-repository-factory.js +33 -0
  112. package/modules/repositories/module-repository-interface.js +129 -0
  113. package/modules/repositories/module-repository-mongo.js +386 -0
  114. package/modules/repositories/module-repository-postgres.js +437 -0
  115. package/modules/repositories/module-repository.js +327 -0
  116. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  117. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  118. package/modules/tests/doubles/test-module-factory.js +16 -0
  119. package/modules/tests/doubles/test-module-repository.js +39 -0
  120. package/modules/use-cases/get-entities-for-user.js +32 -0
  121. package/modules/use-cases/get-entity-options-by-id.js +59 -0
  122. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  123. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  124. package/modules/use-cases/get-module.js +56 -0
  125. package/modules/use-cases/process-authorization-callback.js +122 -0
  126. package/modules/use-cases/refresh-entity-options.js +59 -0
  127. package/modules/use-cases/test-module-auth.js +55 -0
  128. package/modules/utils/map-module-dto.js +18 -0
  129. package/package.json +14 -6
  130. package/prisma-mongodb/schema.prisma +318 -0
  131. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  132. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  133. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  134. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  135. package/prisma-postgresql/schema.prisma +300 -0
  136. package/syncs/manager.js +468 -443
  137. package/syncs/repositories/sync-repository-factory.js +38 -0
  138. package/syncs/repositories/sync-repository-interface.js +109 -0
  139. package/syncs/repositories/sync-repository-mongo.js +239 -0
  140. package/syncs/repositories/sync-repository-postgres.js +319 -0
  141. package/syncs/sync.js +0 -1
  142. package/token/repositories/token-repository-factory.js +33 -0
  143. package/token/repositories/token-repository-interface.js +131 -0
  144. package/token/repositories/token-repository-mongo.js +212 -0
  145. package/token/repositories/token-repository-postgres.js +257 -0
  146. package/token/repositories/token-repository.js +219 -0
  147. package/types/integrations/index.d.ts +2 -6
  148. package/types/module-plugin/index.d.ts +5 -57
  149. package/types/syncs/index.d.ts +0 -2
  150. package/user/repositories/user-repository-factory.js +46 -0
  151. package/user/repositories/user-repository-interface.js +198 -0
  152. package/user/repositories/user-repository-mongo.js +250 -0
  153. package/user/repositories/user-repository-postgres.js +311 -0
  154. package/user/tests/doubles/test-user-repository.js +72 -0
  155. package/user/tests/use-cases/create-individual-user.test.js +24 -0
  156. package/user/tests/use-cases/create-organization-user.test.js +28 -0
  157. package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
  158. package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
  159. package/user/tests/use-cases/login-user.test.js +140 -0
  160. package/user/use-cases/create-individual-user.js +61 -0
  161. package/user/use-cases/create-organization-user.js +47 -0
  162. package/user/use-cases/create-token-for-user-id.js +30 -0
  163. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  164. package/user/use-cases/login-user.js +122 -0
  165. package/user/user.js +77 -0
  166. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  167. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  168. package/websocket/repositories/websocket-connection-repository-mongo.js +155 -0
  169. package/websocket/repositories/websocket-connection-repository-postgres.js +195 -0
  170. package/websocket/repositories/websocket-connection-repository.js +160 -0
  171. package/database/models/State.js +0 -9
  172. package/database/models/Token.js +0 -70
  173. package/database/mongo.js +0 -171
  174. package/encrypt/Cryptor.test.js +0 -32
  175. package/encrypt/encrypt.js +0 -104
  176. package/encrypt/encrypt.test.js +0 -1069
  177. package/handlers/routers/middleware/loadUser.js +0 -15
  178. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  179. package/integrations/create-frigg-backend.js +0 -31
  180. package/integrations/integration-factory.js +0 -251
  181. package/integrations/integration-mapping.js +0 -43
  182. package/integrations/integration-model.js +0 -46
  183. package/integrations/integration-user.js +0 -144
  184. package/integrations/test/integration-base.test.js +0 -144
  185. package/module-plugin/auther.js +0 -393
  186. package/module-plugin/credential.js +0 -22
  187. package/module-plugin/entity-manager.js +0 -70
  188. package/module-plugin/manager.js +0 -169
  189. package/module-plugin/module-factory.js +0 -61
  190. package/module-plugin/test/auther.test.js +0 -97
  191. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  192. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  193. /package/{module-plugin → modules}/requester/basic.js +0 -0
  194. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  195. /package/{module-plugin → modules}/requester/requester.js +0 -0
  196. /package/{module-plugin → modules}/requester/requester.test.js +0 -0
  197. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,44 @@
1
+ const { IntegrationRepositoryMongo } = require('./integration-repository-mongo');
2
+ const { IntegrationRepositoryPostgres } = require('./integration-repository-postgres');
3
+ const config = require('../../database/config');
4
+
5
+ /**
6
+ * Integration Repository Factory
7
+ * Creates the appropriate repository adapter based on database type
8
+ *
9
+ * This implements the Factory pattern for Hexagonal Architecture:
10
+ * - Reads database type from app definition (backend/index.js)
11
+ * - Returns correct adapter (MongoDB or PostgreSQL)
12
+ * - Provides clear error for unsupported databases
13
+ *
14
+ * Usage:
15
+ * ```javascript
16
+ * const repository = createIntegrationRepository();
17
+ * ```
18
+ *
19
+ * @returns {IntegrationRepositoryInterface} Configured repository adapter
20
+ * @throws {Error} If database type is not supported
21
+ */
22
+ function createIntegrationRepository() {
23
+ const dbType = config.DB_TYPE;
24
+
25
+ switch (dbType) {
26
+ case 'mongodb':
27
+ return new IntegrationRepositoryMongo();
28
+
29
+ case 'postgresql':
30
+ return new IntegrationRepositoryPostgres();
31
+
32
+ default:
33
+ throw new Error(
34
+ `Unsupported database type: ${dbType}. Supported values: 'mongodb', 'postgresql'`
35
+ );
36
+ }
37
+ }
38
+
39
+ module.exports = {
40
+ createIntegrationRepository,
41
+ // Export adapters for direct testing
42
+ IntegrationRepositoryMongo,
43
+ IntegrationRepositoryPostgres,
44
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Integration Repository Interface
3
+ * Abstract base class defining the contract for integration persistence adapters
4
+ *
5
+ * This follows the Port in Hexagonal Architecture:
6
+ * - Domain layer depends on this abstraction
7
+ * - Concrete adapters (MongoDB, PostgreSQL) implement this interface
8
+ * - Use cases receive repositories via dependency injection
9
+ *
10
+ * @abstract
11
+ */
12
+ class IntegrationRepositoryInterface {
13
+ /**
14
+ * Find all integrations for a user
15
+ *
16
+ * @param {string|number} userId - User ID
17
+ * @returns {Promise<Array>} Array of integration objects
18
+ * @abstract
19
+ */
20
+ async findIntegrationsByUserId(userId) {
21
+ throw new Error('Method findIntegrationsByUserId must be implemented by subclass');
22
+ }
23
+
24
+ /**
25
+ * Delete integration by ID
26
+ *
27
+ * @param {string|number} integrationId - Integration ID
28
+ * @returns {Promise<Object>} Deletion result
29
+ * @abstract
30
+ */
31
+ async deleteIntegrationById(integrationId) {
32
+ throw new Error('Method deleteIntegrationById must be implemented by subclass');
33
+ }
34
+
35
+ /**
36
+ * Find integration by name
37
+ *
38
+ * @param {string} name - Integration type name
39
+ * @returns {Promise<Object>} Integration object
40
+ * @abstract
41
+ */
42
+ async findIntegrationByName(name) {
43
+ throw new Error('Method findIntegrationByName must be implemented by subclass');
44
+ }
45
+
46
+ /**
47
+ * Find integration by ID
48
+ *
49
+ * @param {string|number} id - Integration ID
50
+ * @returns {Promise<Object>} Integration object
51
+ * @abstract
52
+ */
53
+ async findIntegrationById(id) {
54
+ throw new Error('Method findIntegrationById must be implemented by subclass');
55
+ }
56
+
57
+ /**
58
+ * Update integration status
59
+ *
60
+ * @param {string|number} integrationId - Integration ID
61
+ * @param {string} status - New status
62
+ * @returns {Promise<boolean>} Success indicator
63
+ * @abstract
64
+ */
65
+ async updateIntegrationStatus(integrationId, status) {
66
+ throw new Error('Method updateIntegrationStatus must be implemented by subclass');
67
+ }
68
+
69
+ /**
70
+ * Update integration messages
71
+ *
72
+ * @param {string|number} integrationId - Integration ID
73
+ * @param {string} messageType - Type of message (errors, warnings, info, logs)
74
+ * @param {string} messageTitle - Message title
75
+ * @param {string} messageBody - Message body
76
+ * @param {Date} messageTimestamp - Message timestamp
77
+ * @returns {Promise<boolean>} Success indicator
78
+ * @abstract
79
+ */
80
+ async updateIntegrationMessages(
81
+ integrationId,
82
+ messageType,
83
+ messageTitle,
84
+ messageBody,
85
+ messageTimestamp
86
+ ) {
87
+ throw new Error('Method updateIntegrationMessages must be implemented by subclass');
88
+ }
89
+
90
+ /**
91
+ * Create a new integration
92
+ *
93
+ * @param {Array<string|number>} entities - Array of entity IDs
94
+ * @param {string|number} userId - User ID
95
+ * @param {Object} config - Integration configuration
96
+ * @returns {Promise<Object>} Created integration object
97
+ * @abstract
98
+ */
99
+ async createIntegration(entities, userId, config) {
100
+ throw new Error('Method createIntegration must be implemented by subclass');
101
+ }
102
+
103
+ /**
104
+ * Find integration by user ID (returns single integration)
105
+ *
106
+ * @param {string|number} userId - User ID
107
+ * @returns {Promise<Object|null>} Integration object or null
108
+ * @abstract
109
+ */
110
+ async findIntegrationByUserId(userId) {
111
+ throw new Error('Method findIntegrationByUserId must be implemented by subclass');
112
+ }
113
+ }
114
+
115
+ module.exports = { IntegrationRepositoryInterface };
@@ -0,0 +1,271 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ IntegrationRepositoryInterface,
4
+ } = require('./integration-repository-interface');
5
+
6
+ /**
7
+ * MongoDB Integration Repository Adapter
8
+ * Handles integration persistence using Prisma with MongoDB
9
+ *
10
+ * MongoDB-specific characteristics:
11
+ * - Uses scalar fields for relations (userId, entityIds)
12
+ * - IDs are strings with @db.ObjectId
13
+ * - Arrays used for many-to-many relationships
14
+ *
15
+ * Migration from Mongoose:
16
+ * - Constructor injection of Prisma client
17
+ * - populate() → include in Prisma queries
18
+ * - lean: true → No longer needed (Prisma returns plain objects)
19
+ * - toString() conversions → Done automatically by Prisma
20
+ */
21
+ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
22
+ constructor() {
23
+ super();
24
+ this.prisma = prisma;
25
+ }
26
+
27
+ /**
28
+ * Find all integrations for a user
29
+ * Replaces: IntegrationModel.find({ user: userId }).populate('entities')
30
+ *
31
+ * @param {string} userId - User ID (MongoDB ObjectId as string)
32
+ * @returns {Promise<Array>} Array of integration objects
33
+ */
34
+ async findIntegrationsByUserId(userId) {
35
+ const integrations = await this.prisma.integration.findMany({
36
+ where: { userId },
37
+ include: {
38
+ entities: true,
39
+ },
40
+ });
41
+
42
+ // Map to domain objects (maintains same API)
43
+ return integrations.map((integration) => ({
44
+ id: integration.id,
45
+ entitiesIds: integration.entities.map((e) => e.id),
46
+ userId: integration.userId,
47
+ config: integration.config,
48
+ version: integration.version,
49
+ status: integration.status,
50
+ messages: integration.messages,
51
+ }));
52
+ }
53
+
54
+ /**
55
+ * Delete integration by ID
56
+ * Replaces: IntegrationModel.deleteOne({ _id: integrationId })
57
+ *
58
+ * @param {string} integrationId - Integration ID
59
+ * @returns {Promise<Object>} Deletion result
60
+ */
61
+ async deleteIntegrationById(integrationId) {
62
+ await this.prisma.integration.delete({
63
+ where: { id: integrationId },
64
+ });
65
+
66
+ // Return Mongoose-compatible result
67
+ return { acknowledged: true, deletedCount: 1 };
68
+ }
69
+
70
+ /**
71
+ * Find integration by name
72
+ * Replaces: IntegrationModel.findOne({ 'config.type': name }).populate('entities')
73
+ *
74
+ * @param {string} name - Integration type name
75
+ * @returns {Promise<Object>} Integration object
76
+ */
77
+ async findIntegrationByName(name) {
78
+ const integration = await this.prisma.integration.findFirst({
79
+ where: {
80
+ config: {
81
+ path: ['type'],
82
+ equals: name,
83
+ },
84
+ },
85
+ include: {
86
+ entities: true,
87
+ },
88
+ });
89
+
90
+ if (!integration) {
91
+ throw new Error(`Integration with name ${name} not found`);
92
+ }
93
+
94
+ return {
95
+ id: integration.id,
96
+ entitiesIds: integration.entities.map((e) => e.id),
97
+ userId: integration.userId,
98
+ config: integration.config,
99
+ version: integration.version,
100
+ status: integration.status,
101
+ messages: integration.messages,
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Find integration by ID
107
+ * Replaces: IntegrationModel.findById(id).populate('entities')
108
+ *
109
+ * @param {string} id - Integration ID
110
+ * @returns {Promise<Object>} Integration object
111
+ */
112
+ async findIntegrationById(id) {
113
+ const integration = await this.prisma.integration.findUnique({
114
+ where: { id },
115
+ include: {
116
+ entities: true,
117
+ },
118
+ });
119
+
120
+ if (!integration) {
121
+ throw new Error(`Integration with id ${id} not found`);
122
+ }
123
+
124
+ return {
125
+ id: integration.id,
126
+ entitiesIds: integration.entities.map((e) => e.id),
127
+ userId: integration.userId,
128
+ config: integration.config,
129
+ version: integration.version,
130
+ status: integration.status,
131
+ messages: integration.messages,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Update integration status
137
+ * Replaces: IntegrationModel.updateOne({ _id: integrationId }, { status })
138
+ *
139
+ * @param {string} integrationId - Integration ID
140
+ * @param {string} status - New status
141
+ * @returns {Promise<boolean>} Success indicator
142
+ */
143
+ async updateIntegrationStatus(integrationId, status) {
144
+ await this.prisma.integration.update({
145
+ where: { id: integrationId },
146
+ data: { status },
147
+ });
148
+
149
+ return true; // Mongoose compatibility
150
+ }
151
+
152
+ /**
153
+ * Update integration messages
154
+ * Replaces: IntegrationModel.updateOne with $push operator
155
+ *
156
+ * @param {string} integrationId - Integration ID
157
+ * @param {string} messageType - Type of message (errors, warnings, info, logs)
158
+ * @param {string} messageTitle - Message title
159
+ * @param {string} messageBody - Message body
160
+ * @param {Date} messageTimestamp - Message timestamp
161
+ * @returns {Promise<boolean>} Success indicator
162
+ */
163
+ async updateIntegrationMessages(
164
+ integrationId,
165
+ messageType,
166
+ messageTitle,
167
+ messageBody,
168
+ messageTimestamp
169
+ ) {
170
+ // Get current integration
171
+ const integration = await this.prisma.integration.findUnique({
172
+ where: { id: integrationId },
173
+ });
174
+
175
+ if (!integration) {
176
+ throw new Error(`Integration ${integrationId} not found`);
177
+ }
178
+
179
+ // Parse existing messages (JSON field)
180
+ const messages = integration.messages || {};
181
+ const messageArray = Array.isArray(messages[messageType])
182
+ ? messages[messageType]
183
+ : [];
184
+
185
+ // Add new message
186
+ messageArray.push({
187
+ title: messageTitle,
188
+ message: messageBody,
189
+ timestamp: messageTimestamp,
190
+ });
191
+
192
+ // Update messages
193
+ await this.prisma.integration.update({
194
+ where: { id: integrationId },
195
+ data: {
196
+ [messageType]: messageArray,
197
+ },
198
+ });
199
+
200
+ return true; // Mongoose compatibility
201
+ }
202
+
203
+ /**
204
+ * Create a new integration
205
+ * Replaces: IntegrationModel.create({ entities, user, config })
206
+ *
207
+ * MongoDB-specific: Uses scalar fields for relations
208
+ *
209
+ * @param {Array<string>} entities - Array of entity IDs (MongoDB ObjectIds)
210
+ * @param {string} userId - User ID (MongoDB ObjectId)
211
+ * @param {Object} config - Integration configuration
212
+ * @returns {Promise<Object>} Created integration object
213
+ */
214
+ async createIntegration(entities, userId, config) {
215
+ const data = {
216
+ config,
217
+ version: '0.0.0',
218
+ userId: userId,
219
+ entityIds: entities,
220
+ };
221
+
222
+ const integration = await this.prisma.integration.create({
223
+ data,
224
+ include: {
225
+ entities: true,
226
+ },
227
+ });
228
+
229
+ return {
230
+ id: integration.id,
231
+ entitiesIds: integration.entities.map((e) => e.id),
232
+ userId: integration.userId,
233
+ config: integration.config,
234
+ version: integration.version,
235
+ status: integration.status,
236
+ messages: integration.messages,
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Find integration by user ID (returns single integration)
242
+ * Replaces: IntegrationModel.findOne({ user: userId }).populate('entities')
243
+ *
244
+ * @param {string} userId - User ID
245
+ * @returns {Promise<Object|null>} Integration object or null
246
+ */
247
+ async findIntegrationByUserId(userId) {
248
+ const integration = await this.prisma.integration.findFirst({
249
+ where: { userId },
250
+ include: {
251
+ entities: true,
252
+ },
253
+ });
254
+
255
+ if (!integration) {
256
+ return null;
257
+ }
258
+
259
+ return {
260
+ id: integration.id,
261
+ entitiesIds: integration.entities.map((e) => e.id),
262
+ userId: integration.userId,
263
+ config: integration.config,
264
+ version: integration.version,
265
+ status: integration.status,
266
+ messages: integration.messages,
267
+ };
268
+ }
269
+ }
270
+
271
+ module.exports = { IntegrationRepositoryMongo };