@reldens/storage 0.87.0 → 0.89.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 (65) hide show
  1. package/.claude/driver-methods-complete-analysis.md +1978 -0
  2. package/.claude/test-architecture.md +973 -0
  3. package/CLAUDE.md +141 -7
  4. package/README.md +14 -2
  5. package/bin/reldens-storage.js +1 -1
  6. package/lib/base-driver.js +8 -0
  7. package/lib/entities-generator.js +1 -3
  8. package/lib/entity-templates/mikro-orm-model.template +2 -1
  9. package/lib/generators/models-generation.js +126 -5
  10. package/lib/mikro-orm/mikro-orm-data-server.js +24 -10
  11. package/lib/mikro-orm/mikro-orm-driver.js +184 -116
  12. package/lib/objection-js/objection-js-driver.js +111 -19
  13. package/lib/prisma/prisma-client-loader.js +4 -0
  14. package/lib/prisma/prisma-data-server.js +3 -2
  15. package/lib/prisma/prisma-driver.js +12 -10
  16. package/lib/prisma/prisma-filter-processor.js +193 -146
  17. package/lib/prisma/prisma-metadata-loader.js +0 -4
  18. package/lib/prisma/prisma-relation-resolver.js +292 -267
  19. package/lib/prisma/prisma-type-caster.js +280 -260
  20. package/package.json +15 -6
  21. package/tests/.env.test.example +7 -0
  22. package/tests/fixtures/categories-fixtures.js +164 -0
  23. package/tests/fixtures/expected-entities-mikro-orm/entities/test-categories-entity.js +70 -0
  24. package/tests/fixtures/expected-entities-mikro-orm/entities/test-products-entity.js +95 -0
  25. package/tests/fixtures/expected-entities-mikro-orm/entities/test-reviews-entity.js +84 -0
  26. package/tests/fixtures/expected-entities-mikro-orm/entities-config.js +17 -0
  27. package/tests/fixtures/expected-entities-mikro-orm/entities-translations.js +13 -0
  28. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/registered-models-mikro-orm.js +23 -0
  29. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +57 -0
  30. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +79 -0
  31. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +70 -0
  32. package/tests/fixtures/expected-entities-objection-js/entities/test-categories-entity.js +70 -0
  33. package/tests/fixtures/expected-entities-objection-js/entities/test-products-entity.js +95 -0
  34. package/tests/fixtures/expected-entities-objection-js/entities/test-reviews-entity.js +84 -0
  35. package/tests/fixtures/expected-entities-objection-js/entities-config.js +17 -0
  36. package/tests/fixtures/expected-entities-objection-js/entities-translations.js +13 -0
  37. package/tests/fixtures/expected-entities-objection-js/models/objection-js/registered-models-objection-js.js +23 -0
  38. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-categories-model.js +33 -0
  39. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-products-model.js +42 -0
  40. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-reviews-model.js +33 -0
  41. package/tests/fixtures/expected-entities-prisma/entities/test-categories-entity.js +70 -0
  42. package/tests/fixtures/expected-entities-prisma/entities/test-products-entity.js +95 -0
  43. package/tests/fixtures/expected-entities-prisma/entities/test-reviews-entity.js +84 -0
  44. package/tests/fixtures/expected-entities-prisma/entities-config.js +17 -0
  45. package/tests/fixtures/expected-entities-prisma/entities-translations.js +13 -0
  46. package/tests/fixtures/expected-entities-prisma/models/prisma/registered-models-prisma.js +23 -0
  47. package/tests/fixtures/expected-entities-prisma/models/prisma/test-categories-model.js +43 -0
  48. package/tests/fixtures/expected-entities-prisma/models/prisma/test-products-model.js +50 -0
  49. package/tests/fixtures/expected-entities-prisma/models/prisma/test-reviews-model.js +46 -0
  50. package/tests/fixtures/products-fixtures.js +119 -0
  51. package/tests/fixtures/reviews-fixtures.js +42 -0
  52. package/tests/fixtures/sql/test-schema.sql +59 -0
  53. package/tests/integration/test-drivers.js +257 -0
  54. package/tests/integration/test-nested-filters.js +408 -0
  55. package/tests/integration/test-relations.js +349 -0
  56. package/tests/run-tests.js +213 -0
  57. package/tests/unit/test-drivers.js +89 -0
  58. package/tests/unit/test-entities-generator.js +533 -0
  59. package/tests/unit/test-entity-manager.js +128 -0
  60. package/tests/unit/test-type-mapper.js +232 -0
  61. package/tests/utils/custom-reporter.js +73 -0
  62. package/tests/utils/driver-registry.js +144 -0
  63. package/tests/utils/prisma-subprocess-worker.js +150 -0
  64. package/tests/utils/test-helpers.js +726 -0
  65. package/tests/utils/test-runner.js +63 -0
