@solidstarters/solid-core 1.2.20 → 1.2.22

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 (94) hide show
  1. package/dist/controllers/model-metadata.controller.d.ts +4 -0
  2. package/dist/controllers/model-metadata.controller.d.ts.map +1 -1
  3. package/dist/controllers/model-metadata.controller.js +12 -0
  4. package/dist/controllers/model-metadata.controller.js.map +1 -1
  5. package/dist/controllers/test.controller.d.ts.map +1 -1
  6. package/dist/controllers/test.controller.js +2 -2
  7. package/dist/controllers/test.controller.js.map +1 -1
  8. package/dist/dtos/basic-filters.dto.d.ts +5 -2
  9. package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
  10. package/dist/dtos/basic-filters.dto.js +13 -9
  11. package/dist/dtos/basic-filters.dto.js.map +1 -1
  12. package/dist/dtos/create-field-metadata.dto.d.ts +3 -1
  13. package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
  14. package/dist/dtos/create-field-metadata.dto.js +7 -1
  15. package/dist/dtos/create-field-metadata.dto.js.map +1 -1
  16. package/dist/dtos/create-user.dto.d.ts.map +1 -1
  17. package/dist/dtos/create-user.dto.js +2 -2
  18. package/dist/dtos/create-user.dto.js.map +1 -1
  19. package/dist/entities/field-metadata.entity.d.ts +1 -0
  20. package/dist/entities/field-metadata.entity.d.ts.map +1 -1
  21. package/dist/entities/field-metadata.entity.js +5 -1
  22. package/dist/entities/field-metadata.entity.js.map +1 -1
  23. package/dist/helpers/module-metadata-helper.service.d.ts +5 -0
  24. package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -0
  25. package/dist/helpers/module-metadata-helper.service.js +61 -0
  26. package/dist/helpers/module-metadata-helper.service.js.map +1 -0
  27. package/dist/helpers/schematic.service.js +1 -1
  28. package/dist/helpers/schematic.service.js.map +1 -1
  29. package/dist/index.d.ts +0 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +0 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/jobs/queue-test-subscriber.service.d.ts +1 -0
  34. package/dist/jobs/queue-test-subscriber.service.d.ts.map +1 -1
  35. package/dist/jobs/queue-test-subscriber.service.js +5 -3
  36. package/dist/jobs/queue-test-subscriber.service.js.map +1 -1
  37. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  38. package/dist/seeders/module-metadata-seeder.service.js +30 -23
  39. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  40. package/dist/seeders/seed-data/solid-core-metadata.json +1 -1
  41. package/dist/services/crud-helper.service.d.ts.map +1 -1
  42. package/dist/services/crud-helper.service.js +6 -4
  43. package/dist/services/crud-helper.service.js.map +1 -1
  44. package/dist/services/crud.service.d.ts.map +1 -1
  45. package/dist/services/crud.service.js +26 -35
  46. package/dist/services/crud.service.js.map +1 -1
  47. package/dist/services/field-metadata.service.d.ts +17 -6
  48. package/dist/services/field-metadata.service.d.ts.map +1 -1
  49. package/dist/services/field-metadata.service.js +253 -34
  50. package/dist/services/field-metadata.service.js.map +1 -1
  51. package/dist/services/model-metadata.service.d.ts +9 -1
  52. package/dist/services/model-metadata.service.d.ts.map +1 -1
  53. package/dist/services/model-metadata.service.js +74 -35
  54. package/dist/services/model-metadata.service.js.map +1 -1
  55. package/dist/services/module-metadata.service.d.ts +3 -3
  56. package/dist/services/module-metadata.service.d.ts.map +1 -1
  57. package/dist/services/module-metadata.service.js +12 -18
  58. package/dist/services/module-metadata.service.js.map +1 -1
  59. package/dist/services/solid-introspect.service.d.ts.map +1 -1
  60. package/dist/services/solid-introspect.service.js +9 -1
  61. package/dist/services/solid-introspect.service.js.map +1 -1
  62. package/dist/solid-core.module.d.ts.map +1 -1
  63. package/dist/solid-core.module.js +2 -0
  64. package/dist/solid-core.module.js.map +1 -1
  65. package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.d.ts.map +1 -1
  66. package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.js +1 -1
  67. package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.js.map +1 -1
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/package.json +1 -1
  70. package/src/controllers/model-metadata.controller.ts +6 -0
  71. package/src/controllers/test.controller.ts +4 -2
  72. package/src/dtos/basic-filters.dto.ts +11 -7
  73. package/src/dtos/create-field-metadata.dto.ts +5 -0
  74. package/src/dtos/create-user.dto.ts +1 -1
  75. package/src/entities/field-metadata.entity.ts +3 -0
  76. package/src/helpers/module-metadata-helper.service.ts +25 -0
  77. package/src/helpers/schematic.service.ts +1 -1
  78. package/src/index.ts +0 -1
  79. package/src/jobs/queue-test-subscriber.service.ts +4 -2
  80. package/src/seeders/module-metadata-seeder.service.ts +30 -26
  81. package/src/seeders/seed-data/solid-core-metadata.json +1 -1
  82. package/src/services/crud-helper.service.ts +8 -6
  83. package/src/services/crud.service.ts +14 -22
  84. package/src/services/field-metadata.service.ts +229 -10
  85. package/src/services/model-metadata.service.ts +98 -44
  86. package/src/services/module-metadata.service.ts +13 -18
  87. package/src/services/solid-introspect.service.ts +17 -15
  88. package/src/solid-core.module.ts +2 -0
  89. package/src/subscribers/softDeleteAwareEventSubscriber.subscriber.ts +2 -1
  90. package/dist/commands/basic.command.d.ts +0 -19
  91. package/dist/commands/basic.command.d.ts.map +0 -1
  92. package/dist/commands/basic.command.js +0 -79
  93. package/dist/commands/basic.command.js.map +0 -1
  94. package/src/commands/basic.command.ts +0 -60
