@friggframework/core 2.0.0--canary.454.25d396a.0 → 2.0.0--canary.463.62579dd.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/prisma.js +2 -2
- package/handlers/backend-utils.js +41 -29
- package/package.json +10 -16
- package/prisma-mongodb/schema.prisma +20 -22
- package/prisma-postgresql/schema.prisma +14 -16
- 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/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'`
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
const {
|
|
20
20
|
getModulesDefinitionFromIntegrationClasses,
|
|
21
21
|
} = require('../integrations/utils/map-integration-dto');
|
|
22
|
+
const { loadAppDefinition } = require('./app-definition-loader');
|
|
22
23
|
|
|
23
24
|
const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
24
25
|
const router = Router();
|
|
@@ -50,37 +51,29 @@ const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
const initializeRepositories = () => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const createModuleFactoryWithDefinitions = (
|
|
61
|
-
moduleRepository,
|
|
62
|
-
integrationClasses
|
|
63
|
-
) => {
|
|
64
|
-
const moduleDefinitions =
|
|
65
|
-
getModulesDefinitionFromIntegrationClasses(integrationClasses);
|
|
66
|
-
|
|
67
|
-
return new ModuleFactory({
|
|
68
|
-
moduleRepository,
|
|
69
|
-
moduleDefinitions,
|
|
70
|
-
});
|
|
54
|
+
return {
|
|
55
|
+
processRepository: createProcessRepository(),
|
|
56
|
+
integrationRepository: createIntegrationRepository(),
|
|
57
|
+
moduleRepository: createModuleRepository(),
|
|
58
|
+
};
|
|
71
59
|
};
|
|
72
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Load hydrated integration instance for webhook events WITH integrationId.
|
|
63
|
+
* Must load app definition to find all integration classes, then match
|
|
64
|
+
* against the integration record's type.
|
|
65
|
+
*/
|
|
73
66
|
const loadIntegrationForWebhook = async (integrationId) => {
|
|
74
|
-
const { loadAppDefinition } = require('./app-definition-loader');
|
|
75
67
|
const { integrations: integrationClasses } = loadAppDefinition();
|
|
68
|
+
const { integrationRepository, moduleRepository } = initializeRepositories();
|
|
76
69
|
|
|
77
|
-
const
|
|
78
|
-
initializeRepositories();
|
|
79
|
-
|
|
80
|
-
const moduleFactory = createModuleFactoryWithDefinitions(
|
|
81
|
-
moduleRepository,
|
|
70
|
+
const moduleDefinitions = getModulesDefinitionFromIntegrationClasses(
|
|
82
71
|
integrationClasses
|
|
83
72
|
);
|
|
73
|
+
const moduleFactory = new ModuleFactory({
|
|
74
|
+
moduleRepository,
|
|
75
|
+
moduleDefinitions,
|
|
76
|
+
});
|
|
84
77
|
|
|
85
78
|
const getIntegrationInstance = new GetIntegrationInstance({
|
|
86
79
|
integrationRepository,
|
|
@@ -92,19 +85,42 @@ const loadIntegrationForWebhook = async (integrationId) => {
|
|
|
92
85
|
integrationId
|
|
93
86
|
);
|
|
94
87
|
|
|
88
|
+
if (!integrationRecord) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`No integration found by the ID of ${integrationId}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
95
94
|
return await getIntegrationInstance.execute(
|
|
96
95
|
integrationId,
|
|
97
96
|
integrationRecord.userId
|
|
98
97
|
);
|
|
99
98
|
};
|
|
100
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Load hydrated integration instance for process-based events.
|
|
102
|
+
* Integration class is already known from queue worker context,
|
|
103
|
+
* so we receive it as a parameter rather than loading all classes.
|
|
104
|
+
*/
|
|
101
105
|
const loadIntegrationForProcess = async (processId, integrationClass) => {
|
|
106
|
+
if (!integrationClass) {
|
|
107
|
+
throw new Error('integrationClass parameter is required');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!processId) {
|
|
111
|
+
throw new Error('processId is required in queue message data');
|
|
112
|
+
}
|
|
113
|
+
|
|
102
114
|
const { processRepository, integrationRepository, moduleRepository } =
|
|
103
115
|
initializeRepositories();
|
|
104
116
|
|
|
105
|
-
const
|
|
117
|
+
const moduleDefinitions = getModulesDefinitionFromIntegrationClasses([
|
|
106
118
|
integrationClass,
|
|
107
119
|
]);
|
|
120
|
+
const moduleFactory = new ModuleFactory({
|
|
121
|
+
moduleRepository,
|
|
122
|
+
moduleDefinitions,
|
|
123
|
+
});
|
|
108
124
|
|
|
109
125
|
const getIntegrationInstance = new GetIntegrationInstance({
|
|
110
126
|
integrationRepository,
|
|
@@ -112,10 +128,6 @@ const loadIntegrationForProcess = async (processId, integrationClass) => {
|
|
|
112
128
|
moduleFactory,
|
|
113
129
|
});
|
|
114
130
|
|
|
115
|
-
if (!processId) {
|
|
116
|
-
throw new Error('processId is required in queue message data');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
131
|
const process = await processRepository.findById(processId);
|
|
120
132
|
|
|
121
133
|
if (!process) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.463.62579dd.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/boom": "^10.0.1",
|
|
7
|
-
"
|
|
7
|
+
"@prisma/client": "^6.16.3",
|
|
8
8
|
"bcryptjs": "^2.4.3",
|
|
9
9
|
"body-parser": "^1.20.2",
|
|
10
|
-
"chalk": "^4.1.2",
|
|
11
10
|
"common-tags": "^1.8.2",
|
|
12
11
|
"cors": "^2.8.5",
|
|
13
12
|
"dotenv": "^16.4.7",
|
|
@@ -23,22 +22,17 @@
|
|
|
23
22
|
"uuid": "^9.0.1"
|
|
24
23
|
},
|
|
25
24
|
"peerDependencies": {
|
|
26
|
-
"
|
|
27
|
-
"prisma": "^6.16.3"
|
|
25
|
+
"aws-sdk": "^2.1200.0"
|
|
28
26
|
},
|
|
29
27
|
"peerDependenciesMeta": {
|
|
30
|
-
"
|
|
31
|
-
"optional": true
|
|
32
|
-
},
|
|
33
|
-
"prisma": {
|
|
28
|
+
"aws-sdk": {
|
|
34
29
|
"optional": true
|
|
35
30
|
}
|
|
36
31
|
},
|
|
37
32
|
"devDependencies": {
|
|
38
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
39
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
40
|
-
"@friggframework/test": "2.0.0--canary.
|
|
41
|
-
"@prisma/client": "^6.17.0",
|
|
33
|
+
"@friggframework/eslint-config": "2.0.0--canary.463.62579dd.0",
|
|
34
|
+
"@friggframework/prettier-config": "2.0.0--canary.463.62579dd.0",
|
|
35
|
+
"@friggframework/test": "2.0.0--canary.463.62579dd.0",
|
|
42
36
|
"@types/lodash": "4.17.15",
|
|
43
37
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
44
38
|
"chai": "^4.3.6",
|
|
@@ -48,7 +42,7 @@
|
|
|
48
42
|
"eslint-plugin-promise": "^7.0.0",
|
|
49
43
|
"jest": "^29.7.0",
|
|
50
44
|
"prettier": "^2.7.1",
|
|
51
|
-
"prisma": "^6.
|
|
45
|
+
"prisma": "^6.16.3",
|
|
52
46
|
"sinon": "^16.1.1",
|
|
53
47
|
"typescript": "^5.0.2"
|
|
54
48
|
},
|
|
@@ -60,7 +54,7 @@
|
|
|
60
54
|
"prisma:generate": "npm run prisma:generate:mongo && npm run prisma:generate:postgres",
|
|
61
55
|
"prisma:push:mongo": "npx prisma db push --schema ./prisma-mongodb/schema.prisma",
|
|
62
56
|
"prisma:migrate:postgres": "npx prisma migrate dev --schema ./prisma-postgresql/schema.prisma",
|
|
63
|
-
"
|
|
57
|
+
"postinstall": "npm run prisma:generate"
|
|
64
58
|
},
|
|
65
59
|
"author": "",
|
|
66
60
|
"license": "MIT",
|
|
@@ -77,5 +71,5 @@
|
|
|
77
71
|
"publishConfig": {
|
|
78
72
|
"access": "public"
|
|
79
73
|
},
|
|
80
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "62579dd537cdc5d96c3d36e2441e56778c3a160a"
|
|
81
75
|
}
|
|
@@ -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())
|
|
@@ -89,12 +87,12 @@ model Token {
|
|
|
89
87
|
/// OAuth credentials and API tokens
|
|
90
88
|
/// All sensitive data encrypted with KMS at rest
|
|
91
89
|
model Credential {
|
|
92
|
-
id
|
|
93
|
-
userId
|
|
94
|
-
user
|
|
95
|
-
subType
|
|
90
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
91
|
+
userId String? @db.ObjectId
|
|
92
|
+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
93
|
+
subType String?
|
|
96
94
|
authIsValid Boolean?
|
|
97
|
-
externalId
|
|
95
|
+
externalId String?
|
|
98
96
|
|
|
99
97
|
// Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)
|
|
100
98
|
// Contains: access_token, refresh_token, domain, expires_in, token_type, etc.
|
|
@@ -127,11 +125,11 @@ model Entity {
|
|
|
127
125
|
updatedAt DateTime @updatedAt
|
|
128
126
|
|
|
129
127
|
// Relations - many-to-many with scalar lists
|
|
130
|
-
integrations
|
|
131
|
-
integrationIds
|
|
128
|
+
integrations Integration[] @relation("IntegrationEntities", fields: [integrationIds], references: [id])
|
|
129
|
+
integrationIds String[] @db.ObjectId
|
|
132
130
|
|
|
133
|
-
syncs
|
|
134
|
-
syncIds
|
|
131
|
+
syncs Sync[] @relation("SyncEntities", fields: [syncIds], references: [id])
|
|
132
|
+
syncIds String[] @db.ObjectId
|
|
135
133
|
|
|
136
134
|
dataIdentifiers DataIdentifier[]
|
|
137
135
|
associationObjects AssociationObject[]
|
|
@@ -155,7 +153,7 @@ model Integration {
|
|
|
155
153
|
status IntegrationStatus @default(ENABLED)
|
|
156
154
|
|
|
157
155
|
// Configuration and version
|
|
158
|
-
config Json?
|
|
156
|
+
config Json? // Integration configuration object
|
|
159
157
|
version String?
|
|
160
158
|
|
|
161
159
|
// Entity references (many-to-many via explicit scalar list)
|
|
@@ -322,11 +320,11 @@ model Association {
|
|
|
322
320
|
/// Association object entry
|
|
323
321
|
/// Replaces nested array structure in Mongoose
|
|
324
322
|
model AssociationObject {
|
|
325
|
-
id String
|
|
326
|
-
associationId String
|
|
327
|
-
association Association
|
|
328
|
-
entityId String
|
|
329
|
-
entity Entity
|
|
323
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
324
|
+
associationId String @db.ObjectId
|
|
325
|
+
association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)
|
|
326
|
+
entityId String @db.ObjectId
|
|
327
|
+
entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
|
|
330
328
|
objectType String
|
|
331
329
|
objId String
|
|
332
330
|
metadata Json? // Optional metadata
|
|
@@ -361,4 +359,4 @@ model WebsocketConnection {
|
|
|
361
359
|
|
|
362
360
|
@@index([connectionId])
|
|
363
361
|
@@map("WebsocketConnection")
|
|
364
|
-
}
|
|
362
|
+
}
|
|
@@ -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())
|
|
@@ -87,12 +85,12 @@ model Token {
|
|
|
87
85
|
/// OAuth credentials and API tokens
|
|
88
86
|
/// All sensitive data encrypted with KMS at rest
|
|
89
87
|
model Credential {
|
|
90
|
-
id
|
|
91
|
-
userId
|
|
92
|
-
user
|
|
93
|
-
subType
|
|
88
|
+
id Int @id @default(autoincrement())
|
|
89
|
+
userId Int?
|
|
90
|
+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
91
|
+
subType String?
|
|
94
92
|
authIsValid Boolean?
|
|
95
|
-
externalId
|
|
93
|
+
externalId String?
|
|
96
94
|
|
|
97
95
|
// Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)
|
|
98
96
|
// Contains: access_token, refresh_token, domain, expires_in, token_type, etc.
|
|
@@ -148,7 +146,7 @@ model Integration {
|
|
|
148
146
|
status IntegrationStatus @default(ENABLED)
|
|
149
147
|
|
|
150
148
|
// Configuration and version
|
|
151
|
-
config Json?
|
|
149
|
+
config Json? // Integration configuration object
|
|
152
150
|
version String?
|
|
153
151
|
|
|
154
152
|
// Entity references (many-to-many via implicit join table)
|
|
@@ -227,11 +225,11 @@ model Sync {
|
|
|
227
225
|
/// Data identifier for sync operations
|
|
228
226
|
/// Replaces nested array structure in Mongoose
|
|
229
227
|
model DataIdentifier {
|
|
230
|
-
id Int
|
|
228
|
+
id Int @id @default(autoincrement())
|
|
231
229
|
syncId Int?
|
|
232
|
-
sync Sync?
|
|
230
|
+
sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)
|
|
233
231
|
entityId Int
|
|
234
|
-
entity Entity
|
|
232
|
+
entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
|
|
235
233
|
|
|
236
234
|
// Identifier data (can be any structure)
|
|
237
235
|
idData Json
|
|
@@ -334,7 +332,7 @@ model Process {
|
|
|
334
332
|
|
|
335
333
|
/// Generic state storage
|
|
336
334
|
model State {
|
|
337
|
-
id Int
|
|
335
|
+
id Int @id @default(autoincrement())
|
|
338
336
|
state Json?
|
|
339
337
|
}
|
|
340
338
|
|
|
@@ -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
|
-
};
|