package/CLAUDE.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
4
 
5
+ ## ⚠️ CRITICAL: The Library Works - Fix The Tests
6
+
7
+ **This package is battle-tested in hundreds of production projects handling thousands of API calls successfully.**
8
+
9
+ **Default Assumption: Tests are failing because TEST CODE is incorrect, NOT because library code is broken.**
10
+
11
+ **Your Primary Task:**
12
+ - Fix test files in `tests/` directory to properly use the library
13
+ - Study working examples from reldens-cms and reldens main project
14
+ - Make all tests pass by correcting how they call the library APIs
15
+
16
+ **If You Suspect a Library Bug:**
17
+ - Present complete proof with production evidence
18
+ - Wait for user confirmation before proposing any library changes
19
+ - Do NOT modify `lib/` code without explicit approval
20
+
21
+ ---
22
+
5
23
  ## Package Overview
6
24
 
7
25
  **@reldens/storage** is the database abstraction layer for Reldens. It provides:
@@ -52,6 +70,7 @@ npx reldens-storage-prisma --host=<host> --database=<db> --user=<user> --passwor
52
70
  **BaseDriver** (`lib/base-driver.js`):
53
71
  - Abstract base class for ORM drivers
54
72
  - Provides common interface for all database operations
73
+ - Stores operator mapping for all drivers (`this.operatorsMap`)
55
74
  - Key methods (all must be implemented by drivers):
56
75
  - CRUD: `create()`, `update()`, `delete()`, `upsert()`
57
76
  - Read: `load()`, `loadById()`, `loadAll()`, `loadOne()`
@@ -59,6 +78,9 @@ npx reldens-storage-prisma --host=<host> --database=<db> --user=<user> --passwor
59
78
  - Count: `count()`, `countWithRelations()`
60
79
  - Query: `rawQuery()`, `executeCustomQuery()`
61
80
  - Helpers: `parseRelationsString()`, `isJsonField()`
81
+ - **Operator Mapping**: Maps string operators to SQL operators
82
+ - `GT` → `>`, `GTE` → `>=`, `LT` → `<`, `LTE` → `<=`, `NE` → `!=`, `EQ` → `=`
83
+ - Available to all drivers via `this.operatorsMap`
62
84
 
63
85
  **BaseDataServer** (`lib/base-data-server.js`):
64
86
  - Abstract base class for data servers
@@ -93,8 +115,11 @@ npx reldens-storage-prisma --host=<host> --database=<db> --user=<user> --passwor
93
115
  - Uses `withGraphFetched()` for eager loading
94
116
  - Supports relation modifiers (orderBy, limit)
95
117
  - JSON field handling with `castText()` for LIKE queries
96
- - Filter operators: OR, IN, NOT, LIKE
118
+ - **Filter operators** (case-insensitive): OR, IN, NOT, LIKE, GT, GTE, LT, LTE, NE, EQ
119
+ - **Operator conversion**: String operators converted to uppercase before processing
120
+ - **Nested filtering**: AND/OR operators support nested conditions with proper SQL grouping
97
121
  - Methods: `appendFilters()`, `appendRelationsToQuery()`
