@friggframework/core 2.0.0--canary.454.e2a280d.0 → 2.0.0--canary.458.c150d9a.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.
- package/README.md +0 -28
- package/database/config.js +1 -29
- package/database/prisma.js +2 -2
- package/database/use-cases/test-encryption-use-case.js +5 -6
- package/handlers/routers/user.js +3 -1
- package/modules/module.js +1 -1
- package/modules/use-cases/process-authorization-callback.js +2 -1
- package/package.json +66 -79
- package/prisma-mongodb/schema.prisma +20 -25
- package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/schema.prisma +14 -19
- package/database/use-cases/run-database-migration-use-case.js +0 -137
- package/database/use-cases/run-database-migration-use-case.test.js +0 -310
- package/database/utils/prisma-runner.js +0 -313
- package/database/utils/prisma-runner.test.js +0 -486
- package/handlers/workers/db-migration.js +0 -208
- package/handlers/workers/db-migration.test.js +0 -437
package/README.md
CHANGED
|
@@ -62,34 +62,6 @@ npm install @friggframework/core
|
|
|
62
62
|
yarn add @friggframework/core
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
### Prisma Support (Optional)
|
|
66
|
-
|
|
67
|
-
`@friggframework/core` supports both MongoDB and PostgreSQL via Prisma ORM. **Prisma is an optional peer dependency** - you only need to install it if you're using database features that require migrations or schema generation.
|
|
68
|
-
|
|
69
|
-
**When you need Prisma:**
|
|
70
|
-
- Running database migrations (`prisma migrate`, `prisma db push`)
|
|
71
|
-
- Generating Prisma clients for your application
|
|
72
|
-
- Using the migration Lambda function (`dbMigrate`)
|
|
73
|
-
|
|
74
|
-
**Installation:**
|
|
75
|
-
```bash
|
|
76
|
-
# Install Prisma CLI and Client as dev dependencies
|
|
77
|
-
npm install --save-dev prisma @prisma/client
|
|
78
|
-
|
|
79
|
-
# Or with yarn
|
|
80
|
-
yarn add -D prisma @prisma/client
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Generate Prisma Clients:**
|
|
84
|
-
```bash
|
|
85
|
-
# From @friggframework/core directory
|
|
86
|
-
npm run prisma:generate:mongo # MongoDB only
|
|
87
|
-
npm run prisma:generate:postgres # PostgreSQL only
|
|
88
|
-
npm run prisma:generate # Both databases
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
**Note:** The published npm package includes pre-generated Prisma clients, so you don't need to install Prisma just to use `@friggframework/core` in production. Prisma is only required if you're actively developing migrations or running the migration Lambda function.
|
|
92
|
-
|
|
93
65
|
### Prerequisites
|
|
94
66
|
|
|
95
67
|
- Node.js 16+
|
package/database/config.js
CHANGED
|
@@ -37,35 +37,7 @@ function getDatabaseType() {
|
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
backendModule = require(backendIndexPath);
|
|
43
|
-
} catch (requireError) {
|
|
44
|
-
// Extract the actual file with the error from the stack trace
|
|
45
|
-
// Skip internal Node.js files (node:internal/*) and find first user file
|
|
46
|
-
let errorFile = 'unknown file';
|
|
47
|
-
const stackLines = requireError.stack?.split('\n') || [];
|
|
48
|
-
|
|
49
|
-
for (const line of stackLines) {
|
|
50
|
-
// Match file paths in stack trace, excluding node:internal
|
|
51
|
-
const match = line.match(/\(([^)]+\.js):\d+:\d+\)/) || line.match(/at ([^(]+\.js):\d+:\d+/);
|
|
52
|
-
if (match && match[1] && !match[1].includes('node:internal')) {
|
|
53
|
-
errorFile = match[1];
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Provide better error context for syntax/runtime errors
|
|
59
|
-
throw new Error(
|
|
60
|
-
`[Frigg] Failed to load app definition from ${backendIndexPath}\n` +
|
|
61
|
-
`Error: ${requireError.message}\n` +
|
|
62
|
-
`File with error: ${errorFile}\n` +
|
|
63
|
-
`\nFull stack trace:\n${requireError.stack}\n\n` +
|
|
64
|
-
'This error occurred while loading your app definition or its dependencies. ' +
|
|
65
|
-
'Check the file listed above for syntax errors (trailing commas, missing brackets, etc.)'
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
40
|
+
const backendModule = require(backendIndexPath);
|
|
69
41
|
database = backendModule?.Definition?.database;
|
|
70
42
|
|
|
71
43
|
if (!database) {
|
package/database/prisma.js
CHANGED
|
@@ -77,9 +77,9 @@ const prismaClientSingleton = () => {
|
|
|
77
77
|
let PrismaClient;
|
|
78
78
|
|
|
79
79
|
if (config.DB_TYPE === 'mongodb') {
|
|
80
|
-
PrismaClient = require('
|
|
80
|
+
PrismaClient = require('@prisma-mongodb/client').PrismaClient;
|
|
81
81
|
} else if (config.DB_TYPE === 'postgresql') {
|
|
82
|
-
PrismaClient = require('
|
|
82
|
+
PrismaClient = require('@prisma-postgresql/client').PrismaClient;
|
|
83
83
|
} else {
|
|
84
84
|
throw new Error(
|
|
85
85
|
`Unsupported database type: ${config.DB_TYPE}. Supported values: 'mongodb', 'postgresql'`
|
|
@@ -83,14 +83,13 @@ class TestEncryptionUseCase {
|
|
|
83
83
|
* @private
|
|
84
84
|
*/
|
|
85
85
|
_mapTestDataToCredential(testData) {
|
|
86
|
-
// Note: Using camelCase for Prisma compatibility (both MongoDB and PostgreSQL)
|
|
87
|
-
// Changed from snake_case (user_id, entity_id) to camelCase (userId, externalId)
|
|
88
86
|
return {
|
|
89
|
-
|
|
87
|
+
user_id: 'test-encryption-user',
|
|
88
|
+
entity_id: 'test-encryption-entity',
|
|
90
89
|
data: {
|
|
91
|
-
access_token: testData.testSecret,
|
|
92
|
-
refresh_token: testData.nestedSecret?.value,
|
|
93
|
-
domain: testData.normalField,
|
|
90
|
+
access_token: testData.testSecret,
|
|
91
|
+
refresh_token: testData.nestedSecret?.value,
|
|
92
|
+
domain: testData.normalField,
|
|
94
93
|
},
|
|
95
94
|
};
|
|
96
95
|
}
|
package/handlers/routers/user.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const { createAppHandler } = require('../app-handler-helpers');
|
|
3
3
|
const { checkRequiredParams } = require('@friggframework/core');
|
|
4
|
-
const {
|
|
4
|
+
const {
|
|
5
|
+
createUserRepository,
|
|
6
|
+
} = require('../../user/repositories/user-repository-factory');
|
|
5
7
|
const {
|
|
6
8
|
CreateIndividualUser,
|
|
7
9
|
} = require('../../user/use-cases/create-individual-user');
|
package/modules/module.js
CHANGED
|
@@ -42,7 +42,7 @@ class Module extends Delegate {
|
|
|
42
42
|
const apiParams = {
|
|
43
43
|
...this.definition.env,
|
|
44
44
|
delegate: this,
|
|
45
|
-
...this.apiParamsFromCredential(this.credential.data)
|
|
45
|
+
...(this.credential?.data ? this.apiParamsFromCredential(this.credential.data) : {}), // Handle case when credential is undefined
|
|
46
46
|
...this.apiParamsFromEntity(this.entity),
|
|
47
47
|
};
|
|
48
48
|
this.api = new this.apiClass(apiParams);
|
|
@@ -93,7 +93,8 @@ class ProcessAuthorizationCallback {
|
|
|
93
93
|
);
|
|
94
94
|
credentialDetails.details.authIsValid = true;
|
|
95
95
|
|
|
96
|
-
await this.credentialRepository.upsertCredential(credentialDetails);
|
|
96
|
+
const persisted = await this.credentialRepository.upsertCredential(credentialDetails);
|
|
97
|
+
module.credential = persisted;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
async findOrCreateEntity(entityDetails, moduleName, credentialId) {
|
package/package.json
CHANGED
|
@@ -1,81 +1,68 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"repository": {
|
|
69
|
-
"type": "git",
|
|
70
|
-
"url": "git+https://github.com/friggframework/frigg.git"
|
|
71
|
-
},
|
|
72
|
-
"bugs": {
|
|
73
|
-
"url": "https://github.com/friggframework/frigg/issues"
|
|
74
|
-
},
|
|
75
|
-
"homepage": "https://github.com/friggframework/frigg#readme",
|
|
76
|
-
"description": "",
|
|
77
|
-
"publishConfig": {
|
|
78
|
-
"access": "public"
|
|
79
|
-
},
|
|
80
|
-
"gitHead": "e2a280d643ad826ce11b8267c98eba09d89d4dfd"
|
|
2
|
+
"name": "@friggframework/core",
|
|
3
|
+
"prettier": "@friggframework/prettier-config",
|
|
4
|
+
"version": "2.0.0--canary.458.c150d9a.0",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@hapi/boom": "^10.0.1",
|
|
7
|
+
"@prisma/client": "^6.16.3",
|
|
8
|
+
"aws-sdk": "^2.1200.0",
|
|
9
|
+
"bcryptjs": "^2.4.3",
|
|
10
|
+
"body-parser": "^1.20.2",
|
|
11
|
+
"common-tags": "^1.8.2",
|
|
12
|
+
"cors": "^2.8.5",
|
|
13
|
+
"dotenv": "^16.4.7",
|
|
14
|
+
"express": "^4.19.2",
|
|
15
|
+
"express-async-handler": "^1.2.0",
|
|
16
|
+
"form-data": "^4.0.0",
|
|
17
|
+
"fs-extra": "^11.2.0",
|
|
18
|
+
"lodash": "4.17.21",
|
|
19
|
+
"lodash.get": "^4.4.2",
|
|
20
|
+
"mongoose": "6.11.6",
|
|
21
|
+
"node-fetch": "^2.6.7",
|
|
22
|
+
"serverless-http": "^2.7.0",
|
|
23
|
+
"uuid": "^9.0.1"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@friggframework/eslint-config": "2.0.0--canary.458.c150d9a.0",
|
|
27
|
+
"@friggframework/prettier-config": "2.0.0--canary.458.c150d9a.0",
|
|
28
|
+
"@friggframework/test": "2.0.0--canary.458.c150d9a.0",
|
|
29
|
+
"@types/lodash": "4.17.15",
|
|
30
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
31
|
+
"chai": "^4.3.6",
|
|
32
|
+
"eslint": "^8.22.0",
|
|
33
|
+
"eslint-plugin-import": "^2.29.1",
|
|
34
|
+
"eslint-plugin-n": "^17.10.2",
|
|
35
|
+
"eslint-plugin-promise": "^7.0.0",
|
|
36
|
+
"jest": "^29.7.0",
|
|
37
|
+
"prettier": "^2.7.1",
|
|
38
|
+
"prisma": "^6.16.3",
|
|
39
|
+
"sinon": "^16.1.1",
|
|
40
|
+
"typescript": "^5.0.2"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"lint:fix": "prettier --write --loglevel error . && eslint . --fix",
|
|
44
|
+
"test": "jest --passWithNoTests # TODO",
|
|
45
|
+
"prisma:generate:mongo": "npx prisma generate --schema ./prisma-mongodb/schema.prisma",
|
|
46
|
+
"prisma:generate:postgres": "npx prisma generate --schema ./prisma-postgresql/schema.prisma",
|
|
47
|
+
"prisma:generate": "npm run prisma:generate:mongo && npm run prisma:generate:postgres",
|
|
48
|
+
"prisma:push:mongo": "npx prisma db push --schema ./prisma-mongodb/schema.prisma",
|
|
49
|
+
"prisma:migrate:postgres": "npx prisma migrate dev --schema ./prisma-postgresql/schema.prisma",
|
|
50
|
+
"postinstall": "npm run prisma:generate"
|
|
51
|
+
},
|
|
52
|
+
"author": "",
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"main": "index.js",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/friggframework/frigg.git"
|
|
58
|
+
},
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/friggframework/frigg/issues"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/friggframework/frigg#readme",
|
|
63
|
+
"description": "",
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
},
|
|
67
|
+
"gitHead": "c150d9a112596c02f6e29eae2944f7dcb1ed1489"
|
|
81
68
|
}
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
// Migration from Mongoose ODM to Prisma ORM
|
|
4
4
|
|
|
5
5
|
generator client {
|
|
6
|
-
provider
|
|
7
|
-
output
|
|
8
|
-
binaryTargets = ["native", "rhel-openssl-3.0.x"] // native for local dev, rhel for Lambda deployment
|
|
9
|
-
engineType = "binary" // Use binary engines (smaller size)
|
|
6
|
+
provider = "prisma-client-js"
|
|
7
|
+
output = "../node_modules/@prisma-mongodb/client"
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
datasource db {
|
|
@@ -21,8 +19,8 @@ datasource db {
|
|
|
21
19
|
/// User model with discriminator pattern support
|
|
22
20
|
/// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)
|
|
23
21
|
model User {
|
|
24
|
-
id
|
|
25
|
-
type
|
|
22
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
23
|
+
type UserType
|
|
26
24
|
|
|
27
25
|
// Timestamps
|
|
28
26
|
createdAt DateTime @default(now())
|
|
@@ -88,12 +86,12 @@ model Token {
|
|
|
88
86
|
/// OAuth credentials and API tokens
|
|
89
87
|
/// All sensitive data encrypted with KMS at rest
|
|
90
88
|
model Credential {
|
|
91
|
-
id
|
|
92
|
-
userId
|
|
93
|
-
user
|
|
94
|
-
subType
|
|
89
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
90
|
+
userId String? @db.ObjectId
|
|
91
|
+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
92
|
+
subType String?
|
|
95
93
|
authIsValid Boolean?
|
|
96
|
-
externalId
|
|
94
|
+
externalId String?
|
|
97
95
|
|
|
98
96
|
// Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)
|
|
99
97
|
// Contains: access_token, refresh_token, domain, expires_in, token_type, etc.
|
|
@@ -126,11 +124,11 @@ model Entity {
|
|
|
126
124
|
updatedAt DateTime @updatedAt
|
|
127
125
|
|
|
128
126
|
// Relations - many-to-many with scalar lists
|
|
129
|
-
integrations
|
|
130
|
-
integrationIds
|
|
127
|
+
integrations Integration[] @relation("IntegrationEntities", fields: [integrationIds], references: [id])
|
|
128
|
+
integrationIds String[] @db.ObjectId
|
|
131
129
|
|
|
132
|
-
syncs
|
|
133
|
-
syncIds
|
|
130
|
+
syncs Sync[] @relation("SyncEntities", fields: [syncIds], references: [id])
|
|
131
|
+
syncIds String[] @db.ObjectId
|
|
134
132
|
|
|
135
133
|
dataIdentifiers DataIdentifier[]
|
|
136
134
|
associationObjects AssociationObject[]
|
|
@@ -154,16 +152,13 @@ model Integration {
|
|
|
154
152
|
status IntegrationStatus @default(ENABLED)
|
|
155
153
|
|
|
156
154
|
// Configuration and version
|
|
157
|
-
config Json?
|
|
155
|
+
config Json? // Integration configuration object
|
|
158
156
|
version String?
|
|
159
157
|
|
|
160
158
|
// Entity references (many-to-many via explicit scalar list)
|
|
161
159
|
entities Entity[] @relation("IntegrationEntities", fields: [entityIds], references: [id])
|
|
162
160
|
entityIds String[] @db.ObjectId
|
|
163
161
|
|
|
164
|
-
// Entity reference map (Map<String, String> stored as JSON)
|
|
165
|
-
entityReferenceMap Json? @default("{}")
|
|
166
|
-
|
|
167
162
|
// Message arrays (stored as JSON)
|
|
168
163
|
errors Json @default("[]")
|
|
169
164
|
warnings Json @default("[]")
|
|
@@ -281,11 +276,11 @@ model Association {
|
|
|
281
276
|
/// Association object entry
|
|
282
277
|
/// Replaces nested array structure in Mongoose
|
|
283
278
|
model AssociationObject {
|
|
284
|
-
id String
|
|
285
|
-
associationId String
|
|
286
|
-
association Association
|
|
287
|
-
entityId String
|
|
288
|
-
entity Entity
|
|
279
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
280
|
+
associationId String @db.ObjectId
|
|
281
|
+
association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)
|
|
282
|
+
entityId String @db.ObjectId
|
|
283
|
+
entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
|
|
289
284
|
objectType String
|
|
290
285
|
objId String
|
|
291
286
|
metadata Json? // Optional metadata
|
|
@@ -320,4 +315,4 @@ model WebsocketConnection {
|
|
|
320
315
|
|
|
321
316
|
@@index([connectionId])
|
|
322
317
|
@@map("WebsocketConnection")
|
|
323
|
-
}
|
|
318
|
+
}
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
// Converted from MongoDB schema for relational database support
|
|
4
4
|
|
|
5
5
|
generator client {
|
|
6
|
-
provider
|
|
7
|
-
output
|
|
8
|
-
binaryTargets = ["native", "rhel-openssl-3.0.x"] // native for local dev, rhel for Lambda deployment
|
|
9
|
-
engineType = "binary" // Use binary engines (smaller size)
|
|
6
|
+
provider = "prisma-client-js"
|
|
7
|
+
output = "../node_modules/@prisma-postgresql/client"
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
datasource db {
|
|
@@ -21,8 +19,8 @@ datasource db {
|
|
|
21
19
|
/// User model with discriminator pattern support
|
|
22
20
|
/// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)
|
|
23
21
|
model User {
|
|
24
|
-
id
|
|
25
|
-
type
|
|
22
|
+
id Int @id @default(autoincrement())
|
|
23
|
+
type UserType
|
|
26
24
|
|
|
27
25
|
// Timestamps
|
|
28
26
|
createdAt DateTime @default(now())
|
|
@@ -86,12 +84,12 @@ model Token {
|
|
|
86
84
|
/// OAuth credentials and API tokens
|
|
87
85
|
/// All sensitive data encrypted with KMS at rest
|
|
88
86
|
model Credential {
|
|
89
|
-
id
|
|
90
|
-
userId
|
|
91
|
-
user
|
|
92
|
-
subType
|
|
87
|
+
id Int @id @default(autoincrement())
|
|
88
|
+
userId Int?
|
|
89
|
+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
90
|
+
subType String?
|
|
93
91
|
authIsValid Boolean?
|
|
94
|
-
externalId
|
|
92
|
+
externalId String?
|
|
95
93
|
|
|
96
94
|
// Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)
|
|
97
95
|
// Contains: access_token, refresh_token, domain, expires_in, token_type, etc.
|
|
@@ -147,15 +145,12 @@ model Integration {
|
|
|
147
145
|
status IntegrationStatus @default(ENABLED)
|
|
148
146
|
|
|
149
147
|
// Configuration and version
|
|
150
|
-
config Json?
|
|
148
|
+
config Json? // Integration configuration object
|
|
151
149
|
version String?
|
|
152
150
|
|
|
153
151
|
// Entity references (many-to-many via implicit join table)
|
|
154
152
|
entities Entity[]
|
|
155
153
|
|
|
156
|
-
// Entity reference map (Map<String, String> stored as JSON)
|
|
157
|
-
entityReferenceMap Json? @default("{}")
|
|
158
|
-
|
|
159
154
|
// Message arrays (stored as JSON)
|
|
160
155
|
errors Json @default("[]")
|
|
161
156
|
warnings Json @default("[]")
|
|
@@ -228,11 +223,11 @@ model Sync {
|
|
|
228
223
|
/// Data identifier for sync operations
|
|
229
224
|
/// Replaces nested array structure in Mongoose
|
|
230
225
|
model DataIdentifier {
|
|
231
|
-
id Int
|
|
226
|
+
id Int @id @default(autoincrement())
|
|
232
227
|
syncId Int?
|
|
233
|
-
sync Sync?
|
|
228
|
+
sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)
|
|
234
229
|
entityId Int
|
|
235
|
-
entity Entity
|
|
230
|
+
entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
|
|
236
231
|
|
|
237
232
|
// Identifier data (can be any structure)
|
|
238
233
|
idData Json
|
|
@@ -292,7 +287,7 @@ enum AssociationType {
|
|
|
292
287
|
|
|
293
288
|
/// Generic state storage
|
|
294
289
|
model State {
|
|
295
|
-
id Int
|
|
290
|
+
id Int @id @default(autoincrement())
|
|
296
291
|
state Json?
|
|
297
292
|
}
|
|
298
293
|
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Run Database Migration Use Case
|
|
3
|
-
*
|
|
4
|
-
* Business logic for running Prisma database migrations.
|
|
5
|
-
* Orchestrates Prisma client generation and migration execution.
|
|
6
|
-
*
|
|
7
|
-
* This use case follows the Frigg hexagonal architecture pattern where:
|
|
8
|
-
* - Handlers (adapters) call use cases
|
|
9
|
-
* - Use cases contain business logic and orchestration
|
|
10
|
-
* - Use cases call repositories/utilities for data access
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
class RunDatabaseMigrationUseCase {
|
|
14
|
-
/**
|
|
15
|
-
* @param {Object} dependencies
|
|
16
|
-
* @param {Object} dependencies.prismaRunner - Prisma runner utilities
|
|
17
|
-
*/
|
|
18
|
-
constructor({ prismaRunner }) {
|
|
19
|
-
if (!prismaRunner) {
|
|
20
|
-
throw new Error('prismaRunner dependency is required');
|
|
21
|
-
}
|
|
22
|
-
this.prismaRunner = prismaRunner;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Execute database migration
|
|
27
|
-
*
|
|
28
|
-
* @param {Object} params
|
|
29
|
-
* @param {string} params.dbType - Database type ('postgresql' or 'mongodb')
|
|
30
|
-
* @param {string} params.stage - Deployment stage (determines migration command)
|
|
31
|
-
* @param {boolean} [params.verbose=false] - Enable verbose output
|
|
32
|
-
* @returns {Promise<Object>} Migration result { success, dbType, stage, command, message }
|
|
33
|
-
* @throws {MigrationError} If migration fails
|
|
34
|
-
* @throws {ValidationError} If parameters are invalid
|
|
35
|
-
*/
|
|
36
|
-
async execute({ dbType, stage, verbose = false }) {
|
|
37
|
-
// Validation
|
|
38
|
-
this._validateParams({ dbType, stage });
|
|
39
|
-
|
|
40
|
-
// Step 1: Generate Prisma client
|
|
41
|
-
const generateResult = await this.prismaRunner.runPrismaGenerate(dbType, verbose);
|
|
42
|
-
|
|
43
|
-
if (!generateResult.success) {
|
|
44
|
-
throw new MigrationError(
|
|
45
|
-
`Failed to generate Prisma client: ${generateResult.error || 'Unknown error'}`,
|
|
46
|
-
{ dbType, stage, step: 'generate', output: generateResult.output }
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Step 2: Run migrations based on database type
|
|
51
|
-
let migrationResult;
|
|
52
|
-
let migrationCommand;
|
|
53
|
-
|
|
54
|
-
if (dbType === 'postgresql') {
|
|
55
|
-
migrationCommand = this.prismaRunner.getMigrationCommand(stage);
|
|
56
|
-
migrationResult = await this.prismaRunner.runPrismaMigrate(migrationCommand, verbose);
|
|
57
|
-
|
|
58
|
-
if (!migrationResult.success) {
|
|
59
|
-
throw new MigrationError(
|
|
60
|
-
`PostgreSQL migration failed: ${migrationResult.error || 'Unknown error'}`,
|
|
61
|
-
{ dbType, stage, command: migrationCommand, step: 'migrate', output: migrationResult.output }
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
} else if (dbType === 'mongodb') {
|
|
65
|
-
migrationCommand = 'db push';
|
|
66
|
-
// Use non-interactive mode for automated/Lambda environments
|
|
67
|
-
migrationResult = await this.prismaRunner.runPrismaDbPush(verbose, true);
|
|
68
|
-
|
|
69
|
-
if (!migrationResult.success) {
|
|
70
|
-
throw new MigrationError(
|
|
71
|
-
`MongoDB push failed: ${migrationResult.error || 'Unknown error'}`,
|
|
72
|
-
{ dbType, stage, command: migrationCommand, step: 'push', output: migrationResult.output }
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
throw new ValidationError(`Unsupported database type: ${dbType}. Must be 'postgresql' or 'mongodb'.`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Return success result
|
|
80
|
-
return {
|
|
81
|
-
success: true,
|
|
82
|
-
dbType,
|
|
83
|
-
stage,
|
|
84
|
-
command: migrationCommand,
|
|
85
|
-
message: 'Database migration completed successfully',
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Validate execution parameters
|
|
91
|
-
* @private
|
|
92
|
-
*/
|
|
93
|
-
_validateParams({ dbType, stage }) {
|
|
94
|
-
if (!dbType) {
|
|
95
|
-
throw new ValidationError('dbType is required');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (typeof dbType !== 'string') {
|
|
99
|
-
throw new ValidationError('dbType must be a string');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!stage) {
|
|
103
|
-
throw new ValidationError('stage is required');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (typeof stage !== 'string') {
|
|
107
|
-
throw new ValidationError('stage must be a string');
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Custom error for migration failures
|
|
114
|
-
*/
|
|
115
|
-
class MigrationError extends Error {
|
|
116
|
-
constructor(message, context = {}) {
|
|
117
|
-
super(message);
|
|
118
|
-
this.name = 'MigrationError';
|
|
119
|
-
this.context = context;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Custom error for validation failures
|
|
125
|
-
*/
|
|
126
|
-
class ValidationError extends Error {
|
|
127
|
-
constructor(message) {
|
|
128
|
-
super(message);
|
|
129
|
-
this.name = 'ValidationError';
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
module.exports = {
|
|
134
|
-
RunDatabaseMigrationUseCase,
|
|
135
|
-
MigrationError,
|
|
136
|
-
ValidationError,
|
|
137
|
-
};
|