@reldens/storage 0.117.0 → 0.119.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.
Files changed (78) hide show
  1. package/.claude/test-architecture.md +27 -17
  2. package/CLAUDE.md +67 -11
  3. package/README.md +204 -22
  4. package/bin/reldens-storage.js +43 -10
  5. package/index.js +53 -12
  6. package/lib/drizzle/drizzle-data-server.js +123 -0
  7. package/lib/drizzle/drizzle-driver.js +228 -0
  8. package/lib/drizzle/drizzle-models-generation.js +49 -0
  9. package/lib/drizzle/drizzle-modules-loader.js +32 -0
  10. package/lib/drizzle/drizzle-modules-validator.js +74 -0
  11. package/lib/entities-generator.js +36 -7
  12. package/lib/entity-templates/drizzle-model.template +28 -0
  13. package/lib/entity-templates/query-builder-model.template +17 -0
  14. package/lib/generators/base-generator.js +13 -0
  15. package/lib/generators/entities-generation.js +0 -13
  16. package/lib/generators/models-generation.js +179 -680
  17. package/lib/generators/relations-detection.js +171 -0
  18. package/lib/knex/knex-data-server.js +128 -0
  19. package/lib/knex/knex-driver.js +157 -0
  20. package/lib/knex/knex-modules-loader.js +28 -0
  21. package/lib/knex/knex-modules-validator.js +45 -0
  22. package/lib/kysely/kysely-data-server.js +128 -0
  23. package/lib/kysely/kysely-driver.js +176 -0
  24. package/lib/kysely/kysely-modules-loader.js +28 -0
  25. package/lib/kysely/kysely-modules-validator.js +53 -0
  26. package/lib/mikro-orm/mikro-orm-data-server.js +48 -29
  27. package/lib/mikro-orm/mikro-orm-driver.js +317 -108
  28. package/lib/mikro-orm/mikro-orm-models-generation.js +178 -0
  29. package/lib/mikro-orm/mikro-orm-modules-loader.js +50 -0
  30. package/lib/mikro-orm/mikro-orm-modules-validator.js +46 -0
  31. package/lib/mysql2-connection-config.js +38 -0
  32. package/lib/objection-js/objection-js-data-server.js +71 -125
  33. package/lib/objection-js/objection-js-driver.js +4 -1
  34. package/lib/objection-js/objection-js-models-generation.js +98 -0
  35. package/lib/objection-js/objection-modules-loader.js +28 -0
  36. package/lib/objection-js/objection-modules-validator.js +31 -0
  37. package/lib/package-resolver.js +45 -0
  38. package/lib/prisma/prisma-client-loader.js +11 -1
  39. package/lib/prisma/prisma-driver.js +16 -2
  40. package/lib/prisma/prisma-filter-processor.js +5 -6
  41. package/lib/prisma/prisma-models-generation.js +236 -0
  42. package/lib/prisma/prisma-schema-generator.js +5 -1
  43. package/lib/prisma/prisma-type-caster.js +14 -0
  44. package/lib/query-builder-driver.js +210 -0
  45. package/lib/query-builder-models-generation.js +80 -0
  46. package/lib/relations-loader.js +192 -0
  47. package/lib/type-mapper.js +38 -0
  48. package/package.json +8 -8
  49. package/tests/.env.test.example +6 -0
  50. package/tests/fixtures/categories-fixtures.js +8 -0
  51. package/tests/fixtures/expected-entities/entities/test-product-details-entity.js +10 -1
  52. package/tests/fixtures/expected-entities/entities-translations.js +2 -0
  53. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +13 -8
  54. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-product-details-model.js +30 -10
  55. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +15 -14
  56. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +13 -12
  57. package/tests/fixtures/expected-entities-prisma/models/prisma/test-product-details-model.js +3 -1
  58. package/tests/fixtures/product-details-fixtures.js +4 -1
  59. package/tests/fixtures/reviews-fixtures.js +2 -1
  60. package/tests/fixtures/sql/test-schema.sql +11 -2
  61. package/tests/fixtures/table-columns-fixtures.js +97 -0
  62. package/tests/integration/test-cross-driver-equivalence.js +286 -0
  63. package/tests/integration/test-nested-filters.js +265 -408
  64. package/tests/integration/test-relations.js +135 -0
  65. package/tests/integration/test-reldens-sample-data.js +255 -0
  66. package/tests/integration/test-reldens-shape-relations.js +292 -0
  67. package/tests/run-tests.js +111 -65
  68. package/tests/unit/test-drivers.js +7 -1
  69. package/tests/unit/test-models-generation.js +28 -18
  70. package/tests/utils/column-value-assert.js +139 -0
  71. package/tests/utils/entity-projection.js +111 -0
  72. package/tests/utils/observed-value.js +41 -0
  73. package/tests/utils/projection-difference.js +151 -0
  74. package/tests/utils/relations-shape-support.js +104 -0
  75. package/tests/utils/reldens-schema-loader.js +274 -0
  76. package/tests/utils/test-helpers.js +143 -13
  77. package/tests/utils/test-runner.js +26 -5
  78. package/tests/utils/whole-result-assert.js +243 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ *
