@currentjs/gen 0.3.2 → 0.5.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/CHANGELOG.md +10 -611
- package/README.md +623 -427
- package/dist/cli.js +2 -1
- package/dist/commands/commit.js +25 -42
- package/dist/commands/createApp.js +1 -0
- package/dist/commands/createModule.js +151 -45
- package/dist/commands/diff.js +27 -40
- package/dist/commands/generateAll.js +141 -291
- package/dist/commands/migrateCommit.js +6 -18
- package/dist/commands/migratePush.d.ts +1 -0
- package/dist/commands/migratePush.js +135 -0
- package/dist/commands/migrateUpdate.d.ts +1 -0
- package/dist/commands/migrateUpdate.js +147 -0
- package/dist/commands/newGenerateAll.d.ts +4 -0
- package/dist/commands/newGenerateAll.js +336 -0
- package/dist/generators/controllerGenerator.d.ts +43 -19
- package/dist/generators/controllerGenerator.js +547 -329
- package/dist/generators/domainLayerGenerator.d.ts +21 -0
- package/dist/generators/domainLayerGenerator.js +276 -0
- package/dist/generators/dtoGenerator.d.ts +21 -0
- package/dist/generators/dtoGenerator.js +518 -0
- package/dist/generators/newControllerGenerator.d.ts +55 -0
- package/dist/generators/newControllerGenerator.js +644 -0
- package/dist/generators/newServiceGenerator.d.ts +19 -0
- package/dist/generators/newServiceGenerator.js +266 -0
- package/dist/generators/newStoreGenerator.d.ts +39 -0
- package/dist/generators/newStoreGenerator.js +408 -0
- package/dist/generators/newTemplateGenerator.d.ts +29 -0
- package/dist/generators/newTemplateGenerator.js +510 -0
- package/dist/generators/serviceGenerator.d.ts +16 -51
- package/dist/generators/serviceGenerator.js +167 -586
- package/dist/generators/storeGenerator.d.ts +35 -32
- package/dist/generators/storeGenerator.js +291 -238
- package/dist/generators/storeGeneratorV2.d.ts +31 -0
- package/dist/generators/storeGeneratorV2.js +190 -0
- package/dist/generators/templateGenerator.d.ts +21 -21
- package/dist/generators/templateGenerator.js +393 -268
- package/dist/generators/templates/appTemplates.d.ts +3 -1
- package/dist/generators/templates/appTemplates.js +15 -10
- package/dist/generators/templates/data/appYamlTemplate +5 -2
- package/dist/generators/templates/data/cursorRulesTemplate +315 -221
- package/dist/generators/templates/data/frontendScriptTemplate +45 -11
- package/dist/generators/templates/data/mainViewTemplate +1 -1
- package/dist/generators/templates/data/systemTsTemplate +5 -0
- package/dist/generators/templates/index.d.ts +0 -3
- package/dist/generators/templates/index.js +0 -3
- package/dist/generators/templates/newStoreTemplates.d.ts +5 -0
- package/dist/generators/templates/newStoreTemplates.js +141 -0
- package/dist/generators/templates/storeTemplates.d.ts +1 -5
- package/dist/generators/templates/storeTemplates.js +102 -219
- package/dist/generators/templates/viewTemplates.js +1 -1
- package/dist/generators/useCaseGenerator.d.ts +13 -0
- package/dist/generators/useCaseGenerator.js +188 -0
- package/dist/types/configTypes.d.ts +148 -0
- package/dist/types/configTypes.js +10 -0
- package/dist/utils/childEntityUtils.d.ts +18 -0
- package/dist/utils/childEntityUtils.js +78 -0
- package/dist/utils/commandUtils.d.ts +43 -0
- package/dist/utils/commandUtils.js +124 -0
- package/dist/utils/commitUtils.d.ts +4 -1
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +13 -1
- package/dist/utils/diResolver.d.ts +32 -0
- package/dist/utils/diResolver.js +204 -0
- package/dist/utils/new_parts_of_migrationUtils.d.ts +0 -0
- package/dist/utils/new_parts_of_migrationUtils.js +164 -0
- package/dist/utils/typeUtils.d.ts +19 -0
- package/dist/utils/typeUtils.js +70 -0
- package/package.json +7 -3
|
@@ -414,7 +414,7 @@ window.AppConfig = {
|
|
|
414
414
|
* @returns {any} Converted value
|
|
415
415
|
*/
|
|
416
416
|
function convertFieldValue(value, fieldType) {
|
|
417
|
-
if (
|
|
417
|
+
if (value === undefined || value === null || value === '') {
|
|
418
418
|
return null;
|
|
419
419
|
}
|
|
420
420
|
|
|
@@ -422,6 +422,7 @@ window.AppConfig = {
|
|
|
422
422
|
case 'number':
|
|
423
423
|
case 'int':
|
|
424
424
|
case 'integer':
|
|
425
|
+
case 'id':
|
|
425
426
|
const intVal = parseInt(value, 10);
|
|
426
427
|
return isNaN(intVal) ? null : intVal;
|
|
427
428
|
|
|
@@ -432,10 +433,15 @@ window.AppConfig = {
|
|
|
432
433
|
|
|
433
434
|
case 'boolean':
|
|
434
435
|
case 'bool':
|
|
435
|
-
if (value === 'true') return true;
|
|
436
|
-
if (value === 'false') return false;
|
|
436
|
+
if (value === 'true' || value === 'on' || value === '1') return true;
|
|
437
|
+
if (value === 'false' || value === 'off' || value === '0') return false;
|
|
437
438
|
return Boolean(value);
|
|
439
|
+
|
|
440
|
+
case 'json':
|
|
441
|
+
try { return JSON.parse(value); } catch { return value; }
|
|
438
442
|
|
|
443
|
+
case 'datetime':
|
|
444
|
+
case 'date':
|
|
439
445
|
case 'enum':
|
|
440
446
|
case 'string':
|
|
441
447
|
case 'text':
|
|
@@ -474,6 +480,30 @@ window.AppConfig = {
|
|
|
474
480
|
jsonData[key] = convertedValue;
|
|
475
481
|
}
|
|
476
482
|
}
|
|
483
|
+
|
|
484
|
+
// Unchecked checkboxes are not included in FormData — default to false
|
|
485
|
+
for (const [fieldName, fieldType] of Object.entries(fieldTypes)) {
|
|
486
|
+
const ft = fieldType.toLowerCase();
|
|
487
|
+
if ((ft === 'boolean' || ft === 'bool') && !(fieldName in jsonData)) {
|
|
488
|
+
jsonData[fieldName] = false;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Unflatten dot-notation keys (e.g., "amount.currency") into nested objects
|
|
493
|
+
const finalData = {};
|
|
494
|
+
for (const [key, value] of Object.entries(jsonData)) {
|
|
495
|
+
if (key.includes('.')) {
|
|
496
|
+
const parts = key.split('.');
|
|
497
|
+
let current = finalData;
|
|
498
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
499
|
+
if (!(parts[i] in current)) current[parts[i]] = {};
|
|
500
|
+
current = current[parts[i]];
|
|
501
|
+
}
|
|
502
|
+
current[parts[parts.length - 1]] = value;
|
|
503
|
+
} else {
|
|
504
|
+
finalData[key] = value;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
477
507
|
|
|
478
508
|
const url = form.getAttribute('data-action') || form.action;
|
|
479
509
|
const method = (form.getAttribute('data-method') || form.method || 'POST').toUpperCase();
|
|
@@ -486,16 +516,20 @@ window.AppConfig = {
|
|
|
486
516
|
'Content-Type': 'application/json',
|
|
487
517
|
'Accept': 'application/json'
|
|
488
518
|
}),
|
|
489
|
-
body: JSON.stringify(
|
|
519
|
+
body: JSON.stringify(finalData)
|
|
490
520
|
})
|
|
491
|
-
.then(response =>
|
|
492
|
-
|
|
493
|
-
|
|
521
|
+
.then(response =>
|
|
522
|
+
response.json()
|
|
523
|
+
.catch(() => ({}))
|
|
524
|
+
.then(body => ({ ok: response.ok, status: response.status, statusText: response.statusText, body }))
|
|
525
|
+
)
|
|
526
|
+
.then(({ ok, status, statusText, body }) => {
|
|
527
|
+
if (!ok) {
|
|
528
|
+
const message = body.message || body.error || `HTTP ${status}: ${statusText}`;
|
|
529
|
+
handleFormError({ message }, options);
|
|
530
|
+
return;
|
|
494
531
|
}
|
|
495
|
-
|
|
496
|
-
})
|
|
497
|
-
.then(data => {
|
|
498
|
-
handleFormSuccess(data, strategy, options);
|
|
532
|
+
handleFormSuccess(body, strategy, options);
|
|
499
533
|
})
|
|
500
534
|
.catch(error => {
|
|
501
535
|
console.error('Form submission failed:', error);
|
|
@@ -14,8 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./serviceTemplates"), exports);
|
|
18
|
-
__exportStar(require("./controllerTemplates"), exports);
|
|
19
17
|
__exportStar(require("./storeTemplates"), exports);
|
|
20
|
-
__exportStar(require("./validationTemplates"), exports);
|
|
21
18
|
__exportStar(require("./appTemplates"), exports);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const newStoreTemplates: {
|
|
2
|
+
rowInterface: string;
|
|
3
|
+
storeClass: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const newStoreFileTemplate = "import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';\nimport type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}\n\n{{ROW_INTERFACE}}\n\n{{STORE_CLASS}}\n";
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.newStoreFileTemplate = exports.newStoreTemplates = void 0;
|
|
4
|
+
exports.newStoreTemplates = {
|
|
5
|
+
rowInterface: `export interface {{ENTITY_NAME}}Row {
|
|
6
|
+
id: number;
|
|
7
|
+
{{ROW_FIELDS}}
|
|
8
|
+
created_at: string;
|
|
9
|
+
updated_at: string;
|
|
10
|
+
deleted_at?: string;
|
|
11
|
+
}`,
|
|
12
|
+
storeClass: `/**
|
|
13
|
+
* Data access layer for {{ENTITY_NAME}}
|
|
14
|
+
*/
|
|
15
|
+
export class {{ENTITY_NAME}}Store {
|
|
16
|
+
private tableName = '{{TABLE_NAME}}';
|
|
17
|
+
|
|
18
|
+
constructor(private db: ISqlProvider) {}
|
|
19
|
+
|
|
20
|
+
private toMySQLDatetime(date: Date): string {
|
|
21
|
+
return date.toISOString().slice(0, 19).replace('T', ' ');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private rowToModel(row: {{ENTITY_NAME}}Row): {{ENTITY_NAME}} {
|
|
25
|
+
return new {{ENTITY_NAME}}(
|
|
26
|
+
row.id,
|
|
27
|
+
{{ROW_TO_MODEL_MAPPING}}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getAll(page: number = 1, limit: number = 20): Promise<{{ENTITY_NAME}}[]> {
|
|
32
|
+
const offset = (page - 1) * limit;
|
|
33
|
+
const result = await this.db.query(
|
|
34
|
+
\`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL LIMIT :limit OFFSET :offset\`,
|
|
35
|
+
{ limit: String(limit), offset: String(offset) }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (result.success && result.data) {
|
|
39
|
+
return result.data.map((row: {{ENTITY_NAME}}Row) => this.rowToModel(row));
|
|
40
|
+
}
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async count(): Promise<number> {
|
|
45
|
+
const result = await this.db.query(
|
|
46
|
+
\`SELECT COUNT(*) as count FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL\`,
|
|
47
|
+
{}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
51
|
+
return parseInt(result.data[0].count, 10);
|
|
52
|
+
}
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getById(id: number): Promise<{{ENTITY_NAME}} | null> {
|
|
57
|
+
const result = await this.db.query(
|
|
58
|
+
\`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE id = :id AND deleted_at IS NULL\`,
|
|
59
|
+
{ id }
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
63
|
+
return this.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async insert(entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
|
|
69
|
+
const now = new Date();
|
|
70
|
+
const data: Partial<{{ENTITY_NAME}}Row> = {
|
|
71
|
+
{{INSERT_DATA_MAPPING}},
|
|
72
|
+
created_at: this.toMySQLDatetime(now),
|
|
73
|
+
updated_at: this.toMySQLDatetime(now)
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const fieldsList = Object.keys(data).map(f => \`\\\`\${f}\\\`\`).join(', ');
|
|
77
|
+
const placeholders = Object.keys(data).map(f => \`:\${f}\`).join(', ');
|
|
78
|
+
|
|
79
|
+
const result = await this.db.query(
|
|
80
|
+
\`INSERT INTO \\\`\${this.tableName}\\\` (\${fieldsList}) VALUES (\${placeholders})\`,
|
|
81
|
+
data
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (result.success && result.insertId) {
|
|
85
|
+
const newId = typeof result.insertId === 'string' ? parseInt(result.insertId, 10) : result.insertId;
|
|
86
|
+
return this.getById(newId) as Promise<{{ENTITY_NAME}}>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw new Error('Failed to insert {{ENTITY_NAME}}');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async update(id: number, entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
|
|
93
|
+
const now = new Date();
|
|
94
|
+
const data: Partial<{{ENTITY_NAME}}Row> & { id: number } = {
|
|
95
|
+
{{UPDATE_DATA_MAPPING}},
|
|
96
|
+
updated_at: this.toMySQLDatetime(now),
|
|
97
|
+
id
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const updateFields = {{UPDATE_FIELDS_ARRAY}}.map(f => \`\\\`\${f}\\\` = :\${f}\`).join(', ');
|
|
101
|
+
|
|
102
|
+
const result = await this.db.query(
|
|
103
|
+
\`UPDATE \\\`\${this.tableName}\\\` SET \${updateFields}, updated_at = :updated_at WHERE id = :id\`,
|
|
104
|
+
data
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (result.success) {
|
|
108
|
+
return this.getById(id) as Promise<{{ENTITY_NAME}}>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
throw new Error('Failed to update {{ENTITY_NAME}}');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async softDelete(id: number): Promise<boolean> {
|
|
115
|
+
const now = new Date();
|
|
116
|
+
const result = await this.db.query(
|
|
117
|
+
\`UPDATE \\\`\${this.tableName}\\\` SET deleted_at = :deleted_at WHERE id = :id\`,
|
|
118
|
+
{ deleted_at: this.toMySQLDatetime(now), id }
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return result.success;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async hardDelete(id: number): Promise<boolean> {
|
|
125
|
+
const result = await this.db.query(
|
|
126
|
+
\`DELETE FROM \\\`\${this.tableName}\\\` WHERE id = :id\`,
|
|
127
|
+
{ id }
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return result.success;
|
|
131
|
+
}
|
|
132
|
+
{{GET_BY_PARENT_ID_METHOD}}
|
|
133
|
+
{{GET_RESOURCE_OWNER_METHOD}}}`
|
|
134
|
+
};
|
|
135
|
+
exports.newStoreFileTemplate = `import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';
|
|
136
|
+
import type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}
|
|
137
|
+
|
|
138
|
+
{{ROW_INTERFACE}}
|
|
139
|
+
|
|
140
|
+
{{STORE_CLASS}}
|
|
141
|
+
`;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
export declare const storeTemplates: {
|
|
2
2
|
rowInterface: string;
|
|
3
3
|
storeClass: string;
|
|
4
|
-
conversionMethods: string;
|
|
5
|
-
};
|
|
6
|
-
export declare const fileTemplates: {
|
|
7
|
-
storeFile: string;
|
|
8
|
-
storeInterface: string;
|
|
9
4
|
};
|
|
5
|
+
export declare const storeFileTemplate = "import { Injectable } from '../../../../system';\nimport { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';\nimport type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}\n\n{{ROW_INTERFACE}}\n\n{{STORE_CLASS}}\n";
|
|
@@ -1,260 +1,143 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.storeFileTemplate = exports.storeTemplates = void 0;
|
|
4
4
|
exports.storeTemplates = {
|
|
5
5
|
rowInterface: `export interface {{ENTITY_NAME}}Row {
|
|
6
6
|
id: number;
|
|
7
7
|
{{ROW_FIELDS}}
|
|
8
|
-
created_at:
|
|
9
|
-
updated_at:
|
|
10
|
-
deleted_at?:
|
|
8
|
+
created_at: string;
|
|
9
|
+
updated_at: string;
|
|
10
|
+
deleted_at?: string;
|
|
11
11
|
}`,
|
|
12
|
-
storeClass:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
storeClass: `/**
|
|
13
|
+
* Data access layer for {{ENTITY_NAME}}
|
|
14
|
+
*/
|
|
15
|
+
@Injectable()
|
|
16
|
+
export class {{ENTITY_NAME}}Store {
|
|
17
|
+
private tableName = '{{TABLE_NAME}}';
|
|
18
|
+
|
|
16
19
|
constructor(private db: ISqlProvider) {}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const query = 'SELECT * FROM {{TABLE_NAME}} WHERE id = :id AND deleted_at IS NULL';
|
|
21
|
-
const result = await this.db.query(query, { id });
|
|
22
|
-
|
|
23
|
-
if (!result.success || result.data.length === 0) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {{ENTITY_NAME}}Store.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
|
|
28
|
-
} catch (error) {
|
|
29
|
-
if (error instanceof MySQLConnectionError) {
|
|
30
|
-
throw new Error(\`Database connection error while fetching {{ENTITY_NAME}} with id \${id}: \${error.message}\`);
|
|
31
|
-
} else if (error instanceof MySQLQueryError) {
|
|
32
|
-
throw new Error(\`Query error while fetching {{ENTITY_NAME}} with id \${id}: \${error.message}\`);
|
|
33
|
-
}
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
21
|
+
private toMySQLDatetime(date: Date): string {
|
|
22
|
+
return date.toISOString().slice(0, 19).replace('T', ' ');
|
|
36
23
|
}
|
|
37
24
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (!result.success) {
|
|
44
|
-
return [];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return result.data.map((row: {{ENTITY_NAME}}Row) => {{ENTITY_NAME}}Store.rowToModel(row));
|
|
25
|
+
private rowToModel(row: {{ENTITY_NAME}}Row): {{ENTITY_NAME}} {
|
|
26
|
+
return new {{ENTITY_NAME}}(
|
|
27
|
+
row.id,
|
|
28
|
+
{{ROW_TO_MODEL_MAPPING}}
|
|
29
|
+
);
|
|
48
30
|
}
|
|
49
31
|
|
|
50
|
-
async
|
|
32
|
+
async getAll(page: number = 1, limit: number = 20): Promise<{{ENTITY_NAME}}[]> {
|
|
51
33
|
const offset = (page - 1) * limit;
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return result.data.map((row: {{ENTITY_NAME}}Row) => {{ENTITY_NAME}}Store.rowToModel(row));
|
|
60
|
-
}
|
|
34
|
+
const result = await this.db.query(
|
|
35
|
+
\`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL LIMIT :limit OFFSET :offset\`,
|
|
36
|
+
{ limit: String(limit), offset: String(offset) }
|
|
37
|
+
);
|
|
61
38
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Runtime validation against SQL injection
|
|
66
|
-
for (const key of filterKeys) {
|
|
67
|
-
if (!{{ENTITY_NAME}}Store.FILTERABLE_FIELDS.includes(key)) {
|
|
68
|
-
throw new Error(\`Invalid filter field: \${key}\`);
|
|
69
|
-
}
|
|
39
|
+
if (result.success && result.data) {
|
|
40
|
+
return result.data.map((row: {{ENTITY_NAME}}Row) => this.rowToModel(row));
|
|
70
41
|
}
|
|
71
|
-
|
|
72
|
-
const whereConditions = filterKeys.map(key => \`\${key} = :\${key}\`);
|
|
73
|
-
|
|
74
|
-
if (whereConditions.length === 0) {
|
|
75
|
-
throw new Error('At least one filter condition is required');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const query = \`SELECT * FROM {{TABLE_NAME}} WHERE \${whereConditions.join(' AND ')} AND deleted_at IS NULL LIMIT 1\`;
|
|
79
|
-
const result = await this.db.query(query, filters);
|
|
80
|
-
|
|
81
|
-
if (!result.success || result.data.length === 0) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return {{ENTITY_NAME}}Store.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
|
|
42
|
+
return [];
|
|
86
43
|
}
|
|
87
44
|
|
|
88
|
-
async
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const whereConditions = filterKeys.map(key => \`\${key} = :\${key}\`);
|
|
99
|
-
|
|
100
|
-
if (whereConditions.length === 0) {
|
|
101
|
-
return this.getAll();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const query = \`SELECT * FROM {{TABLE_NAME}} WHERE \${whereConditions.join(' AND ')} AND deleted_at IS NULL ORDER BY created_at DESC\`;
|
|
105
|
-
const result = await this.db.query(query, filters);
|
|
106
|
-
|
|
107
|
-
if (!result.success) {
|
|
108
|
-
return [];
|
|
45
|
+
async count(): Promise<number> {
|
|
46
|
+
const result = await this.db.query(
|
|
47
|
+
\`SELECT COUNT(*) as count FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL\`,
|
|
48
|
+
{}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
52
|
+
return parseInt(result.data[0].count, 10);
|
|
109
53
|
}
|
|
110
|
-
|
|
111
|
-
return result.data.map((row: {{ENTITY_NAME}}Row) => {{ENTITY_NAME}}Store.rowToModel(row));
|
|
54
|
+
return 0;
|
|
112
55
|
}
|
|
113
56
|
|
|
114
|
-
async
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const placeholders = fields.map(field => \`:\${field}\`).join(', ');
|
|
123
|
-
|
|
124
|
-
const query = \`INSERT INTO {{TABLE_NAME}} (\${fields.join(', ')}) VALUES (\${placeholders})\`;
|
|
125
|
-
const result = await this.db.query(query, row);
|
|
126
|
-
|
|
127
|
-
if (!result.success || !result.insertId) {
|
|
128
|
-
throw new Error('Failed to insert {{ENTITY_NAME}}: Insert operation did not return a valid ID');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return this.getById(Number(result.insertId));
|
|
132
|
-
} catch (error) {
|
|
133
|
-
if (error instanceof MySQLQueryError) {
|
|
134
|
-
throw new Error(\`Failed to insert {{ENTITY_NAME}}: \${error.message}\`);
|
|
135
|
-
} else if (error instanceof MySQLConnectionError) {
|
|
136
|
-
throw new Error(\`Database connection error while inserting {{ENTITY_NAME}}: \${error.message}\`);
|
|
137
|
-
}
|
|
138
|
-
throw error;
|
|
57
|
+
async getById(id: number): Promise<{{ENTITY_NAME}} | null> {
|
|
58
|
+
const result = await this.db.query(
|
|
59
|
+
\`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE id = :id AND deleted_at IS NULL\`,
|
|
60
|
+
{ id }
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
64
|
+
return this.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
|
|
139
65
|
}
|
|
66
|
+
return null;
|
|
140
67
|
}
|
|
141
68
|
|
|
142
|
-
async
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (updateFields.length === 0) {
|
|
162
|
-
return existing;
|
|
69
|
+
async insert(entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
|
|
70
|
+
const now = new Date();
|
|
71
|
+
const data: Partial<{{ENTITY_NAME}}Row> = {
|
|
72
|
+
{{INSERT_DATA_MAPPING}},
|
|
73
|
+
created_at: this.toMySQLDatetime(now),
|
|
74
|
+
updated_at: this.toMySQLDatetime(now)
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const fieldsList = Object.keys(data).map(f => \`\\\`\${f}\\\`\`).join(', ');
|
|
78
|
+
const placeholders = Object.keys(data).map(f => \`:\${f}\`).join(', ');
|
|
79
|
+
|
|
80
|
+
const result = await this.db.query(
|
|
81
|
+
\`INSERT INTO \\\`\${this.tableName}\\\` (\${fieldsList}) VALUES (\${placeholders})\`,
|
|
82
|
+
data
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (result.success && result.insertId) {
|
|
86
|
+
const newId = typeof result.insertId === 'string' ? parseInt(result.insertId, 10) : result.insertId;
|
|
87
|
+
return this.getById(newId) as Promise<{{ENTITY_NAME}}>;
|
|
163
88
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const query = \`UPDATE {{TABLE_NAME}} SET \${updateFields.join(', ')}, updated_at = :updated_at WHERE id = :id\`;
|
|
168
|
-
await this.db.query(query, params);
|
|
169
|
-
|
|
170
|
-
return this.getById(id);
|
|
89
|
+
|
|
90
|
+
throw new Error('Failed to insert {{ENTITY_NAME}}');
|
|
171
91
|
}
|
|
172
92
|
|
|
173
|
-
async
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
93
|
+
async update(id: number, entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
|
|
94
|
+
const now = new Date();
|
|
95
|
+
const data: Partial<{{ENTITY_NAME}}Row> & { id: number } = {
|
|
96
|
+
{{UPDATE_DATA_MAPPING}},
|
|
97
|
+
updated_at: this.toMySQLDatetime(now),
|
|
98
|
+
id
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const updateFields = {{UPDATE_FIELDS_ARRAY}}.map(f => \`\\\`\${f}\\\` = :\${f}\`).join(', ');
|
|
102
|
+
|
|
103
|
+
const result = await this.db.query(
|
|
104
|
+
\`UPDATE \\\`\${this.tableName}\\\` SET \${updateFields}, updated_at = :updated_at WHERE id = :id\`,
|
|
105
|
+
data
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (result.success) {
|
|
109
|
+
return this.getById(id) as Promise<{{ENTITY_NAME}}>;
|
|
179
110
|
}
|
|
180
|
-
|
|
181
|
-
|
|
111
|
+
|
|
112
|
+
throw new Error('Failed to update {{ENTITY_NAME}}');
|
|
182
113
|
}
|
|
183
114
|
|
|
184
115
|
async softDelete(id: number): Promise<boolean> {
|
|
185
|
-
const
|
|
186
|
-
const result = await this.db.query(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
116
|
+
const now = new Date();
|
|
117
|
+
const result = await this.db.query(
|
|
118
|
+
\`UPDATE \\\`\${this.tableName}\\\` SET deleted_at = :deleted_at WHERE id = :id\`,
|
|
119
|
+
{ deleted_at: this.toMySQLDatetime(now), id }
|
|
120
|
+
);
|
|
190
121
|
|
|
191
|
-
|
|
192
|
-
let query = 'SELECT COUNT(*) as count FROM {{TABLE_NAME}} WHERE deleted_at IS NULL';
|
|
193
|
-
let params: Record<string, any> = {};
|
|
194
|
-
|
|
195
|
-
if (filters && Object.keys(filters).length > 0) {
|
|
196
|
-
const whereConditions = Object.keys(filters).map(key => \`\${key} = :\${key}\`);
|
|
197
|
-
params = { ...filters };
|
|
198
|
-
query += \` AND \${whereConditions.join(' AND ')}\`;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const result = await this.db.query(query, params);
|
|
202
|
-
|
|
203
|
-
if (!result.success || result.data.length === 0) {
|
|
204
|
-
return 0;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return result.data[0].count;
|
|
122
|
+
return result.success;
|
|
208
123
|
}
|
|
209
124
|
|
|
210
|
-
{
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
row.id,
|
|
215
|
-
{{ROW_TO_MODEL_MAPPING}}
|
|
125
|
+
async hardDelete(id: number): Promise<boolean> {
|
|
126
|
+
const result = await this.db.query(
|
|
127
|
+
\`DELETE FROM \\\`\${this.tableName}\\\` WHERE id = :id\`,
|
|
128
|
+
{ id }
|
|
216
129
|
);
|
|
217
|
-
}
|
|
218
130
|
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
id: model.id,
|
|
222
|
-
{{MODEL_TO_ROW_MAPPING}},
|
|
223
|
-
updated_at: new Date()
|
|
224
|
-
};
|
|
131
|
+
return result.success;
|
|
225
132
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const result: Record<string, any> = {};
|
|
229
|
-
|
|
230
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
231
|
-
// Only include properties that are not functions and are in updatable fields
|
|
232
|
-
if (typeof value !== 'function' && {{ENTITY_NAME}}Store.UPDATABLE_FIELDS.includes(key)) {
|
|
233
|
-
result[key] = value;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return result;
|
|
238
|
-
}`
|
|
133
|
+
{{GET_BY_PARENT_ID_METHOD}}
|
|
134
|
+
{{GET_RESOURCE_OWNER_METHOD}}}`
|
|
239
135
|
};
|
|
240
|
-
exports.
|
|
241
|
-
|
|
242
|
-
import {
|
|
243
|
-
import { ProviderMysql, ISqlProvider, MySQLQueryError, MySQLConnectionError } from '@currentjs/provider-mysql';
|
|
136
|
+
exports.storeFileTemplate = `import { Injectable } from '../../../../system';
|
|
137
|
+
import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';
|
|
138
|
+
import type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}
|
|
244
139
|
|
|
245
140
|
{{ROW_INTERFACE}}
|
|
246
141
|
|
|
247
|
-
{{STORE_CLASS}}
|
|
248
|
-
|
|
249
|
-
getById(id: number): Promise<TModel | null>;
|
|
250
|
-
getAll(page?: number, limit?: number): Promise<TModel[]>;
|
|
251
|
-
getAllByUserId(userId: number, page?: number, limit?: number): Promise<TModel[]>;
|
|
252
|
-
getBy(filters: Partial<TRow>): Promise<TModel | null>;
|
|
253
|
-
getAllBy(filters: Partial<TRow>): Promise<TModel[]>;
|
|
254
|
-
insert(model: Omit<TModel, 'id'>): Promise<TModel>;
|
|
255
|
-
update(id: number, updates: Partial<Omit<TModel, 'id' | 'createdAt'>>): Promise<TModel | null>;
|
|
256
|
-
upsert(model: Partial<TModel>): Promise<TModel>;
|
|
257
|
-
softDelete(id: number): Promise<boolean>;
|
|
258
|
-
count(filters?: Partial<TRow>): Promise<number>;
|
|
259
|
-
}`
|
|
260
|
-
};
|
|
142
|
+
{{STORE_CLASS}}
|
|
143
|
+
`;
|