122
+ - **Upsert behavior**: Check if record exists → update if found, create if not
98
123
 
99
124
  **MikroORM Driver** (`lib/mikro-orm/`):
100
125
  - `mikro-orm-driver.js`: Driver implementation
@@ -122,6 +147,8 @@ npx reldens-storage-prisma --host=<host> --database=<db> --user=<user> --passwor
122
147
  - Binary targets configuration
123
148
  - Data proxy support
124
149
  - Windows permission error handling
150
+ - **Prisma.DbNull handling**: PrismaDataServer passes `Prisma.DbNull` to driver, which passes it to type caster
151
+ - **Type caster isolation**: PrismaTypeCaster never requires `@prisma/client` directly, receives `prismaDbNull` as prop
125
152
 
126
153
  ### Generators
127
154
 
@@ -413,8 +440,8 @@ PrismaClientLoader.load(projectPath, customPath, connectionData)
413
440
 
414
441
  **Parameters:**
415
442
  - `projectPath` (string): Project root directory path
416
- - `customPath` (string|null): Optional custom path to Prisma client (overrides default)
417
- - `connectionData` (object): Database connection configuration with properties:
443
+ - `customPath` (string|null): Optional custom path to a Prisma client (overrides default)
444
+ - `connectionData` (object|null): Optional database connection configuration with properties:
418
445
  - `client` (string): Database client type (mysql, postgresql, etc.)
419
446
  - `user` (string): Database username
420
447
  - `password` (string): Database password
@@ -426,13 +453,27 @@ PrismaClientLoader.load(projectPath, customPath, connectionData)
426
453
 
427
454
  **Behavior:**
428
455
  - If `customPath` is provided, uses that path
429
- - Otherwise uses default path: `projectPath/prisma/client`
456
+ - Otherwise, uses a default path: `projectPath/prisma/client`
430
457
  - Validates that Prisma Client exists at the path
431
458
  - Requires `prismaModule.PrismaClient` export
432
- - Builds connection string from connectionData
433
- - Returns initialized PrismaClient with connection configuration
459
+ - If `connectionData` is null: Uses default connection from Prisma schema datasource
460
+ - If `connectionData` is provided: Builds custom connection string and overrides datasource
461
+ - Returns initialized PrismaClient instance
462
+
463
+ **Usage Examples:**
464
+
465
+ Using the default connection from schema:
466
+ ```javascript
467
+ const { PrismaClientLoader } = require('@reldens/storage');
468
+
469
+ const prismaClient = PrismaClientLoader.load(process.cwd(), null, null);
470
+ if(!prismaClient){
471
+ console.error('Failed to load Prisma client');
472
+ process.exit(1);
473
+ }
474
+ ```
434
475
 
