@solidstarters/solid-core 1.2.20 → 1.2.21

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 (40) hide show
  1. package/dist/dtos/create-field-metadata.dto.d.ts +3 -1
  2. package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
  3. package/dist/dtos/create-field-metadata.dto.js +7 -1
  4. package/dist/dtos/create-field-metadata.dto.js.map +1 -1
  5. package/dist/entities/field-metadata.entity.d.ts +1 -0
  6. package/dist/entities/field-metadata.entity.d.ts.map +1 -1
  7. package/dist/entities/field-metadata.entity.js +5 -1
  8. package/dist/entities/field-metadata.entity.js.map +1 -1
  9. package/dist/helpers/module-metadata-helper.service.d.ts +5 -0
  10. package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -0
  11. package/dist/helpers/module-metadata-helper.service.js +61 -0
  12. package/dist/helpers/module-metadata-helper.service.js.map +1 -0
  13. package/dist/services/crud.service.d.ts.map +1 -1
  14. package/dist/services/crud.service.js +26 -35
  15. package/dist/services/crud.service.js.map +1 -1
  16. package/dist/services/field-metadata.service.d.ts +17 -6
  17. package/dist/services/field-metadata.service.d.ts.map +1 -1
  18. package/dist/services/field-metadata.service.js +220 -34
  19. package/dist/services/field-metadata.service.js.map +1 -1
  20. package/dist/services/model-metadata.service.d.ts +5 -1
  21. package/dist/services/model-metadata.service.d.ts.map +1 -1
  22. package/dist/services/model-metadata.service.js +46 -31
  23. package/dist/services/model-metadata.service.js.map +1 -1
  24. package/dist/services/module-metadata.service.d.ts +3 -3
  25. package/dist/services/module-metadata.service.d.ts.map +1 -1
  26. package/dist/services/module-metadata.service.js +8 -14
  27. package/dist/services/module-metadata.service.js.map +1 -1
  28. package/dist/solid-core.module.d.ts.map +1 -1
  29. package/dist/solid-core.module.js +2 -0
  30. package/dist/solid-core.module.js.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +1 -1
  33. package/src/dtos/create-field-metadata.dto.ts +5 -0
  34. package/src/entities/field-metadata.entity.ts +3 -0
  35. package/src/helpers/module-metadata-helper.service.ts +25 -0
  36. package/src/services/crud.service.ts +14 -22
  37. package/src/services/field-metadata.service.ts +200 -10
  38. package/src/services/model-metadata.service.ts +57 -40
  39. package/src/services/module-metadata.service.ts +5 -14
  40. package/src/solid-core.module.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.20",