@@ -1,16 +1,19 @@
1
- import { Injectable, NotFoundException } from '@nestjs/common';
2
- import { FieldMetadata } from '../entities/field-metadata.entity';
3
- import { DataSource, Repository, SelectQueryBuilder } from 'typeorm';
1
+ import { Injectable, Logger, NotFoundException } from '@nestjs/common';
4
2
  import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
5
- import { CascadeType, ComputedFieldValueType, CreateFieldMetadataDto, DecryptWhenType, EncryptionType, MediaType, PSQLType, RelationType, SelectionValueType } from '../dtos/create-field-metadata.dto';
6
- import { ModelMetadata } from '../entities/model-metadata.entity';
7
- import { SolidFieldType } from '../dtos/create-field-metadata.dto'
3
+ import * as fs from 'fs/promises'; // Use the Promise-based version of fs for async/await
4
+ import { ModuleMetadataHelperService } from 'src/helpers/module-metadata-helper.service';
8
5
  import { SolidRegistry } from 'src/helpers/solid-registry';
6
+ import { DataSource, Repository, SelectQueryBuilder } from 'typeorm';
9
7
  import { BasicFilterDto } from '../dtos/basic-filters.dto';
10
- import { CrudHelperService } from './crud-helper.service';
11
- import { UpdateFieldMetaDataDto } from '../dtos/update-field-metadata.dto';
8
+ import { CascadeType, ComputedFieldValueType, CreateFieldMetadataDto, DecryptWhenType, EncryptionType, MediaType, PSQLType, RelationType, SelectionValueType, SolidFieldType } from '../dtos/create-field-metadata.dto';
12
9
  import { SelectionDynamicQueryDto } from '../dtos/selection-dynamic-query.dto';
10
+ import { UpdateFieldMetaDataDto } from '../dtos/update-field-metadata.dto';
11
+ import { FieldMetadata } from '../entities/field-metadata.entity';
12
+ import { ModelMetadata } from '../entities/model-metadata.entity';
13
13
  import { ISelectionProviderValues } from '../interfaces';
14
+ import { CrudHelperService } from './crud-helper.service';
15
+ import { classify } from '@angular-devkit/core/src/utils/strings';
16
+
14
17
 
15
18
  @Injectable()