435
- **Usage Example:**
476
+ Using custom connection:
436
477
  ```javascript
437
478
  const { PrismaClientLoader } = require('@reldens/storage');
438
479
 
@@ -458,3 +499,96 @@ if(!prismaClient){
458
499
  **Used By:**
459
500
  - `bin/reldens-storage.js`: CLI entity generator
460
501
  - External packages: `@reldens/cms` CLI tools (update-password, generate-entities, generate-sitemap)
502
+
503
+ ## Test Suite Architecture
504
+
505
+ **CRITICAL:** For detailed test suite architecture, execution flow, and lifecycle hooks, see: `.claude/test-architecture.md`
506
+
507
+ ### The Golden Rule: Connect Once, Test Many
508
+
509
+ **DO NOT** create database connections, tables, or entities in `beforeEach()` hooks. These operations happen **ONCE per driver** in `before()` hooks.
510
+
511
+ **Correct test lifecycle:**
512
+ 1. `before()` hook - RUNS ONCE per driver:
513
+ - Connect to database
514
+ - Create tables via SQL
515
+ - Generate entities (introspects database, creates models)
516
+ - For Prisma: Generate schema → Generate client → Reconnect
517
+ - Get repository references
518
+
519
+ 2. `beforeEach()` hook - RUNS BEFORE EACH TEST:
520
+ - DELETE data from tables (fast cleanup)
521
+ - Never drop/recreate tables
522
+ - Never reconnect to database
523
+
524
+ 3. `after()` hook - RUNS ONCE per driver:
525
+ - DROP all tables
526
+ - Disconnect from database
527
+
528
+ ### Test Database Operations
529
+
530
+ - **cleanDatabase()**: Uses DELETE statements to clear data between tests (fast)
531
+ - **dropTestTables()**: Uses DROP TABLE statements for final cleanup (slow, runs once)
532
+ - **executeRawSQL()**: Creates tables from SQL schema file (slow, runs once)
533
+
534
+ ### DataServer Flow
535
+
536
+ All three drivers follow this pattern:
537
+
538
+ ```
539
+ 1. new DataServer({config, rawEntities})
540
+
541
+ 2. await dataServer.connect()
542
+
543
+ 3. await executeRawSQL(dataServer, schemaSql) ← Tables must exist before next step
544
+
545
+ 4. await generateTestEntities(dataServer, driverName)
546
+ ├─ Introspects database schema
547
+ ├─ Creates models from tables
548
+ ├─ [Prisma only]: Generate schema + client + reconnect
549
+ └─ Calls dataServer.generateEntities()
550
+
551
+ 5. dataServer.getEntity('entityName') ← Returns repository
552
+ ```
553
+
554
+ ### Why Tables Must Exist Before Entity Generation
555
+
556
+ - `generateTestEntities()` calls `fetchEntitiesFromDatabase()`
557
+ - This queries MySQL `information_schema` for actual table structures
558
+ - Without tables, introspection returns empty/null
559
+ - Entity generation fails without table metadata
560
+
561
+ ### Prisma Special Handling
562
+
563
+ Prisma requires additional subprocess steps during entity generation:
564
+
565
+ 1. Generate `schema.prisma` file from database (subprocess: `npx reldens-storage-prisma`)
566
+ 2. Generate PrismaClient code (subprocess: `npx prisma generate`)
567
+ 3. Disconnect old client
568
+ 4. Clear Node.js module cache
569
+ 5. Reconnect with newly generated client
570
+ 6. Now PrismaClient has models for the current database schema
571
+
572
+ This happens ONCE during the `before()` hook inside `generateTestEntities()`.
573
+
574
+ **For critical patterns including:**
575
+ - Why tests use dynamic models (not generated models)
576
+ - Prisma client loading pattern
577
+ - Common pitfalls and solutions
578
+
579
+ **See:** `.claude/test-architecture.md` - Critical Patterns section
580
+
581
+ ### Test Files
582
+
583
+ **Integration tests:**
584
+ - `tests/integration/test-cross-driver-compatibility.js`: Full CRUD cycle for all 3 drivers
585
+ - `tests/integration/test-nested-filters.js`: Complex filter syntax (AND/OR/NOT/IN/LIKE)
586
+ - `tests/integration/test-relations.js`: Relation loading and nested relations
587
+
588
+ **Test utilities:**
589
+ - `tests/utils/test-helpers.js`: Database setup, entity generation, cleanup utilities
590
+ - `tests/utils/test-runner.js`: Test framework with suite/group/test methods, uses Logger for output
591
+
592
+ **All integration test files use the correct lifecycle pattern as of 2026-01-18.**
593
+
594
+ For complete details on test architecture, common issues, performance comparison, and troubleshooting, see `.claude/test-architecture.md`.
package/README.md CHANGED
@@ -186,6 +186,18 @@ Note: The PrismaDataServer requires the Prisma schema to be generated first. Mak
186
186
 
187
187
  If you need to load a Prisma Client instance in your CLI tools or applications:
188
188
 
189
+ Using the default connection from schema:
190
+ ```javascript
191
+ const { PrismaClientLoader } = require('@reldens/storage');
192
+
193
+ const prismaClient = PrismaClientLoader.load(process.cwd(), null, null);
194
+ if(!prismaClient){
195
+ console.error('Failed to load Prisma client');
196
+ process.exit(1);
197
+ }
198
+ ```
199
+
200
+ Using custom connection:
189
201
  ```javascript
