@solidstarters/solid-core 1.2.98 → 1.2.100
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/setting.controller.d.ts +1 -1
- package/dist/controllers/setting.controller.d.ts.map +1 -1
- package/dist/controllers/setting.controller.js +5 -3
- package/dist/controllers/setting.controller.js.map +1 -1
- package/dist/dtos/update-settings.dto.d.ts.map +1 -1
- package/dist/dtos/update-settings.dto.js +1 -2
- package/dist/dtos/update-settings.dto.js.map +1 -1
- package/dist/entities/common.entity.d.ts +3 -0
- package/dist/entities/common.entity.d.ts.map +1 -1
- package/dist/entities/common.entity.js +9 -1
- package/dist/entities/common.entity.js.map +1 -1
- package/dist/entities/menu-item-metadata.entity.js +1 -1
- package/dist/entities/menu-item-metadata.entity.js.map +1 -1
- package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.d.ts +1 -0
- package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.js +3 -1
- package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.d.ts +8 -0
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -0
- package/dist/helpers/model-metadata-helper.service.js +118 -0
- package/dist/helpers/model-metadata-helper.service.js.map +1 -0
- package/dist/helpers/schematic.service.d.ts +4 -1
- package/dist/helpers/schematic.service.d.ts.map +1 -1
- package/dist/helpers/schematic.service.js +8 -10
- package/dist/helpers/schematic.service.js.map +1 -1
- package/dist/helpers/solid-registry.d.ts +3 -1
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js +4 -0
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +3 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +7 -2
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/system-fields-seeder.service.d.ts +13 -0
- package/dist/seeders/system-fields-seeder.service.d.ts.map +1 -0
- package/dist/seeders/system-fields-seeder.service.js +56 -0
- package/dist/seeders/system-fields-seeder.service.js.map +1 -0
- package/dist/services/authentication.service.js +4 -4
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/computed-fields/concat-computed-field-provider.service.d.ts.map +1 -1
- package/dist/services/computed-fields/concat-computed-field-provider.service.js +3 -0
- package/dist/services/computed-fields/concat-computed-field-provider.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +1 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +7 -6
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/model-metadata.service.js +1 -0
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/setting.service.d.ts +1 -1
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +27 -1
- package/dist/services/setting.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +8 -2
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +4 -5
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +12 -12
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.d.ts +12 -0
- package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -0
- package/dist/subscribers/created-by-updated-by.subscriber.js +67 -0
- package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -0
- package/dist/subscribers/model.subscriber.d.ts +4 -1
- package/dist/subscribers/model.subscriber.d.ts.map +1 -1
- package/dist/subscribers/model.subscriber.js +20 -67
- package/dist/subscribers/model.subscriber.js.map +1 -1
- package/dist/subscribers/{softDeleteAwareEventSubscriber.subscriber.d.ts → soft-delete-aware-event.subscriber.d.ts} +1 -1
- package/dist/subscribers/soft-delete-aware-event.subscriber.d.ts.map +1 -0
- package/dist/subscribers/{softDeleteAwareEventSubscriber.subscriber.js → soft-delete-aware-event.subscriber.js} +1 -1
- package/dist/subscribers/soft-delete-aware-event.subscriber.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/controllers/setting.controller.ts +3 -2
- package/src/dtos/update-settings.dto.ts +2 -3
- package/src/entities/common.entity.ts +8 -2
- package/src/entities/menu-item-metadata.entity.ts +1 -1
- package/src/helpers/field-crud-managers/PasswordFieldCrudManager.ts +5 -1
- package/src/helpers/model-metadata-helper.service.ts +109 -0
- package/src/helpers/schematic.service.ts +10 -11
- package/src/helpers/solid-registry.ts +6 -1
- package/src/index.ts +2 -2
- package/src/seeders/module-metadata-seeder.service.ts +6 -0
- package/src/seeders/system-fields-seeder.service.ts +53 -0
- package/src/services/authentication.service.ts +4 -4
- package/src/services/computed-fields/concat-computed-field-provider.service.ts +3 -0
- package/src/services/crud.service.ts +7 -6
- package/src/services/model-metadata.service.ts +1 -1
- package/src/services/setting.service.ts +30 -1
- package/src/solid-core.module.ts +8 -1
- package/src/subscribers/audit.subscriber.ts +10 -11
- package/src/subscribers/created-by-updated-by.subscriber.ts +54 -0
- package/src/subscribers/model.subscriber.ts +16 -70
- package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.d.ts.map +0 -1
- package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.js.map +0 -1
- package/src/services/pending_import_issues +0 -3
- /package/src/subscribers/{softDeleteAwareEventSubscriber.subscriber.ts → soft-delete-aware-event.subscriber.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidstarters/solid-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.100",
|
|
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",
|
|
@@ -88,7 +88,8 @@ export class SettingController {
|
|
|
88
88
|
|
|
89
89
|
@ApiBearerAuth("jwt")
|
|
90
90
|
@Post('/bulk-update')
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
@UseInterceptors(AnyFilesInterceptor())
|
|
92
|
+
async updateSettings(@Body() updateSettingsDto: UpdateSettingsDto, @UploadedFiles() files: Array<Express.Multer.File>) {
|
|
93
|
+
return this.service.updateSettings(updateSettingsDto.settings, files);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Column, CreateDateColumn, DeleteDateColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
|
|
2
|
+
import type { User } from "./user.entity";
|
|
3
3
|
|
|
4
4
|
export abstract class CommonEntity {
|
|
5
5
|
@PrimaryGeneratedColumn({ type: 'integer' })
|
|
@@ -25,4 +25,10 @@ export abstract class CommonEntity {
|
|
|
25
25
|
|
|
26
26
|
@Column({ type: "int", name: 'default_entity_locale_id', default: null })
|
|
27
27
|
defaultEntityLocaleId: number;
|
|
28
|
+
|
|
29
|
+
@ManyToOne(() => require('./user.entity').User?.default ?? require('./user.entity').User, { nullable: true })
|
|
30
|
+
createdBy: User;
|
|
31
|
+
|
|
32
|
+
@ManyToOne(() => require('./user.entity').User?.default ?? require('./user.entity').User, { nullable: true })
|
|
33
|
+
updatedBy: User;
|
|
28
34
|
}
|
|
@@ -23,7 +23,7 @@ export class MenuItemMetadata extends CommonEntity {
|
|
|
23
23
|
@ManyToOne(() => ActionMetadata, { onDelete: "CASCADE", nullable: true })
|
|
24
24
|
@JoinColumn({ referencedColumnName: 'id' })
|
|
25
25
|
action: ActionMetadata;
|
|
26
|
-
@ManyToMany(() => RoleMetadata, roleMetadata => roleMetadata.
|
|
26
|
+
@ManyToMany(() => RoleMetadata, roleMetadata => roleMetadata.menuItems, { cascade: true })
|
|
27
27
|
@JoinTable()
|
|
28
28
|
roles: RoleMetadata[];
|
|
29
29
|
@Column({ name: "sequence_number", type: "int", nullable: true })
|
|
@@ -9,6 +9,7 @@ export interface PasswordFieldOptions {
|
|
|
9
9
|
required: boolean | undefined | null;
|
|
10
10
|
regexPattern: string | undefined | null;
|
|
11
11
|
fieldName: string | undefined | null;
|
|
12
|
+
isUpdate: Boolean;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export class PasswordFieldCrudManager implements FieldCrudManager {
|
|
@@ -24,7 +25,10 @@ export class PasswordFieldCrudManager implements FieldCrudManager {
|
|
|
24
25
|
|
|
25
26
|
private applyValidations(fieldValue: any, dto: any): ValidationError[] {
|
|
26
27
|
const errors: ValidationError[] = [];
|
|
27
|
-
this.isApplyRequiredValidation() && isEmpty(fieldValue)
|
|
28
|
+
if (!this.options?.isUpdate && this.isApplyRequiredValidation() && isEmpty(fieldValue)) {
|
|
29
|
+
errors.push({ field: this.options.fieldName, error: `Field: ${this.options.fieldName} is required` });
|
|
30
|
+
}
|
|
31
|
+
// this.isApplyRequiredValidation() && isEmpty(fieldValue) ? errors.push({ field: this.options.fieldName, error: `Field: ${this.options.fieldName} is required` }): "no errors";
|
|
28
32
|
if (isNotEmpty(fieldValue)) {
|
|
29
33
|
errors.push(...this.applyFormatValidations(fieldValue, dto));
|
|
30
34
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Return the system fields metadata for a model
|
|
2
|
+
|
|
3
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
4
|
+
import { SolidRegistry } from "./solid-registry";
|
|
5
|
+
import { In } from "typeorm";
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class ModelMetadataHelperService {
|
|
9
|
+
private readonly logger = new Logger(ModelMetadataHelperService.name);
|
|
10
|
+
|
|
11
|
+
constructor(private readonly registry: SolidRegistry) {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getSystemFieldsMetadata(): any[] {
|
|
15
|
+
const systemFieldsMetadata = [
|
|
16
|
+
{
|
|
17
|
+
name: "id",
|
|
18
|
+
displayName: "Id",
|
|
19
|
+
type: "int",
|
|
20
|
+
ormType: "bigint",
|
|
21
|
+
isSystem: true,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "createdAt",
|
|
25
|
+
displayName: "Created At",
|
|
26
|
+
type: "datetime",
|
|
27
|
+
ormType: "timestamp",
|
|
28
|
+
isSystem: true,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "updatedAt",
|
|
32
|
+
displayName: "Updated At",
|
|
33
|
+
type: "datetime",
|
|
34
|
+
ormType: "timestamp",
|
|
35
|
+
isSystem: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "deletedAt",
|
|
39
|
+
displayName: "Deleted At",
|
|
40
|
+
type: "datetime",
|
|
41
|
+
ormType: "timestamp",
|
|
42
|
+
isSystem: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "deletedTracker",
|
|
46
|
+
displayName: "Deleted Tracker",
|
|
47
|
+
type: "shortText",
|
|
48
|
+
ormType: "varchar",
|
|
49
|
+
isSystem: true,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "publishedAt",
|
|
53
|
+
displayName: "Published At",
|
|
54
|
+
type: "datetime",
|
|
55
|
+
ormType: "timestamp",
|
|
56
|
+
isSystem: true,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "localeName",
|
|
60
|
+
displayName: "Locale",
|
|
61
|
+
type: "shortText",
|
|
62
|
+
ormType: "varchar",
|
|
63
|
+
isSystem: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "defaultEntityLocaleId",
|
|
67
|
+
displayName: "Default Entity Locale Id",
|
|
68
|
+
type: "int",
|
|
69
|
+
ormType: "integer",
|
|
70
|
+
isSystem: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "createdBy",
|
|
74
|
+
displayName: "Created By",
|
|
75
|
+
type: "relation",
|
|
76
|
+
ormType: "int",
|
|
77
|
+
isSystem: true,
|
|
78
|
+
relationType: "many-to-one",
|
|
79
|
+
relationCoModelSingularName: "user",
|
|
80
|
+
relationCreateInverse: false,
|
|
81
|
+
relationCascade: "restrict",
|
|
82
|
+
relationModelModuleName: "solid-core"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "updatedBy",
|
|
86
|
+
displayName: "Updated By",
|
|
87
|
+
type: "relation",
|
|
88
|
+
ormType: "int",
|
|
89
|
+
isSystem: true,
|
|
90
|
+
relationType: "many-to-one",
|
|
91
|
+
relationCoModelSingularName: "user",
|
|
92
|
+
relationCreateInverse: false,
|
|
93
|
+
relationCascade: "restrict",
|
|
94
|
+
relationModelModuleName: "solid-core"
|
|
95
|
+
},
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
// Do an additional check and add a warning if the common entity keys and system field metadata keys don't match exactly
|
|
99
|
+
const commonEntityKeys = this.registry.getCommonEntityKeys();
|
|
100
|
+
const systemFieldNames = systemFieldsMetadata.map(field => field.name);
|
|
101
|
+
const missingKeys = commonEntityKeys.filter(key => !systemFieldNames.includes(key));
|
|
102
|
+
if (missingKeys.length > 0) {
|
|
103
|
+
this.logger.warn(`Missing system fields metadata for common entity keys: ${missingKeys.join(', ')}`);
|
|
104
|
+
}
|
|
105
|
+
return systemFieldsMetadata;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Injectable, Logger } from '@nestjs/common';
|
|
2
2
|
import { CommandService } from './command.service';
|
|
3
3
|
import { snakeCase } from "lodash";
|
|
4
|
+
import { CommonEntity } from 'src/entities/common.entity';
|
|
5
|
+
import { SolidRegistry } from './solid-registry';
|
|
4
6
|
|
|
5
7
|
export const ADD_MODULE_COMMAND = 'add-module';
|
|
6
8
|
export type GenerateModuleOptions = {
|
|
@@ -14,25 +16,19 @@ type FieldOptions = {
|
|
|
14
16
|
dataSource: string;
|
|
15
17
|
fields: any[]; //FIXME This type can be improved
|
|
16
18
|
modelEnableSoftDelete?: boolean;
|
|
17
|
-
parentModel?: string;
|
|
19
|
+
parentModel?: string;
|
|
18
20
|
parentModule?: string;
|
|
21
|
+
draftPublishWorkflowEnabled?: boolean;
|
|
19
22
|
};
|
|
20
23
|
export const REMOVE_FIELDS_COMMAND = 'remove-fields';
|
|
21
24
|
export const REFRESH_MODEL_COMMAND = 'refresh-model';
|
|
22
25
|
|
|
23
|
-
enum SYSTEM_FIELDS_TO_IGNORE_FOR_CODE_GENERATION {
|
|
24
|
-
ID = 'id',
|
|
25
|
-
CREATED_AT = 'createdAt',
|
|
26
|
-
UPDATED_AT = 'updatedAt',
|
|
27
|
-
DELETED_AT = 'deletedAt'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
//TODO Rename to CodeBuilder service
|
|
31
27
|
@Injectable()
|
|
32
28
|
export class SchematicService {
|
|
33
29
|
private readonly logger = new Logger(SchematicService.name);
|
|
34
30
|
private readonly SCHEMATIC_PROJECT = '@solidstarters/solid-code-builder';
|
|
35
|
-
constructor(private readonly commandService: CommandService) { }
|
|
31
|
+
constructor(private readonly commandService: CommandService, private readonly solidRegistry: SolidRegistry) { }
|
|
36
32
|
|
|
37
33
|
async executeSchematicCommand(
|
|
38
34
|
command: string,
|
|
@@ -79,9 +75,13 @@ export class SchematicService {
|
|
|
79
75
|
modelCommand += ` --parent-module=${fieldOptions.parentModule}`;
|
|
80
76
|
}
|
|
81
77
|
|
|
78
|
+
if (fieldOptions.draftPublishWorkflowEnabled) {
|
|
79
|
+
modelCommand += ` --draft-publish-workflow-enabled=${fieldOptions.draftPublishWorkflowEnabled}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
82
|
let fieldCommand = fieldOptions.fields
|
|
83
83
|
.filter((field) => {
|
|
84
|
-
return !
|
|
84
|
+
return !this.solidRegistry.getCommonEntityKeys().includes(field.name);
|
|
85
85
|
})
|
|
86
86
|
.map((field) => {
|
|
87
87
|
return `--fields='${JSON.stringify(field)}'`;
|
|
@@ -101,5 +101,4 @@ export class SchematicService {
|
|
|
101
101
|
throw new Error('Schematic command not supported.');
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
|
|
105
104
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
2
|
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
|
3
|
-
import {
|
|
3
|
+
import { CommonEntity } from 'src/entities/common.entity';
|
|
4
4
|
import { SecurityRule } from 'src/entities/security-rule.entity';
|
|
5
5
|
import { EntityManager } from 'typeorm';
|
|
6
|
+
import { ISelectionProvider, ISelectionProviderContext } from "../interfaces";
|
|
6
7
|
import { Locale } from 'src/entities/locale.entity';
|
|
7
8
|
|
|
8
9
|
type ControllerMetadata = {
|
|
@@ -147,4 +148,8 @@ export class SolidRegistry {
|
|
|
147
148
|
return entityMetadata.target;
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
getCommonEntityKeys(): (keyof CommonEntity) [] {
|
|
152
|
+
return Reflect.getMetadataKeys(CommonEntity.prototype) as (keyof CommonEntity)[];
|
|
153
|
+
}
|
|
154
|
+
|
|
150
155
|
}
|
package/src/index.ts
CHANGED
|
@@ -242,7 +242,7 @@ export * from './repository/field.repository'
|
|
|
242
242
|
|
|
243
243
|
//softDeleteAwareEventSubscriber.subscriber.ts
|
|
244
244
|
export * from './subscribers/model.subscriber'
|
|
245
|
-
export * from './subscribers/
|
|
245
|
+
export * from './subscribers/soft-delete-aware-event.subscriber' //rename
|
|
246
246
|
export * from './subscribers/view-metadata.subscriber' //rename
|
|
247
247
|
export * from './subscribers/audit.subscriber'
|
|
248
248
|
|
|
@@ -259,4 +259,4 @@ export * from './interfaces'
|
|
|
259
259
|
export * from './solid-core.module'
|
|
260
260
|
|
|
261
261
|
export * from './winston.logger'
|
|
262
|
-
export
|
|
262
|
+
export { default as datetimeTransformer } from './transformers/datetime-transformer'
|
|
@@ -36,6 +36,7 @@ import { CreateSecurityRuleDto } from 'src/dtos/create-security-rule.dto';
|
|
|
36
36
|
import { SecurityRuleRepository } from 'src/repository/security-rule.repository';
|
|
37
37
|
import { ListOfValuesService } from 'src/services/list-of-values.service';
|
|
38
38
|
import { CreateListOfValuesDto } from 'src/dtos/create-list-of-values.dto';
|
|
39
|
+
import { SystemFieldsSeederService } from './system-fields-seeder.service';
|
|
39
40
|
|
|
40
41
|
@Injectable()
|
|
41
42
|
export class ModuleMetadataSeederService {
|
|
@@ -68,6 +69,7 @@ export class ModuleMetadataSeederService {
|
|
|
68
69
|
@InjectRepository(Setting, 'default')
|
|
69
70
|
readonly settingsRepo: Repository<Setting>,
|
|
70
71
|
readonly securityRuleRepo: SecurityRuleRepository,
|
|
72
|
+
readonly systemFieldsSeederService: SystemFieldsSeederService
|
|
71
73
|
) { }
|
|
72
74
|
|
|
73
75
|
async seed() {
|
|
@@ -83,6 +85,10 @@ export class ModuleMetadataSeederService {
|
|
|
83
85
|
this.logger.log(`Seeding media storage providers`);
|
|
84
86
|
await this.mediaStorageProviderSeederService.seed();
|
|
85
87
|
|
|
88
|
+
// Seed any missing system fields metadata for all models.
|
|
89
|
+
this.logger.log(`Seeding system fields metadata`);
|
|
90
|
+
await this.systemFieldsSeederService.seed();
|
|
91
|
+
|
|
86
92
|
// Read the module metadata from a file specified in the .env
|
|
87
93
|
// Add the core json file as the first entry in the above array.
|
|
88
94
|
// Please note the sequence of core files is important below...
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// This class will add the system fields in the field-metadata table if they are missing.
|
|
2
|
+
// Fetch all the models and their fields metadata and check if the system fields are present.
|
|
3
|
+
|
|
4
|
+
import { InjectRepository } from "@nestjs/typeorm";
|
|
5
|
+
import { ModelMetadata } from "src/entities/model-metadata.entity";
|
|
6
|
+
import { ModelMetadataHelperService } from "src/helpers/model-metadata-helper.service";
|
|
7
|
+
import { FieldRepository } from "src/repository/field.repository";
|
|
8
|
+
import { Repository } from "typeorm";
|
|
9
|
+
|
|
10
|
+
export class SystemFieldsSeederService {
|
|
11
|
+
// This service is responsible for seeding the system fields metadata for all models.
|
|
12
|
+
// It will check if the system fields are already present in the field-metadata table.
|
|
13
|
+
// If not, it will add them.
|
|
14
|
+
constructor(
|
|
15
|
+
private readonly modelHelperService: ModelMetadataHelperService,
|
|
16
|
+
@InjectRepository(ModelMetadata)
|
|
17
|
+
private readonly modelRepository: Repository<ModelMetadata>, // Replace with actual model repository type
|
|
18
|
+
@InjectRepository(FieldRepository)
|
|
19
|
+
private readonly fieldRepository: Repository<FieldRepository>, // Replace with actual field repository type
|
|
20
|
+
) {}
|
|
21
|
+
|
|
22
|
+
async seed() {
|
|
23
|
+
// Get the model repo
|
|
24
|
+
const models = await this.modelRepository.find({
|
|
25
|
+
relations: ['fields'], // Assuming 'fields' is the relation to field metadata
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
for (const model of models) {
|
|
29
|
+
// Check if the system fields are already present
|
|
30
|
+
await this.seedMissingSystemFields(model);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private async seedMissingSystemFields(model: ModelMetadata) {
|
|
35
|
+
const existingSystemFields = model.fields.filter(field => field.isSystem);
|
|
36
|
+
const systemFieldsMetadata = this.modelHelperService.getSystemFieldsMetadata();
|
|
37
|
+
|
|
38
|
+
// Find out which system fields are missing
|
|
39
|
+
const missingFields = systemFieldsMetadata.filter(
|
|
40
|
+
sysField => !existingSystemFields.some(field => field.name === sysField.name)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// If there are missing fields, add them
|
|
44
|
+
if (missingFields.length > 0) {
|
|
45
|
+
const newFields = missingFields.map(field => ({
|
|
46
|
+
...field,
|
|
47
|
+
model: model, // Associate the field with the current model
|
|
48
|
+
}));
|
|
49
|
+
await this.fieldRepository.save(newFields);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -362,7 +362,7 @@ export class AuthenticationService {
|
|
|
362
362
|
solidAppName: process.env.SOLID_APP_NAME,
|
|
363
363
|
solidAppWebsiteUrl: process.env.SOLID_APP_WEBSITE_URL,
|
|
364
364
|
firstName: user.username,
|
|
365
|
-
fullName: user.fullName,
|
|
365
|
+
fullName: user.fullName ? user.fullName : user.username,
|
|
366
366
|
emailVerificationTokenOnRegistration: user.emailVerificationTokenOnRegistration,
|
|
367
367
|
},
|
|
368
368
|
this.commonConfiguration.shouldQueueEmails,
|
|
@@ -379,7 +379,7 @@ export class AuthenticationService {
|
|
|
379
379
|
otp: user.mobileVerificationTokenOnRegistration,
|
|
380
380
|
mobileVerificationTokenOnRegistration: user.mobileVerificationTokenOnRegistration,
|
|
381
381
|
firstName: user.username,
|
|
382
|
-
fullName: user.fullName,
|
|
382
|
+
fullName: user.fullName ? user.fullName : user.username,
|
|
383
383
|
}
|
|
384
384
|
);
|
|
385
385
|
}
|
|
@@ -544,7 +544,7 @@ export class AuthenticationService {
|
|
|
544
544
|
solidAppWebsiteUrl: process.env.SOLID_APP_WEBSITE_URL,
|
|
545
545
|
firstName: user.username,
|
|
546
546
|
emailVerificationTokenOnLogin: user.emailVerificationTokenOnLogin,
|
|
547
|
-
fullName: user.fullName,
|
|
547
|
+
fullName: user.fullName ? user.fullName : user.username,
|
|
548
548
|
},
|
|
549
549
|
this.commonConfiguration.shouldQueueEmails,
|
|
550
550
|
'user',
|
|
@@ -560,7 +560,7 @@ export class AuthenticationService {
|
|
|
560
560
|
otp: user.mobileVerificationTokenOnLogin,
|
|
561
561
|
mobileVerificationTokenOnLogin: user.mobileVerificationTokenOnLogin,
|
|
562
562
|
firstName: user.username,
|
|
563
|
-
fullName: user.fullName,
|
|
563
|
+
fullName: user.fullName ? user.fullName : user.username,
|
|
564
564
|
}
|
|
565
565
|
);
|
|
566
566
|
}
|
|
@@ -25,6 +25,9 @@ export class ConcatComputedFieldProvider implements IComputedFieldProvider<any>
|
|
|
25
25
|
const fields = ctxt.fields;
|
|
26
26
|
const slugify = ctxt.slugify || false;
|
|
27
27
|
|
|
28
|
+
if (!Array.isArray(fields) || fields?.length === 0) {
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
28
31
|
let concatenatedString = '';
|
|
29
32
|
for (let i = 0; i < fields.length; i++) {
|
|
30
33
|
const field = fields[i];
|
|
@@ -109,8 +109,8 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
private async validateAndTransformDto(field: FieldMetadata, dto: any, files: Express.Multer.File[], hasMediaFields: boolean, isPartialUpdate: boolean = false) {
|
|
113
|
-
const fieldManager: FieldCrudManager = this.fieldCrudManager(field, this.entityManager, isPartialUpdate);
|
|
112
|
+
private async validateAndTransformDto(field: FieldMetadata, dto: any, files: Express.Multer.File[], hasMediaFields: boolean, isPartialUpdate: boolean = false, isUpdate: boolean = false) {
|
|
113
|
+
const fieldManager: FieldCrudManager = this.fieldCrudManager(field, this.entityManager, isPartialUpdate, isUpdate);
|
|
114
114
|
const validationErrors = fieldManager.validate(dto, files);
|
|
115
115
|
const errors = (validationErrors instanceof Promise) ? await validationErrors : validationErrors;
|
|
116
116
|
if (errors.length > 0) {
|
|
@@ -150,10 +150,11 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
//TODO: Will the updates be partial i.e PATCH or full i.e PUT
|
|
153
|
-
async update(id: number, updateDto: any, files: Express.Multer.File[] = [], isPartialUpdate: boolean = false, solidRequestContext: any = {}): Promise<T> {
|
|
153
|
+
async update(id: number, updateDto: any, files: Express.Multer.File[] = [], isPartialUpdate: boolean = false, solidRequestContext: any = {}, isUpdate: boolean = false): Promise<T> {
|
|
154
154
|
if (!id) {
|
|
155
155
|
throw new Error('Id is required for update');
|
|
156
156
|
}
|
|
157
|
+
isUpdate = true;
|
|
157
158
|
const model = await this.loadModel();
|
|
158
159
|
// Check wheather user has update permission for model
|
|
159
160
|
if (solidRequestContext.activeUser) {
|
|
@@ -185,7 +186,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
185
186
|
// 2. Loop through the fields with a switch statement
|
|
186
187
|
// 3. Handle the fields based on field type
|
|
187
188
|
for (const field of fieldsToProcess) {
|
|
188
|
-
const transformed = await this.validateAndTransformDto(field, updateDto, files, hasMediaFields, isPartialUpdate);
|
|
189
|
+
const transformed = await this.validateAndTransformDto(field, updateDto, files, hasMediaFields, isPartialUpdate, isUpdate);
|
|
189
190
|
updateDto = transformed.dto;
|
|
190
191
|
hasMediaFields = transformed.hasMediaFields;
|
|
191
192
|
}
|
|
@@ -257,8 +258,8 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
|
|
260
|
-
private fieldCrudManager(fieldMetadata: FieldMetadata, entityManager: EntityManager, isPartialUpdate: boolean = false): FieldCrudManager {
|
|
261
|
-
const commonOptions = { required: fieldMetadata.required && !isPartialUpdate, fieldName: fieldMetadata.name };
|
|
261
|
+
private fieldCrudManager(fieldMetadata: FieldMetadata, entityManager: EntityManager, isPartialUpdate: boolean = false, isUpdate: boolean = false): FieldCrudManager {
|
|
262
|
+
const commonOptions = { required: fieldMetadata.required && !isPartialUpdate, fieldName: fieldMetadata.name, isUpdate};
|
|
262
263
|
switch (fieldMetadata.type) {
|
|
263
264
|
case SolidFieldType.shortText: {
|
|
264
265
|
const options = { ...commonOptions, length: fieldMetadata.max, regexPattern: fieldMetadata.regexPattern };
|
|
@@ -928,7 +928,7 @@ export class ModelMetadataService {
|
|
|
928
928
|
modelEnableSoftDelete: model.enableSoftDelete,
|
|
929
929
|
parentModel: model.parentModel?.singularName,
|
|
930
930
|
parentModule: model.parentModel?.module?.name,
|
|
931
|
-
|
|
931
|
+
draftPublishWorkflowEnabled: model.draftPublishWorkflow,
|
|
932
932
|
},
|
|
933
933
|
dryRun
|
|
934
934
|
);
|
|
@@ -48,6 +48,7 @@ export class SettingService extends CRUDService<Setting> {
|
|
|
48
48
|
authPagesLayout: "center",
|
|
49
49
|
authPagesTheme: "light",
|
|
50
50
|
appLogo: "",
|
|
51
|
+
companylogo: "",
|
|
51
52
|
appLogoPosition: "in_form_view",
|
|
52
53
|
showAuthContent: false,
|
|
53
54
|
appTitle: process.env.SOLID_APP_NAME || "Solid App",
|
|
@@ -130,6 +131,7 @@ export class SettingService extends CRUDService<Setting> {
|
|
|
130
131
|
authPagesLayout: "center",
|
|
131
132
|
authPagesTheme: "light",
|
|
132
133
|
appLogo: "",
|
|
134
|
+
companylogo: "",
|
|
133
135
|
appLogoPosition: "in_form_view", //in_form_view | in_image_view
|
|
134
136
|
showAuthContent: false,
|
|
135
137
|
appTitle: process.env.SOLID_APP_NAME || "Solid App",
|
|
@@ -176,14 +178,41 @@ export class SettingService extends CRUDService<Setting> {
|
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
180
|
|
|
179
|
-
async updateSettings(settings: Record<string, any>): Promise<Setting[]> {
|
|
181
|
+
async updateSettings(settings: Record<string, any> = {}, files: Array<Express.Multer.File> = []): Promise<Setting[]> {
|
|
180
182
|
const existingSettings = await this.repo.find();
|
|
181
183
|
const existingKeys = new Set(existingSettings.map(s => s.key));
|
|
182
184
|
|
|
183
185
|
const settingsToUpdate: Setting[] = [];
|
|
184
186
|
const settingsToCreate: Setting[] = [];
|
|
185
187
|
|
|
188
|
+
if (files && files.length > 0) {
|
|
189
|
+
for (const file of files) {
|
|
190
|
+
const key = file.fieldname;
|
|
191
|
+
const fileStoragePath = `${this.configService.get('app-builder.fileStorageDir')}/${file.filename}-${file.originalname}`;
|
|
192
|
+
|
|
193
|
+
await this.fileService.copyFile(file.path, fileStoragePath);
|
|
194
|
+
await this.fileService.deleteFile(file.path);
|
|
195
|
+
|
|
196
|
+
if (existingKeys.has(key)) {
|
|
197
|
+
const existingSetting = existingSettings.find(s => s.key === key);
|
|
198
|
+
if (existingSetting) {
|
|
199
|
+
existingSetting.value = fileStoragePath;
|
|
200
|
+
settingsToUpdate.push(existingSetting);
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
const newSetting = new Setting();
|
|
204
|
+
newSetting.key = key;
|
|
205
|
+
newSetting.value = fileStoragePath;
|
|
206
|
+
settingsToCreate.push(newSetting);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
186
211
|
for (const [key, value] of Object.entries(settings)) {
|
|
212
|
+
if (files && files.some(f => f.fieldname === key)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
187
216
|
const stringValue = typeof value === 'boolean' ? value.toString() :
|
|
188
217
|
Array.isArray(value) ? value.join(',') :
|
|
189
218
|
value === null || value === undefined ? '' : String(value);
|
package/src/solid-core.module.ts
CHANGED
|
@@ -119,7 +119,7 @@ import { Msg91OTPService } from './services/sms/Msg91OTPService';
|
|
|
119
119
|
import { Msg91SMSService } from './services/sms/Msg91SMSService';
|
|
120
120
|
// import { UserService } from './services/user.service';
|
|
121
121
|
import { Msg91WhatsappService } from './services/whatsapp/Msg91WhatsappService';
|
|
122
|
-
import { SoftDeleteAwareEventSubscriber } from './subscribers/
|
|
122
|
+
import { SoftDeleteAwareEventSubscriber } from './subscribers/soft-delete-aware-event.subscriber';
|
|
123
123
|
|
|
124
124
|
import { PermissionMetadataController } from './controllers/permission-metadata.controller';
|
|
125
125
|
import { PermissionMetadata } from './entities/permission-metadata.entity';
|
|
@@ -181,6 +181,9 @@ import { ImportTransactionErrorLog } from './entities/import-transaction-error-l
|
|
|
181
181
|
import { ImportTransactionErrorLogService } from './services/import-transaction-error-log.service';
|
|
182
182
|
import { ImportTransactionErrorLogController } from './controllers/import-transaction-error-log.controller';
|
|
183
183
|
import { LocaleListSelectionProvider } from './services/selection-providers/locale-list-selection-provider.service';
|
|
184
|
+
import { CreatedByUpdatedBySubscriber } from './subscribers/created-by-updated-by.subscriber';
|
|
185
|
+
import { SystemFieldsSeederService } from './seeders/system-fields-seeder.service';
|
|
186
|
+
import { ModelMetadataHelperService } from './helpers/model-metadata-helper.service';
|
|
184
187
|
|
|
185
188
|
|
|
186
189
|
@Global()
|
|
@@ -299,6 +302,7 @@ import { LocaleListSelectionProvider } from './services/selection-providers/loca
|
|
|
299
302
|
ModuleMetadataService,
|
|
300
303
|
ModuleMetadataHelperService,
|
|
301
304
|
ModelMetadataService,
|
|
305
|
+
ModelMetadataHelperService,
|
|
302
306
|
FieldMetadataService,
|
|
303
307
|
RemoveFieldsCommand,
|
|
304
308
|
RefreshModelCommand,
|
|
@@ -391,6 +395,9 @@ import { LocaleListSelectionProvider } from './services/selection-providers/loca
|
|
|
391
395
|
FieldRepository,
|
|
392
396
|
ImportTransactionService,
|
|
393
397
|
ImportTransactionErrorLogService,
|
|
398
|
+
CreatedByUpdatedBySubscriber,
|
|
399
|
+
SystemFieldsSeederService,
|
|
400
|
+
|
|
394
401
|
],
|
|
395
402
|
exports: [
|
|
396
403
|
ModuleMetadataService,
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import { Connection, EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
|
|
2
|
-
import { ChatterMessageService } from '../services/chatter-message.service';
|
|
3
|
-
import { EntityMetadata } from 'typeorm';
|
|
4
|
-
import { InjectRepository } from '@nestjs/typeorm';
|
|
5
|
-
import { ModelMetadata } from '../entities/model-metadata.entity';
|
|
6
|
-
import { Repository } from 'typeorm';
|
|
7
1
|
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
|
3
|
+
import { DataSource, EntityMetadata, EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, Repository, UpdateEvent } from 'typeorm';
|
|
4
|
+
import { ModelMetadata } from '../entities/model-metadata.entity';
|
|
5
|
+
import { ChatterMessageService } from '../services/chatter-message.service';
|
|
8
6
|
@Injectable()
|
|
9
7
|
@EventSubscriber()
|
|
10
8
|
export class AuditSubscriber implements EntitySubscriberInterface {
|
|
11
|
-
|
|
9
|
+
|
|
12
10
|
constructor(
|
|
13
|
-
|
|
11
|
+
@InjectDataSource()
|
|
12
|
+
private readonly dataSource: DataSource,
|
|
14
13
|
private readonly chatterMessageService: ChatterMessageService,
|
|
15
14
|
@InjectRepository(ModelMetadata)
|
|
16
15
|
private readonly modelMetadataRepo: Repository<ModelMetadata>,
|
|
17
16
|
) {
|
|
18
|
-
|
|
17
|
+
this.dataSource.subscribers.push(this);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<boolean> {
|
|
@@ -33,8 +32,8 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
33
32
|
return false;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
const auditFields = model.fields.filter(field =>
|
|
37
|
-
field.enableAuditTracking &&
|
|
35
|
+
const auditFields = model.fields.filter(field =>
|
|
36
|
+
field.enableAuditTracking &&
|
|
38
37
|
!['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(field.type) &&
|
|
39
38
|
!(field.type === 'relation' && field.relationType === 'one-to-many')
|
|
40
39
|
);
|