16
19
  export class FieldMetadataService {
@@ -20,9 +23,174 @@ export class FieldMetadataService {
20
23
  @InjectDataSource()
21
24
  private readonly dataSource: DataSource,
22
25
  private readonly solidRegistry: SolidRegistry,
23
- private readonly crudHelperService: CrudHelperService
26
+ private readonly crudHelperService: CrudHelperService,
27
+ private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
24
28
  ) { }
25
29
 
30
+ private logger = new Logger(FieldMetadataService.name);
31
+
32
+ async updateInverseField(field: FieldMetadata, fieldRepository: Repository<FieldMetadata>, modelRepository: Repository<ModelMetadata>) {
33
+ if (!field.model || !field.model.module) {
34
+ throw new Error('Model and module are required to update inverse field');
35
+ }
36
+ // Update the inverse field in the db
37
+ const savedInverseField = await this.updateInverseFieldInDb(field, fieldRepository, modelRepository);
38
+ // Update the inverse field in the file
39
+ this.updateRelationInverseFieldInFile(savedInverseField, field.relationModelSingularName, field.model.module.name);
40
+ }
41
+
42
+ private async updateInverseFieldInDb(field: FieldMetadata, fieldRepository: Repository<FieldMetadata>, modelRepository: Repository<ModelMetadata>): Promise<FieldMetadata> {
43
+ const { moduleName, modelName } = this.validateForInverseField(field);
44
+
45
+ // Get the relation model reference
46
+ const relationModel = await this.getRelationModel(modelRepository, field, moduleName);
47
+
48
+ // const {id, createdAt, updatedAt, deletedAt, ...fieldKeys} = field;
49
+ switch (field.relationType) {
50
+ case RelationType.manyToOne: {
51
+ const fieldName = field.relationModelFieldName || `${modelName}s`;
52
+ const inverseField: FieldMetadata = {
53
+ ...field,
54
+ name: fieldName,
55
+ displayName: classify(fieldName),
56
+ description: classify(fieldName),
57
+ type: SolidFieldType.relation,
58
+ isSystem: field.isSystem,
59
+ relationType: RelationType.oneToMany,
60
+ relationModelSingularName: modelName,
61
+ relationCreateInverse: true,
62
+ relationCascade: field.relationCascade,
63
+ relationModelModuleName: moduleName,
64
+ relationModelFieldName: field.name,
65
+ required: false,
66
+ unique: false,
67
+ index: false,
68
+ private: false,
69
+ encrypt: false,
70
+ model: relationModel,
71
+ columnName:null,
72
+ relationJoinTableName: null,
73
+ relationJoinColumnName: null,
74
+ joinColumnName: null,
75
+ id : null,
76
+ }
77
+
78
+ // Load the inverse field,
79
+ const savedField = await this.saveInverseField(fieldRepository, relationModel, inverseField);
80
+ return savedField;
81
+ }
82
+ case RelationType.oneToMany: {
83
+ const fieldName = field.relationModelFieldName || `${modelName}`;
84
+ const inverseField: FieldMetadata = {
85
+ ...field,
86
+ name: fieldName,
87
+ displayName: classify(fieldName),
88
+ description: classify(fieldName),
89
+ type: SolidFieldType.relation,
90
+ isSystem: field.isSystem,
91
+ relationType: RelationType.manyToOne,
92
+ relationModelSingularName: modelName,
93
+ relationCreateInverse: true,
94
+ relationCascade: null,
95
+ relationModelModuleName: moduleName,
96
+ relationModelFieldName: field.name,
97
+ required: false,
98
+ unique: false,
99
+ index: false,
100
+ private: false,
101
+ encrypt: false,
102
+ model: relationModel,
103
+ columnName:null,
104
+ relationJoinTableName: null,
105
+ relationJoinColumnName: null,
106
+ joinColumnName: null,
107
+ id : null,
108
+ }
109
+
110
+ // Load the inverse field,
111
+ const savedField = await this.saveInverseField(fieldRepository, relationModel, inverseField);
112
+ return savedField;
113
+ }
114
+ case RelationType.manyTomany: {
115
+ const fieldName = field.relationModelFieldName;
116
+ // Logic to create a manyToMany inverse field definition
117
+ const inverseFieldManyToMany: FieldMetadata = {
118
+ ...field,
119
+ name: fieldName,
120
+ displayName: classify(fieldName),
121
+ description: classify(fieldName),
122
+ type: SolidFieldType.relation,
123
+ isSystem: field.isSystem,
124
+ relationType: RelationType.manyTomany,
125
+ relationModelSingularName: modelName,
126
+ relationCreateInverse: true,
127
+ relationCascade: null,
128
+ relationModelModuleName: moduleName,
129
+ relationModelFieldName: field.name,
130
+ required: false,
131
+ unique: false,
132
+ index: false,
133
+ private: false,
134
+ encrypt: false,
135
+ model: relationModel,
136
+ columnName:null,
137
+ relationJoinTableName: null,
138
+ relationJoinColumnName: null,
139
+ joinColumnName: null,
140
+ isRelationManyToManyOwner: false,
141
+ id : null,
142
+ }
143
+ const savedField = await this.saveInverseField(fieldRepository, relationModel, inverseFieldManyToMany);
144
+ return savedField;
145
+ }
146
+ default:
147
+ throw new Error(`Invalid relation type for field ${field.name} with relation type ${field.relationType}`);
148
+ }
149
+ }
150
+
151
+ private async getRelationModel(modelRepository: Repository<ModelMetadata>, field: FieldMetadata, moduleName: string) {
152
+ return await modelRepository.findOne({
153
+ where: {
154
+ singularName: field.relationModelSingularName,
155
+ module: {
156
+ name: field.relationModelModuleName ?? moduleName
157
+ }
158
+ }
159
+ });
160
+ }
161
+
162
+ private async saveInverseField(fieldRepository: Repository<FieldMetadata>, relationModel: ModelMetadata, inverseField: FieldMetadata): Promise<FieldMetadata> {
163
+ const existingInverseField = await fieldRepository.findOne({
164
+ where: {
165
+ model: relationModel,
166
+ name: inverseField.name
167
+ }
168
+ });
169
+
170
+ if (existingInverseField) {
171
+ const updatedField = fieldRepository.merge(existingInverseField, inverseField);
172
+ const savedField = await fieldRepository.save(updatedField);
173
+ return savedField;
174
+ }
175
+ else {
176
+ const savedField = await fieldRepository.save(fieldRepository.create(inverseField));
177
+ return savedField;
178
+ }
179
+ }
180
+
181
+ private validateForInverseField(field: FieldMetadata) {
182
+ if (field.type !== SolidFieldType.relation) {
183
+ throw new Error('Only relation fields can have inverse fields');
184
+ }
185
+ const modelName = field.model.singularName;
186
+ const moduleName = field.model.module.name;
187
+
188
+ if (!modelName || !moduleName) {
189
+ throw new Error('Model name and module name are required to create inverse field');
190
+ }
191
+ return { moduleName, modelName };
192
+ }
193
+
26
194
  async findMany(basicFilterDto: BasicFilterDto) {
27
195
  const alias = 'fieldMetadata';
28
196
  // Extract the required keys from the input query
@@ -586,7 +754,8 @@ export class FieldMetadataService {
586
754
  "columnName",
587
755
  "relationJoinColumnName",
588
756
  "joinColumnName",
589
- "relationJoinTableName"
757
+ "relationJoinTableName",
758
+ "isRelationManyToManyOwner",
590
759
  ];
591
760
 
592
761
  case SolidFieldType.mediaSingle:
@@ -874,4 +1043,54 @@ export class FieldMetadataService {
874
1043
  return selectionProviderInstance.value(query.optionValue, selectionDynamicProviderCtxt);
875
1044
  }
876
1045
 
1046
+ private async updateRelationInverseFieldInFile(savedInverseField: FieldMetadata, inverseModelName: string, moduleName: string) {
1047
+ try {
1048
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
1049
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
1050
+
1051
+ // Create the config object for the inverse field
1052
+ const fieldObject: Record<string, any> = await this.createFieldConfig(savedInverseField);
1053
+
1054
+ // Find the field config object in the json file
1055
+ const model = metaData.moduleMetadata.models.find((model: any) => model.singularName === inverseModelName);
1056
+
1057
+ // Replace the current field object with the above field object
1058
+ const fieldIndex = model.fields.findIndex((field: any) => field.name === savedInverseField.name);
1059
+ if (fieldIndex === -1) {
1060
+ model.fields.push(fieldObject);
1061
+ }
1062
+ else {
1063
+ model.fields[fieldIndex] = fieldObject;
1064
+ }
1065
+
1066
+ // Write the updated object back to the file
1067
+ const updatedContent = JSON.stringify(metaData, null, 2);
1068
+ await fs.writeFile(filePath, updatedContent);
1069
+ } catch (error) {
1070
+ this.logger.error('File creation failed:', error);
1071
+ throw new Error('File creation failed, rolling back transaction'); // Trigger rollback
1072
+ }
1073
+ }
1074
+
1075
+ //Moved existing reusable logic to a separate function
1076
+ async createFieldConfig(field: FieldMetadata) {
1077
+ const fieldsRequiredBasedOnType = await this.fetchCurrentFieldsBasedOnType(field.type);
1078
+ const fieldObject: Record<string, any> = {};
1079
+
1080
+ // Assign default or placeholder values for required fields
1081
+ fieldsRequiredBasedOnType.forEach((requiredField) => {
1082
+ fieldObject[requiredField] = field[requiredField];
1083
+ });
1084
+
1085
+ if (field.type == "mediaSingle" || field.type == "mediaMultiple") {
1086
+ if (field.mediaStorageProvider) {
1087
+ delete fieldObject.mediaStorageProviderId
1088
+ fieldObject.mediaStorageProviderUserKey = field.mediaStorageProvider.name
1089
+ }
1090
+ }
1091
+
1092
+ return fieldObject;
1093
+ }
877
1094
  }
1095
+
1096
+
@@ -1,15 +1,19 @@
1
1
  import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
2
2
  import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
3
3
  import * as fs from 'fs/promises'; // Use the Promise-based version of fs for async/await
4
- import * as path from 'path'; // To handle file paths
5
4
  import { DataSource, EntityManager, Repository, SelectQueryBuilder } from 'typeorm';
6
5
  import { CreateModelMetadataDto } from '../dtos/create-model-metadata.dto';
7
6
  import { ModelMetadata } from '../entities/model-metadata.entity';
8
7
  import { ModuleMetadata } from '../entities/module-metadata.entity';
9
8
 
9
+ import { SolidFieldType } from 'src/dtos/create-field-metadata.dto';
10
+ import { ModuleMetadataHelperService } from 'src/helpers/module-metadata-helper.service';
10
11
  import { BasicFilterDto } from '../dtos/basic-filters.dto';
11
12
  import { UpdateModelMetaDataDto } from '../dtos/update-model-metadata.dto';
13
+ import { ActionMetadata } from '../entities/action-metadata.entity';
12
14
  import { FieldMetadata } from '../entities/field-metadata.entity';
15
+ import { MenuItemMetadata } from '../entities/menu-item-metadata.entity';
16
+ import { ViewMetadata } from '../entities/view-metadata.entity';
13
17
  import {
14
18
  REFRESH_MODEL_COMMAND,
15
19
  REMOVE_FIELDS_COMMAND,
@@ -19,11 +23,7 @@ import { CodeGenerationOptions } from '../interfaces';
19
23
  import { CrudHelperService } from './crud-helper.service';
20
24
  import { FieldMetadataService } from './field-metadata.service';
21
25
  import { MediaStorageProviderMetadataService } from './media-storage-provider-metadata.service';
22
- import { ViewMetadata } from '../entities/view-metadata.entity';
23
- import { ActionMetadata } from '../entities/action-metadata.entity';
24
- import { MenuItemMetadata } from '../entities/menu-item-metadata.entity';
25
26
  import { RoleMetadataService } from './role-metadata.service';
26
- import { PermissionMetadataSeederService } from 'src/seeders/permission-metadata-seeder.service';
27
27
 
28
28
  @Injectable()
29
29
  export class ModelMetadataService {
@@ -40,6 +40,7 @@ export class ModelMetadataService {
40
40
  private readonly mediaStorageProviderMetadataService: MediaStorageProviderMetadataService,
41
41
  private readonly fieldMetadataService: FieldMetadataService,
42
42
  private readonly roleService: RoleMetadataService,
43
+ private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
43
44
  ) { }
44
45
 
45
46
  async findMany(basicFilterDto: BasicFilterDto) {
@@ -118,17 +119,34 @@ export class ModelMetadataService {
118
119
 
119
120
  try {
120
121
  return await this.dataSource.transaction(async (manager: EntityManager) => {
122
+ const modelRepository = manager.getRepository(ModelMetadata);
123
+ const fieldRepository = manager.getRepository(FieldMetadata);
124
+
121
125
  // Step 1: Write initial data to the database
122
126
  const model = await this.createInDB(manager, createDto);
123
- await this.createInFile(model.id, manager.getRepository(ModelMetadata));
127
+ await this.createInFile(model.id, modelRepository);
128
+
129
+ await this.handleInverseRelationFieldsUpdates(model, fieldRepository, modelRepository);
130
+
124
131
  return model
125
132
  });
126
133
  } catch (error) {
127
- console.error('Transaction failed:', error);
134
+ // console.error('Transaction failed:', error);
135
+ this.logger.error('Transaction failed:', error);
128
136
  throw error;
129
137
  }
130
138
  }
131
139
 
140
+ // Iterate through the fields in the createDto & get all the relation fields which have create inverse as true
141
+ private async handleInverseRelationFieldsUpdates(model: ModelMetadata, fieldRepository: Repository<FieldMetadata>, modelRepository: Repository<ModelMetadata>) {
142
+ const fields: FieldMetadata[] = await this.getRelationInverseFields(model.id, fieldRepository);
143
+
144
+ // Call a function which will iterate through each field and create an inverse field entry for the respective model i.e call updateInverseField on the related model
145
+ for (const field of fields) {
146
+ await this.fieldMetadataService.updateInverseField(field, fieldRepository, modelRepository);
147
+ }
148
+ }
149
+
132
150
  async update(id: number, updateModelMetaDataDto: UpdateModelMetaDataDto) {
133
151
  //To DO start the transaction
134
152
  // call create In db
@@ -136,13 +154,20 @@ export class ModelMetadataService {
136
154
 
137
155
  try {
138
156
  return await this.dataSource.transaction(async (manager: EntityManager) => {
157
+ const modelRepository = manager.getRepository(ModelMetadata);
158
+ const fieldRepository = manager.getRepository(FieldMetadata);
159
+
139
160
  // Step 1: Write initial data to the database
140
161
  const model = await this.updateInDb(manager, id, updateModelMetaDataDto)
141
- await this.updateInFile(model.id, manager.getRepository(ModelMetadata));
162
+ await this.updateInFile(model.id, modelRepository);
163
+
164
+ await this.handleInverseRelationFieldsUpdates(model, fieldRepository, modelRepository);
165
+
142
166
  // return model
143
167
  });
144
168
  } catch (error) {
145
- console.error('Transaction failed:', error);
169
+ // console.error('Transaction failed:', error);
170
+ this.logger.error('Transaction failed:', error);
146
171
  throw error;
147
172
  }
148
173
  }
@@ -177,6 +202,7 @@ export class ModelMetadataService {
177
202
  fieldMetadata['mediaStorageProvider'] = await this.mediaStorageProviderMetadataService.findOne(fieldMetadata.mediaStorageProviderId);
178
203
  }
179
204
  // console.log(fieldMetadata.displayName);
205
+ // this.logger.debug(fieldMetadata.displayName);
180
206
 
181
207
  const fieldMetadataObject = this.fieldMetadataRepo.create(fieldMetadata);
182
208
  const affectedField = await manager.save(fieldMetadataObject);
@@ -304,15 +330,11 @@ export class ModelMetadataService {
304
330
  where: {
305
331
  id: modelId,
306
332
  },
307
- relations: ["fields", "module"], //FIXME: Check with jenender and change to relations to avoid confusion
333
+ relations: ["fields", "fields.mediaStorageProvider", "module"], //FIXME: Check with jenender and change to relations to avoid confusion
308
334
  });
309
335
 
310
- const folderPath = path.resolve(process.cwd(), 'module-metadata', model.module.name);
311
- const filePath = path.join(folderPath, `${model.module.name}-metadata.json`);
312
-
313
- // Read the existing JSON file
314
- const fileContent = await fs.readFile(filePath, 'utf8');
315
- const metaData = JSON.parse(fileContent);
336
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(model.module.name);
337
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
316
338
 
317
339
  const modelMetaData = {
318
340
  singularName: model.singularName,
@@ -331,13 +353,7 @@ export class ModelMetadataService {
331
353
  const field = model.fields[i];
332
354
  if (!field.isSystem) {
333
355
 
334
- const fieldsRequiredBasedOnType = await this.fieldMetadataService.fetchCurrentFieldsBasedOnType(field.type);
335
- const fieldObject: Record<string, any> = {};
336
-
337
- // Assign default or placeholder values for required fields
338
- fieldsRequiredBasedOnType.forEach((requiredField) => {
339
- fieldObject[requiredField] = field[requiredField];
340
- });
356
+ const fieldObject: Record<string, any> = await this.fieldMetadataService.createFieldConfig(field);
341
357
  modelMetaData.fields.push(fieldObject);
342
358
  listViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}`, sortable: true, filterable: true } })
343
359
  formViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}` } })
@@ -435,7 +451,8 @@ export class ModelMetadataService {
435
451
  await fs.writeFile(filePath, updatedContent);
436
452
 
437
453
  } catch (error) {
438
- console.error('File creation failed:', error);
454
+ // console.error('File creation failed:', error);
455
+ this.logger.error('File creation failed:', error);
439
456
  throw new Error('File creation failed, rolling back transaction'); // Trigger rollback
440
457
  }
441
458
  }
@@ -476,7 +493,7 @@ export class ModelMetadataService {
476
493
  // Existing field
477
494
  const existingField = existingFields.find((field) => field.id === fieldMetadata.id);
478
495
  if (existingField) {
479
- if (fieldMetadata.mediaStorageProviderId) {
496
+ if (fieldMetadata.mediaStorageProviderId) {
480
497
  fieldMetadata['mediaStorageProvider'] = await this.mediaStorageProviderMetadataService.findOne(fieldMetadata.mediaStorageProviderId);
481
498
  }
482
499
  Object.assign(existingField, fieldMetadata);
@@ -562,12 +579,8 @@ export class ModelMetadataService {
562
579
  relations: ["fields", "fields.mediaStorageProvider", "module"], //FIXME: Check with jenender and change to relations to avoid confusion
563
580
  });
564
581
 
565
- const folderPath = path.resolve(process.cwd(), 'module-metadata', model.module.name);
566
- const filePath = path.join(folderPath, `${model.module.name}-metadata.json`);
567
-
568
- // Read the existing JSON file
569
- const fileContent = await fs.readFile(filePath, 'utf8');
570
- const metaData = JSON.parse(fileContent);
582
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(model.module.name);
583
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
571
584
 
572
585
  const modelMetaData = {
573
586
  singularName: model.singularName,
@@ -583,18 +596,7 @@ export class ModelMetadataService {
583
596
  const field = model.fields[i];
584
597
  if (!field.isSystem && !field.isMarkedForRemoval) {
585
598
 
586
- const fieldsRequiredBasedOnType = await this.fieldMetadataService.fetchCurrentFieldsBasedOnType(field.type);
587
- const fieldObject: Record<string, any> = {};
588
-
589
- // Assign default or placeholder values for required fields
590
- fieldsRequiredBasedOnType.forEach((requiredField) => {
591
- fieldObject[requiredField] = field[requiredField];
592
-
593
- });
594
- if (field.type == "mediaSingle" || field.type == "mediaMultiple") {
595
- delete fieldObject.mediaStorageProviderId
596
- fieldObject.mediaStorageProviderUserKey = field.mediaStorageProvider.name
597
- }
599
+ const fieldObject: Record<string, any> = await this.fieldMetadataService.createFieldConfig(field);
598
600
  modelMetaData.fields.push(fieldObject);
599
601
  }
600
602
  }
@@ -617,7 +619,8 @@ export class ModelMetadataService {
617
619
  await fs.writeFile(filePath, updatedContent);
618
620
 
619
621
  } catch (error) {
620
- console.error('File creation failed:', error);
622
+ // console.error('File creation failed:', error);
623
+ this.logger.error('File creation failed:', error);
621
624
  throw new Error('File creation failed, rolling back transaction'); // Trigger rollback
622
625
  }
623
626
  }
@@ -907,5 +910,56 @@ export class ModelMetadataService {
907
910
  return output;
908
911
  }
909
912
 
913
+ async updateUserKey(data: any) {
914
+ const { modelName, fieldName } = data;
915
+
916
+ const model = await this.modelMetadataRepo.findOne({
917
+ where: { singularName: modelName },
918
+ relations: ['fields', 'userKeyField'],
919
+ });
920
+
921
+ if (!model) {
922
+ throw new Error(`Model with name ${modelName} not found`);
923
+ }
924
+
925
+ if (model.userKeyField) {
926
+ throw new Error(`User key is already set to ${model.userKeyField.name}. No changes were made.`);
927
+ }
928
+
929
+ const fieldToUpdate = model.fields.find(field => field.name === fieldName);
930
+ if (!fieldToUpdate) {
931
+ throw new Error(`Field with name ${fieldName} not found in model ${modelName}`);
932
+ }
933
+
934
+ fieldToUpdate.isUserKey = true;
935
+
936
+ model.userKeyField = fieldToUpdate;
937
+
938
+ await this.modelMetadataRepo.save(model);
939
+
940
+ return {
941
+ message: `User key has been successfully updated to ${fieldName}.`,
942
+ success: true
943
+ };
944
+ }
945
+
946
+ private async getRelationInverseFields(modelId: number, repo: Repository<FieldMetadata>): Promise<FieldMetadata[]> {
947
+ return await repo.find({
948
+ where: {
949
+ model : {
950
+ id: modelId
951
+ },
952
+ type: SolidFieldType.relation,
953
+ relationCreateInverse: true
954
+ },
955
+ relations: {
956
+ model: {
957
+ module: true
958
+ }
959
+ }
960
+ });
961
+ }
910
962
 
911
963
  }
964
+
965
+
@@ -20,6 +20,7 @@ import { SolidRegistry } from '../helpers/solid-registry';
20
20
  import { CodeGenerationOptions, ModuleMetadataConfiguration } from '../interfaces';
21
21
  import { CrudHelperService } from './crud-helper.service';
22
22
  import { ModelMetadataService } from './model-metadata.service';
23
+ import { ModuleMetadataHelperService } from 'src/helpers/module-metadata-helper.service';
23
24
 
24
25
  @Injectable()
25
26
  export class ModuleMetadataService {
@@ -38,6 +39,7 @@ export class ModuleMetadataService {
38
39
  @Inject(forwardRef(() => ModelMetadataService))
39
40
  private readonly modelMetadataService: ModelMetadataService,
40
41
  private readonly solidRegistry: SolidRegistry,
42
+ private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
41
43
  ) { }
42
44
 
43
45
  async findMany(basicFilterDto: BasicFilterDto) {
@@ -110,7 +112,8 @@ export class ModuleMetadataService {
110
112
  return module
111
113
  });
112
114
  } catch (error) {
113
- console.error('Transaction failed:', error);
115
+ // console.error('Transaction failed:', error);
116
+ this.logger.error('Transaction failed:', error);
114
117
  throw error;
115
118
  }
116
119
  }
@@ -154,7 +157,7 @@ export class ModuleMetadataService {
154
157
 
155
158
  // Create the folder path inside 'module-metadata'
156
159
  const folderPath = path.resolve(process.cwd(), 'module-metadata', module.name);
157
- const filePath = this.getModuleMetadataFilePath(module);
160
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(module.name);
158
161
 
159
162
  // Ensure the folder exists
160
163
  await fs.mkdir(folderPath, { recursive: true });
@@ -163,7 +166,8 @@ export class ModuleMetadataService {
163
166
  await fs.writeFile(filePath, metadataJson);
164
167
 
165
168
  } catch (error) {
166
- console.error('File creation failed:', error);
169
+ // console.error('File creation failed:', error);
170
+ this.logger.error('File creation failed:', error);
167
171
  throw new Error('File creation failed, rolling back transaction'); // Trigger rollback
168
172
  }
169
173
  }
@@ -177,7 +181,8 @@ export class ModuleMetadataService {
177
181
  return module
178
182
  });
179
183
  } catch (error) {
180
- console.error('Transaction failed:', error);
184
+ // console.error('Transaction failed:', error);
185
+ this.logger.error('Transaction failed:', error);
181
186
  throw error;
182
187
  }