190
202
  const { PrismaClientLoader } = require('@reldens/storage');
191
203
 
@@ -210,8 +222,8 @@ if(!prismaClient){
210
222
 
211
223
  Parameters:
212
224
  - `projectPath`: Project root directory
213
- - `customPath`: Optional custom path to Prisma client (null for default)
214
- - `connectionData`: Database connection configuration object
225
+ - `customPath`: Optional custom path to a Prisma client (null for default)
226
+ - `connectionData`: Optional database connection configuration object (null to use schema default)
215
227
 
216
228
  ## Custom Drivers
217
229
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  *
@@ -21,6 +21,14 @@ class BaseDriver
21
21
  this.sortBy = sc.get(props, 'sortBy', false);
22
22
  this.sortDirection = sc.get(props, 'sortDirection', 'ASC');
23
23
  this.entityConfig = sc.get(props, 'entityConfig', {});
24
+ this.operatorsMap = {
25
+ 'GT': '>',
26
+ 'GTE': '>=',
27
+ 'LT': '<',
28
+ 'LTE': '<=',
29
+ 'NE': '!=',
30
+ 'EQ': '='
31
+ };
24
32
  }
25
33
 
26
34
  databaseName()
@@ -402,9 +402,7 @@ class EntitiesGenerator
402
402
  Logger.critical('Failed to fetch driver key.');
403
403
  return false;
404
404
  }
405
- if('objection-js' === driverKey){
406
- this.modelsGeneration.setAllTablesData(tables);
407
- }
405
+ this.modelsGeneration.setAllTablesData(tables);
408
406
  let tablesToGenerate = this.filterTablesToGenerate(tables, driverKey);
409
407
  if(0 === Object.keys(tablesToGenerate).length && !this.isOverride){
410
408
  return true;
@@ -25,11 +25,12 @@ class {{modelClassName}}
25
25
 
26
26
  const schema = new EntitySchema({
27
27
  class: {{modelClassName}},
28
+ tableName: '{{tableName}}',
28
29
  properties: {
29
30
  {{entityPropertiesDefinition}}
30
31
  },
31
32
  });
32
-
33
+ {{fkMappingsAttachment}}
33
34
  module.exports = {
34
35
  {{modelClassName}},
35
36
  entity: {{modelClassName}},
@@ -20,6 +20,12 @@ class ModelsGeneration extends BaseGenerator
20
20
  this.generatedModels = {};
21
21
  this.allTablesData = {};
22
22
  this.removeIdFromMultipleRelations = sc.get(props, 'removeIdFromMultipleRelations', true);
23
+ this.mikroOrmRelationKindMap = {
24
+ 'BelongsToOneRelation': 'm:1',
25
+ 'HasOneRelation': '1:1',
26
+ 'HasManyRelation': '1:m',
27
+ 'ManyToManyRelation': 'm:n'
28
+ };
23
29
  }
24
30
 
25
31
  async generateModelFile(tableName, tableData, driverKey, entityInfo, relationMetadata = {})
@@ -40,7 +46,12 @@ class ModelsGeneration extends BaseGenerator
40
46
  .map(columnName => 'this.'+columnName+' = '+columnName+';')
41
47
  .join('\n ');
42
48
  let modelRelations = this.generateModelRelations(tableName, tableData, relationMetadata, driverKey);
43
- let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
49
+ let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey, tableName, tableData);
50
+ let fkMappingsJson = this.generateFkMappings(tableName, tableData, driverKey);
51
+ let fkMappingsAttachment = '';
52
+ if('mikro-orm' === driverKey && fkMappingsJson){
53
+ fkMappingsAttachment = 'schema._fkMappings = '+fkMappingsJson+';';
54
+ }
44
55
  let idColumn = this.generateIdColumn(tableData.columns, driverKey);
