pgpm 4.7.9 → 4.8.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/commands/export.js +226 -82
- package/esm/commands/export.js +227 -83
- package/package.json +4 -3
package/commands/export.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@pgpmjs/core");
|
|
4
|
+
const export_1 = require("@pgpmjs/export");
|
|
4
5
|
const env_1 = require("@pgpmjs/env");
|
|
5
6
|
const types_1 = require("@pgpmjs/types");
|
|
6
7
|
const path_1 = require("path");
|
|
@@ -14,13 +15,19 @@ Export Command:
|
|
|
14
15
|
|
|
15
16
|
Options:
|
|
16
17
|
--help, -h Show this help message
|
|
18
|
+
--graphql-endpoint <url> GraphQL endpoint for meta/services data (enables GraphQL mode)
|
|
19
|
+
--migrate-endpoint <url> GraphQL endpoint for db_migrate data (optional, for sql_actions)
|
|
20
|
+
--migrate-host <host> Host header for migrate endpoint (e.g. db_migrate.localhost:3000)
|
|
21
|
+
--migrate-headers <json> Extra headers for migrate endpoint as JSON (e.g. '{"X-Schemata":"db_migrate"}')
|
|
22
|
+
--token <token> Bearer token for GraphQL authentication
|
|
17
23
|
--author <name> Project author (default: from git config)
|
|
18
24
|
--extensionName <name> Extension name
|
|
19
25
|
--metaExtensionName <name> Meta extension name (default: svc)
|
|
20
26
|
--cwd <directory> Working directory (default: current directory)
|
|
21
27
|
|
|
22
28
|
Examples:
|
|
23
|
-
pgpm export Export migrations from selected database
|
|
29
|
+
pgpm export Export migrations from selected database (SQL mode)
|
|
30
|
+
pgpm export --graphql-endpoint 'http://[::1]:3002/graphql' --migrate-endpoint 'http://[::1]:3000/graphql' --migrate-host db_migrate.localhost:3000
|
|
24
31
|
`;
|
|
25
32
|
exports.default = async (argv, prompter, _options) => {
|
|
26
33
|
// Show usage if explicitly requested
|
|
@@ -33,91 +40,228 @@ exports.default = async (argv, prompter, _options) => {
|
|
|
33
40
|
const project = new core_1.PgpmPackage(cwd);
|
|
34
41
|
project.ensureWorkspace();
|
|
35
42
|
project.resetCwd(project.workspacePath);
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
const graphqlEndpoint = argv['graphql-endpoint'] || argv.graphqlEndpoint;
|
|
44
|
+
const migrateEndpoint = argv['migrate-endpoint'] || argv.migrateEndpoint;
|
|
45
|
+
const migrateHost = argv['migrate-host'] || argv.migrateHost;
|
|
46
|
+
const migrateHeadersRaw = argv['migrate-headers'] || argv.migrateHeaders;
|
|
47
|
+
const token = argv.token;
|
|
48
|
+
if (graphqlEndpoint) {
|
|
49
|
+
// =========================================================================
|
|
50
|
+
// GraphQL export mode
|
|
51
|
+
// =========================================================================
|
|
52
|
+
console.log(`GraphQL export mode: ${graphqlEndpoint}`);
|
|
53
|
+
const metaClient = new export_1.GraphQLClient({
|
|
54
|
+
endpoint: graphqlEndpoint,
|
|
55
|
+
token,
|
|
56
|
+
headers: { 'X-Meta-Schema': 'true' }
|
|
57
|
+
});
|
|
58
|
+
// Fetch databases via GraphQL
|
|
59
|
+
const dbRows = await metaClient.fetchAllNodes('databases', 'id\nname');
|
|
60
|
+
if (!dbRows.length) {
|
|
61
|
+
console.log('No databases found via GraphQL.');
|
|
62
|
+
prompter.close();
|
|
63
|
+
return;
|
|
52
64
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
const { database_ids: selectedDatabaseName } = await prompter.prompt(argv, [
|
|
66
|
+
{
|
|
67
|
+
type: 'list',
|
|
68
|
+
name: 'database_ids',
|
|
69
|
+
message: 'Select database',
|
|
70
|
+
options: dbRows.map(db => db.name),
|
|
71
|
+
required: true
|
|
72
|
+
}
|
|
73
|
+
]);
|
|
74
|
+
const selectedDatabase = dbRows.find(db => db.name === selectedDatabaseName);
|
|
75
|
+
if (!selectedDatabase) {
|
|
76
|
+
console.log('Database not found.');
|
|
77
|
+
prompter.close();
|
|
78
|
+
return;
|
|
67
79
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
80
|
+
const databaseId = selectedDatabase.id;
|
|
81
|
+
const { author, extensionName, metaExtensionName } = await prompter.prompt(argv, [
|
|
82
|
+
{
|
|
83
|
+
type: 'text',
|
|
84
|
+
name: 'author',
|
|
85
|
+
message: 'Project author',
|
|
86
|
+
default: `${username} <${email}>`,
|
|
87
|
+
required: true
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'text',
|
|
91
|
+
name: 'extensionName',
|
|
92
|
+
message: 'Extension name',
|
|
93
|
+
default: selectedDatabaseName,
|
|
94
|
+
required: true
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
type: 'text',
|
|
98
|
+
name: 'metaExtensionName',
|
|
99
|
+
message: 'Meta extension name',
|
|
100
|
+
default: `${selectedDatabaseName}-service`,
|
|
101
|
+
required: true
|
|
102
|
+
}
|
|
103
|
+
]);
|
|
104
|
+
// Fetch schemas via GraphQL
|
|
105
|
+
const schemaRows = await metaClient.fetchAllNodes('schemas', 'id\nschemaName\nname', { databaseId });
|
|
106
|
+
// Convert camelCase to snake_case for schema rows
|
|
107
|
+
const pgSchemaRows = schemaRows.map(s => (0, export_1.graphqlRowToPostgresRow)(s));
|
|
108
|
+
// Normalize comma-separated schema_names string into an array for checkbox override
|
|
109
|
+
if (typeof argv.schema_names === 'string') {
|
|
110
|
+
argv.schema_names = argv.schema_names.split(',').map((s) => s.trim()).filter(Boolean);
|
|
96
111
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
const { schema_names } = await prompter.prompt(argv, [
|
|
113
|
+
{
|
|
114
|
+
type: 'checkbox',
|
|
115
|
+
name: 'schema_names',
|
|
116
|
+
message: 'Select schema_name(s)',
|
|
117
|
+
options: pgSchemaRows.map(s => s.schema_name),
|
|
118
|
+
default: pgSchemaRows.map(s => s.schema_name),
|
|
119
|
+
required: true
|
|
120
|
+
}
|
|
121
|
+
]);
|
|
122
|
+
const outdir = (0, path_1.resolve)(project.workspacePath, 'packages/');
|
|
123
|
+
await (0, export_1.exportGraphQL)({
|
|
124
|
+
project,
|
|
125
|
+
metaEndpoint: graphqlEndpoint,
|
|
126
|
+
migrateEndpoint,
|
|
127
|
+
migrateHeaders: {
|
|
128
|
+
...(migrateHost ? { Host: migrateHost } : {}),
|
|
129
|
+
...(migrateHeadersRaw ? JSON.parse(migrateHeadersRaw) : {})
|
|
130
|
+
},
|
|
131
|
+
token,
|
|
132
|
+
headers: { 'X-Meta-Schema': 'true' },
|
|
133
|
+
databaseId,
|
|
134
|
+
databaseName: selectedDatabaseName,
|
|
135
|
+
schema_names,
|
|
136
|
+
schemas: pgSchemaRows,
|
|
137
|
+
author,
|
|
138
|
+
outdir,
|
|
139
|
+
extensionName,
|
|
140
|
+
metaExtensionName,
|
|
141
|
+
prompter,
|
|
142
|
+
argv,
|
|
143
|
+
username
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// =========================================================================
|
|
148
|
+
// SQL export mode (original behavior)
|
|
149
|
+
// =========================================================================
|
|
150
|
+
const options = (0, env_1.getEnvOptions)();
|
|
151
|
+
const db = await (0, pg_cache_1.getPgPool)({
|
|
152
|
+
database: 'postgres'
|
|
153
|
+
});
|
|
154
|
+
const databasesResult = await db.query(`
|
|
155
|
+
SELECT datname FROM pg_catalog.pg_database
|
|
156
|
+
WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
|
|
157
|
+
AND datname !~ '^pg_';
|
|
158
|
+
`);
|
|
159
|
+
// If --database_ids is provided but --databases is not, auto-detect
|
|
160
|
+
// which postgres database contains the matching metaschema database name.
|
|
161
|
+
if (argv.database_ids && !argv.databases) {
|
|
162
|
+
const targetName = argv.database_ids;
|
|
163
|
+
for (const row of databasesResult.rows) {
|
|
164
|
+
try {
|
|
165
|
+
const candidatePool = await (0, pg_cache_1.getPgPool)({ database: row.datname });
|
|
166
|
+
const check = await candidatePool.query(`SELECT name FROM metaschema_public.database WHERE name = $1 LIMIT 1`, [targetName]);
|
|
167
|
+
if (check.rows.length > 0) {
|
|
168
|
+
argv.databases = row.datname;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Skip databases that don't have metaschema_public
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!argv.databases) {
|
|
177
|
+
console.error(`Could not find database "${targetName}" in any postgres database.`);
|
|
178
|
+
prompter.close();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const { databases: dbname } = await prompter.prompt(argv, [
|
|
183
|
+
{
|
|
184
|
+
type: 'list',
|
|
185
|
+
name: 'databases',
|
|
186
|
+
message: 'Select a database',
|
|
187
|
+
options: databasesResult.rows.map(row => row.datname),
|
|
188
|
+
required: true
|
|
189
|
+
}
|
|
190
|
+
]);
|
|
191
|
+
const selectedDb = await (0, pg_cache_1.getPgPool)({
|
|
192
|
+
database: dbname
|
|
193
|
+
});
|
|
194
|
+
const dbsResult = await selectedDb.query(`
|
|
195
|
+
SELECT id, name FROM metaschema_public.database;
|
|
196
|
+
`);
|
|
197
|
+
const { database_ids: selectedDatabaseName } = await prompter.prompt(argv, [
|
|
198
|
+
{
|
|
199
|
+
type: 'list',
|
|
200
|
+
name: 'database_ids',
|
|
201
|
+
message: 'Select database_id',
|
|
202
|
+
options: dbsResult.rows.map(db => db.name),
|
|
203
|
+
required: true
|
|
204
|
+
}
|
|
205
|
+
]);
|
|
206
|
+
const selectedDatabase = dbsResult.rows.find(db => db.name === selectedDatabaseName);
|
|
207
|
+
const dbInfo = {
|
|
208
|
+
dbname,
|
|
209
|
+
databaseName: selectedDatabaseName,
|
|
210
|
+
database_ids: [selectedDatabase.id]
|
|
211
|
+
};
|
|
212
|
+
const { author, extensionName, metaExtensionName } = await prompter.prompt(argv, [
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
name: 'author',
|
|
216
|
+
message: 'Project author',
|
|
217
|
+
default: `${username} <${email}>`,
|
|
218
|
+
required: true
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: 'text',
|
|
222
|
+
name: 'extensionName',
|
|
223
|
+
message: 'Extension name',
|
|
224
|
+
default: selectedDatabaseName || dbname,
|
|
225
|
+
required: true
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
type: 'text',
|
|
229
|
+
name: 'metaExtensionName',
|
|
230
|
+
message: 'Meta extension name',
|
|
231
|
+
default: `${selectedDatabaseName || dbname}-service`,
|
|
232
|
+
required: true
|
|
233
|
+
}
|
|
234
|
+
]);
|
|
235
|
+
const schemasResult = await selectedDb.query(`SELECT * FROM metaschema_public.schema WHERE database_id = $1`, [dbInfo.database_ids[0]]);
|
|
236
|
+
// Normalize comma-separated schema_names string into an array for checkbox override
|
|
237
|
+
if (typeof argv.schema_names === 'string') {
|
|
238
|
+
argv.schema_names = argv.schema_names.split(',').map((s) => s.trim()).filter(Boolean);
|
|
107
239
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
240
|
+
const { schema_names } = await prompter.prompt(argv, [
|
|
241
|
+
{
|
|
242
|
+
type: 'checkbox',
|
|
243
|
+
name: 'schema_names',
|
|
244
|
+
message: 'Select schema_name(s)',
|
|
245
|
+
options: schemasResult.rows.map(s => s.schema_name),
|
|
246
|
+
default: schemasResult.rows.map(s => s.schema_name),
|
|
247
|
+
required: true
|
|
248
|
+
}
|
|
249
|
+
]);
|
|
250
|
+
const outdir = (0, path_1.resolve)(project.workspacePath, 'packages/');
|
|
251
|
+
await (0, export_1.exportMigrations)({
|
|
252
|
+
project,
|
|
253
|
+
options,
|
|
254
|
+
dbInfo,
|
|
255
|
+
author,
|
|
256
|
+
schema_names,
|
|
257
|
+
outdir,
|
|
258
|
+
extensionName,
|
|
259
|
+
metaExtensionName,
|
|
260
|
+
prompter,
|
|
261
|
+
argv,
|
|
262
|
+
username
|
|
263
|
+
});
|
|
264
|
+
}
|
|
121
265
|
prompter.close();
|
|
122
266
|
console.log(`
|
|
123
267
|
|
package/esm/commands/export.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PgpmPackage } from '@pgpmjs/core';
|
|
2
|
+
import { exportMigrations, exportGraphQL, GraphQLClient, graphqlRowToPostgresRow } from '@pgpmjs/export';
|
|
2
3
|
import { getEnvOptions } from '@pgpmjs/env';
|
|
3
4
|
import { getGitConfigInfo } from '@pgpmjs/types';
|
|
4
5
|
import { resolve } from 'path';
|
|
@@ -12,13 +13,19 @@ Export Command:
|
|
|
12
13
|
|
|
13
14
|
Options:
|
|
14
15
|
--help, -h Show this help message
|
|
16
|
+
--graphql-endpoint <url> GraphQL endpoint for meta/services data (enables GraphQL mode)
|
|
17
|
+
--migrate-endpoint <url> GraphQL endpoint for db_migrate data (optional, for sql_actions)
|
|
18
|
+
--migrate-host <host> Host header for migrate endpoint (e.g. db_migrate.localhost:3000)
|
|
19
|
+
--migrate-headers <json> Extra headers for migrate endpoint as JSON (e.g. '{"X-Schemata":"db_migrate"}')
|
|
20
|
+
--token <token> Bearer token for GraphQL authentication
|
|
15
21
|
--author <name> Project author (default: from git config)
|
|
16
22
|
--extensionName <name> Extension name
|
|
17
23
|
--metaExtensionName <name> Meta extension name (default: svc)
|
|
18
24
|
--cwd <directory> Working directory (default: current directory)
|
|
19
25
|
|
|
20
26
|
Examples:
|
|
21
|
-
pgpm export Export migrations from selected database
|
|
27
|
+
pgpm export Export migrations from selected database (SQL mode)
|
|
28
|
+
pgpm export --graphql-endpoint 'http://[::1]:3002/graphql' --migrate-endpoint 'http://[::1]:3000/graphql' --migrate-host db_migrate.localhost:3000
|
|
22
29
|
`;
|
|
23
30
|
export default async (argv, prompter, _options) => {
|
|
24
31
|
// Show usage if explicitly requested
|
|
@@ -31,91 +38,228 @@ export default async (argv, prompter, _options) => {
|
|
|
31
38
|
const project = new PgpmPackage(cwd);
|
|
32
39
|
project.ensureWorkspace();
|
|
33
40
|
project.resetCwd(project.workspacePath);
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
const graphqlEndpoint = argv['graphql-endpoint'] || argv.graphqlEndpoint;
|
|
42
|
+
const migrateEndpoint = argv['migrate-endpoint'] || argv.migrateEndpoint;
|
|
43
|
+
const migrateHost = argv['migrate-host'] || argv.migrateHost;
|
|
44
|
+
const migrateHeadersRaw = argv['migrate-headers'] || argv.migrateHeaders;
|
|
45
|
+
const token = argv.token;
|
|
46
|
+
if (graphqlEndpoint) {
|
|
47
|
+
// =========================================================================
|
|
48
|
+
// GraphQL export mode
|
|
49
|
+
// =========================================================================
|
|
50
|
+
console.log(`GraphQL export mode: ${graphqlEndpoint}`);
|
|
51
|
+
const metaClient = new GraphQLClient({
|
|
52
|
+
endpoint: graphqlEndpoint,
|
|
53
|
+
token,
|
|
54
|
+
headers: { 'X-Meta-Schema': 'true' }
|
|
55
|
+
});
|
|
56
|
+
// Fetch databases via GraphQL
|
|
57
|
+
const dbRows = await metaClient.fetchAllNodes('databases', 'id\nname');
|
|
58
|
+
if (!dbRows.length) {
|
|
59
|
+
console.log('No databases found via GraphQL.');
|
|
60
|
+
prompter.close();
|
|
61
|
+
return;
|
|
50
62
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const { database_ids: selectedDatabaseName } = await prompter.prompt(argv, [
|
|
64
|
+
{
|
|
65
|
+
type: 'list',
|
|
66
|
+
name: 'database_ids',
|
|
67
|
+
message: 'Select database',
|
|
68
|
+
options: dbRows.map(db => db.name),
|
|
69
|
+
required: true
|
|
70
|
+
}
|
|
71
|
+
]);
|
|
72
|
+
const selectedDatabase = dbRows.find(db => db.name === selectedDatabaseName);
|
|
73
|
+
if (!selectedDatabase) {
|
|
74
|
+
console.log('Database not found.');
|
|
75
|
+
prompter.close();
|
|
76
|
+
return;
|
|
65
77
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
const databaseId = selectedDatabase.id;
|
|
79
|
+
const { author, extensionName, metaExtensionName } = await prompter.prompt(argv, [
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
name: 'author',
|
|
83
|
+
message: 'Project author',
|
|
84
|
+
default: `${username} <${email}>`,
|
|
85
|
+
required: true
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: 'text',
|
|
89
|
+
name: 'extensionName',
|
|
90
|
+
message: 'Extension name',
|
|
91
|
+
default: selectedDatabaseName,
|
|
92
|
+
required: true
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: 'text',
|
|
96
|
+
name: 'metaExtensionName',
|
|
97
|
+
message: 'Meta extension name',
|
|
98
|
+
default: `${selectedDatabaseName}-service`,
|
|
99
|
+
required: true
|
|
100
|
+
}
|
|
101
|
+
]);
|
|
102
|
+
// Fetch schemas via GraphQL
|
|
103
|
+
const schemaRows = await metaClient.fetchAllNodes('schemas', 'id\nschemaName\nname', { databaseId });
|
|
104
|
+
// Convert camelCase to snake_case for schema rows
|
|
105
|
+
const pgSchemaRows = schemaRows.map(s => graphqlRowToPostgresRow(s));
|
|
106
|
+
// Normalize comma-separated schema_names string into an array for checkbox override
|
|
107
|
+
if (typeof argv.schema_names === 'string') {
|
|
108
|
+
argv.schema_names = argv.schema_names.split(',').map((s) => s.trim()).filter(Boolean);
|
|
94
109
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
const { schema_names } = await prompter.prompt(argv, [
|
|
111
|
+
{
|
|
112
|
+
type: 'checkbox',
|
|
113
|
+
name: 'schema_names',
|
|
114
|
+
message: 'Select schema_name(s)',
|
|
115
|
+
options: pgSchemaRows.map(s => s.schema_name),
|
|
116
|
+
default: pgSchemaRows.map(s => s.schema_name),
|
|
117
|
+
required: true
|
|
118
|
+
}
|
|
119
|
+
]);
|
|
120
|
+
const outdir = resolve(project.workspacePath, 'packages/');
|
|
121
|
+
await exportGraphQL({
|
|
122
|
+
project,
|
|
123
|
+
metaEndpoint: graphqlEndpoint,
|
|
124
|
+
migrateEndpoint,
|
|
125
|
+
migrateHeaders: {
|
|
126
|
+
...(migrateHost ? { Host: migrateHost } : {}),
|
|
127
|
+
...(migrateHeadersRaw ? JSON.parse(migrateHeadersRaw) : {})
|
|
128
|
+
},
|
|
129
|
+
token,
|
|
130
|
+
headers: { 'X-Meta-Schema': 'true' },
|
|
131
|
+
databaseId,
|
|
132
|
+
databaseName: selectedDatabaseName,
|
|
133
|
+
schema_names,
|
|
134
|
+
schemas: pgSchemaRows,
|
|
135
|
+
author,
|
|
136
|
+
outdir,
|
|
137
|
+
extensionName,
|
|
138
|
+
metaExtensionName,
|
|
139
|
+
prompter,
|
|
140
|
+
argv,
|
|
141
|
+
username
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// =========================================================================
|
|
146
|
+
// SQL export mode (original behavior)
|
|
147
|
+
// =========================================================================
|
|
148
|
+
const options = getEnvOptions();
|
|
149
|
+
const db = await getPgPool({
|
|
150
|
+
database: 'postgres'
|
|
151
|
+
});
|
|
152
|
+
const databasesResult = await db.query(`
|
|
153
|
+
SELECT datname FROM pg_catalog.pg_database
|
|
154
|
+
WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
|
|
155
|
+
AND datname !~ '^pg_';
|
|
156
|
+
`);
|
|
157
|
+
// If --database_ids is provided but --databases is not, auto-detect
|
|
158
|
+
// which postgres database contains the matching metaschema database name.
|
|
159
|
+
if (argv.database_ids && !argv.databases) {
|
|
160
|
+
const targetName = argv.database_ids;
|
|
161
|
+
for (const row of databasesResult.rows) {
|
|
162
|
+
try {
|
|
163
|
+
const candidatePool = await getPgPool({ database: row.datname });
|
|
164
|
+
const check = await candidatePool.query(`SELECT name FROM metaschema_public.database WHERE name = $1 LIMIT 1`, [targetName]);
|
|
165
|
+
if (check.rows.length > 0) {
|
|
166
|
+
argv.databases = row.datname;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// Skip databases that don't have metaschema_public
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (!argv.databases) {
|
|
175
|
+
console.error(`Could not find database "${targetName}" in any postgres database.`);
|
|
176
|
+
prompter.close();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const { databases: dbname } = await prompter.prompt(argv, [
|
|
181
|
+
{
|
|
182
|
+
type: 'list',
|
|
183
|
+
name: 'databases',
|
|
184
|
+
message: 'Select a database',
|
|
185
|
+
options: databasesResult.rows.map(row => row.datname),
|
|
186
|
+
required: true
|
|
187
|
+
}
|
|
188
|
+
]);
|
|
189
|
+
const selectedDb = await getPgPool({
|
|
190
|
+
database: dbname
|
|
191
|
+
});
|
|
192
|
+
const dbsResult = await selectedDb.query(`
|
|
193
|
+
SELECT id, name FROM metaschema_public.database;
|
|
194
|
+
`);
|
|
195
|
+
const { database_ids: selectedDatabaseName } = await prompter.prompt(argv, [
|
|
196
|
+
{
|
|
197
|
+
type: 'list',
|
|
198
|
+
name: 'database_ids',
|
|
199
|
+
message: 'Select database_id',
|
|
200
|
+
options: dbsResult.rows.map(db => db.name),
|
|
201
|
+
required: true
|
|
202
|
+
}
|
|
203
|
+
]);
|
|
204
|
+
const selectedDatabase = dbsResult.rows.find(db => db.name === selectedDatabaseName);
|
|
205
|
+
const dbInfo = {
|
|
206
|
+
dbname,
|
|
207
|
+
databaseName: selectedDatabaseName,
|
|
208
|
+
database_ids: [selectedDatabase.id]
|
|
209
|
+
};
|
|
210
|
+
const { author, extensionName, metaExtensionName } = await prompter.prompt(argv, [
|
|
211
|
+
{
|
|
212
|
+
type: 'text',
|
|
213
|
+
name: 'author',
|
|
214
|
+
message: 'Project author',
|
|
215
|
+
default: `${username} <${email}>`,
|
|
216
|
+
required: true
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
type: 'text',
|
|
220
|
+
name: 'extensionName',
|
|
221
|
+
message: 'Extension name',
|
|
222
|
+
default: selectedDatabaseName || dbname,
|
|
223
|
+
required: true
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
type: 'text',
|
|
227
|
+
name: 'metaExtensionName',
|
|
228
|
+
message: 'Meta extension name',
|
|
229
|
+
default: `${selectedDatabaseName || dbname}-service`,
|
|
230
|
+
required: true
|
|
231
|
+
}
|
|
232
|
+
]);
|
|
233
|
+
const schemasResult = await selectedDb.query(`SELECT * FROM metaschema_public.schema WHERE database_id = $1`, [dbInfo.database_ids[0]]);
|
|
234
|
+
// Normalize comma-separated schema_names string into an array for checkbox override
|
|
235
|
+
if (typeof argv.schema_names === 'string') {
|
|
236
|
+
argv.schema_names = argv.schema_names.split(',').map((s) => s.trim()).filter(Boolean);
|
|
105
237
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
238
|
+
const { schema_names } = await prompter.prompt(argv, [
|
|
239
|
+
{
|
|
240
|
+
type: 'checkbox',
|
|
241
|
+
name: 'schema_names',
|
|
242
|
+
message: 'Select schema_name(s)',
|
|
243
|
+
options: schemasResult.rows.map(s => s.schema_name),
|
|
244
|
+
default: schemasResult.rows.map(s => s.schema_name),
|
|
245
|
+
required: true
|
|
246
|
+
}
|
|
247
|
+
]);
|
|
248
|
+
const outdir = resolve(project.workspacePath, 'packages/');
|
|
249
|
+
await exportMigrations({
|
|
250
|
+
project,
|
|
251
|
+
options,
|
|
252
|
+
dbInfo,
|
|
253
|
+
author,
|
|
254
|
+
schema_names,
|
|
255
|
+
outdir,
|
|
256
|
+
extensionName,
|
|
257
|
+
metaExtensionName,
|
|
258
|
+
prompter,
|
|
259
|
+
argv,
|
|
260
|
+
username
|
|
261
|
+
});
|
|
262
|
+
}
|
|
119
263
|
prompter.close();
|
|
120
264
|
console.log(`
|
|
121
265
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgpm",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PostgreSQL Package Manager - Database migration and package management CLI",
|
|
6
6
|
"main": "index.js",
|
|
@@ -46,8 +46,9 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@inquirerer/utils": "^3.3.4",
|
|
49
|
-
"@pgpmjs/core": "^6.
|
|
49
|
+
"@pgpmjs/core": "^6.9.0",
|
|
50
50
|
"@pgpmjs/env": "^2.15.3",
|
|
51
|
+
"@pgpmjs/export": "^0.1.0",
|
|
51
52
|
"@pgpmjs/logger": "^2.4.3",
|
|
52
53
|
"@pgpmjs/types": "^2.19.3",
|
|
53
54
|
"@pgsql/quotes": "^17.1.0",
|
|
@@ -75,5 +76,5 @@
|
|
|
75
76
|
"pg",
|
|
76
77
|
"pgsql"
|
|
77
78
|
],
|
|
78
|
-
"gitHead": "
|
|
79
|
+
"gitHead": "f42b4df50a1cdbcb03b3c52200d953b5e7b24507"
|
|
79
80
|
}
|