3
+ * Reldens - QueryBuilderModelsGeneration
4
+ *
5
+ */
6
+
7
+ const { BaseGenerator } = require('./generators/base-generator');
8
+ const { sc } = require('@reldens/utils');
9
+
10
+ class QueryBuilderModelsGeneration extends BaseGenerator
11
+ {
12
+
13
+ constructor(relationsDetection)
14
+ {
15
+ super();
16
+ this.relationsDetection = relationsDetection;
17
+ }
18
+
19
+ modelTableName(tableName)
20
+ {
21
+ return tableName;
22
+ }
23
+
24
+ generateEntityProperties(columns, tableName, tableData)
25
+ {
26
+ return '';
27
+ }
28
+
29
+ generateFkMappings(tableName, tableData)
30
+ {
31
+ return '';
32
+ }
33
+
34
+ generateIdColumn(columns)
35
+ {
36
+ let primaryKeyColumn = this.relationsDetection.detectPrimaryKeyColumn(columns);
37
+ if(!primaryKeyColumn){
38
+ return '';
39
+ }
40
+ if('id' === primaryKeyColumn){
41
+ return '';
42
+ }
43
+ return '\n static get idColumn()\n {\n return \''+primaryKeyColumn+'\';\n }\n';
44
+ }
45
+
46
+ generateRelations(tableName, tableData, relationMetadata)
47
+ {
48
+ let allRelations = Object.assign(
49
+ {},
50
+ this.relationsDetection.detectForwardRelations(tableName, tableData),
51
+ this.relationsDetection.detectReverseRelations(tableName)
52
+ );
53
+ let relationKeys = Object.keys(allRelations);
54
+ if(0 === relationKeys.length){
55
+ return '';
56
+ }
57
+ let relationMappings = [];
58
+ for(let relationKey of relationKeys){
59
+ relationMappings.push(this.relationMappingDefinition(relationKey, allRelations[relationKey]));
60
+ }
61
+ return '\n static get relationMappings()\n {\n return {\n'
62
+ +relationMappings.join(',\n')
63
+ +'\n };\n }';
64
+ }
65
+
66
+ relationMappingDefinition(relationKey, relation)
67
+ {
68
+ let relatedTable = sc.get(relation, 'referencedTable', relation.referencingTable);
69
+ let relationMapping = ' '+relationKey+': {\n';
70
+ relationMapping += ' relation: \''+relation.relationType+'\',\n';
71
+ relationMapping += ' tableName: \''+relatedTable+'\',\n';
72
+ relationMapping += ' from: \''+relation.fromColumn+'\',\n';
73
+ relationMapping += ' to: \''+relation.toColumn+'\'\n';
74
+ relationMapping += ' }';
75
+ return relationMapping;
76
+ }
77
+
78
+ }
79
+
80
+ module.exports.QueryBuilderModelsGeneration = QueryBuilderModelsGeneration;
@@ -0,0 +1,192 @@
1
+ /**
2
+ *
3
+ * Reldens - RelationsLoader
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+
9
+ class RelationsLoader
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.driver = sc.get(props, 'driver', false);
15
+ this.server = sc.get(props, 'server', false);
16
+ this.relationMappings = sc.get(this.driver.rawModel, 'relationMappings', {});
17
+ }
18
+
19
+ normalizeRelations(relations)
20
+ {
21
+ if(sc.isString(relations)){
22
+ relations = this.driver.parseRelationsString(relations);
23
+ }
24
+ if(!sc.isArray(relations)){
25
+ return Object.keys(this.relationMappings);
26
+ }
27
+ if(0 === relations.length){
28
+ return Object.keys(this.relationMappings);
29
+ }
30
+ return relations;
31
+ }
32
+
33
+ relatedDriver(mapping)
34
+ {
35
+ if(!this.server){
36
+ Logger.error('Missing server on relations loader for table: '+mapping.tableName);
37
+ return false;
38
+ }
39
+ let entities = this.server.entityManager.entities;
40
+ for(let entityKey of Object.keys(entities)){
41
+ if(entities[entityKey].tableName() === mapping.tableName){
42
+ return entities[entityKey];
43
+ }
44
+ }
45
+ Logger.error('Related entity not found for table: '+mapping.tableName);
46
+ return false;
47
+ }
48
+
49
+ async populate(result, relations)
50
+ {
51
+ if(!result){
52
+ return result;
53
+ }
54
+ let rows = sc.isArray(result) ? result : [result];
55
+ if(0 === rows.length){
56
+ return result;
57
+ }
58
+ for(let relationPath of this.normalizeRelations(relations)){
59
+ await this.populateRelation(rows, relationPath);
60
+ }
61
+ return result;
62
+ }
63
+
64
+ async populateRelation(rows, relationPath)
65
+ {
66
+ let pathParts = relationPath.split('.');
67
+ let relationKey = pathParts.shift();
68
+ let mapping = sc.get(this.relationMappings, relationKey, false);
69
+ if(!mapping){
70
+ Logger.warning('Unknown relation "'+relationKey+'" on table "'+this.driver.tableName()+'".');
71
+ return false;
72
+ }
73
+ let relatedDriver = this.relatedDriver(mapping);
74
+ if(!relatedDriver){
75
+ return false;
76
+ }
77
+ let relatedRows = await this.fetchRelatedRows(rows, mapping, relatedDriver);
78
+ this.assignRelatedRows(rows, relatedRows, mapping, relationKey);
79
+ if(0 === pathParts.length){
80
+ return true;
81
+ }
82
+ if(0 === relatedRows.length){
83
+ return true;
84
+ }
85
+ await relatedDriver.relationsLoader.populate(relatedRows, [pathParts.join('.')]);
86
+ return true;
87
+ }
88
+
89
+ async fetchRelatedRows(rows, mapping, relatedDriver)
90
+ {
91
+ let values = [...new Set(
92
+ rows.map(row => row[mapping.from]).filter(value => sc.isNumber(value) || sc.isString(value))
93
+ )];
94
+ if(0 === values.length){
95
+ return [];
96
+ }
97
+ return await relatedDriver.selectRows(
98
+ {[mapping.to]: {operator: 'IN', value: values}},
99
+ Object.assign(relatedDriver.selectOptions(false), {offset: 0})
100
+ );
101
+ }
102
+
103
+ assignRelatedRows(rows, relatedRows, mapping, relationKey)
104
+ {
105
+ let isMany = 'HasManyRelation' === mapping.relation;
106
+ for(let row of rows){
107
+ let matches = relatedRows.filter(relatedRow => relatedRow[mapping.to] === row[mapping.from]);
108
+ if(isMany){
109
+ row[relationKey] = matches;
110
+ continue;
111
+ }
112
+ row[relationKey] = matches.shift() || null;
113
+ }
114
+ }
115
+
116
+ extractNestedRelations(params, relations)
117
+ {
118
+ let nested = {};
119
+ for(let relationKey of this.normalizeRelations(relations)){
120
+ if(!sc.hasOwn(this.relationMappings, relationKey)){
121
+ continue;
122
+ }
123
+ if(!sc.hasOwn(params, relationKey)){
124
+ continue;
125
+ }
126
+ nested[relationKey] = params[relationKey];
127
+ }
128
+ return nested;
129
+ }
130
+
131
+ async createWithRelations(params, relations)
132
+ {
133
+ let nested = this.extractNestedRelations(params, relations);
134
+ let nestedKeys = Object.keys(nested);
135
+ if(0 === nestedKeys.length){
136
+ return await this.driver.create(params);
137
+ }
138
+ let data = sc.omitProps(params, nestedKeys);
139
+ await this.createParents(data, nested);
140
+ let created = await this.driver.create(data);
141
+ if(!created){
142
+ return false;
143
+ }
144
+ await this.createChildren(created, nested);
145
+ return await this.populate(created, nestedKeys);
146
+ }
147
+
148
+ async createParents(data, nested)
149
+ {
150
+ for(let relationKey of Object.keys(nested)){
151
+ let mapping = sc.get(this.relationMappings, relationKey, false);
152
+ if('BelongsToOneRelation' !== mapping.relation){
153
+ continue;
154
+ }
155
+ let relatedDriver = this.relatedDriver(mapping);
156
+ if(!relatedDriver){
157
+ continue;
158
+ }
159
+ let parent = await relatedDriver.create(nested[relationKey]);
160
+ if(!parent){
161
+ continue;
162
+ }
163
+ data[mapping.from] = parent[mapping.to];
164
+ }
165
+ }
166
+
167
+ async createChildren(created, nested)
168
+ {
169
+ for(let relationKey of Object.keys(nested)){
170
+ let mapping = sc.get(this.relationMappings, relationKey, false);
171
+ if('BelongsToOneRelation' === mapping.relation){
172
+ continue;
173
+ }
174
+ let relatedDriver = this.relatedDriver(mapping);
175
+ if(!relatedDriver){
176
+ continue;
177
+ }
178
+ await this.createChildRows(created, nested[relationKey], mapping, relatedDriver);
179
+ }
180
+ }
181
+
182
+ async createChildRows(created, children, mapping, relatedDriver)
183
+ {
184
+ let childRows = sc.isArray(children) ? children : [children];
185
+ for(let childRow of childRows){
186
+ await relatedDriver.create(Object.assign({}, childRow, {[mapping.to]: created[mapping.from]}));
187
+ }
188
+ }
189
+
190
+ }
191
+
192
+ module.exports.RelationsLoader = RelationsLoader;
@@ -81,6 +81,44 @@ class TypeMapper
81
81
  return typeMap[dbType.toLowerCase()] || 'String';
82
82
  }
83
83
 
84
+ mapDbTypeToDrizzleBuilder(dbType)
85
+ {
86
+ let typeMap = {
87
+ 'int': 'int',
88
+ 'integer': 'int',
89
+ 'tinyint': 'tinyint',
90
+ 'smallint': 'smallint',
91
+ 'mediumint': 'mediumint',
92
+ 'bigint': 'bigint',
93
+ 'decimal': 'decimal',
94
+ 'float': 'float',
95
+ 'double': 'double',
96
+ 'varchar': 'varchar',
97
+ 'char': 'char',
98
+ 'text': 'text',
99
+ 'tinytext': 'text',
100
+ 'mediumtext': 'text',
101
+ 'longtext': 'text',
102
+ 'date': 'date',
103
+ 'datetime': 'datetime',
104
+ 'timestamp': 'timestamp',
105
+ 'time': 'time',
106
+ 'year': 'year',
107
+ 'boolean': 'boolean',
108
+ 'bool': 'boolean',
109
+ 'bit': 'binary',
110
+ 'json': 'json',
111
+ 'enum': 'mysqlEnum',
112
+ 'binary': 'binary',
113
+ 'varbinary': 'varbinary',
114
+ 'blob': 'binary',
115
+ 'tinyblob': 'binary',
116
+ 'mediumblob': 'binary',
117
+ 'longblob': 'binary'
118
+ };
119
+ return typeMap[dbType.toLowerCase()] || 'text';
120
+ }
121
+
84
122
  }
85
123
 
86
124
  module.exports.TypeMapper = new TypeMapper();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.117.0",
4
+ "version": "0.119.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -27,6 +27,11 @@
27
27
  "mikro-orm",
28
28
  "prisma",
29
29
  "prisma-orm",
30
+ "knex",
31
+ "kysely",
32
+ "drizzle",
33
+ "drizzle-orm",
34
+ "query-builder",
30
35
  "driver",
31
36
  "orm",
32
37
  "database",
@@ -51,14 +56,9 @@
51
56
  "test:driver": "node tests/run-tests.js --driver="
52
57
  },
53
58
  "dependencies": {
54
- "@mikro-orm/core": "7.2.0",
55
- "@mikro-orm/mongodb": "7.2.0",
56
- "@mikro-orm/mysql": "7.2.0",
57
- "@reldens/server-utils": "^0.57.0",
59
+ "@reldens/server-utils": "^0.58.0",
58
60
  "@reldens/utils": "^0.58.0",
59
61
  "knex": "3.3.0",
60
- "mysql": "2.18.1",
61
- "mysql2": "3.24.4",
62
- "objection": "3.1.5"
62
+ "mysql2": "3.24.4"
63
63
  }
64
64
  }
@@ -5,4 +5,10 @@ RELDENS_TEST_DB_PASSWORD=test_password
5
5
  RELDENS_TEST_DB_NAME=reldens_storage_test
6
6
  RELDENS_TEST_DB_CLIENT=mysql
7
7
  RELDENS_DB_URL=mysql://test_user:test_password@localhost:3306/reldens_storage_test
8
+ RELDENS_TEST_OBJECTION_ENABLED=0
9
+ RELDENS_TEST_MIKRO_ORM_ENABLED=0
8
10
  RELDENS_TEST_PRISMA_ENABLED=0
11
+ RELDENS_TEST_KYSELY_ENABLED=0
12
+ RELDENS_TEST_DRIZZLE_ENABLED=0
13
+ RELDENS_TEST_MIGRATIONS_PATH=
14
+ RELDENS_TEST_RELDENS_DB_NAME=reldens_storage_test_full
@@ -154,6 +154,14 @@ module.exports.CategoriesFixtures = {
154
154
  display_order: 2,
155
155
  slug: 'relations-test-2'
156
156
  },
157
+ category_cross_driver_create: {
158
+ id: 1700,
159
+ name: 'Cross Driver Create',
160
+ description: 'Category created with the same explicit id by every driver',
161
+ is_active: 1,
162
+ display_order: 7,
163
+ slug: 'cross-driver-create'
164
+ },
157
165
  category_create_nested: {
158
166
  name: 'Nested Create Category',
159
167
  description: 'Category for createWithRelations test',
@@ -35,6 +35,14 @@ class TestProductDetailsEntity extends EntityProperties
35
35
  dimensions: {
36
36
  dbType: 'varchar'
37
37
  },
38
+ customData: {
39
+ type: 'textarea',
40
+ dbType: 'text'
41
+ },
42
+ useTimeOut: {
43
+ type: 'number',
44
+ dbType: 'int'
45
+ },
38
46
  created_at: {
39
47
  type: 'datetime',
40
48
  dbType: 'timestamp'
@@ -47,7 +55,8 @@ class TestProductDetailsEntity extends EntityProperties
47
55
  let propertiesKeys = Object.keys(properties);
48
56
  let showProperties = propertiesKeys;
49
57
  let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
50
- let listProperties = propertiesKeys;
58
+ let listProperties = [...propertiesKeys];
59
+ listProperties.splice(listProperties.indexOf('customData'), 1);
51
60
  return {
52
61
  showProperties,
53
62
  editProperties,
@@ -27,6 +27,8 @@ module.exports.entitiesTranslations = {
27
27
  'product_id': 'Product ID',
28
28
  'weight': 'Weight',
29
29
  'dimensions': 'Dimensions',
30
+ 'customData': 'CustomData',
31
+ 'useTimeOut': 'UseTimeOut',
30
32
  'created_at': 'Created At',
31
33
  'updated_at': 'Updated At'
32
34
  },
@@ -34,14 +34,19 @@ const schema = new EntitySchema({
34
34
  class: TestCategoriesModel,
35
35
  tableName: 'test_categories',
36
36
  properties: {
37
- id: { type: 'number', primary: true },
38
- name: { type: 'string' },
39
- slug: { type: 'string' },
40
- description: { type: 'string', nullable: true },
41
- is_active: { type: 'number', nullable: true },
42
- display_order: { type: 'number', nullable: true },
43
- created_at: { type: 'Date', nullable: true },
44
- updated_at: { type: 'Date', nullable: true },
37
+ id: { type: 'int', primary: true },
38
+ name: { type: 'varchar' },
39
+ slug: { type: 'varchar' },
40
+ description: { type: 'text', nullable: true },
41
+ is_active: { type: 'tinyint', nullable: true },
42
+ display_order: { type: 'int', nullable: true },
43
+ created_at: { type: 'timestamp', nullable: true },
44
+ updated_at: { type: 'timestamp', nullable: true },
45
+ related_test_product_details: {
46
+ kind: '1:m',
47
+ entity: () => require('./test-product-details-model').TestProductDetailsModel,
48
+ mappedBy: 'related_test_categories'
49
+ },
45
50
  related_test_products: {
46
51
  kind: '1:m',
47
52
  entity: () => require('./test-products-model').TestProductsModel,
@@ -10,20 +10,23 @@ const { EntitySchema } = MikroOrmCore;
10
10
  class TestProductDetailsModel
11
11
  {
12
12
 
13
- constructor(id, product_id, weight, dimensions, created_at, updated_at)
13
+ constructor(id, product_id, weight, dimensions, customData, useTimeOut, category_id, created_at, updated_at)
14
14
  {
15
15
  this.id = id;
16
16
  this.product_id = product_id;
17
17
  this.weight = weight;
18
18
  this.dimensions = dimensions;
19
+ this.customData = customData;
20
+ this.useTimeOut = useTimeOut;
21
+ this.category_id = category_id;
19
22
  this.created_at = created_at;
20
23
  this.updated_at = updated_at;
21
24
  }
22
25
 
23
26
  static createByProps(props)
24
27
  {
25
- const {id, product_id, weight, dimensions, created_at, updated_at} = props;
26
- return new this(id, product_id, weight, dimensions, created_at, updated_at);
28
+ const {id, product_id, weight, dimensions, customData, useTimeOut, category_id, created_at, updated_at} = props;
29
+ return new this(id, product_id, weight, dimensions, customData, useTimeOut, category_id, created_at, updated_at);
27
30
  }
28
31
 
29
32
  }
@@ -32,16 +35,27 @@ const schema = new EntitySchema({
32
35
  class: TestProductDetailsModel,
33
36
  tableName: 'test_product_details',
34
37
  properties: {
35
- id: { type: 'number', primary: true },
36
- product_id: { type: 'number', persist: false },
37
- weight: { type: 'number', nullable: true },
38
- dimensions: { type: 'string', nullable: true },
39
- created_at: { type: 'Date', nullable: true },
40
- updated_at: { type: 'Date', nullable: true },
38
+ id: { type: 'int', primary: true },
39
+ product_id: { type: 'int' },
40
+ weight: { type: 'decimal', nullable: true },
41
+ dimensions: { type: 'varchar', nullable: true },
42
+ customData: { type: 'text', nullable: true },
43
+ useTimeOut: { type: 'int', nullable: true },
44
+ category_id: { type: 'int', nullable: true },
45
+ created_at: { type: 'timestamp', nullable: true },
46
+ updated_at: { type: 'timestamp', nullable: true },
41
47
  related_test_products: {
42
48
  kind: '1:1',
43
49
  entity: () => require('./test-products-model').TestProductsModel,
44
- joinColumns: ['product_id']
50
+ joinColumns: ['product_id'],
51
+ persist: false
52
+ },
53
+ related_test_categories: {
54
+ kind: 'm:1',
55
+ entity: () => require('./test-categories-model').TestCategoriesModel,
56
+ joinColumns: ['category_id'],
57
+ nullable: true,
58
+ persist: false
45
59
  }
46
60
  },
47
61
  });
@@ -51,6 +65,12 @@ schema._fkMappings = {
51
65
  "entityName": "TestProductsModel",
52
66
  "referencedColumn": "id",
53
67
  "nullable": false
68
+ },
69
+ "category_id": {
70
+ "relationKey": "related_test_categories",
71
+ "entityName": "TestCategoriesModel",
72
+ "referencedColumn": "id",
73
+ "nullable": true
54
74
  }
55
75
  };
56
76
  module.exports = {
@@ -39,23 +39,24 @@ const schema = new EntitySchema({
39
39
  class: TestProductsModel,
40
40
  tableName: 'test_products',
41
41
  properties: {
42
- id: { type: 'number', primary: true },
43
- category_id: { type: 'number', persist: false },
44
- name: { type: 'string' },
45
- sku: { type: 'string' },
46
- description: { type: 'string', nullable: true },
47
- price: { type: 'number' },
48
- stock_quantity: { type: 'number', nullable: true },
49
- is_featured: { type: 'number', nullable: true },
50
- metadata: { type: 'object', nullable: true },
51
- tags: { type: 'string', nullable: true },
52
- status: { type: 'undefined', nullable: true },
53
- created_at: { type: 'Date', nullable: true },
54
- updated_at: { type: 'Date', nullable: true },
42
+ id: { type: 'int', primary: true },
43
+ category_id: { type: 'int' },
44
+ name: { type: 'varchar' },
45
+ sku: { type: 'varchar' },
46
+ description: { type: 'text', nullable: true },
47
+ price: { type: 'decimal' },
48
+ stock_quantity: { type: 'int', nullable: true },
49
+ is_featured: { type: 'tinyint', nullable: true },
50
+ metadata: { type: 'json', nullable: true },
51
+ tags: { type: 'varchar', nullable: true },
52
+ status: { type: 'enum', nullable: true },
53
+ created_at: { type: 'timestamp', nullable: true },
54
+ updated_at: { type: 'timestamp', nullable: true },
55
55
  related_test_categories: {
56
56
  kind: 'm:1',
57
57
  entity: () => require('./test-categories-model').TestCategoriesModel,
58
- joinColumns: ['category_id']
58
+ joinColumns: ['category_id'],
59
+ persist: false
59
60
  },
60
61
  related_test_product_details: {
61
62
  kind: '1:1',
@@ -37,21 +37,22 @@ const schema = new EntitySchema({
37
37
  class: TestReviewsModel,
38
38
  tableName: 'test_reviews',
39
39
  properties: {
40
- id: { type: 'number', primary: true },
41
- product_id: { type: 'number', persist: false },
42
- reviewer_name: { type: 'string' },
43
- reviewer_email: { type: 'string' },
44
- rating: { type: 'number' },
45
- title: { type: 'string', nullable: true },
46
- comment: { type: 'string', nullable: true },
47
- is_verified: { type: 'number', nullable: true },
48
- helpful_count: { type: 'number', nullable: true },
49
- created_at: { type: 'Date', nullable: true },
50
- updated_at: { type: 'Date', nullable: true },
40
+ id: { type: 'int', primary: true },
41
+ product_id: { type: 'int' },
42
+ reviewer_name: { type: 'varchar' },
43
+ reviewer_email: { type: 'varchar' },
44
+ rating: { type: 'tinyint' },
45
+ title: { type: 'varchar', nullable: true },
46
+ comment: { type: 'text', nullable: true },
47
+ is_verified: { type: 'tinyint', nullable: true },
48
+ helpful_count: { type: 'int', nullable: true },
49
+ created_at: { type: 'timestamp', nullable: true },
50
+ updated_at: { type: 'timestamp', nullable: true },
51
51
  related_test_products: {
52
52
  kind: 'm:1',
53
53
  entity: () => require('./test-products-model').TestProductsModel,
54
- joinColumns: ['product_id']
54
+ joinColumns: ['product_id'],
55
+ persist: false
55
56
  }
56
57
  },
57
58
  });
@@ -7,12 +7,14 @@
7
7
  class TestProductDetailsModel
8
8
  {
9
9
 
10
- constructor(id, product_id, weight, dimensions, created_at, updated_at)
10
+ constructor(id, product_id, weight, dimensions, customData, useTimeOut, created_at, updated_at)
11
11
  {
12
12
  this.id = id;
13
13
  this.product_id = product_id;
14
14
  this.weight = weight;
15
15
  this.dimensions = dimensions;
16
+ this.customData = customData;
17
+ this.useTimeOut = useTimeOut;
16
18
  this.created_at = created_at;
17
19
  this.updated_at = updated_at;
18
20
  }
@@ -10,7 +10,10 @@ module.exports.ProductDetailsFixtures = {
10
10
  id: 4600,
11
11
  product_id: 2600,
12
12
  weight: 1.25,
13
- dimensions: '10x20x30'
13
+ dimensions: '10x20x30',
14
+ customData: '{"origin":"fixture"}',
15
+ useTimeOut: 45,
16
+ total_views: 9007199254740991
14
17
  },
15
18
  product_details_create_nested: {
16
19
  weight: 2.5,
@@ -26,7 +26,8 @@ module.exports.ReviewsFixtures = {
26
26
  title: 'Good value',
27
27
  comment: 'Works as expected',
28
28
  is_verified: 1,
29
- helpful_count: 5
29
+ helpful_count: 5,
30
+ category_slug: 'relations-test-1'
30
31
  },
31
32
  review_relations_2: {
32
33
  id: 3601,
@@ -45,11 +45,17 @@ CREATE TABLE `test_product_details` (
45
45
  `product_id` INT UNSIGNED NOT NULL,
46
46
  `weight` DECIMAL(10,2) NULL,
47
47
  `dimensions` VARCHAR(100) NULL,
48
+ `customData` TEXT NULL,
49
+ `useTimeOut` INT NULL,
50
+ `total_views` BIGINT NULL,
51
+ `category_id` INT UNSIGNED NULL DEFAULT NULL,
48
52
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
49
53
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
50
54
  PRIMARY KEY (`id`),
51
55
  UNIQUE KEY `product_id` (`product_id`),
52
- CONSTRAINT `fk_product_details_product` FOREIGN KEY (`product_id`) REFERENCES `test_products` (`id`) ON DELETE CASCADE
56
+ KEY `category_id` (`category_id`),
57
+ CONSTRAINT `fk_product_details_product` FOREIGN KEY (`product_id`) REFERENCES `test_products` (`id`) ON DELETE CASCADE,
58
+ CONSTRAINT `fk_product_details_category` FOREIGN KEY (`category_id`) REFERENCES `test_categories` (`id`) ON DELETE SET NULL
53
59
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
54
60
 
55
61
  CREATE TABLE `test_reviews` (
@@ -62,11 +68,14 @@ CREATE TABLE `test_reviews` (
62
68
  `comment` TEXT NULL,
63
69
  `is_verified` TINYINT(1) NOT NULL DEFAULT 0,
64
70
  `helpful_count` INT NOT NULL DEFAULT 0,
71
+ `category_slug` VARCHAR(100) NULL,
65
72
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
66
73
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
67
74
  PRIMARY KEY (`id`),
68
75
  KEY `product_id` (`product_id`),
69
76
  KEY `rating` (`rating`),
70
77
  KEY `is_verified` (`is_verified`),
71
- CONSTRAINT `fk_reviews_product` FOREIGN KEY (`product_id`) REFERENCES `test_products` (`id`) ON DELETE CASCADE
78
+ KEY `category_slug` (`category_slug`),
79
+ CONSTRAINT `fk_reviews_product` FOREIGN KEY (`product_id`) REFERENCES `test_products` (`id`) ON DELETE CASCADE,
80
+ CONSTRAINT `fk_reviews_category_slug` FOREIGN KEY (`category_slug`) REFERENCES `test_categories` (`slug`) ON DELETE SET NULL
72
81
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;