45
56
  let replacements = {
46
57
  modelClassName,
@@ -49,6 +60,7 @@ class ModelsGeneration extends BaseGenerator
49
60
  modelPropertiesConstructor,
50
61
  modelRelations,
51
62
  entityPropertiesDefinition,
63
+ fkMappingsAttachment,
52
64
  idColumn
53
65
  };
54
66
  let modelContent = this.applyReplacements(modelTemplateContent, replacements);
@@ -461,24 +473,85 @@ class ModelsGeneration extends BaseGenerator
461
473
  return 'HasManyRelation';
462
474
  }
463
475
 
464
- getEntityPropertiesDefinition(columns, driverKey)
476
+ getEntityPropertiesDefinition(columns, driverKey, tableName = null, tableData = null)
465
477
  {
466
478
  if('mikro-orm' === driverKey){
467
479
  let entityProps = [];
480
+ if(!tableName || !tableData){
481
+ for(let columnName of Object.keys(columns)){
482
+ let column = columns[columnName];
483
+ let isPrimary = 'PRI' === column.key;
484
+ let type = TypeMapper.mapDbTypeToJsType(column.type);
485
+ let hasDefault = null !== column.default && undefined !== column.default;
486
+ let propDef = columnName+': { type: \''+type+'\'';
487
+ if(isPrimary){
488
+ propDef += ', primary: true';
489
+ }
490
+ if(column.nullable || hasDefault){
491
+ propDef += ', nullable: true';
492
+ }
493
+ propDef += ' }';
494
+ entityProps.push(propDef);
495
+ }
496
+ return entityProps.join(',\n ');
497
+ }
498
+ let relations = this.detectObjectionJsRelations(tableName, tableData);
499
+ let fkColumnsUsedByRelations = {};
500
+ for(let relationKey of Object.keys(relations)){
501
+ let relation = relations[relationKey];
502
+ if(relation.fromColumn){
503
+ fkColumnsUsedByRelations[relation.fromColumn] = true;
504
+ }
505
+ }
468
506
  for(let columnName of Object.keys(columns)){
469
507
  let column = columns[columnName];
508
+ if(fkColumnsUsedByRelations[columnName]){
509
+ let type = TypeMapper.mapDbTypeToJsType(column.type);
510
+ let propDef = columnName+': { type: \''+type+'\', persist: false }';
511
+ entityProps.push(propDef);
512
+ continue;
513
+ }
470
514
  let isPrimary = 'PRI' === column.key;
471
515
  let type = TypeMapper.mapDbTypeToJsType(column.type);
516
+ let hasDefault = null !== column.default && undefined !== column.default;
472
517
  let propDef = columnName+': { type: \''+type+'\'';
473
518
  if(isPrimary){
474
519
  propDef += ', primary: true';
475
520
  }
476
- if(column.nullable){
521
+ if(column.nullable || hasDefault){
477
522
  propDef += ', nullable: true';
478
523
  }
479
524
  propDef += ' }';
480
525
  entityProps.push(propDef);
481
526
  }
527
+ for(let relationKey of Object.keys(relations)){
528
+ let relation = relations[relationKey];
529
+ let relatedModelClass = sc.capitalizedCamelCase(relation.referencedTable)+'Model';
530
+ let kind = sc.get(this.mikroOrmRelationKindMap, relation.relationType, 'm:1');
531
+ let propDef = relationKey+': {\n';
532
+ propDef += ' kind: \''+kind+'\',\n';
533
+ propDef += ' entity: \''+relatedModelClass+'\'';
534
+ if(relation.fromColumn){
535
+ propDef += ',\n joinColumn: \''+relation.fromColumn+'\'';
536
+ }
537
+ propDef += '\n }';
538
+ entityProps.push(propDef);
539
+ }
540
+ let reverseRelations = this.detectReverseObjectionJsRelations(tableName);
541
+ for(let relationKey of Object.keys(reverseRelations)){
542
+ let relation = reverseRelations[relationKey];
543
+ let relatedModelClass = sc.capitalizedCamelCase(relation.referencingTable)+'Model';
544
+ let kind = sc.get(this.mikroOrmRelationKindMap, relation.relationType, '1:m');
545
+ let mappedBy = this.findMappedByKey(tableName, relation.referencingTable, relation);
546
+ let propDef = relationKey+': {\n';
547
+ propDef += ' kind: \''+kind+'\',\n';
548
+ propDef += ' entity: \''+relatedModelClass+'\'';
549
+ if(mappedBy){
550
+ propDef += ',\n mappedBy: \''+mappedBy+'\'';
551
+ }
552
+ propDef += '\n }';
553
+ entityProps.push(propDef);
554
+ }
482
555
  return entityProps.join(',\n ');
483
556
  }
484
557
  if('prisma' === driverKey){
@@ -502,6 +575,52 @@ class ModelsGeneration extends BaseGenerator
502
575
  return '';
503
576
  }
504
577
 
578
+ findMappedByKey(currentTable, referencingTable, reverseRelation)
579
+ {
580
+ if(!this.allTablesData[referencingTable]){
581
+ return 'related_'+currentTable;
582
+ }
583
+ let referencingTableData = this.allTablesData[referencingTable];
584
+ let forwardRelations = this.detectObjectionJsRelations(referencingTable, referencingTableData);
585
+ for(let forwardKey of Object.keys(forwardRelations)){
586
+ let forward = forwardRelations[forwardKey];
587
+ if(forward.referencedTable === currentTable && forward.toColumn === reverseRelation.fromColumn){
588
+ return forwardKey;
589
+ }
590
+ }
591
+ return 'related_'+currentTable;
592
+ }
593
+
594
+ generateFkMappings(tableName, tableData, driverKey)
595
+ {
596
+ if('mikro-orm' !== driverKey){
597
+ return '';
598
+ }
599
+ if(!tableName || !tableData){
600
+ return '';
601
+ }
602
+ let fkMappings = {};
603
+ let referenceCounts = this.countReferencesPerTable(tableData);
604
+ for(let columnName of Object.keys(tableData.columns)){
605
+ let column = tableData.columns[columnName];
606
+ if(!sc.hasOwn(column, 'referencedTable')){
607
+ continue;
608
+ }
609
+ let relationKey = this.generateForwardRelationKey(column.referencedTable, columnName, referenceCounts);
610
+ let relatedModelClass = sc.capitalizedCamelCase(column.referencedTable)+'Model';
611
+ fkMappings[columnName] = {
612
+ relationKey: relationKey,
613
+ entityName: relatedModelClass,
614
+ referencedColumn: column.referencedColumn,
615
+ nullable: column.nullable || false
616
+ };
617
+ }
618
+ if(0 === Object.keys(fkMappings).length){
619
+ return '';
620
+ }
621
+ return JSON.stringify(fkMappings, null, 4);
622
+ }
623
+
505
624
  generateRegisteredModelsFile(entitiesInfo, existingModels, driverKey, templatePath)
506
625
  {
507
626
  if(!FileHandler.exists(templatePath)){
@@ -541,8 +660,9 @@ class ModelsGeneration extends BaseGenerator
541
660
  }
542
661
  let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
543
662
  let modelFileName = entity.modelFileName || sc.kebabCase(tableName)+'-model.js';
663
+ let variableName = 'mikro-orm' !== driverKey ? '{ '+modelClassName+' }' : sc.camelCase(tableName)+'Model';
544
664
  registeredModels.push(
545
- 'const { '+modelClassName+' } = require(\'./'+modelFileName.replace('.js', '')+'\');'
665
+ 'const '+variableName+' = require(\'./'+modelFileName.replace('.js', '')+'\');'
546
666
  );
547
667
  }
548
668
  return registeredModels.join('\n');
@@ -559,7 +679,8 @@ class ModelsGeneration extends BaseGenerator
559
679
  }
560
680
  }
