@friggframework/core 2.0.0--canary.461.61382d8.0 → 2.0.0--canary.461.3d6d8ad.0

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 (120) hide show
  1. package/database/use-cases/check-migration-status-use-case.js +81 -0
  2. package/generated/prisma-mongodb/client.d.ts +1 -0
  3. package/generated/prisma-mongodb/client.js +4 -0
  4. package/generated/prisma-mongodb/default.d.ts +1 -0
  5. package/generated/prisma-mongodb/default.js +4 -0
  6. package/generated/prisma-mongodb/edge.d.ts +1 -0
  7. package/generated/prisma-mongodb/edge.js +336 -0
  8. package/generated/prisma-mongodb/index-browser.js +318 -0
  9. package/generated/prisma-mongodb/index.d.ts +22993 -0
  10. package/generated/prisma-mongodb/index.js +361 -0
  11. package/generated/prisma-mongodb/package.json +183 -0
  12. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  13. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  14. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  15. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  16. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  17. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  18. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  19. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  20. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  21. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  22. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  23. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  24. package/generated/prisma-mongodb/schema.prisma +364 -0
  25. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  26. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  27. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  28. package/generated/prisma-mongodb/wasm.js +343 -0
  29. package/generated/prisma-postgresql/client.d.ts +1 -0
  30. package/generated/prisma-postgresql/client.js +4 -0
  31. package/generated/prisma-postgresql/default.d.ts +1 -0
  32. package/generated/prisma-postgresql/default.js +4 -0
  33. package/generated/prisma-postgresql/edge.d.ts +1 -0
  34. package/generated/prisma-postgresql/edge.js +358 -0
  35. package/generated/prisma-postgresql/index-browser.js +340 -0
  36. package/generated/prisma-postgresql/index.d.ts +25171 -0
  37. package/generated/prisma-postgresql/index.js +383 -0
  38. package/generated/prisma-postgresql/package.json +183 -0
  39. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  40. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  41. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  42. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  43. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  44. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  45. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  46. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  47. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  48. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  49. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  50. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  51. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  52. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  53. package/generated/prisma-postgresql/schema.prisma +347 -0
  54. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  55. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  56. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  57. package/generated/prisma-postgresql/wasm.js +365 -0
  58. package/handlers/routers/db-migration.js +52 -0
  59. package/package.json +5 -5
  60. package/application/commands/integration-commands.test.js +0 -123
  61. package/core/Worker.test.js +0 -159
  62. package/database/encryption/encryption-integration.test.js +0 -553
  63. package/database/encryption/encryption-schema-registry.test.js +0 -392
  64. package/database/encryption/field-encryption-service.test.js +0 -525
  65. package/database/encryption/mongo-decryption-fix-verification.test.js +0 -348
  66. package/database/encryption/postgres-decryption-fix-verification.test.js +0 -371
  67. package/database/encryption/postgres-relation-decryption.test.js +0 -245
  68. package/database/encryption/prisma-encryption-extension.test.js +0 -439
  69. package/database/repositories/migration-status-repository-s3.test.js +0 -158
  70. package/database/use-cases/check-encryption-health-use-case.test.js +0 -192
  71. package/database/use-cases/get-migration-status-use-case.test.js +0 -171
  72. package/database/use-cases/run-database-migration-use-case.test.js +0 -310
  73. package/database/use-cases/trigger-database-migration-use-case.test.js +0 -250
  74. package/database/utils/prisma-runner.test.js +0 -486
  75. package/encrypt/Cryptor.test.js +0 -144
  76. package/errors/base-error.test.js +0 -32
  77. package/errors/fetch-error.test.js +0 -79
  78. package/errors/halt-error.test.js +0 -11
  79. package/errors/validation-errors.test.js +0 -120
  80. package/handlers/auth-flow.integration.test.js +0 -147
  81. package/handlers/integration-event-dispatcher.test.js +0 -209
  82. package/handlers/routers/db-migration.test.js +0 -51
  83. package/handlers/routers/health.test.js +0 -210
  84. package/handlers/routers/integration-webhook-routers.test.js +0 -126
  85. package/handlers/use-cases/check-integrations-health-use-case.test.js +0 -125
  86. package/handlers/webhook-flow.integration.test.js +0 -356
  87. package/handlers/workers/db-migration.test.js +0 -50
  88. package/handlers/workers/integration-defined-workers.test.js +0 -184
  89. package/integrations/tests/integration-router-multi-auth.test.js +0 -369
  90. package/integrations/tests/use-cases/create-integration.test.js +0 -131
  91. package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -150
  92. package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +0 -92
  93. package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -150
  94. package/integrations/tests/use-cases/get-integration-instance.test.js +0 -176
  95. package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -176
  96. package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
  97. package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
  98. package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
  99. package/integrations/tests/use-cases/update-integration.test.js +0 -141
  100. package/integrations/use-cases/create-process.test.js +0 -178
  101. package/integrations/use-cases/get-process.test.js +0 -190
  102. package/integrations/use-cases/load-integration-context-full.test.js +0 -329
  103. package/integrations/use-cases/load-integration-context.test.js +0 -114
  104. package/integrations/use-cases/update-process-metrics.test.js +0 -308
  105. package/integrations/use-cases/update-process-state.test.js +0 -256
  106. package/lambda/TimeoutCatcher.test.js +0 -68
  107. package/logs/logger.test.js +0 -76
  108. package/modules/module-hydration.test.js +0 -205
  109. package/modules/requester/requester.test.js +0 -28
  110. package/queues/queuer-util.test.js +0 -132
  111. package/user/tests/use-cases/create-individual-user.test.js +0 -24
  112. package/user/tests/use-cases/create-organization-user.test.js +0 -28
  113. package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
  114. package/user/tests/use-cases/get-user-from-adopter-jwt.test.js +0 -113
  115. package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
  116. package/user/tests/use-cases/get-user-from-x-frigg-headers.test.js +0 -346
  117. package/user/tests/use-cases/login-user.test.js +0 -220
  118. package/user/tests/user-password-encryption-isolation.test.js +0 -237
  119. package/user/tests/user-password-hashing.test.js +0 -235
  120. package/websocket/repositories/websocket-connection-repository.test.js +0 -227