183
188
  }
@@ -205,12 +210,12 @@ export class ModuleMetadataService {
205
210
 
206
211
  async updateInFile(module: ModuleMetadata) {
207
212
  try {
208
- const filePath = this.getModuleMetadataFilePath(module);
213
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(module.name);
209
214
 
210
215
  // Read the existing JSON file
211
216
  let metaData;
212
217
  try {
213
- metaData = await this.getModuleMetadata(filePath);
218
+ metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
214
219
 
215
220
  } catch (error) {
216
221
  metaData = {
@@ -249,23 +254,13 @@ export class ModuleMetadataService {
249
254
  await fs.writeFile(filePath, updatedContent);
250
255
 
251
256
  } catch (error) {
252
- console.error('File creation failed:', error);
257
+ // console.error('File creation failed:', error);
258
+ this.logger.error('File creation failed:', error);
253
259
  throw new Error('File creation failed, rolling back transaction'); // Trigger rollback
254
260
  }
255
261
  }
256
262
 
257
263
 
258
- private async getModuleMetadata(filePath: string): Promise<any> {
259
- const fileContent = await fs.readFile(filePath, 'utf8');
260
- return JSON.parse(fileContent);
261
- }
262
-
263
- private getModuleMetadataFilePath(module: ModuleMetadata) {
264
- const folderPath = path.resolve(process.cwd(), 'module-metadata', module.name);
265
- const filePath = path.join(folderPath, `${module.name}-metadata.json`);
266
- return filePath;
267
- }
268
-
269
264
  async upsert(updateModuleMetadataDto: UpdateModuleMetadataDto) {
270
265
  this.logger.log(`Module Upsert called for : ${updateModuleMetadataDto.name}`);
271
266
  // First check if module already exists using name
@@ -128,21 +128,23 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
128
128
  return !!isSolidDatabaseModule;
129
129
  }
130
130
 
131
- private isModule(provider: InstanceWrapper) {
132
- // Check if the provider is a module
131
+
132
+ private isModule(provider: InstanceWrapper): boolean {
133
133
  const metatype = provider.metatype;
134
- return metatype && Reflect.getMetadata('imports', metatype);
134
+ // Check if it's a Static Module (Class-Based)
135
+ if (metatype && typeof metatype === 'function' && Reflect.getMetadata('imports', metatype)) {
136
+ return true;
137
+ }
138
+
139
+ // Ensure provider.instance is an object before checking for 'module'
140
+ if (provider.instance && typeof provider.instance === 'object') {
141
+ // Check if it's a Dynamic Module (Object-Based)
142
+ if ('module' in provider.instance && typeof provider.instance.module === 'function') {
143
+ return true;
144
+ }
145
+ }
146
+
147
+ return false;
135
148
  }
136
- // This method uses the @SolidSeeder decorator to identify if a provider is a seeder
137
- // private isSeeder(provider: InstanceWrapper) {
138
- // const { instance } = provider;
139
- // if (!instance) return false;
140
-
141
- // const seederMetadata = this.reflector.get(
142
- // 'SOLID_SERVICE',
143
- // instance.constructor,
144
- // );
145
- // if (!seederMetadata || !seederMetadata.seeder) return false;
146
- // return true;
147
- // }
149
+
148
150
  }
@@ -135,6 +135,7 @@ import { UserService } from './services/user.service';
135
135
  import { Setting } from './entities/setting.entity';
136
136
  import { SettingService } from './services/setting.service';
137
137
  import { SettingController } from './controllers/setting.controller';
138
+ import { ModuleMetadataHelperService } from './helpers/module-metadata-helper.service';
138
139
 
139
140
 
140
141
  @Global()
@@ -223,6 +224,7 @@ import { SettingController } from './controllers/setting.controller';
223
224
  useClass: PermissionsGuard,
224
225
  },
225
226
  ModuleMetadataService,
227
+ ModuleMetadataHelperService,
226
228
  ModelMetadataService,
227
229
  FieldMetadataService,
228
230
  RemoveFieldsCommand,