561
681
  let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
562
- registeredEntitiesObject.push(sc.camelCase(tableName)+': '+modelClassName);
682
+ let variableName = 'mikro-orm' === driverKey ? sc.camelCase(tableName)+'Model' : modelClassName;
683
+ registeredEntitiesObject.push(sc.camelCase(tableName)+': '+variableName);
563
684
  }
564
685
  return registeredEntitiesObject.join(',\n ');
565
686
  }
@@ -10,7 +10,7 @@ const { MySQLTablesProvider } = require('../mysql-tables-provider');
10
10
  const { MikroORM } = require('@mikro-orm/core');
11
11
  const { MySqlDriver } = require('@mikro-orm/mysql');
12
12
  const { MongoDriver } = require('@mikro-orm/mongodb');
13
- const { ErrorManager, Logger, sc } = require('@reldens/utils');
13
+ const { Logger, sc } = require('@reldens/utils');
14
14
 
15
15
  class MikroOrmDataServer extends BaseDataServer
16
16
  {
@@ -28,6 +28,27 @@ class MikroOrmDataServer extends BaseDataServer
28
28
  this.warnWhenNoEntities = Boolean(props.warnWhenNoEntities);
29
29
  }
30
30
 
31
+ getEntitiesToRegister()
32
+ {
33
+ if(this.entitiesPath){
34
+ //Logger.debug('MikroORM using entitiesPath');
35
+ return this.entitiesPath;
36
+ }
37
+ if(0 < Object.keys(this.entities).length){
38
+ //Logger.debug('MikroORM using this.entities: '+Object.keys(this.entities).join(', '));
39
+ return this.entities;
40
+ }
41
+ if(!this.rawEntities){
42
+ //Logger.debug('MikroORM has no entities!');
43
+ return [];
44
+ }
45
+ let entities = Object.values(this.rawEntities);
46
+ //Logger.debug('MikroORM extracting from rawEntities, count: '+entities.length);
47
+ return entities.map(rawEntity => {
48
+ return (rawEntity && rawEntity.schema) ? rawEntity.schema : rawEntity;
49
+ });
50
+ }
51
+
31
52
  async connect()
32
53
  {
33
54
  if(this.initialized){
@@ -39,15 +60,7 @@ class MikroOrmDataServer extends BaseDataServer
39
60
  clientUrl: this.connectString,
40
61
  allowGlobalContext: true,
41
62
  };
42
- if(this.rawEntities){
43
- providedSetup.entities = Object.values(this.rawEntities);
44
- }
45
- if(0 < Object.keys(this.entities).length){
46
- providedSetup.entities = this.entities;
47
- }
48
- if(this.entitiesPath){
49
- providedSetup.entities = this.entitiesPath;
50
- }
63
+ providedSetup.entities = this.getEntitiesToRegister();
51
64
  providedSetup.discovery = {warnWhenNoEntities: this.warnWhenNoEntities};
52
65
  this.orm = await MikroORM.init(providedSetup);
53
66
  this.initialized = Date.now();
@@ -60,6 +73,7 @@ class MikroOrmDataServer extends BaseDataServer
60
73
  return false;
61
74
  }
62
75
  await this.orm.close();
76
+ this.initialized = null;
63
77
  return true;
64
78
  }
65
79