@@ -0,0 +1,365 @@
1
+
2
+ /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
+ /* eslint-disable */
4
+
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+
7
+ const {
8
+ PrismaClientKnownRequestError,
9
+ PrismaClientUnknownRequestError,
10
+ PrismaClientRustPanicError,
11
+ PrismaClientInitializationError,
12
+ PrismaClientValidationError,
13
+ getPrismaClient,
14
+ sqltag,
15
+ empty,
16
+ join,
17
+ raw,
18
+ skip,
19
+ Decimal,
20
+ Debug,
21
+ objectEnumValues,
22
+ makeStrictEnum,
23
+ Extensions,
24
+ warnOnce,
25
+ defineDmmfProperty,
26
+ Public,
27
+ getRuntime,
28
+ createParam,
29
+ } = require('./runtime/wasm-engine-edge.js')
30
+
31
+
32
+ const Prisma = {}
33
+
34
+ exports.Prisma = Prisma
35
+ exports.$Enums = {}
36
+
37
+ /**
38
+ * Prisma Client JS version: 6.17.0
39
+ * Query Engine version: c0aafc03b8ef6cdced8654b9a817999e02457d6a
40
+ */
41
+ Prisma.prismaVersion = {
42
+ client: "6.17.0",
43
+ engine: "c0aafc03b8ef6cdced8654b9a817999e02457d6a"
44
+ }
45
+
46
+ Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
47
+ Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
48
+ Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
49
+ Prisma.PrismaClientInitializationError = PrismaClientInitializationError
50
+ Prisma.PrismaClientValidationError = PrismaClientValidationError
51
+ Prisma.Decimal = Decimal
52
+
53
+ /**
54
+ * Re-export of sql-template-tag
55
+ */
56
+ Prisma.sql = sqltag
57
+ Prisma.empty = empty
58
+ Prisma.join = join
59
+ Prisma.raw = raw
60
+ Prisma.validator = Public.validator
61
+
62
+ /**
63
+ * Extensions
64
+ */
65
+ Prisma.getExtensionContext = Extensions.getExtensionContext
66
+ Prisma.defineExtension = Extensions.defineExtension
67
+
68
+ /**
69
+ * Shorthand utilities for JSON filtering
70
+ */
71
+ Prisma.DbNull = objectEnumValues.instances.DbNull
72
+ Prisma.JsonNull = objectEnumValues.instances.JsonNull
73
+ Prisma.AnyNull = objectEnumValues.instances.AnyNull
74
+
75
+ Prisma.NullTypes = {
76
+ DbNull: objectEnumValues.classes.DbNull,
77
+ JsonNull: objectEnumValues.classes.JsonNull,
78
+ AnyNull: objectEnumValues.classes.AnyNull
79
+ }
80
+
81
+
82
+
83
+
84
+
85
+ /**
86
+ * Enums
87
+ */
88
+ exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
89
+ ReadUncommitted: 'ReadUncommitted',
90
+ ReadCommitted: 'ReadCommitted',
91
+ RepeatableRead: 'RepeatableRead',
92
+ Serializable: 'Serializable'
93
+ });
94
+
95
+ exports.Prisma.UserScalarFieldEnum = {
96
+ id: 'id',
97
+ type: 'type',
98
+ createdAt: 'createdAt',
99
+ updatedAt: 'updatedAt',
100
+ email: 'email',
101
+ username: 'username',
102
+ hashword: 'hashword',
103
+ appUserId: 'appUserId',
104
+ organizationId: 'organizationId',
105
+ appOrgId: 'appOrgId',
106
+ name: 'name'
107
+ };
108
+
109
+ exports.Prisma.TokenScalarFieldEnum = {
110
+ id: 'id',
111
+ token: 'token',
112
+ created: 'created',
113
+ expires: 'expires',
114
+ userId: 'userId'
115
+ };
116
+
117
+ exports.Prisma.CredentialScalarFieldEnum = {
118
+ id: 'id',
119
+ userId: 'userId',
120
+ subType: 'subType',
121
+ authIsValid: 'authIsValid',
122
+ externalId: 'externalId',
123
+ data: 'data',
124
+ createdAt: 'createdAt',
125
+ updatedAt: 'updatedAt'
126
+ };
127
+
128
+ exports.Prisma.EntityScalarFieldEnum = {
129
+ id: 'id',
130
+ credentialId: 'credentialId',
131
+ subType: 'subType',
132
+ userId: 'userId',
133
+ name: 'name',
134
+ moduleName: 'moduleName',
135
+ externalId: 'externalId',
136
+ createdAt: 'createdAt',
137
+ updatedAt: 'updatedAt'
138
+ };
139
+
140
+ exports.Prisma.IntegrationScalarFieldEnum = {
141
+ id: 'id',
142
+ userId: 'userId',
143
+ status: 'status',
144
+ config: 'config',
145
+ version: 'version',
146
+ errors: 'errors',
147
+ warnings: 'warnings',
148
+ info: 'info',
149
+ logs: 'logs',
150
+ createdAt: 'createdAt',
151
+ updatedAt: 'updatedAt'
152
+ };
153
+
154
+ exports.Prisma.IntegrationMappingScalarFieldEnum = {
155
+ id: 'id',
156
+ integrationId: 'integrationId',
157
+ sourceId: 'sourceId',
158
+ mapping: 'mapping',
159
+ createdAt: 'createdAt',
160
+ updatedAt: 'updatedAt'
161
+ };
162
+
163
+ exports.Prisma.SyncScalarFieldEnum = {
164
+ id: 'id',
165
+ integrationId: 'integrationId',
166
+ hash: 'hash',
167
+ name: 'name'
168
+ };
169
+
170
+ exports.Prisma.DataIdentifierScalarFieldEnum = {
171
+ id: 'id',
172
+ syncId: 'syncId',
173
+ entityId: 'entityId',
174
+ idData: 'idData',
175
+ hash: 'hash'
176
+ };
177
+
178
+ exports.Prisma.AssociationScalarFieldEnum = {
179
+ id: 'id',
180
+ integrationId: 'integrationId',
181
+ name: 'name',
182
+ type: 'type',
183
+ primaryObject: 'primaryObject'
184
+ };
185
+
186
+ exports.Prisma.AssociationObjectScalarFieldEnum = {
187
+ id: 'id',
188
+ associationId: 'associationId',
189
+ entityId: 'entityId',
190
+ objectType: 'objectType',
191
+ objId: 'objId',
192
+ metadata: 'metadata'
193
+ };
194
+
195
+ exports.Prisma.ProcessScalarFieldEnum = {
196
+ id: 'id',
197
+ userId: 'userId',
198
+ integrationId: 'integrationId',
199
+ name: 'name',
200
+ type: 'type',
201
+ state: 'state',
202
+ context: 'context',
203
+ results: 'results',
204
+ parentProcessId: 'parentProcessId',
205
+ createdAt: 'createdAt',
206
+ updatedAt: 'updatedAt'
207
+ };
208
+
209
+ exports.Prisma.StateScalarFieldEnum = {
210
+ id: 'id',
211
+ state: 'state'
212
+ };
213
+
214
+ exports.Prisma.WebsocketConnectionScalarFieldEnum = {
215
+ id: 'id',
216
+ connectionId: 'connectionId'
217
+ };
218
+
219
+ exports.Prisma.SortOrder = {
220
+ asc: 'asc',
221
+ desc: 'desc'
222
+ };
223
+
224
+ exports.Prisma.JsonNullValueInput = {
225
+ JsonNull: Prisma.JsonNull
226
+ };
227
+
228
+ exports.Prisma.NullableJsonNullValueInput = {
229
+ DbNull: Prisma.DbNull,
230
+ JsonNull: Prisma.JsonNull
231
+ };
232
+
233
+ exports.Prisma.QueryMode = {
234
+ default: 'default',
235
+ insensitive: 'insensitive'
236
+ };
237
+
238
+ exports.Prisma.NullsOrder = {
239
+ first: 'first',
240
+ last: 'last'
241
+ };
242
+
243
+ exports.Prisma.JsonNullValueFilter = {
244
+ DbNull: Prisma.DbNull,
245
+ JsonNull: Prisma.JsonNull,
246
+ AnyNull: Prisma.AnyNull
247
+ };
248
+ exports.UserType = exports.$Enums.UserType = {
249
+ INDIVIDUAL: 'INDIVIDUAL',
250
+ ORGANIZATION: 'ORGANIZATION'
251
+ };
252
+
253
+ exports.IntegrationStatus = exports.$Enums.IntegrationStatus = {
254
+ ENABLED: 'ENABLED',
255
+ NEEDS_CONFIG: 'NEEDS_CONFIG',
256
+ PROCESSING: 'PROCESSING',
257
+ DISABLED: 'DISABLED',
258
+ ERROR: 'ERROR'
259
+ };
260
+
261
+ exports.AssociationType = exports.$Enums.AssociationType = {
262
+ ONE_TO_MANY: 'ONE_TO_MANY',
263
+ ONE_TO_ONE: 'ONE_TO_ONE',
264
+ MANY_TO_ONE: 'MANY_TO_ONE'
265
+ };
266
+
267
+ exports.Prisma.ModelName = {
268
+ User: 'User',
269
+ Token: 'Token',
270
+ Credential: 'Credential',
271
+ Entity: 'Entity',
272
+ Integration: 'Integration',
273
+ IntegrationMapping: 'IntegrationMapping',
274
+ Sync: 'Sync',
275
+ DataIdentifier: 'DataIdentifier',
276
+ Association: 'Association',
277
+ AssociationObject: 'AssociationObject',
278
+ Process: 'Process',
279
+ State: 'State',
280
+ WebsocketConnection: 'WebsocketConnection'
281
+ };
282
+ /**
283
+ * Create the Client
284
+ */
285
+ const config = {
286
+ "generator": {
287
+ "name": "client",
288
+ "provider": {
289
+ "fromEnvVar": null,
290
+ "value": "prisma-client-js"
291
+ },
292
+ "output": {
293
+ "value": "/home/runner/work/frigg/frigg/packages/core/generated/prisma-postgresql",
294
+ "fromEnvVar": null
295
+ },
296
+ "config": {
297
+ "engineType": "binary"
298
+ },
299
+ "binaryTargets": [
300
+ {
301
+ "fromEnvVar": null,
302
+ "value": "debian-openssl-3.0.x",
303
+ "native": true
304
+ },
305
+ {
306
+ "fromEnvVar": null,
307
+ "value": "rhel-openssl-3.0.x"
308
+ }
309
+ ],
310
+ "previewFeatures": [],
311
+ "sourceFilePath": "/home/runner/work/frigg/frigg/packages/core/prisma-postgresql/schema.prisma",
312
+ "isCustomOutput": true
313
+ },
314
+ "relativeEnvPaths": {
315
+ "rootEnvPath": null
316
+ },
317
+ "relativePath": "../../prisma-postgresql",
318
+ "clientVersion": "6.17.0",
319
+ "engineVersion": "c0aafc03b8ef6cdced8654b9a817999e02457d6a",
320
+ "datasourceNames": [
321
+ "db"
322
+ ],
323
+ "activeProvider": "postgresql",
324
+ "postinstall": false,
325
+ "ciName": "GitHub Actions",
326
+ "inlineDatasources": {
327
+ "db": {
328
+ "url": {
329
+ "fromEnvVar": "DATABASE_URL",
330
+ "value": null
331
+ }
332
+ }
333
+ },
334
+ "inlineSchema": "// Frigg Framework - Prisma Schema (PostgreSQL)\n// PostgreSQL database schema for enterprise integration platform\n// Converted from MongoDB schema for relational database support\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"../generated/prisma-postgresql\"\n binaryTargets = [\"native\", \"rhel-openssl-3.0.x\"] // native for local dev, rhel for Lambda deployment\n engineType = \"binary\" // Use binary engines (smaller size)\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ============================================================================\n// USER MODELS\n// ============================================================================\n\n/// User model with discriminator pattern support\n/// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)\nmodel User {\n id Int @id @default(autoincrement())\n type UserType\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // IndividualUser fields (nullable for organizations)\n email String?\n username String?\n hashword String? // Bcrypt hashed password (handled in application layer)\n appUserId String?\n organizationId Int?\n\n // Self-referential relation for organization membership\n organization User? @relation(\"OrgMembers\", fields: [organizationId], references: [id], onDelete: NoAction, onUpdate: NoAction)\n members User[] @relation(\"OrgMembers\")\n\n // OrganizationUser fields (nullable for individuals)\n appOrgId String?\n name String?\n\n // Relations\n tokens Token[]\n credentials Credential[]\n entities Entity[]\n integrations Integration[]\n processes Process[]\n\n @@unique([email])\n @@unique([username])\n @@unique([appOrgId])\n @@index([type])\n @@index([appUserId])\n}\n\nenum UserType {\n INDIVIDUAL\n ORGANIZATION\n}\n\n// ============================================================================\n// AUTHENTICATION MODELS\n// ============================================================================\n\n/// Authentication tokens with expiration\n/// Bcrypt hashed tokens stored (handled in application layer)\nmodel Token {\n id Int @id @default(autoincrement())\n token String // Bcrypt hashed\n created DateTime @default(now())\n expires DateTime?\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@index([userId])\n @@index([expires])\n}\n\n// ============================================================================\n// CREDENTIAL & ENTITY MODELS\n// ============================================================================\n\n/// OAuth credentials and API tokens\n/// All sensitive data encrypted with KMS at rest\nmodel Credential {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n subType String?\n authIsValid Boolean?\n externalId String?\n\n // Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)\n // Contains: access_token, refresh_token, domain, expires_in, token_type, etc.\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n entities Entity[]\n\n @@index([userId])\n @@index([externalId])\n}\n\n/// External service entities (API connections)\nmodel Entity {\n id Int @id @default(autoincrement())\n credentialId Int?\n credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull)\n subType String?\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n name String?\n moduleName String?\n externalId String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations - many-to-many with implicit join tables\n integrations Integration[]\n syncs Sync[]\n\n dataIdentifiers DataIdentifier[]\n associationObjects AssociationObject[]\n\n @@index([userId])\n @@index([externalId])\n @@index([moduleName])\n @@index([credentialId])\n}\n\n// ============================================================================\n// INTEGRATION MODELS\n// ============================================================================\n\n/// Main integration configuration and state\nmodel Integration {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n status IntegrationStatus @default(ENABLED)\n\n // Configuration and version\n config Json? // Integration configuration object\n version String?\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n // Message arrays (stored as JSON)\n errors Json @default(\"[]\")\n warnings Json @default(\"[]\")\n info Json @default(\"[]\")\n logs Json @default(\"[]\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n associations Association[]\n syncs Sync[]\n mappings IntegrationMapping[]\n processes Process[]\n\n @@index([userId])\n @@index([status])\n}\n\nenum IntegrationStatus {\n ENABLED\n NEEDS_CONFIG\n PROCESSING\n DISABLED\n ERROR\n}\n\n/// Integration-specific data mappings\n/// All mapping data encrypted with KMS\nmodel IntegrationMapping {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n sourceId String?\n\n // Encrypted mapping data (handled via Prisma middleware)\n mapping Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, sourceId])\n @@index([integrationId])\n @@index([sourceId])\n}\n\n// ============================================================================\n// SYNC MODELS\n// ============================================================================\n\n/// Bidirectional data synchronization tracking\nmodel Sync {\n id Int @id @default(autoincrement())\n integrationId Int?\n integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n hash String\n name String\n\n // Data identifiers (extracted to separate model)\n dataIdentifiers DataIdentifier[]\n\n @@index([integrationId])\n @@index([hash])\n @@index([name])\n}\n\n/// Data identifier for sync operations\n/// Replaces nested array structure in Mongoose\nmodel DataIdentifier {\n id Int @id @default(autoincrement())\n syncId Int?\n sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n\n // Identifier data (can be any structure)\n idData Json\n\n hash String\n\n @@index([syncId])\n @@index([entityId])\n @@index([hash])\n}\n\n// ============================================================================\n// ASSOCIATION MODELS\n// ============================================================================\n\n/// Entity associations with cardinality tracking\nmodel Association {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n name String\n type AssociationType\n primaryObject String\n\n // Associated objects (extracted to separate model)\n objects AssociationObject[]\n\n @@index([integrationId])\n @@index([name])\n}\n\n/// Association object entry\n/// Replaces nested array structure in Mongoose\nmodel AssociationObject {\n id Int @id @default(autoincrement())\n associationId Int\n association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n objectType String\n objId String\n metadata Json? // Optional metadata\n\n @@index([associationId])\n @@index([entityId])\n}\n\nenum AssociationType {\n ONE_TO_MANY\n ONE_TO_ONE\n MANY_TO_ONE\n}\n\n// ============================================================================\n// PROCESS MODELS\n// ============================================================================\n\n/// Generic Process Model - tracks any long-running operation\n/// Used for: CRM syncs, data migrations, bulk operations, etc.\nmodel Process {\n id Int @id @default(autoincrement())\n\n // Core references\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Process identification\n name String // e.g., \"zoho-crm-contact-sync\", \"pipedrive-lead-sync\"\n type String // e.g., \"CRM_SYNC\", \"DATA_MIGRATION\", \"BULK_OPERATION\"\n\n // State machine\n state String // Current state (integration-defined states)\n\n // Flexible storage\n context Json @default(\"{}\") // Process-specific data (pagination, metadata, etc.)\n results Json @default(\"{}\") // Process results and metrics\n\n // Hierarchy support - self-referential relation\n parentProcessId Int?\n parentProcess Process? @relation(\"ProcessHierarchy\", fields: [parentProcessId], references: [id], onDelete: SetNull)\n childProcesses Process[] @relation(\"ProcessHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@index([integrationId])\n @@index([type])\n @@index([state])\n @@index([name])\n @@index([parentProcessId])\n}\n\n// ============================================================================\n// UTILITY MODELS\n// ============================================================================\n\n/// Generic state storage\nmodel State {\n id Int @id @default(autoincrement())\n state Json?\n}\n\n/// AWS API Gateway WebSocket connection tracking\nmodel WebsocketConnection {\n id Int @id @default(autoincrement())\n connectionId String?\n\n @@index([connectionId])\n}\n",
335
+ "inlineSchemaHash": "83470c60e30fb97964722dfafe8951eb953907b3ef72c7a78bf9e670536682c4",
336
+ "copyEngine": true
337
+ }
338
+ config.dirname = '/'
339
+
340
+ config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"type\",\"kind\":\"enum\",\"type\":\"UserType\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"username\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hashword\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appUserId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organizationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrgMembers\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrgMembers\"},{\"name\":\"appOrgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tokens\",\"kind\":\"object\",\"type\":\"Token\",\"relationName\":\"TokenToUser\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToUser\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToUser\"},{\"name\":\"integrations\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToUser\"},{\"name\":\"processes\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessToUser\"}],\"dbName\":null},\"Token\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"created\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"TokenToUser\"}],\"dbName\":null},\"Credential\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CredentialToUser\"},{\"name\":\"subType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"authIsValid\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"externalId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"CredentialToEntity\"}],\"dbName\":null},\"Entity\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"credentialId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"credential\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToEntity\"},{\"name\":\"subType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"EntityToUser\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moduleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"externalId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"integrations\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"EntityToIntegration\"},{\"name\":\"syncs\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"EntityToSync\"},{\"name\":\"dataIdentifiers\",\"kind\":\"object\",\"type\":\"DataIdentifier\",\"relationName\":\"DataIdentifierToEntity\"},{\"name\":\"associationObjects\",\"kind\":\"object\",\"type\":\"AssociationObject\",\"relationName\":\"AssociationObjectToEntity\"}],\"dbName\":null},\"Integration\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"IntegrationToUser\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"IntegrationStatus\"},{\"name\":\"config\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"version\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToIntegration\"},{\"name\":\"errors\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"warnings\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"info\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"logs\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"associations\",\"kind\":\"object\",\"type\":\"Association\",\"relationName\":\"AssociationToIntegration\"},{\"name\":\"syncs\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"IntegrationToSync\"},{\"name\":\"mappings\",\"kind\":\"object\",\"type\":\"IntegrationMapping\",\"relationName\":\"IntegrationToIntegrationMapping\"},{\"name\":\"processes\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"IntegrationToProcess\"}],\"dbName\":null},\"IntegrationMapping\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToIntegrationMapping\"},{\"name\":\"sourceId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"mapping\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Sync\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToSync\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToSync\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"dataIdentifiers\",\"kind\":\"object\",\"type\":\"DataIdentifier\",\"relationName\":\"DataIdentifierToSync\"}],\"dbName\":null},\"DataIdentifier\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"syncId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"sync\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"DataIdentifierToSync\"},{\"name\":\"entityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"entity\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"DataIdentifierToEntity\"},{\"name\":\"idData\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"Association\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"AssociationToIntegration\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"enum\",\"type\":\"AssociationType\"},{\"name\":\"primaryObject\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"objects\",\"kind\":\"object\",\"type\":\"AssociationObject\",\"relationName\":\"AssociationToAssociationObject\"}],\"dbName\":null},\"AssociationObject\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"associationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"association\",\"kind\":\"object\",\"type\":\"Association\",\"relationName\":\"AssociationToAssociationObject\"},{\"name\":\"entityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"entity\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"AssociationObjectToEntity\"},{\"name\":\"objectType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"objId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"Json\"}],\"dbName\":null},\"Process\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ProcessToUser\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToProcess\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"context\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"results\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"parentProcessId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"parentProcess\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessHierarchy\"},{\"name\":\"childProcesses\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessHierarchy\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"State\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"Json\"}],\"dbName\":null},\"WebsocketConnection\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"connectionId\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
341
+ defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
342
+ config.engineWasm = {
343
+ getRuntime: async () => require('./query_engine_bg.js'),
344
+ getQueryEngineWasmModule: async () => {
345
+ const loader = (await import('#wasm-engine-loader')).default
346
+ const engine = (await loader).default
347
+ return engine
348
+ }
349
+ }
350
+ config.compilerWasm = undefined
351
+
352
+ config.injectableEdgeEnv = () => ({
353
+ parsed: {
354
+ DATABASE_URL: typeof globalThis !== 'undefined' && globalThis['DATABASE_URL'] || typeof process !== 'undefined' && process.env && process.env.DATABASE_URL || undefined
355
+ }
356
+ })
357
+
358
+ if (typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined) {
359
+ Debug.enable(typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined)
360
+ }
361
+
362
+ const PrismaClient = getPrismaClient(config)
363
+ exports.PrismaClient = PrismaClient
364
+ Object.assign(exports, Prisma)
365
+
@@ -4,6 +4,7 @@
4
4
  * HTTP API for triggering and monitoring database migrations.
