@solidstarters/solid-core 1.2.62 → 1.2.64
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/interfaces.d.ts +3 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/services/crud-helper.service.d.ts +1 -0
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +14 -1
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +6 -0
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +89 -47
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +4 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +146 -104
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/interfaces.ts +5 -1
- package/src/services/crud-helper.service.ts +19 -1
- package/src/services/crud.service.ts +110 -56
- package/src/services/model-metadata.service.ts +160 -212
- package/src/services/1.js +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidstarters/solid-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.64",
|
|
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",
|
package/src/interfaces.ts
CHANGED
|
@@ -158,11 +158,18 @@ export class CrudHelperService {
|
|
|
158
158
|
|
|
159
159
|
buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string): SelectQueryBuilder<any> { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
|
|
160
160
|
let { limit, offset, showSoftDeleted, filters } = basicFilterDto;
|
|
161
|
-
const { fields, sort, groupBy, populate = [] } = basicFilterDto;
|
|
161
|
+
const { fields, sort, groupBy, populate = [], populateMedia=[] } = basicFilterDto;
|
|
162
162
|
|
|
163
163
|
// Normalize the fields, sort, groupBy and populate options i.e (since they can be either a string or an array of strings, when coming from the request)
|
|
164
164
|
const normalizedFields = this.normalize(fields);
|
|
165
165
|
const normalizedPopulate = this.normalize(populate);
|
|
166
|
+
const normalizedPopulateMedia = this.normalize(populateMedia);
|
|
167
|
+
|
|
168
|
+
// if normalizedPopulateMedia, has any nested media paths, then add then to populate excluding the last part
|
|
169
|
+
const additionalPopulate = this.additionalRelationsRequiredForMediaPopulation(normalizedPopulateMedia);
|
|
170
|
+
// Add the additional populate relations to the normalizedPopulate, if they are not already present
|
|
171
|
+
normalizedPopulate.push(...additionalPopulate.filter((relation) => !normalizedPopulate.includes(relation)));
|
|
172
|
+
|
|
166
173
|
const normalizedSort = this.normalize(sort);
|
|
167
174
|
const normalizedGroupBy = this.normalize(groupBy);
|
|
168
175
|
if (normalizedGroupBy.length > 1) {
|
|
@@ -223,6 +230,17 @@ export class CrudHelperService {
|
|
|
223
230
|
return qb;
|
|
224
231
|
}
|
|
225
232
|
|
|
233
|
+
additionalRelationsRequiredForMediaPopulation(normalizedPopulateMedia: string[]) {
|
|
234
|
+
// Populate relations containing the media field
|
|
235
|
+
return normalizedPopulateMedia
|
|
236
|
+
.filter(pm => pm.includes("."))
|
|
237
|
+
.map((pm) => {
|
|
238
|
+
const mediaPathParts = pm.split('.');
|
|
239
|
+
if (mediaPathParts.length <= 1) return pm;
|
|
240
|
+
return mediaPathParts.slice(0, -1).join('.');
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
226
244
|
private buildPopulateQuery(normalizedPopulate: string[], entityAlias: string, qb: SelectQueryBuilder<any>) {
|
|
227
245
|
normalizedPopulate.forEach((relation) => {
|
|
228
246
|
this.buildJoinQueryForRelation(qb, entityAlias, relation);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadRequestException } from "@nestjs/common";
|
|
1
|
+
import { BadRequestException, Optional } from "@nestjs/common";
|
|
2
2
|
import { ConfigService } from "@nestjs/config";
|
|
3
3
|
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
4
4
|
import { EntityManager, In, IsNull, Not, QueryFailedError, SelectQueryBuilder } from "typeorm";
|
|
@@ -28,14 +28,13 @@ import { SelectionDynamicFieldCrudManager } from "../helpers/field-crud-managers
|
|
|
28
28
|
import { SelectionStaticFieldCrudManager } from "../helpers/field-crud-managers/SelectionStaticFieldCrudManager";
|
|
29
29
|
import { ShortTextFieldCrudManager } from "../helpers/field-crud-managers/ShortTextFieldCrudManager";
|
|
30
30
|
import { UUIDFieldCrudManager } from "../helpers/field-crud-managers/UUIDFieldCrudManager";
|
|
31
|
-
import { FieldCrudManager } from "../interfaces";
|
|
31
|
+
import { FieldCrudManager, MediaWithFullUrl } from "../interfaces";
|
|
32
32
|
import { CrudHelperService } from "./crud-helper.service";
|
|
33
33
|
import { FileService } from "./file.service";
|
|
34
34
|
import { getMediaStorageProvider } from "./mediaStorageProviders";
|
|
35
35
|
import { ModelMetadataService } from "./model-metadata.service";
|
|
36
36
|
import { ModuleMetadataService } from "./module-metadata.service";
|
|
37
37
|
import { UserContextService } from "./user-context.service";
|
|
38
|
-
import { Optional } from "@nestjs/common";
|
|
39
38
|
const DEFAULT_LIMIT = 10;
|
|
40
39
|
const DEFAULT_OFFSET = 0;
|
|
41
40
|
export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
|
|
@@ -496,42 +495,85 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
496
495
|
return r;
|
|
497
496
|
}
|
|
498
497
|
|
|
499
|
-
|
|
500
|
-
const model = await this.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
mediaStorageProvider: true,
|
|
498
|
+
private async handlePopulateMedia(populateMedia: string[], entities: T[]) {
|
|
499
|
+
const model = await this.entityManager.getRepository(ModelMetadata).findOne({
|
|
500
|
+
where: {
|
|
501
|
+
singularName: this.modelName,
|
|
504
502
|
},
|
|
505
|
-
|
|
503
|
+
relations: ['fields', 'fields.mediaStorageProvider', 'fields.model','module'],
|
|
506
504
|
});
|
|
507
505
|
|
|
508
|
-
|
|
509
|
-
)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
506
|
+
// Will iterate through every entity & all populateMedia & call getMediaDetails for each field
|
|
507
|
+
for (const entity of entities) {
|
|
508
|
+
const mediaObj: Record<string, any> = {};
|
|
509
|
+
for (const mediaFieldPath of populateMedia) {
|
|
510
|
+
mediaObj[mediaFieldPath] = await this.getMediaObject(mediaFieldPath, model, entity);
|
|
511
|
+
}
|
|
512
|
+
entity['_media'] = mediaObj;
|
|
513
|
+
}
|
|
514
|
+
return entities;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
private async getMediaObject(mediaFieldPath: string, model: ModelMetadata, entity: T) {
|
|
518
|
+
if (mediaFieldPath.includes('.')) { // mediaFieldPath is a nested field
|
|
519
|
+
const pathParts = mediaFieldPath.split('.');
|
|
520
|
+
const mediaFieldMetadata = await this.getFieldMetadataRecursively(pathParts, model.fields);
|
|
521
|
+
if (!mediaFieldMetadata) {
|
|
522
|
+
throw new BadRequestException(`Media field ${mediaFieldPath} not found in model ${this.modelName}`);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// We can assume that the media field entity model is already populated as part of the entity data
|
|
526
|
+
const mediaFieldEntity = this.getMediaFieldEntity(entity, pathParts);
|
|
527
|
+
if (!mediaFieldEntity) {
|
|
528
|
+
throw new BadRequestException(`Media field path ${mediaFieldPath} is not populated in model ${this.modelName}`);
|
|
529
|
+
}
|
|
530
|
+
const mediaWithFullUrl = await this.getMediaWithFullUrl(mediaFieldEntity, mediaFieldMetadata);
|
|
531
|
+
return mediaWithFullUrl;
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
// mediaFieldPath is a single field
|
|
535
|
+
const mediaFieldMetadata = model.fields.find(field => field.name === mediaFieldPath);
|
|
536
|
+
if (!mediaFieldMetadata) {
|
|
537
|
+
throw new BadRequestException(`Media field ${mediaFieldPath} not found in model ${this.modelName}`);
|
|
538
|
+
}
|
|
539
|
+
const mediaWithFullUrl = await this.getMediaWithFullUrl(entity, mediaFieldMetadata);
|
|
540
|
+
return mediaWithFullUrl;
|
|
529
541
|
}
|
|
530
542
|
}
|
|
531
543
|
|
|
544
|
+
private getMediaFieldEntity(entity: T, mediaPathParts: string[]) {
|
|
545
|
+
let mediaFieldEntity = entity;
|
|
546
|
+
for (let i = 0; i < mediaPathParts.length - 1; i++) {
|
|
547
|
+
const pathPart = mediaPathParts[i];
|
|
548
|
+
if (mediaFieldEntity[pathPart]) {
|
|
549
|
+
mediaFieldEntity = mediaFieldEntity[pathPart];
|
|
550
|
+
} else {
|
|
551
|
+
throw new BadRequestException(`Media field ${pathPart} not found in entity ${JSON.stringify(entity)}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return mediaFieldEntity;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async getMediaWithFullUrl(mediaEntity: any, mediaFieldMetadata: FieldMetadata): Promise<MediaWithFullUrl>{
|
|
558
|
+
const storageProviderMetadata = mediaFieldMetadata.mediaStorageProvider;
|
|
559
|
+
const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
|
|
560
|
+
const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
|
|
561
|
+
const mediaDetails = await storageProvider.retrieve(mediaEntity, mediaFieldMetadata);
|
|
562
|
+
return mediaDetails as MediaWithFullUrl;
|
|
563
|
+
}
|
|
564
|
+
|
|
532
565
|
async findOne(id: number, query: any, solidRequestContext: any = {}) {
|
|
533
566
|
const { populate = [], fields = [], populateMedia = [] } = query;
|
|
534
567
|
|
|
568
|
+
// const normalizedFields = this.crudHelperService.normalize(fields);
|
|
569
|
+
const normalizedPopulate = this.crudHelperService.normalize(populate);
|
|
570
|
+
const normalizedPopulateMedia = this.crudHelperService.normalize(populateMedia);
|
|
571
|
+
|
|
572
|
+
// if normalizedPopulateMedia, has any nested media paths, then add then to populate excluding the last part
|
|
573
|
+
const additionalPopulate = this.crudHelperService.additionalRelationsRequiredForMediaPopulation(normalizedPopulateMedia);
|
|
574
|
+
// Add the additional populate relations to the normalizedPopulate, if they are not already present
|
|
575
|
+
normalizedPopulate.push(...additionalPopulate.filter((relation) => !normalizedPopulate.includes(relation)));
|
|
576
|
+
|
|
535
577
|
const model = await this.loadModel();
|
|
536
578
|
// Check wheather user has update permission for model
|
|
537
579
|
if (solidRequestContext.activeUser) {
|
|
@@ -541,42 +583,21 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
541
583
|
}
|
|
542
584
|
}
|
|
543
585
|
|
|
544
|
-
|
|
586
|
+
let entity = await this.repo.findOne({
|
|
545
587
|
where: {
|
|
546
588
|
//@ts-ignore
|
|
547
589
|
id: id,
|
|
548
590
|
},
|
|
549
|
-
relations:
|
|
591
|
+
relations: normalizedPopulate,
|
|
550
592
|
select: fields,
|
|
551
593
|
});
|
|
552
594
|
if (!entity) {
|
|
553
595
|
throw new Error(`Entity [${this.moduleName}.${this.modelName}] with id ${id} not found`);
|
|
554
596
|
}
|
|
555
597
|
// Populate the entity with the media
|
|
556
|
-
if (
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
model: true,
|
|
560
|
-
mediaStorageProvider: true,
|
|
561
|
-
},
|
|
562
|
-
module: true,
|
|
563
|
-
});
|
|
564
|
-
const mediaObj: Record<string, any> = {};
|
|
565
|
-
for (const mediaField of model.fields.filter(field => field.type === 'mediaSingle' || field.type === 'mediaMultiple')) {
|
|
566
|
-
if (!populateMedia.includes(mediaField.name)) {
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
const storageProviderMetadata = mediaField.mediaStorageProvider;
|
|
570
|
-
const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
|
|
571
|
-
const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
|
|
572
|
-
const mediaResult = await storageProvider.retrieve(entity, mediaField);
|
|
573
|
-
let obj = { [mediaField.name]: mediaResult }
|
|
574
|
-
mediaObj[mediaField.name] = mediaResult;
|
|
575
|
-
// entity['media'][mediaField.name] = await storageProvider.retrieve(entity, mediaField);
|
|
576
|
-
}
|
|
577
|
-
if (Object.keys(mediaObj).length > 0) {
|
|
578
|
-
entity['_media'] = mediaObj;
|
|
579
|
-
}
|
|
598
|
+
if (normalizedPopulateMedia.length > 0) {
|
|
599
|
+
const populatedEntities = await this.handlePopulateMedia(normalizedPopulateMedia, [entity]);
|
|
600
|
+
entity = populatedEntities[0] as Awaited<T>;
|
|
580
601
|
}
|
|
581
602
|
return entity;
|
|
582
603
|
}
|
|
@@ -775,4 +796,37 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
775
796
|
}
|
|
776
797
|
|
|
777
798
|
|
|
799
|
+
async getFieldMetadataRecursively(pathParts: string[], fields: FieldMetadata[]) {
|
|
800
|
+
if (!pathParts || pathParts.length === 0) {
|
|
801
|
+
throw new BadRequestException('Path parts cannot be empty');
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const [currentPart, ...remainingParts] = pathParts;
|
|
805
|
+
const field = fields.find(field => field.name === currentPart);
|
|
806
|
+
|
|
807
|
+
if (!field) {
|
|
808
|
+
throw new BadRequestException(`Field ${currentPart} not found in model ${this.modelName}`);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Base case: last part, return the field
|
|
812
|
+
if (remainingParts.length === 0) {
|
|
813
|
+
return field;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (!field.relationCoModelSingularName) {
|
|
817
|
+
throw new BadRequestException(`Field ${field.name} does not define a relationCoModelSingularName`);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const relationCoModel = await this.entityManager.getRepository(ModelMetadata).findOne({
|
|
821
|
+
where: { singularName: field.relationCoModelSingularName },
|
|
822
|
+
relations: ['fields', 'fields.mediaStorageProvider', 'fields.model'],
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
if (!relationCoModel) {
|
|
826
|
+
throw new BadRequestException(`Model ${field.relationCoModelSingularName} not found`);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return this.getFieldMetadataRecursively(remainingParts, relationCoModel.fields);
|
|
830
|
+
}
|
|
778
831
|
}
|
|
832
|
+
|
|
@@ -232,105 +232,6 @@ export class ModelMetadataService {
|
|
|
232
232
|
model = await manager.save(updatedModelMetadataDto);
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
// const modelViews = [{
|
|
236
|
-
// name: `${model.singularName}-list-view`,
|
|
237
|
-
// displayName: `${model.displayName}`,
|
|
238
|
-
// type: 'list',
|
|
239
|
-
// context: "{}",
|
|
240
|
-
// module: resolvedModule,
|
|
241
|
-
// model: model,
|
|
242
|
-
// layout: JSON.stringify({
|
|
243
|
-
// type: "list",
|
|
244
|
-
// attrs: {
|
|
245
|
-
// pagination: true,
|
|
246
|
-
// pageSizeOptions: [
|
|
247
|
-
// 10,
|
|
248
|
-
// 25,
|
|
249
|
-
// 50
|
|
250
|
-
// ],
|
|
251
|
-
// enableGlobalSearch: true,
|
|
252
|
-
// create: true,
|
|
253
|
-
// edit: true,
|
|
254
|
-
// delete: true
|
|
255
|
-
// },
|
|
256
|
-
// children: listViewLayout
|
|
257
|
-
// }, null, 2)
|
|
258
|
-
// },
|
|
259
|
-
// {
|
|
260
|
-
// name: `${model.singularName}-form-view`,
|
|
261
|
-
// displayName: `${model.displayName}`,
|
|
262
|
-
// type: 'form',
|
|
263
|
-
// context: "{}",
|
|
264
|
-
// module: model.module,
|
|
265
|
-
// model: model,
|
|
266
|
-
// layout: JSON.stringify(
|
|
267
|
-
// {
|
|
268
|
-
// type: "form",
|
|
269
|
-
// attrs: { name: "form-1", label: `${model.displayName}`, className: "grid" },
|
|
270
|
-
// children: [
|
|
271
|
-
// {
|
|
272
|
-
// type: "sheet",
|
|
273
|
-
// attrs: { name: "sheet-1" },
|
|
274
|
-
// children: [
|
|
275
|
-
// {
|
|
276
|
-
// type: "row",
|
|
277
|
-
// attrs: { name: "group-1", label: "", className: "" },
|
|
278
|
-
// children: [
|
|
279
|
-
// {
|
|
280
|
-
// type: "column",
|
|
281
|
-
// attrs: { name: "group-1", label: "", className: "col-6" },
|
|
282
|
-
// children: formViewLayout
|
|
283
|
-
// }
|
|
284
|
-
// ]
|
|
285
|
-
// }
|
|
286
|
-
// ]
|
|
287
|
-
// }
|
|
288
|
-
// ]
|
|
289
|
-
// }, null, 2)
|
|
290
|
-
// }
|
|
291
|
-
// ];
|
|
292
|
-
// const viewRepo = manager.getRepository(ViewMetadata);
|
|
293
|
-
// for (let j = 0; j < modelViews.length; j++) {
|
|
294
|
-
// const view = modelViews[j];
|
|
295
|
-
// const createdView = await viewRepo.create(view);
|
|
296
|
-
// await viewRepo.save(createdView);
|
|
297
|
-
// }
|
|
298
|
-
|
|
299
|
-
// const view = await viewRepo.findOneBy({ name: `${model.singularName}-list-view` });
|
|
300
|
-
|
|
301
|
-
// const action = {
|
|
302
|
-
// displayName: `${model.displayName} List View`,
|
|
303
|
-
// name: `${model.singularName}-list-view`,
|
|
304
|
-
// type: "solid",
|
|
305
|
-
// domain: "",
|
|
306
|
-
// context: "",
|
|
307
|
-
// customComponent: `/admin/address-master/${model.singularName}/all`,
|
|
308
|
-
// customIsModal: true,
|
|
309
|
-
// serverEndpoint: "",
|
|
310
|
-
// view: view,
|
|
311
|
-
// module: resolvedModule,
|
|
312
|
-
// model: model
|
|
313
|
-
// };
|
|
314
|
-
// const actionRepo = manager.getRepository(ActionMetadata);
|
|
315
|
-
// const createdAction = await actionRepo.create(action);
|
|
316
|
-
// const newAction = await actionRepo.save(createdAction);
|
|
317
|
-
|
|
318
|
-
// const adminRole = await this.roleService.findRoleByName('Admin');
|
|
319
|
-
|
|
320
|
-
// const menu = {
|
|
321
|
-
// displayName: `${model.displayName}`,
|
|
322
|
-
// name: `${model.singularName}`,
|
|
323
|
-
// sequenceNumber: 1,
|
|
324
|
-
// action: newAction,
|
|
325
|
-
// module: resolvedModule,
|
|
326
|
-
// roles: [adminRole],
|
|
327
|
-
// parentMenuItemUserKey: ""
|
|
328
|
-
// };
|
|
329
|
-
|
|
330
|
-
// const menuRepo = manager.getRepository(MenuItemMetadata);
|
|
331
|
-
// const createdMenu = await menuRepo.create(menu);
|
|
332
|
-
// await menuRepo.save(createdMenu);
|
|
333
|
-
|
|
334
235
|
return model;
|
|
335
236
|
}
|
|
336
237
|
|
|
@@ -360,122 +261,14 @@ export class ModelMetadataService {
|
|
|
360
261
|
fields: []
|
|
361
262
|
}
|
|
362
263
|
|
|
363
|
-
const listViewLayoutFields = [{ type: "field", attrs: { name: `id`, sortable: true, filterable: true } }];
|
|
364
|
-
const formViewLayoutFields = [];
|
|
365
|
-
|
|
366
264
|
for (let i = 0; i < model.fields.length; i++) {
|
|
367
265
|
const field = model.fields[i];
|
|
368
|
-
if (
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
modelMetaData.fields.push(fieldObject);
|
|
372
|
-
listViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}`, sortable: true, filterable: true } })
|
|
373
|
-
formViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}` } })
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
const column1Fields = [];
|
|
378
|
-
const column2Fields = [];
|
|
379
|
-
|
|
380
|
-
// Distribute fields between two columns
|
|
381
|
-
for (let i = 0; i < formViewLayoutFields.length; i++) {
|
|
382
|
-
if (i % 2 === 0) {
|
|
383
|
-
column1Fields.push(formViewLayoutFields[i]);
|
|
384
|
-
} else {
|
|
385
|
-
column2Fields.push(formViewLayoutFields[i]);
|
|
386
|
-
}
|
|
266
|
+
if (field.isSystem) continue;
|
|
267
|
+
const fieldObject: Record<string, any> = await this.fieldMetadataService.createFieldConfig(field);
|
|
268
|
+
modelMetaData.fields.push(fieldObject);
|
|
387
269
|
}
|
|
388
|
-
const action = {
|
|
389
|
-
displayName: `${model.displayName} List View`,
|
|
390
|
-
name: `${model.singularName}-list-view`,
|
|
391
|
-
type: "solid",
|
|
392
|
-
domain: "",
|
|
393
|
-
context: "",
|
|
394
|
-
customComponent: `/admin/address-master/${model.singularName}/all`,
|
|
395
|
-
customIsModal: true,
|
|
396
|
-
serverEndpoint: "",
|
|
397
|
-
viewUserKey: `${model.singularName}-list-view`,
|
|
398
|
-
moduleUserKey: `${model.module.name}`,
|
|
399
|
-
modelUserKey: `${model.singularName}`
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
const menu = {
|
|
403
|
-
displayName: `${model.displayName}`,
|
|
404
|
-
name: `${model.singularName}-menu-item`,
|
|
405
|
-
sequenceNumber: 1,
|
|
406
|
-
actionUserKey: `${model.singularName}-list-view`,
|
|
407
|
-
moduleUserKey: `${model.module.name}`,
|
|
408
|
-
parentMenuItemUserKey: ""
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
const modelListview = {
|
|
412
|
-
name: `${model.singularName}-list-view`,
|
|
413
|
-
displayName: `${model.displayName}`,
|
|
414
|
-
type: "list",
|
|
415
|
-
context: "{}",
|
|
416
|
-
moduleUserKey: `${model.module.name}`,
|
|
417
|
-
modelUserKey: `${model.singularName}`,
|
|
418
|
-
layout: {
|
|
419
|
-
type: "list",
|
|
420
|
-
attrs: {
|
|
421
|
-
pagination: true,
|
|
422
|
-
pageSizeOptions: [
|
|
423
|
-
10,
|
|
424
|
-
25,
|
|
425
|
-
50
|
|
426
|
-
],
|
|
427
|
-
enableGlobalSearch: true,
|
|
428
|
-
create: true,
|
|
429
|
-
edit: true,
|
|
430
|
-
delete: true
|
|
431
|
-
},
|
|
432
|
-
children: listViewLayoutFields
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const modelFormView = {
|
|
438
|
-
name: `${model.singularName}-form-view`,
|
|
439
|
-
displayName: `${model.displayName}`,
|
|
440
|
-
type: "form",
|
|
441
|
-
context: "{}",
|
|
442
|
-
moduleUserKey: `${model.module.name}`,
|
|
443
|
-
modelUserKey: `${model.singularName}`,
|
|
444
|
-
layout: {
|
|
445
|
-
type: "form",
|
|
446
|
-
attrs: { name: "form-1", label: `${model.displayName}`, className: "grid" },
|
|
447
|
-
children: [
|
|
448
|
-
{
|
|
449
|
-
type: "sheet",
|
|
450
|
-
attrs: { name: "sheet-1" },
|
|
451
|
-
children: [
|
|
452
|
-
{
|
|
453
|
-
type: "row",
|
|
454
|
-
attrs: { name: "sheet-1" },
|
|
455
|
-
children: [
|
|
456
|
-
{
|
|
457
|
-
type: "column",
|
|
458
|
-
attrs: { name: "group-1", label: "", className: "col-6" },
|
|
459
|
-
children: column1Fields
|
|
460
|
-
},
|
|
461
|
-
{
|
|
462
|
-
type: "column",
|
|
463
|
-
attrs: { name: "group-2", label: "", className: "col-6" },
|
|
464
|
-
children: column2Fields
|
|
465
|
-
}]
|
|
466
|
-
},
|
|
467
|
-
]
|
|
468
|
-
}
|
|
469
|
-
]
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
270
|
// Update the `models` array
|
|
473
271
|
metaData.moduleMetadata.models.push(modelMetaData);
|
|
474
|
-
metaData.menus.push(menu);
|
|
475
|
-
metaData.actions.push(action);
|
|
476
|
-
metaData.views.push(modelListview);
|
|
477
|
-
metaData.views.push(modelFormView);
|
|
478
|
-
|
|
479
272
|
|
|
480
273
|
// Write the updated object back to the file
|
|
481
274
|
const updatedContent = JSON.stringify(metaData, null, 2);
|
|
@@ -734,6 +527,163 @@ export class ModelMetadataService {
|
|
|
734
527
|
await this.generateCode(inverseOptions);
|
|
735
528
|
}
|
|
736
529
|
|
|
530
|
+
await this.generateVAMConfig(model.id);
|
|
531
|
+
|
|
532
|
+
return `${removeFieldCodeOuput} \n ${refreshModelCodeOutput}`;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Generate the View, Action and Menu configuration for the model
|
|
536
|
+
private async generateVAMConfig(modelId: number) {
|
|
537
|
+
try {
|
|
538
|
+
return await this.dataSource.transaction(async (manager: EntityManager) => {
|
|
539
|
+
const modelRepository = manager.getRepository(ModelMetadata);
|
|
540
|
+
const model = await modelRepository.findOne({
|
|
541
|
+
where: {
|
|
542
|
+
id: modelId
|
|
543
|
+
},
|
|
544
|
+
relations: ["fields", "module"]
|
|
545
|
+
});
|
|
546
|
+
await this.populateVAMConfigInDb(model);
|
|
547
|
+
await this.populateVAMConfigInFile(model);
|
|
548
|
+
});
|
|
549
|
+
} catch (error) {
|
|
550
|
+
this.logger.error('generateVAMConfig Transaction failed:', error);
|
|
551
|
+
throw error;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private async populateVAMConfigInFile(model: ModelMetadata) {
|
|
556
|
+
try {
|
|
557
|
+
const filePath = this.moduleMetadataHelperService.getModuleMetadataFilePath(model.module.name);
|
|
558
|
+
const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
|
|
559
|
+
|
|
560
|
+
const listViewLayoutFields = [{ type: "field", attrs: { name: `id`, sortable: true, filterable: true } }];
|
|
561
|
+
const formViewLayoutFields = [];
|
|
562
|
+
|
|
563
|
+
for (let i = 0; i < model.fields.length; i++) {
|
|
564
|
+
const field = model.fields[i];
|
|
565
|
+
if (field.isSystem) continue;
|
|
566
|
+
listViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}`, sortable: true, filterable: true } })
|
|
567
|
+
formViewLayoutFields.push({ type: "field", attrs: { name: `${field.name}` } })
|
|
568
|
+
}
|
|
569
|
+
this.populateVAMConfigInFileInternal(formViewLayoutFields, model, listViewLayoutFields, metaData);
|
|
570
|
+
// Write the updated object back to the file
|
|
571
|
+
const updatedContent = JSON.stringify(metaData, null, 2);
|
|
572
|
+
await fs.writeFile(filePath, updatedContent);
|
|
573
|
+
|
|
574
|
+
} catch (error) {
|
|
575
|
+
// console.error('File creation failed:', error);
|
|
576
|
+
this.logger.error('File updation failed for View, action, menus config:', error);
|
|
577
|
+
throw new Error('File updation failed for View, action, menus config'); // Trigger rollback
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Populate the View, Actions and Menus in the config file
|
|
582
|
+
private populateVAMConfigInFileInternal(formViewLayoutFields: any[], model: ModelMetadata, listViewLayoutFields: { type: string; attrs: { name: string; sortable: boolean; filterable: boolean; }; }[], metaData: any) {
|
|
583
|
+
const column1Fields = [];
|
|
584
|
+
const column2Fields = [];
|
|
585
|
+
|
|
586
|
+
// Distribute fields between two columns
|
|
587
|
+
for (let i = 0; i < formViewLayoutFields.length; i++) {
|
|
588
|
+
if (i % 2 === 0) {
|
|
589
|
+
column1Fields.push(formViewLayoutFields[i]);
|
|
590
|
+
} else {
|
|
591
|
+
column2Fields.push(formViewLayoutFields[i]);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const action = {
|
|
595
|
+
displayName: `${model.displayName} List View`,
|
|
596
|
+
name: `${model.singularName}-list-view`,
|
|
597
|
+
type: "solid",
|
|
598
|
+
domain: "",
|
|
599
|
+
context: "",
|
|
600
|
+
customComponent: `/admin/address-master/${model.singularName}/all`,
|
|
601
|
+
customIsModal: true,
|
|
602
|
+
serverEndpoint: "",
|
|
603
|
+
viewUserKey: `${model.singularName}-list-view`,
|
|
604
|
+
moduleUserKey: `${model.module.name}`,
|
|
605
|
+
modelUserKey: `${model.singularName}`
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const menu = {
|
|
609
|
+
displayName: `${model.displayName}`,
|
|
610
|
+
name: `${model.singularName}-menu-item`,
|
|
611
|
+
sequenceNumber: 1,
|
|
612
|
+
actionUserKey: `${model.singularName}-list-view`,
|
|
613
|
+
moduleUserKey: `${model.module.name}`,
|
|
614
|
+
parentMenuItemUserKey: ""
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
const modelListview = {
|
|
618
|
+
name: `${model.singularName}-list-view`,
|
|
619
|
+
displayName: `${model.displayName}`,
|
|
620
|
+
type: "list",
|
|
621
|
+
context: "{}",
|
|
622
|
+
moduleUserKey: `${model.module.name}`,
|
|
623
|
+
modelUserKey: `${model.singularName}`,
|
|
624
|
+
layout: {
|
|
625
|
+
type: "list",
|
|
626
|
+
attrs: {
|
|
627
|
+
pagination: true,
|
|
628
|
+
pageSizeOptions: [
|
|
629
|
+
10,
|
|
630
|
+
25,
|
|
631
|
+
50
|
|
632
|
+
],
|
|
633
|
+
enableGlobalSearch: true,
|
|
634
|
+
create: true,
|
|
635
|
+
edit: true,
|
|
636
|
+
delete: true
|
|
637
|
+
},
|
|
638
|
+
children: listViewLayoutFields
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
const modelFormView = {
|
|
644
|
+
name: `${model.singularName}-form-view`,
|
|
645
|
+
displayName: `${model.displayName}`,
|
|
646
|
+
type: "form",
|
|
647
|
+
context: "{}",
|
|
648
|
+
moduleUserKey: `${model.module.name}`,
|
|
649
|
+
modelUserKey: `${model.singularName}`,
|
|
650
|
+
layout: {
|
|
651
|
+
type: "form",
|
|
652
|
+
attrs: { name: "form-1", label: `${model.displayName}`, className: "grid" },
|
|
653
|
+
children: [
|
|
654
|
+
{
|
|
655
|
+
type: "sheet",
|
|
656
|
+
attrs: { name: "sheet-1" },
|
|
657
|
+
children: [
|
|
658
|
+
{
|
|
659
|
+
type: "row",
|
|
660
|
+
attrs: { name: "sheet-1" },
|
|
661
|
+
children: [
|
|
662
|
+
{
|
|
663
|
+
type: "column",
|
|
664
|
+
attrs: { name: "group-1", label: "", className: "col-6" },
|
|
665
|
+
children: column1Fields
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
type: "column",
|
|
669
|
+
attrs: { name: "group-2", label: "", className: "col-6" },
|
|
670
|
+
children: column2Fields
|
|
671
|
+
}
|
|
672
|
+
]
|
|
673
|
+
},
|
|
674
|
+
]
|
|
675
|
+
}
|
|
676
|
+
]
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
metaData.menus.push(menu);
|
|
680
|
+
metaData.actions.push(action);
|
|
681
|
+
metaData.views.push(modelListview);
|
|
682
|
+
metaData.views.push(modelFormView);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
//Populate the View, Actions and Menus in the database
|
|
686
|
+
private async populateVAMConfigInDb(model: ModelMetadata) {
|
|
737
687
|
const jsonFieldsList = model.fields.filter((field: FieldMetadata) => field.isSystem !== true);
|
|
738
688
|
|
|
739
689
|
const listViewLayout = jsonFieldsList.map(field => ({
|
|
@@ -872,8 +822,6 @@ export class ModelMetadataService {
|
|
|
872
822
|
const createdMenu = menuRepo.create(menuData);
|
|
873
823
|
await menuRepo.save(createdMenu);
|
|
874
824
|
}
|
|
875
|
-
|
|
876
|
-
return `${removeFieldCodeOuput} \n ${refreshModelCodeOutput}`;
|
|
877
825
|
}
|
|
878
826
|
|
|
879
827
|
private async generateCode(options: CodeGenerationOptions) {
|