@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.
- package/dist/controllers/model-metadata.controller.d.ts +4 -0
- package/dist/controllers/model-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/model-metadata.controller.js +12 -0
- package/dist/controllers/model-metadata.controller.js.map +1 -1
- package/dist/controllers/test.controller.d.ts.map +1 -1
- package/dist/controllers/test.controller.js +2 -2
- package/dist/controllers/test.controller.js.map +1 -1
- package/dist/dtos/basic-filters.dto.d.ts +5 -2
- package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
- package/dist/dtos/basic-filters.dto.js +13 -9
- package/dist/dtos/basic-filters.dto.js.map +1 -1
- package/dist/dtos/create-field-metadata.dto.d.ts +3 -1
- package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-field-metadata.dto.js +7 -1
- package/dist/dtos/create-field-metadata.dto.js.map +1 -1
- package/dist/dtos/create-user.dto.d.ts.map +1 -1
- package/dist/dtos/create-user.dto.js +2 -2
- package/dist/dtos/create-user.dto.js.map +1 -1
- package/dist/entities/field-metadata.entity.d.ts +1 -0
- package/dist/entities/field-metadata.entity.d.ts.map +1 -1
- package/dist/entities/field-metadata.entity.js +5 -1
- package/dist/entities/field-metadata.entity.js.map +1 -1
- package/dist/helpers/module-metadata-helper.service.d.ts +5 -0
- package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -0
- package/dist/helpers/module-metadata-helper.service.js +61 -0
- package/dist/helpers/module-metadata-helper.service.js.map +1 -0
- package/dist/helpers/schematic.service.js +1 -1
- package/dist/helpers/schematic.service.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/jobs/queue-test-subscriber.service.d.ts +1 -0
- package/dist/jobs/queue-test-subscriber.service.d.ts.map +1 -1
- package/dist/jobs/queue-test-subscriber.service.js +5 -3
- package/dist/jobs/queue-test-subscriber.service.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +30 -23
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +1 -1
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +6 -4
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +26 -35
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/field-metadata.service.d.ts +17 -6
- package/dist/services/field-metadata.service.d.ts.map +1 -1
- package/dist/services/field-metadata.service.js +253 -34
- package/dist/services/field-metadata.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +9 -1
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +74 -35
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts +3 -3
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +12 -18
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/solid-introspect.service.d.ts.map +1 -1
- package/dist/services/solid-introspect.service.js +9 -1
- package/dist/services/solid-introspect.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +2 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.d.ts.map +1 -1
- package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.js +1 -1
- package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/controllers/model-metadata.controller.ts +6 -0
- package/src/controllers/test.controller.ts +4 -2
- package/src/dtos/basic-filters.dto.ts +11 -7
- package/src/dtos/create-field-metadata.dto.ts +5 -0
- package/src/dtos/create-user.dto.ts +1 -1
- package/src/entities/field-metadata.entity.ts +3 -0
- package/src/helpers/module-metadata-helper.service.ts +25 -0
- package/src/helpers/schematic.service.ts +1 -1
- package/src/index.ts +0 -1
- package/src/jobs/queue-test-subscriber.service.ts +4 -2
- package/src/seeders/module-metadata-seeder.service.ts +30 -26
- package/src/seeders/seed-data/solid-core-metadata.json +1 -1
- package/src/services/crud-helper.service.ts +8 -6
- package/src/services/crud.service.ts +14 -22
- package/src/services/field-metadata.service.ts +229 -10
- package/src/services/model-metadata.service.ts +98 -44
- package/src/services/module-metadata.service.ts +13 -18
- package/src/services/solid-introspect.service.ts +17 -15
- package/src/solid-core.module.ts +2 -0
- package/src/subscribers/softDeleteAwareEventSubscriber.subscriber.ts +2 -1
- package/dist/commands/basic.command.d.ts +0 -19
- package/dist/commands/basic.command.d.ts.map +0 -1
- package/dist/commands/basic.command.js +0 -79
- package/dist/commands/basic.command.js.map +0 -1
- 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
|
|
6
|
-
import {
|
|
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 {
|
|
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,
|
|
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,
|
|
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
|
|
311
|
-
const
|
|
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
|
|
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
|
|
566
|
-
const
|
|
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
|
|
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.
|
|
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
|
-
|
|
132
|
-
|
|
131
|
+
|
|
132
|
+
private isModule(provider: InstanceWrapper): boolean {
|
|
133
133
|
const metatype = provider.metatype;
|
|
134
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/solid-core.module.ts
CHANGED
|
@@ -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,
|