5
5
  *
6
6
  * Endpoints:
7
+ * - GET /db-migrate/status - Check if migrations are pending
7
8
  * - POST /db-migrate - Trigger async migration (queues job)
8
9
  * - GET /db-migrate/:processId - Check migration status
9
10
  *
@@ -27,6 +28,11 @@ const {
27
28
  ValidationError: GetValidationError,
28
29
  NotFoundError,
29
30
  } = require('../../database/use-cases/get-migration-status-use-case');
31
+ const {
32
+ CheckMigrationStatusUseCase,
33
+ ValidationError: CheckValidationError,
34
+ } = require('../../database/use-cases/check-migration-status-use-case');
35
+ const prismaRunner = require('../../database/utils/prisma-runner');
30
36
 
31
37
  const router = Router();
32
38
 
@@ -40,6 +46,7 @@ const triggerMigrationUseCase = new TriggerDatabaseMigrationUseCase({
40
46
  // Note: QueuerUtil is used directly in the use case (static utility)
41
47
  });
42
48
  const getStatusUseCase = new GetMigrationStatusUseCase({ migrationStatusRepository });
49
+ const checkMigrationStatusUseCase = new CheckMigrationStatusUseCase({ prismaRunner });
43
50
 
44
51
  /**
45
52
  * Admin API key validation middleware
@@ -118,6 +125,51 @@ router.post(
118
125
  })
119
126
  );
120
127
 
128
+ /**
129
+ * GET /db-migrate/status
130
+ *
131
+ * Check if database has pending migrations
132
+ *
133
+ * Query params:
134
+ * - stage: string (optional, defaults to STAGE env var or 'production')
135
+ *
136
+ * Response (200 OK):
137
+ * {
138
+ * upToDate: boolean,
139
+ * pendingMigrations: number,
140
+ * dbType: 'postgresql',
141
+ * stage: string,
142
+ * recommendation?: string (if migrations pending),
143
+ * error?: string (if database check failed)
144
+ * }
145
+ */
146
+ router.get(
147
+ '/db-migrate/status',
148
+ catchAsyncError(async (req, res) => {
149
+ const stage = req.query.stage || process.env.STAGE || 'production';
150
+ const dbType = process.env.DB_TYPE || 'postgresql'; // Hardcoded for PostgreSQL migrations
151
+
152
+ console.log(`Checking migration status: dbType=${dbType}, stage=${stage}`);
153
+
154
+ try {
155
+ const status = await checkMigrationStatusUseCase.execute(dbType, stage);
156
+
157
+ res.status(200).json(status);
158
+ } catch (error) {
159
+ // Handle validation errors (400 Bad Request)
160
+ if (error instanceof CheckValidationError) {
161
+ return res.status(400).json({
162
+ success: false,
163
+ error: error.message,
164
+ });
165
+ }
166
+
167
+ // Re-throw other errors for global error handler
168
+ throw error;
169
+ }
170
+ })
171
+ );
172
+
121
173
  /**
122
174
  * GET /db-migrate/:migrationId
123
175
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.461.61382d8.0",
4
+ "version": "2.0.0--canary.461.3d6d8ad.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -37,9 +37,9 @@
37
37
  }
38
38
  },
39
39
  "devDependencies": {
40
- "@friggframework/eslint-config": "2.0.0--canary.461.61382d8.0",
41
- "@friggframework/prettier-config": "2.0.0--canary.461.61382d8.0",
42
- "@friggframework/test": "2.0.0--canary.461.61382d8.0",
40
+ "@friggframework/eslint-config": "2.0.0--canary.461.3d6d8ad.0",
41
+ "@friggframework/prettier-config": "2.0.0--canary.461.3d6d8ad.0",
42
+ "@friggframework/test": "2.0.0--canary.461.3d6d8ad.0",
43
43
  "@prisma/client": "^6.17.0",
44
44
  "@types/lodash": "4.17.15",
45
45
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -79,5 +79,5 @@
79
79
  "publishConfig": {
80
80
  "access": "public"
81
81
  },
82
- "gitHead": "61382d8a96346ed9732fbaf38b977e13e0a4471d"
82
+ "gitHead": "3d6d8ad065c2530fee34dcda5ee304979c718a65"
83
83
  }
@@ -1,123 +0,0 @@
1
- jest.mock('../../database/config', () => ({
2
- DB_TYPE: 'mongodb',
3
- getDatabaseType: jest.fn(() => 'mongodb'),
4
- PRISMA_LOG_LEVEL: 'error,warn',
5
- PRISMA_QUERY_LOGGING: false,
6
- }));
7
-
8
- const mockFindExecute = jest.fn();
9
-
10
- jest.mock('../../integrations/use-cases/find-integration-context-by-external-entity-id', () => {
11
- return {
12
- FindIntegrationContextByExternalEntityIdUseCase: jest
13
- .fn()
14
- .mockImplementation(() => ({
15
- execute: mockFindExecute,
16
- })),
17
- };
18
- });
19
-
20
- const {
21
- createIntegrationCommands,
22
- findIntegrationContextByExternalEntityId,
23
- } = require('./integration-commands');
24
- const {
25
- FindIntegrationContextByExternalEntityIdUseCase,
26
- } = require('../../integrations/use-cases/find-integration-context-by-external-entity-id');
27
- const { DummyIntegration } = require('../../integrations/tests/doubles/dummy-integration-class');
28
-
29
- describe('integration commands', () => {
30
- beforeEach(() => {
31
- jest.clearAllMocks();
32
- mockFindExecute.mockReset();
33
- });
34
-
35
- it('requires an integrationClass when creating commands', () => {
36
- expect(() => createIntegrationCommands()).toThrow(
37
- 'integrationClass is required',
38
- );
39
- });
40
-
41
- it('creates use cases with default repositories', () => {
42
- createIntegrationCommands({
43
- integrationClass: DummyIntegration,
44
- });
45
-
46
- // Verify that the use case is created with default repositories instantiated internally
47
- expect(
48
- FindIntegrationContextByExternalEntityIdUseCase,
49
- ).toHaveBeenCalledWith({
50
- integrationRepository: expect.any(Object),
51
- moduleRepository: expect.any(Object),
52
- loadIntegrationContextUseCase: expect.any(Object),
53
- });
54
- });
55
-
56
- it('returns context when findIntegrationContextByExternalEntityId succeeds', async () => {
57
- const expectedContext = { record: { id: 'integration-1' } };
58
- mockFindExecute.mockResolvedValue({ context: expectedContext });
59
- const commands = createIntegrationCommands({
60
- integrationClass: DummyIntegration,
61
- });
62
-
63
- const result = await commands.findIntegrationContextByExternalEntityId(
64
- 'ext-1',
65
- );
66
-
67
- expect(mockFindExecute).toHaveBeenCalledWith({
68
- externalEntityId: 'ext-1',
69
- });
70
- expect(result).toEqual({ context: expectedContext });
71
- });
72
-
73
- it('maps known errors to status codes', async () => {
74
- const error = Object.assign(new Error('Entity missing'), {
75
- code: 'ENTITY_NOT_FOUND',
76
- });
77
- mockFindExecute.mockRejectedValue(error);
78
- const commands = createIntegrationCommands({
79
- integrationClass: DummyIntegration,
80
- });
81
-
82
- const result = await commands.findIntegrationContextByExternalEntityId(
83
- 'ext-1',
84
- );
85
-
86
- expect(result).toEqual({
87
- error: 401,
88
- reason: 'Entity missing',
89
- code: 'ENTITY_NOT_FOUND',
90
- });
91
- });
92
-
93
- it('delegates loadIntegrationContextById to the loader use case', async () => {
94
- // This test verifies that the command properly delegates to the use case
95
- // We can't easily mock the internal use case, so we'll test the integration
96
- const commands = createIntegrationCommands({
97
- integrationClass: DummyIntegration,
98
- });
99
-
100
- // The actual use case will be called - this is more of an integration test
101
- // For unit testing, we'd need to refactor to allow DI of the use case
102
- // But since we've decided to always use default use cases, this is acceptable
103
- const result = await commands.loadIntegrationContextById('integration-1');
104
-
105
- // Result will have error since we don't have a real database
106
- expect(result).toHaveProperty('error');
107
- });
108
-
109
- it('exposes a one-off helper for finding integration context by external entity id', async () => {
110
- const expectedContext = { record: { id: 'integration-1' } };
111
- mockFindExecute.mockResolvedValue({ context: expectedContext });
112
-
113
- const result = await findIntegrationContextByExternalEntityId({
114
- integrationClass: DummyIntegration,
115
- externalEntityId: 'ext-2',
116
- });
117
-
118
- expect(mockFindExecute).toHaveBeenCalledWith({
119
- externalEntityId: 'ext-2',
120
- });
121
- expect(result).toEqual({ context: expectedContext });
122
- });
123
- });