@apso/cli 0.12.0 → 0.13.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/dist/commands/migrate.js +26 -32
- package/dist/lib/generators/go.d.ts +1 -1
- package/dist/lib/generators/go.js +46 -4
- package/dist/lib/generators/python.d.ts +1 -1
- package/dist/lib/generators/python.js +40 -4
- package/dist/lib/templates/entities/entity-col-uuid.eta +4 -3
- package/dist/lib/templates/entities/entity.eta +1 -2
- package/dist/lib/types/generator.d.ts +8 -0
- package/npm-shrinkwrap.json +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/dist/commands/migrate.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const path = tslib_1.__importStar(require("path"));
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
6
|
const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
|
|
6
7
|
const apsorc_parser_1 = require("../lib/apsorc-parser");
|
|
7
|
-
const snapshot_1 = require("../lib/migrate/snapshot");
|
|
8
8
|
const sandbox_1 = require("../lib/migrate/sandbox");
|
|
9
|
+
const lib_1 = require("../lib");
|
|
10
|
+
const file_system_1 = require("../lib/utils/file-system");
|
|
9
11
|
class Migrate extends base_command_1.default {
|
|
10
12
|
async run() {
|
|
11
13
|
const { flags } = await this.parse(Migrate);
|
|
@@ -24,37 +26,6 @@ class Migrate extends base_command_1.default {
|
|
|
24
26
|
const msg = error instanceof Error ? error.message : String(error);
|
|
25
27
|
this.error(`Failed to parse .apsorc: ${msg}`);
|
|
26
28
|
}
|
|
27
|
-
// Quick check against snapshot
|
|
28
|
-
const snapshot = (0, snapshot_1.readSnapshot)();
|
|
29
|
-
const currentComparable = {
|
|
30
|
-
entities: parsed.entities.map((e) => {
|
|
31
|
-
var _a;
|
|
32
|
-
return ({
|
|
33
|
-
name: e.name,
|
|
34
|
-
fields: (_a = e.fields) === null || _a === void 0 ? void 0 : _a.map((f) => {
|
|
35
|
-
var _a;
|
|
36
|
-
return ({
|
|
37
|
-
name: f.name,
|
|
38
|
-
type: f.type,
|
|
39
|
-
nullable: f.nullable,
|
|
40
|
-
unique: f.unique,
|
|
41
|
-
default: (_a = f.default) !== null && _a !== void 0 ? _a : undefined,
|
|
42
|
-
index: f.index,
|
|
43
|
-
});
|
|
44
|
-
}),
|
|
45
|
-
primaryKeyType: e.primaryKeyType,
|
|
46
|
-
created_at: e.created_at,
|
|
47
|
-
updated_at: e.updated_at,
|
|
48
|
-
scopeBy: e.scopeBy,
|
|
49
|
-
});
|
|
50
|
-
}),
|
|
51
|
-
};
|
|
52
|
-
if (!(0, snapshot_1.hasChanges)(currentComparable, snapshot)) {
|
|
53
|
-
if (!flags.sql) {
|
|
54
|
-
this.log("Schema unchanged. No migration needed.");
|
|
55
|
-
}
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
29
|
if (!flags.sql) {
|
|
59
30
|
this.log("Schema changes detected. Running migration sandbox...");
|
|
60
31
|
}
|
|
@@ -85,6 +56,29 @@ class Migrate extends base_command_1.default {
|
|
|
85
56
|
this.log("");
|
|
86
57
|
this.log(`${result.upSql.length} statement(s) generated and tested.`);
|
|
87
58
|
this.log("Migration validated against local PGlite.");
|
|
59
|
+
// Generate language-native migration files for Python and Go
|
|
60
|
+
const language = parsed.language || "typescript";
|
|
61
|
+
if (language !== "typescript") {
|
|
62
|
+
const generatorConfig = {
|
|
63
|
+
language,
|
|
64
|
+
rootFolder: parsed.rootFolder || "src",
|
|
65
|
+
apiType: parsed.apiType || "rest",
|
|
66
|
+
entities: parsed.entities,
|
|
67
|
+
relationshipMap: parsed.relationshipMap,
|
|
68
|
+
};
|
|
69
|
+
const generator = (0, lib_1.createGenerator)(generatorConfig);
|
|
70
|
+
const migrationFiles = await generator.generateMigration({
|
|
71
|
+
entities: parsed.entities,
|
|
72
|
+
relationshipMap: parsed.relationshipMap,
|
|
73
|
+
upSql: result.upSql,
|
|
74
|
+
downSql: result.downSql,
|
|
75
|
+
});
|
|
76
|
+
for (const file of migrationFiles) {
|
|
77
|
+
const fullPath = path.join(process.cwd(), file.path);
|
|
78
|
+
await (0, file_system_1.createFile)(fullPath, file.content);
|
|
79
|
+
this.log(`Generated migration file: ${file.path}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
88
82
|
if (flags.apply) {
|
|
89
83
|
(0, sandbox_1.applyMigration)({
|
|
90
84
|
entities: parsed.entities,
|
|
@@ -30,7 +30,7 @@ export declare class GoGenerator extends BaseGenerator {
|
|
|
30
30
|
generateService(options: ServiceGenerationOptions): Promise<GeneratedFile[]>;
|
|
31
31
|
generateDto(options: DtoGenerationOptions): Promise<GeneratedFile[]>;
|
|
32
32
|
generateModule(_options: EntityGenerationOptions): Promise<GeneratedFile[]>;
|
|
33
|
-
generateMigration(
|
|
33
|
+
generateMigration(options: MigrationGenerationOptions): Promise<GeneratedFile[]>;
|
|
34
34
|
generateEnums(entities: Entity[], _apiType: string): Promise<GeneratedFile[]>;
|
|
35
35
|
generateIndexModule(entities: Entity[], _apiType: string): Promise<GeneratedFile[]>;
|
|
36
36
|
generateGuards(_entities: Entity[], auth?: AuthConfig): Promise<GeneratedFile[]>;
|
|
@@ -278,10 +278,31 @@ class GoGenerator extends base_1.BaseGenerator {
|
|
|
278
278
|
// Route registration is handled in the index module
|
|
279
279
|
return [];
|
|
280
280
|
}
|
|
281
|
-
async generateMigration(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
281
|
+
async generateMigration(options) {
|
|
282
|
+
const { upSql, downSql, migrationName } = options;
|
|
283
|
+
if (!upSql || upSql.length === 0) {
|
|
284
|
+
return [];
|
|
285
|
+
}
|
|
286
|
+
const timestamp = new Date()
|
|
287
|
+
.toISOString()
|
|
288
|
+
.replace(/[-:T]/g, "")
|
|
289
|
+
.slice(0, 14);
|
|
290
|
+
const name = migrationName || "auto";
|
|
291
|
+
const filename = `${timestamp}_${name}.sql`;
|
|
292
|
+
const upStatements = upSql.map((sql) => `${sql};`).join("\n");
|
|
293
|
+
const downStatements = (downSql || []).map((sql) => `${sql};`).join("\n");
|
|
294
|
+
const content = `-- +goose Up
|
|
295
|
+
${upStatements}
|
|
296
|
+
|
|
297
|
+
-- +goose Down
|
|
298
|
+
${downStatements}
|
|
299
|
+
`;
|
|
300
|
+
return [
|
|
301
|
+
{
|
|
302
|
+
path: `migrations/${filename}`,
|
|
303
|
+
content,
|
|
304
|
+
},
|
|
305
|
+
];
|
|
285
306
|
}
|
|
286
307
|
async generateEnums(entities, _apiType) {
|
|
287
308
|
if (entities.length === 0) {
|
|
@@ -324,11 +345,32 @@ class GoGenerator extends base_1.BaseGenerator {
|
|
|
324
345
|
const content = await this.renderTemplate("./index-module", {
|
|
325
346
|
entities: entitiesData,
|
|
326
347
|
});
|
|
348
|
+
// Generate model registry for AutoMigrate support
|
|
349
|
+
const modelImports = entities
|
|
350
|
+
.map((e) => `\t// ${e.name} model`)
|
|
351
|
+
.join("\n");
|
|
352
|
+
const modelRefs = entities
|
|
353
|
+
.map((e) => `\t\t&${(0, casing_1.pascalCase)(e.name)}{},`)
|
|
354
|
+
.join("\n");
|
|
355
|
+
const registryContent = `package models
|
|
356
|
+
|
|
357
|
+
// All returns all registered GORM models for AutoMigrate.
|
|
358
|
+
// This file is auto-generated by the Apso CLI.
|
|
359
|
+
func All() []interface{} {
|
|
360
|
+
\treturn []interface{}{
|
|
361
|
+
${modelRefs}
|
|
362
|
+
\t}
|
|
363
|
+
}
|
|
364
|
+
`;
|
|
327
365
|
return [
|
|
328
366
|
{
|
|
329
367
|
path: "routes/routes.go",
|
|
330
368
|
content,
|
|
331
369
|
},
|
|
370
|
+
{
|
|
371
|
+
path: "models/registry.go",
|
|
372
|
+
content: registryContent,
|
|
373
|
+
},
|
|
332
374
|
];
|
|
333
375
|
}
|
|
334
376
|
async generateGuards(_entities, auth) {
|
|
@@ -14,7 +14,7 @@ export declare class PythonGenerator extends BaseGenerator {
|
|
|
14
14
|
generateService(options: ServiceGenerationOptions): Promise<GeneratedFile[]>;
|
|
15
15
|
generateDto(options: DtoGenerationOptions): Promise<GeneratedFile[]>;
|
|
16
16
|
generateModule(_options: EntityGenerationOptions): Promise<GeneratedFile[]>;
|
|
17
|
-
generateMigration(
|
|
17
|
+
generateMigration(options: MigrationGenerationOptions): Promise<GeneratedFile[]>;
|
|
18
18
|
generateEnums(entities: Entity[], _apiType: string): Promise<GeneratedFile[]>;
|
|
19
19
|
generateIndexModule(entities: Entity[], _apiType: string): Promise<GeneratedFile[]>;
|
|
20
20
|
generateGuards(_entities: Entity[], _auth?: AuthConfig): Promise<GeneratedFile[]>;
|
|
@@ -206,10 +206,46 @@ class PythonGenerator extends base_1.BaseGenerator {
|
|
|
206
206
|
// The router registration is handled in the index module
|
|
207
207
|
return [];
|
|
208
208
|
}
|
|
209
|
-
async generateMigration(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
async generateMigration(options) {
|
|
210
|
+
const { upSql, downSql, migrationName } = options;
|
|
211
|
+
if (!upSql || upSql.length === 0) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
const timestamp = new Date()
|
|
215
|
+
.toISOString()
|
|
216
|
+
.replace(/[-:T]/g, "")
|
|
217
|
+
.slice(0, 14);
|
|
218
|
+
const revision = timestamp;
|
|
219
|
+
const name = migrationName || "auto";
|
|
220
|
+
const filename = `${timestamp}_${name}.py`;
|
|
221
|
+
const upStatements = upSql
|
|
222
|
+
.map((sql) => ` op.execute("""${sql}""")`)
|
|
223
|
+
.join("\n");
|
|
224
|
+
const downStatements = (downSql || [])
|
|
225
|
+
.map((sql) => ` op.execute("""${sql}""")`)
|
|
226
|
+
.join("\n");
|
|
227
|
+
const content = `"""${name}"""
|
|
228
|
+
from alembic import op
|
|
229
|
+
|
|
230
|
+
revision = '${revision}'
|
|
231
|
+
down_revision = None
|
|
232
|
+
branch_labels = None
|
|
233
|
+
depends_on = None
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def upgrade() -> None:
|
|
237
|
+
${upStatements}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def downgrade() -> None:
|
|
241
|
+
${downStatements || " pass"}
|
|
242
|
+
`;
|
|
243
|
+
return [
|
|
244
|
+
{
|
|
245
|
+
path: `alembic/versions/${filename}`,
|
|
246
|
+
content,
|
|
247
|
+
},
|
|
248
|
+
];
|
|
213
249
|
}
|
|
214
250
|
async generateEnums(entities, _apiType) {
|
|
215
251
|
if (entities.length === 0) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<% const {field} = it; %>
|
|
2
2
|
|
|
3
|
+
<% if (field.primary ) {%>
|
|
4
|
+
@PrimaryGeneratedColumn('uuid')
|
|
5
|
+
<% } else { %>
|
|
3
6
|
@Column({ "type": "uuid"<% if (field.default !== null) {%>, default: <%= field.default %><% } %> })
|
|
4
7
|
<% if (field.index ) {%>
|
|
5
8
|
@Index()
|
|
6
9
|
<% } %>
|
|
7
|
-
<% if (field.primary ) {%>
|
|
8
|
-
@PrimaryColumn()
|
|
9
10
|
<% } %>
|
|
10
11
|
|
|
11
|
-
<%= field.name %>: string;
|
|
12
|
+
<%= field.name %>: string;
|
|
@@ -58,8 +58,7 @@ export class <%= it.name %> {
|
|
|
58
58
|
<% if (it.createPrimaryKey) { %>
|
|
59
59
|
// Fields
|
|
60
60
|
<% if (it.primaryKeyType === 'uuid') { %>
|
|
61
|
-
@
|
|
62
|
-
@PrimaryColumn()
|
|
61
|
+
@PrimaryGeneratedColumn('uuid')
|
|
63
62
|
id: string;
|
|
64
63
|
<% } else if (it.primaryKeyType === 'text') { %>
|
|
65
64
|
@Column({ type: 'text' })
|
|
@@ -173,6 +173,14 @@ export interface MigrationGenerationOptions {
|
|
|
173
173
|
* Migration name/version
|
|
174
174
|
*/
|
|
175
175
|
migrationName?: string;
|
|
176
|
+
/**
|
|
177
|
+
* SQL statements for the up direction (from PGlite sandbox)
|
|
178
|
+
*/
|
|
179
|
+
upSql?: string[];
|
|
180
|
+
/**
|
|
181
|
+
* SQL statements for the down direction (from PGlite sandbox)
|
|
182
|
+
*/
|
|
183
|
+
downSql?: string[];
|
|
176
184
|
}
|
|
177
185
|
/**
|
|
178
186
|
* Interface that all language-specific generators must implement
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apso/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@apso/cli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.13.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@electric-sql/pglite": "^0.2.17",
|
package/oclif.manifest.json
CHANGED