@reldens/storage 0.110.0 → 0.112.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/CLAUDE.md +20 -0
- package/lib/generators/base-generator.js +22 -0
- package/lib/generators/entities-generation.js +1 -0
- package/lib/mysql-tables-provider.js +12 -8
- package/package.json +1 -1
- package/tests/fixtures/expected-entities/entities/test-products-entity.js +1 -0
- package/tests/fixtures/expected-entities/entities/test-reviews-entity.js +1 -0
- package/tests/unit/test-entities-generation.js +34 -0
package/CLAUDE.md
CHANGED
|
@@ -284,6 +284,26 @@ All generated entity relations follow the `related_*` prefix pattern:
|
|
|
284
284
|
|
|
285
285
|
This pattern is consistent across all ORM drivers and is defined in `entities-config.js`.
|
|
286
286
|
|
|
287
|
+
## Reference Properties Delete Rule
|
|
288
|
+
|
|
289
|
+
Every generated `type: 'reference'` property also carries the foreign key referential action as
|
|
290
|
+
`onDelete: '<rule>'`, so consumers can tell what a parent delete does to the child rows without querying the
|
|
291
|
+
schema themselves.
|
|
292
|
+
|
|
293
|
+
- `MysqlTablesProvider` joins `information_schema.REFERENTIAL_CONSTRAINTS` on the FK constraint name and stores
|
|
294
|
+
the raw `DELETE_RULE` as `column.referencedDeleteRule`.
|
|
295
|
+
- `BaseGenerator.mapDeleteRule()` normalizes it to camel case, matching the Prisma vocabulary: `CASCADE` becomes
|
|
296
|
+
`cascade`, `SET NULL` becomes `setNull`, `NO ACTION` becomes `noAction`, `SET DEFAULT` becomes `setDefault`,
|
|
297
|
+
`RESTRICT` stays `restrict`.
|
|
298
|
+
- `BaseGenerator.addReferenceDeleteRule()` pushes the attribute, called from
|
|
299
|
+
`EntitiesGeneration.addTypeAttribute()` right after the `alias`. Only the entities are affected, the ORM model
|
|
300
|
+
generation does not emit it.
|
|
301
|
+
- The property is omitted when there is no rule to report, so an entity generated before this existed simply has
|
|
302
|
+
no `onDelete` and consumers must treat a missing value as unknown rather than assuming a default.
|
|
303
|
+
|
|
304
|
+
Regenerating entities is required after changing a foreign key referential action: the rule is read live from
|
|
305
|
+
`information_schema`, not from `prisma/schema.prisma`, and `prisma db pull` alone does not update it.
|
|
306
|
+
|
|
287
307
|
## Prisma Driver Validation System
|
|
288
308
|
|
|
289
309
|
### ensureRequiredFields() Method
|
|
@@ -16,6 +16,28 @@ class BaseGenerator
|
|
|
16
16
|
return result;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
addReferenceDeleteRule(props, column)
|
|
20
|
+
{
|
|
21
|
+
let deleteRule = this.mapDeleteRule(column.referencedDeleteRule);
|
|
22
|
+
if(!deleteRule){
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
props.push('onDelete: \''+deleteRule+'\'');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
mapDeleteRule(deleteRule)
|
|
29
|
+
{
|
|
30
|
+
if(!deleteRule){
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
let ruleParts = String(deleteRule).toLowerCase().split(' ');
|
|
34
|
+
let mappedRule = ruleParts.shift();
|
|
35
|
+
for(let rulePart of ruleParts){
|
|
36
|
+
mappedRule += rulePart.charAt(0).toUpperCase()+rulePart.slice(1);
|
|
37
|
+
}
|
|
38
|
+
return mappedRule;
|
|
39
|
+
}
|
|
40
|
+
|
|
19
41
|
}
|
|
20
42
|
|
|
21
43
|
module.exports.BaseGenerator = BaseGenerator;
|
|
@@ -228,6 +228,7 @@ class EntitiesGeneration extends BaseGenerator
|
|
|
228
228
|
props.push('reference: \''+referenceTable+'\'');
|
|
229
229
|
let alias = this.generateForwardRelationKey(referenceTable, columnName, referenceCounts);
|
|
230
230
|
props.push('alias: \''+alias+'\'');
|
|
231
|
+
this.addReferenceDeleteRule(props, column);
|
|
231
232
|
return;
|
|
232
233
|
}
|
|
233
234
|
if('date' === type || 'datetime' === type || 'timestamp' === type){
|
|
@@ -53,16 +53,19 @@ class MySQLTablesProvider
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
let foreignKeysQuery = 'SELECT ' +
|
|
56
|
-
' TABLE_NAME, ' +
|
|
57
|
-
' COLUMN_NAME, ' +
|
|
58
|
-
' REFERENCED_TABLE_NAME, ' +
|
|
59
|
-
' REFERENCED_COLUMN_NAME ' +
|
|
56
|
+
' keyColumnUsage.TABLE_NAME, ' +
|
|
57
|
+
' keyColumnUsage.COLUMN_NAME, ' +
|
|
58
|
+
' keyColumnUsage.REFERENCED_TABLE_NAME, ' +
|
|
59
|
+
' keyColumnUsage.REFERENCED_COLUMN_NAME, ' +
|
|
60
|
+
' referentialConstraints.DELETE_RULE ' +
|
|
60
61
|
'FROM ' +
|
|
61
|
-
' information_schema.KEY_COLUMN_USAGE ' +
|
|
62
|
+
' information_schema.KEY_COLUMN_USAGE keyColumnUsage ' +
|
|
63
|
+
' LEFT JOIN information_schema.REFERENTIAL_CONSTRAINTS referentialConstraints ' +
|
|
64
|
+
' ON referentialConstraints.CONSTRAINT_SCHEMA = keyColumnUsage.CONSTRAINT_SCHEMA ' +
|
|
65
|
+
' AND referentialConstraints.CONSTRAINT_NAME = keyColumnUsage.CONSTRAINT_NAME ' +
|
|
62
66
|
'WHERE ' +
|
|
63
|
-
' TABLE_SCHEMA = \'' + server.config.database + '\' ' +
|
|
64
|
-
' AND REFERENCED_TABLE_NAME IS NOT NULL;';
|
|
65
|
-
|
|
67
|
+
' keyColumnUsage.TABLE_SCHEMA = \'' + server.config.database + '\' ' +
|
|
68
|
+
' AND keyColumnUsage.REFERENCED_TABLE_NAME IS NOT NULL;';
|
|
66
69
|
let foreignKeysResult = await server.rawQuery(foreignKeysQuery);
|
|
67
70
|
if(!sc.isArray(foreignKeysResult) || 0 === foreignKeysResult.length){
|
|
68
71
|
return tables;
|
|
@@ -71,6 +74,7 @@ class MySQLTablesProvider
|
|
|
71
74
|
if(tables[row.TABLE_NAME] && tables[row.TABLE_NAME].columns[row.COLUMN_NAME]){
|
|
72
75
|
tables[row.TABLE_NAME].columns[row.COLUMN_NAME].referencedTable = row.REFERENCED_TABLE_NAME;
|
|
73
76
|
tables[row.TABLE_NAME].columns[row.COLUMN_NAME].referencedColumn = row.REFERENCED_COLUMN_NAME;
|
|
77
|
+
tables[row.TABLE_NAME].columns[row.COLUMN_NAME].referencedDeleteRule = row.DELETE_RULE;
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
return tables;
|
package/package.json
CHANGED
|
@@ -38,9 +38,43 @@ class EntitiesGenerationTest
|
|
|
38
38
|
await this.testAddUniqueAttribute();
|
|
39
39
|
await this.testGetPropertyAttributesUnique();
|
|
40
40
|
await this.testGeneratePropertiesConfigUnique();
|
|
41
|
+
await this.testAddReferenceDeleteRule();
|
|
41
42
|
return this.runner.getResults();
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
async testAddReferenceDeleteRule()
|
|
46
|
+
{
|
|
47
|
+
this.runner.group('addReferenceDeleteRule');
|
|
48
|
+
let generation = new EntitiesGeneration({});
|
|
49
|
+
await this.runner.test('should map a CASCADE rule', async () => {
|
|
50
|
+
let props = [];
|
|
51
|
+
generation.addReferenceDeleteRule(props, this.createColumn({referencedDeleteRule: 'CASCADE'}));
|
|
52
|
+
assert.deepStrictEqual(props, ['onDelete: \'cascade\'']);
|
|
53
|
+
});
|
|
54
|
+
await this.runner.test('should map a multi word rule to camel case', async () => {
|
|
55
|
+
let props = [];
|
|
56
|
+
generation.addReferenceDeleteRule(props, this.createColumn({referencedDeleteRule: 'SET NULL'}));
|
|
57
|
+
assert.deepStrictEqual(props, ['onDelete: \'setNull\'']);
|
|
58
|
+
let noActionProps = [];
|
|
59
|
+
generation.addReferenceDeleteRule(noActionProps, this.createColumn({referencedDeleteRule: 'NO ACTION'}));
|
|
60
|
+
assert.deepStrictEqual(noActionProps, ['onDelete: \'noAction\'']);
|
|
61
|
+
});
|
|
62
|
+
await this.runner.test('should push nothing when the column has no delete rule', async () => {
|
|
63
|
+
let props = [];
|
|
64
|
+
generation.addReferenceDeleteRule(props, this.createColumn({}));
|
|
65
|
+
assert.deepStrictEqual(props, []);
|
|
66
|
+
});
|
|
67
|
+
await this.runner.test('should include the delete rule on a reference property', async () => {
|
|
68
|
+
let column = this.createColumn({
|
|
69
|
+
type: 'int',
|
|
70
|
+
referencedTable: 'test_categories',
|
|
71
|
+
referencedDeleteRule: 'SET NULL'
|
|
72
|
+
});
|
|
73
|
+
let props = generation.getPropertyAttributes(column, 'category_id', {test_categories: 1});
|
|
74
|
+
assert.ok(-1 !== props.indexOf('onDelete: \'setNull\''));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
44
78
|
async testAddUniqueAttribute()
|
|
45
79
|
{
|
|
46
80
|
this.runner.group('addUniqueAttribute');
|