3
+ "version": "1.2.21",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -142,6 +142,7 @@ export enum MediaType {
142
142
  export enum RelationType {
143
143
  manyToOne = 'many-to-one',
144
144
  manyTomany = 'many-to-many',
145
+ oneToMany = 'one-to-many',
145
146
  }
146
147
 
147
148
  export enum CascadeType {
@@ -320,6 +321,10 @@ export class CreateFieldMetadataDto {
320
321
  @IsOptional()
321
322
  relationModelFieldName: string;
322
323
 
324
+ @ApiProperty({description: 'Only for type=relation, many-to-many. This field is used to set the owner of the many-to-many relation'})
325
+ @IsOptional()
326
+ isRelationManyToManyOwner: boolean;
327
+
323
328
  @ApiProperty({
324
329
  description:
325
330
  'Dynamic provider for selection. Only for type=selectionDynamic',
@@ -99,6 +99,9 @@ export class FieldMetadata extends CommonEntity {
99
99
  @Column({ name: 'relation_model_field_name', nullable: true })
100
100
  relationModelFieldName: string;
101
101
 
102
+ @Column({ name: 'is_relation_many_to_many_owner', nullable: true })
103
+ isRelationManyToManyOwner: boolean;
104
+
102
105
  @Column({ name: 'selection_dynamic_provider', nullable: true })
103
106
  selectionDynamicProvider: string;
104
107
 
@@ -0,0 +1,25 @@
1
+ import { Injectable, ModuleMetadata } from "@nestjs/common";
2
+ import * as fs from 'fs/promises'; // Use the Promise-based version of fs for async/await
3
+ import * as path from 'path'; // To handle file paths
4
+ import { ModuleMetadataConfiguration } from "src/interfaces";
5
+
6
+ @Injectable()
7
+ export class ModuleMetadataHelperService {
8
+ // async getModuleMetadataConfig(moduleName: string): Promise<ModuleMetadata> {
9
+ // const filePath = this.getModuleMetadataFilePath(moduleName);
10
+ // const metadata = await this.getModuleMetadata(filePath);
11
+ // return metadata;
12
+ // }
13
+
14
+ async getModuleMetadataConfiguration(configFilePath: string): Promise<any> {
15
+ const fileContent = await fs.readFile(configFilePath, 'utf8');
16
+ return JSON.parse(fileContent);
17
+ }
18
+
19
+ getModuleMetadataFilePath(moduleName: string): string {
20
+ const folderPath = path.resolve(process.cwd(), 'module-metadata', moduleName);
21
+ const filePath = path.join(folderPath, `${moduleName}-metadata.json`);
22
+ return filePath;
23
+ }
24
+
25
+ }
@@ -66,8 +66,8 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
66
66
  let hasMediaFields = false;
67
67
 
68
68
  const model = await this.loadModel();
69
- const inverseRelationFields = await this.loadInverseRelationFields();
70
- const fieldsToProcess = [...model.fields, ...inverseRelationFields];
69
+ // const inverseRelationFields = await this.loadInverseRelationFields();
70
+ const fieldsToProcess = [...model.fields];
71
71
 
72
72
  // 2. Loop through the fields with a switch statement
73
73
  // 3. Handle the fields based on field type
@@ -185,8 +185,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
185
185
  let hasMediaFields = false;
186
186
 
187
187
  const model = await this.loadModel();
188
- const inverseRelationFields = await this.loadInverseRelationFields();
189
- const fieldsToProcess = [...model.fields, ...inverseRelationFields];
188
+ const fieldsToProcess = [...model.fields];
190
189
 
191
190
  // 2. Loop through the fields with a switch statement
192
191
  // 3. Handle the fields based on field type
@@ -305,9 +304,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
305
304
  }
306
305
  case SolidFieldType.relation: {
307
306
  // Identify if the field is for the inverse side or not
308
- const inverseSide = (fieldMetadata.model.singularName !== this.modelName) ? true : false;
309
307
  if (fieldMetadata.relationType === RelationType.manyToOne) {
310
- if (!inverseSide) {
311
308
  const manyToOneOptions: ManyToOneRelationFieldOptions = {
312
309
  ...commonOptions,
313
310
  relationModelSingularName: fieldMetadata.relationModelSingularName,
@@ -316,23 +313,20 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
316
313
  entityManager,
317
314
  }
318
315
  return new ManyToOneRelationFieldCrudManager(manyToOneOptions);
319
- }
320
- else {
321
- const inverseFieldMetadata = fieldMetadata; //Setting an alias for clarity purpose
316
+ }
317
+ else if (fieldMetadata.relationType === RelationType.oneToMany) {
322
318
  const oneToManyOptions: OneToManyRelationFieldOptions = {
323
319
  ...commonOptions,
324
- required: false,
325
- relationModelSingularName: inverseFieldMetadata.model.singularName,
326
- modelSingularName: inverseFieldMetadata.relationModelSingularName,
320
+ relationModelSingularName: fieldMetadata.relationModelSingularName,
321
+ modelSingularName: fieldMetadata.model.singularName,
327
322
  entityManager,
328
- inverseFieldName: inverseFieldMetadata.name,
329
- inverseRelationModelFieldName: inverseFieldMetadata.relationModelFieldName,
323
+ inverseFieldName: fieldMetadata.relationModelFieldName,
324
+ inverseRelationModelFieldName: fieldMetadata.name,
330
325
  }
331
326
  return new OneToManyRelationFieldCrudManager(oneToManyOptions);
332
- }
333
327
  }
334
328
  else if (fieldMetadata.relationType === RelationType.manyTomany) {
335
- if (!inverseSide) {
329
+ if (fieldMetadata.isRelationManyToManyOwner) {
336
330
  const manyToManyOptions: ManyToManyRelationFieldOptions = {
337
331
  ...commonOptions,
338
332
  relationModelSingularName: fieldMetadata.relationModelSingularName,
@@ -344,16 +338,14 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
344
338
  return new ManyToManyRelationFieldCrudManager(manyToManyOptions);
345
339
  }
346
340
  else {
347
- const inverseFieldMetadata = fieldMetadata; //Setting an alias for clarity purpose
348
341
  const inverseManyToManyOptions: ManyToManyRelationFieldOptions = {
349
342
  ...commonOptions,
350
- required: false,
351
- relationModelSingularName: inverseFieldMetadata.model.singularName,
352
- modelSingularName: inverseFieldMetadata.relationModelSingularName,
343
+ relationModelSingularName: fieldMetadata.relationModelSingularName,
344
+ modelSingularName: fieldMetadata.model.singularName,
353
345
  isInverseSide: true,
354
346
  entityManager,
355
- fieldName: inverseFieldMetadata.name,
356
- relationModelFieldName: inverseFieldMetadata.relationModelFieldName,
347
+ fieldName: fieldMetadata.relationModelFieldName,
348
+ relationModelFieldName: fieldMetadata.name,
357
349
  }
358
350
  return new ManyToManyRelationFieldCrudManager(inverseManyToManyOptions);
359
351
  }
@@ -1,16 +1,18 @@
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
+
14
16
 
15
17
  @Injectable()
16
18
  export class FieldMetadataService {
@@ -20,9 +22,146 @@ export class FieldMetadataService {
20
22
  @InjectDataSource()
21
23
  private readonly dataSource: DataSource,
22
24
  private readonly solidRegistry: SolidRegistry,
23
- private readonly crudHelperService: CrudHelperService
25
+ private readonly crudHelperService: CrudHelperService,
26
+ private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
24
27
  ) { }
25
28
 
29
+ private logger = new Logger(FieldMetadataService.name);
30
+
31
+ async updateInverseField(field: FieldMetadata, fieldRepository: Repository<FieldMetadata>, modelRepository: Repository<ModelMetadata>) {
32
+ if (!field.model || !field.model.module) {
33
+ throw new Error('Model and module are required to update inverse field');
34
+ }
35
+ // Update the inverse field in the db
36
+ const savedInverseField = await this.updateInverseFieldInDb(field, fieldRepository, modelRepository);
37
+ // Update the inverse field in the file
38
+ this.updateRelationInverseFieldInFile(savedInverseField, field.relationModelSingularName, field.model.module.name);
39
+ }
40
+
41
+ private async updateInverseFieldInDb(field: FieldMetadata, fieldRepository: Repository<FieldMetadata>, modelRepository: Repository<ModelMetadata>): Promise<FieldMetadata> {
42
+ const { moduleName, modelName } = this.validateForInverseField(field);
43
+
44
+ // Get the relation model reference
45
+ const relationModel = await this.getRelationModel(modelRepository, field, moduleName);
46
+
47
+ // const {id, createdAt, updatedAt, deletedAt, ...fieldKeys} = field;
48
+ switch (field.relationType) {
49
+ case RelationType.manyToOne: {
50
+ const inverseField: FieldMetadata = {
51
+ ...field,
52
+ name: field.relationModelFieldName ?? `${modelName}s`,
53
+ displayName: `Inverse ${field.displayName}`,
54
+ description: `Inverse field for ${field.displayName}`,
55
+ type: SolidFieldType.relation,
56
+ isSystem: field.isSystem,
57
+ relationType: RelationType.oneToMany,
58
+ relationModelSingularName: modelName,
59
+ relationCreateInverse: true,
60
+ relationCascade: field.relationCascade,
61
+ relationModelModuleName: moduleName,
62
+ relationModelFieldName: field.name,
63
+ required: false,
64
+ unique: false,
65
+ index: false,
66
+ private: false,
67
+ encrypt: false,
68
+ model: relationModel,
69
+ columnName:null,
70
+ relationJoinTableName: null,
71
+ relationJoinColumnName: null,
72
+ joinColumnName: null,
73
+ id : null,
74
+ // createdAt: null,
75
+ // updatedAt: null,
76
+ // deletedAt: null,
77
+ }
78
+
79
+ // Load the inverse field,
80
+ const savedField = await this.saveInverseField(fieldRepository, relationModel, inverseField);
81
+ return savedField;
82
+ }
83
+ case RelationType.manyTomany: {
84
+ // Logic to create a manyToMany inverse field definition
85
+ const inverseFieldManyToMany: FieldMetadata = {
86
+ ...field,
87
+ name: field.relationModelFieldName,
88
+ displayName: `Inverse ${field.displayName}`,
89
+ description: `Inverse field for ${field.displayName}`,
90
+ type: SolidFieldType.relation,
91
+ isSystem: field.isSystem,
92
+ relationType: RelationType.manyTomany,
93
+ relationModelSingularName: modelName,
94
+ relationCreateInverse: true,
95
+ relationCascade: field.relationCascade,
96
+ relationModelModuleName: moduleName,
97
+ relationModelFieldName: field.name,
98
+ required: false,
99
+ unique: false,
100
+ index: false,
101
+ private: false,
102
+ encrypt: false,
103
+ model: relationModel,
104
+ columnName:null,
105
+ relationJoinTableName: null,
106
+ relationJoinColumnName: null,
107
+ joinColumnName: null,
108
+ isRelationManyToManyOwner: false,
109
+ id : null,
110
+ // createdAt: null,
111
+ // updatedAt: null,
112
+ // deletedAt: null,
113
+ }
114
+ const savedField = await this.saveInverseField(fieldRepository, relationModel, inverseFieldManyToMany);
115
+ return savedField;
116
+ }
117
+ default:
118
+ throw new Error(`Invalid relation type for field ${field.name} with relation type ${field.relationType}`);
119
+ }
120
+ }
121
+
122
+ private async getRelationModel(modelRepository: Repository<ModelMetadata>, field: FieldMetadata, moduleName: string) {
123
+ return await modelRepository.findOne({
124
+ where: {
125
+ singularName: field.relationModelSingularName,
126
+ module: {
127
+ name: field.relationModelModuleName ?? moduleName
128
+ }
129
+ }
130
+ });
131
+ }
132
+
133
+ private async saveInverseField(fieldRepository: Repository<FieldMetadata>, relationModel: ModelMetadata, inverseField: FieldMetadata): Promise<FieldMetadata> {
134
+ const existingInverseField = await fieldRepository.findOne({
135
+ where: {
136
+ model: relationModel,
137
+ name: inverseField.name
138
+ }
139
+ });
140
+
141
+ if (existingInverseField) {
142
+ const updatedField = fieldRepository.merge(existingInverseField, inverseField);
143
+ const savedField = await fieldRepository.save(updatedField);
144
+ return savedField;
145
+ }
146
+ else {
147
+ const savedField = await fieldRepository.save(fieldRepository.create(inverseField));
148
+ return savedField;
149
+ }
150
+ }
151
+
152
+ private validateForInverseField(field: FieldMetadata) {
153
+ if (field.type !== SolidFieldType.relation) {
154
+ throw new Error('Only relation fields can have inverse fields');
155
+ }
156
+ const modelName = field.model.singularName;
157
+ const moduleName = field.model.module.name;
158
+
159
+ if (!modelName || !moduleName) {
160
+ throw new Error('Model name and module name are required to create inverse field');
161
+ }
162
+ return { moduleName, modelName };
163
+ }
164
+
26
165
  async findMany(basicFilterDto: BasicFilterDto) {
27
166
  const alias = 'fieldMetadata';
28
167
  // Extract the required keys from the input query
@@ -586,7 +725,8 @@ export class FieldMetadataService {
586
725
  "columnName",
587
726
  "relationJoinColumnName",
588
727
  "joinColumnName",
589
- "relationJoinTableName"
728
+ "relationJoinTableName",
729
+ "isRelationManyToManyOwner",
590
730
  ];
591
731
 
592
732
  case SolidFieldType.mediaSingle:
@@ -874,4 +1014,54 @@ export class FieldMetadataService {
874
1014
  return selectionProviderInstance.value(query.optionValue, selectionDynamicProviderCtxt);
875
1015
  }
876
1016
 
1017
+ private async updateRelationInverseFieldInFile(savedInverseField: FieldMetadata, inverseModelName: string, moduleName: string) {
1018
+ try {
1019
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
1020
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
1021
+
1022
+ // Create the config object for the inverse field
1023
+ const fieldObject: Record<string, any> = await this.createFieldConfig(savedInverseField);
1024
+
1025
+ // Find the field config object in the json file
1026
+ const model = metaData.moduleMetadata.models.find((model: any) => model.singularName === inverseModelName);
1027
+
1028
+ // Replace the current field object with the above field object
1029
+ const fieldIndex = model.fields.findIndex((field: any) => field.name === savedInverseField.name);
1030
+ if (fieldIndex === -1) {
1031
+ model.fields.push(fieldObject);
1032
+ }
1033
+ else {
1034
+ model.fields[fieldIndex] = fieldObject;
1035
+ }
1036
+
1037
+ // Write the updated object back to the file
1038
+ const updatedContent = JSON.stringify(metaData, null, 2);
1039
+ await fs.writeFile(filePath, updatedContent);
1040
+ } catch (error) {
1041
+ this.logger.error('File creation failed:', error);
1042
+ throw new Error('File creation failed, rolling back transaction'); // Trigger rollback
1043
+ }
1044
+ }
1045
+
1046
+ //Moved existing reusable logic to a separate function
1047
+ async createFieldConfig(field: FieldMetadata) {
1048
+ const fieldsRequiredBasedOnType = await this.fetchCurrentFieldsBasedOnType(field.type);
1049
+ const fieldObject: Record<string, any> = {};
1050
+
1051
+ // Assign default or placeholder values for required fields
1052
+ fieldsRequiredBasedOnType.forEach((requiredField) => {
1053
+ fieldObject[requiredField] = field[requiredField];
1054
+ });
1055
+
1056
+ if (field.type == "mediaSingle" || field.type == "mediaMultiple") {
1057
+ if (field.mediaStorageProvider) {
1058
+ delete fieldObject.mediaStorageProviderId
1059
+ fieldObject.mediaStorageProviderUserKey = field.mediaStorageProvider.name
1060
+ }
1061
+ }
1062
+
1063
+ return fieldObject;
1064
+ }
877
1065
  }
1066
+
1067
+
@@ -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,9 +119,15 @@ 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) {
@@ -129,6 +136,16 @@ export class ModelMetadataService {
129
136
  }
130
137
  }
131
138
 
139
+ // Iterate through the fields in the createDto & get all the relation fields which have create inverse as true
140
+ private async handleInverseRelationFieldsUpdates(model: ModelMetadata, fieldRepository: Repository<FieldMetadata>, modelRepository: Repository<ModelMetadata>) {
141
+ const fields: FieldMetadata[] = await this.getRelationInverseFields(model.id, fieldRepository);
142
+
143
+ // 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
144
+ for (const field of fields) {
145
+ await this.fieldMetadataService.updateInverseField(field, fieldRepository, modelRepository);
146
+ }
147
+ }
148
+
132
149
  async update(id: number, updateModelMetaDataDto: UpdateModelMetaDataDto) {
133
150
  //To DO start the transaction
134
151
  // call create In db
@@ -136,9 +153,15 @@ export class ModelMetadataService {
136
153
 
137
154
  try {
138
155
  return await this.dataSource.transaction(async (manager: EntityManager) => {
156
+ const modelRepository = manager.getRepository(ModelMetadata);
157
+ const fieldRepository = manager.getRepository(FieldMetadata);
158
+
139
159
  // Step 1: Write initial data to the database
140
160
  const model = await this.updateInDb(manager, id, updateModelMetaDataDto)
141
- await this.updateInFile(model.id, manager.getRepository(ModelMetadata));
161
+ await this.updateInFile(model.id, modelRepository);
162
+
163
+ await this.handleInverseRelationFieldsUpdates(model, fieldRepository, modelRepository);
164
+
142
165
  // return model
143
166
  });
144
167
  } catch (error) {
@@ -304,15 +327,11 @@ export class ModelMetadataService {
304
327
  where: {
305
328
  id: modelId,
306
329
  },
307
- relations: ["fields", "module"], //FIXME: Check with jenender and change to relations to avoid confusion
330
+ relations: ["fields", "fields.mediaStorageProvider", "module"], //FIXME: Check with jenender and change to relations to avoid confusion
308
331
  });
309
332
 
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);
333
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(model.module.name);
334
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
316
335
 
317
336
  const modelMetaData = {
318
337
  singularName: model.singularName,
@@ -331,13 +350,7 @@ export class ModelMetadataService {
331
350
  const field = model.fields[i];
332
351
  if (!field.isSystem) {
333
352
 
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
- });
353
+ const fieldObject: Record<string, any> = await this.fieldMetadataService.createFieldConfig(field);
341
354
  modelMetaData.fields.push(fieldObject);
342
355
  listViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}`, sortable: true, filterable: true } })
343
356
  formViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}` } })
@@ -476,7 +489,7 @@ export class ModelMetadataService {
476
489
  // Existing field
477
490
  const existingField = existingFields.find((field) => field.id === fieldMetadata.id);
478
491
  if (existingField) {
479
- if (fieldMetadata.mediaStorageProviderId) {
492
+ if (fieldMetadata.mediaStorageProviderId) {
480
493
  fieldMetadata['mediaStorageProvider'] = await this.mediaStorageProviderMetadataService.findOne(fieldMetadata.mediaStorageProviderId);
481
494
  }
482
495
  Object.assign(existingField, fieldMetadata);
@@ -562,12 +575,8 @@ export class ModelMetadataService {
562
575
  relations: ["fields", "fields.mediaStorageProvider", "module"], //FIXME: Check with jenender and change to relations to avoid confusion
563
576
  });
564
577
 
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);
578
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(model.module.name);
579
+ const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
571
580
 
572
581
  const modelMetaData = {
573
582
  singularName: model.singularName,
@@ -583,18 +592,7 @@ export class ModelMetadataService {
583
592
  const field = model.fields[i];
584
593
  if (!field.isSystem && !field.isMarkedForRemoval) {
585
594
 
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
- }
595
+ const fieldObject: Record<string, any> = await this.fieldMetadataService.createFieldConfig(field);
598
596
  modelMetaData.fields.push(fieldObject);
599
597
  }
600
598
  }
@@ -908,4 +906,23 @@ export class ModelMetadataService {
908
906
  }
909
907
 
910
908
 
909
+ private async getRelationInverseFields(modelId: number, repo: Repository<FieldMetadata>): Promise<FieldMetadata[]> {
910
+ return await repo.find({
911
+ where: {
912
+ model : {
913
+ id: modelId
914
+ },
915
+ type: SolidFieldType.relation,
916
+ relationCreateInverse: true
917
+ },
918
+ relations: {
919
+ model: {
920
+ module: true
921
+ }
922
+ }
923
+ });
924
+ }
925
+
911
926
  }
927
+
928
+
@@ -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) {
@@ -154,7 +156,7 @@ export class ModuleMetadataService {
154
156
 
155
157
  // Create the folder path inside 'module-metadata'
156
158
  const folderPath = path.resolve(process.cwd(), 'module-metadata', module.name);
157
- const filePath = this.getModuleMetadataFilePath(module);
159
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(module.name);
158
160
 
159
161
  // Ensure the folder exists
160
162
  await fs.mkdir(folderPath, { recursive: true });
@@ -205,12 +207,12 @@ export class ModuleMetadataService {
205
207
 
206
208
  async updateInFile(module: ModuleMetadata) {
207
209
  try {
208
- const filePath = this.getModuleMetadataFilePath(module);
210
+ const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(module.name);
209
211
 
210
212
  // Read the existing JSON file
211
213
  let metaData;
212
214
  try {
213
- metaData = await this.getModuleMetadata(filePath);
215
+ metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
214
216
 
215
217
  } catch (error) {
216
218
  metaData = {
@@ -255,17 +257,6 @@ export class ModuleMetadataService {
255
257
  }
256
258
 
257
259
 
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
260
  async upsert(updateModuleMetadataDto: UpdateModuleMetadataDto) {
270
261
  this.logger.log(`Module Upsert called for : ${updateModuleMetadataDto.name}`);
271
262
  // First check if module already exists using name
@@ -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,