@solidstarters/solid-core 1.2.98 → 1.2.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/controllers/setting.controller.d.ts +1 -1
  2. package/dist/controllers/setting.controller.d.ts.map +1 -1
  3. package/dist/controllers/setting.controller.js +5 -3
  4. package/dist/controllers/setting.controller.js.map +1 -1
  5. package/dist/dtos/update-settings.dto.d.ts.map +1 -1
  6. package/dist/dtos/update-settings.dto.js +1 -2
  7. package/dist/dtos/update-settings.dto.js.map +1 -1
  8. package/dist/entities/common.entity.d.ts +3 -0
  9. package/dist/entities/common.entity.d.ts.map +1 -1
  10. package/dist/entities/common.entity.js +9 -1
  11. package/dist/entities/common.entity.js.map +1 -1
  12. package/dist/entities/menu-item-metadata.entity.js +1 -1
  13. package/dist/entities/menu-item-metadata.entity.js.map +1 -1
  14. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.d.ts +1 -0
  15. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.d.ts.map +1 -1
  16. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.js +3 -1
  17. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.js.map +1 -1
  18. package/dist/helpers/model-metadata-helper.service.d.ts +8 -0
  19. package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -0
  20. package/dist/helpers/model-metadata-helper.service.js +118 -0
  21. package/dist/helpers/model-metadata-helper.service.js.map +1 -0
  22. package/dist/helpers/schematic.service.d.ts +4 -1
  23. package/dist/helpers/schematic.service.d.ts.map +1 -1
  24. package/dist/helpers/schematic.service.js +8 -10
  25. package/dist/helpers/schematic.service.js.map +1 -1
  26. package/dist/helpers/solid-registry.d.ts +3 -1
  27. package/dist/helpers/solid-registry.d.ts.map +1 -1
  28. package/dist/helpers/solid-registry.js +4 -0
  29. package/dist/helpers/solid-registry.js.map +1 -1
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +7 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/seeders/system-fields-seeder.service.d.ts +13 -0
  35. package/dist/seeders/system-fields-seeder.service.d.ts.map +1 -0
  36. package/dist/seeders/system-fields-seeder.service.js +56 -0
  37. package/dist/seeders/system-fields-seeder.service.js.map +1 -0
  38. package/dist/services/authentication.service.js +4 -4
  39. package/dist/services/authentication.service.js.map +1 -1
  40. package/dist/services/computed-fields/concat-computed-field-provider.service.d.ts.map +1 -1
  41. package/dist/services/computed-fields/concat-computed-field-provider.service.js +3 -0
  42. package/dist/services/computed-fields/concat-computed-field-provider.service.js.map +1 -1
  43. package/dist/services/crud.service.d.ts +1 -1
  44. package/dist/services/crud.service.d.ts.map +1 -1
  45. package/dist/services/crud.service.js +7 -6
  46. package/dist/services/crud.service.js.map +1 -1
  47. package/dist/services/model-metadata.service.js +1 -0
  48. package/dist/services/model-metadata.service.js.map +1 -1
  49. package/dist/services/setting.service.d.ts +1 -1
  50. package/dist/services/setting.service.d.ts.map +1 -1
  51. package/dist/services/setting.service.js +27 -1
  52. package/dist/services/setting.service.js.map +1 -1
  53. package/dist/solid-core.module.d.ts.map +1 -1
  54. package/dist/solid-core.module.js +8 -2
  55. package/dist/solid-core.module.js.map +1 -1
  56. package/dist/subscribers/audit.subscriber.d.ts +4 -5
  57. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  58. package/dist/subscribers/audit.subscriber.js +12 -12
  59. package/dist/subscribers/audit.subscriber.js.map +1 -1
  60. package/dist/subscribers/created-by-updated-by.subscriber.d.ts +12 -0
  61. package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -0
  62. package/dist/subscribers/created-by-updated-by.subscriber.js +67 -0
  63. package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -0
  64. package/dist/subscribers/model.subscriber.d.ts +4 -1
  65. package/dist/subscribers/model.subscriber.d.ts.map +1 -1
  66. package/dist/subscribers/model.subscriber.js +20 -67
  67. package/dist/subscribers/model.subscriber.js.map +1 -1
  68. package/dist/subscribers/{softDeleteAwareEventSubscriber.subscriber.d.ts → soft-delete-aware-event.subscriber.d.ts} +1 -1
  69. package/dist/subscribers/soft-delete-aware-event.subscriber.d.ts.map +1 -0
  70. package/dist/subscribers/{softDeleteAwareEventSubscriber.subscriber.js → soft-delete-aware-event.subscriber.js} +1 -1
  71. package/dist/subscribers/soft-delete-aware-event.subscriber.js.map +1 -0
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +1 -1
  74. package/src/controllers/setting.controller.ts +3 -2
  75. package/src/dtos/update-settings.dto.ts +2 -3
  76. package/src/entities/common.entity.ts +8 -2
  77. package/src/entities/menu-item-metadata.entity.ts +1 -1
  78. package/src/helpers/field-crud-managers/PasswordFieldCrudManager.ts +5 -1
  79. package/src/helpers/model-metadata-helper.service.ts +109 -0
  80. package/src/helpers/schematic.service.ts +10 -11
  81. package/src/helpers/solid-registry.ts +6 -1
  82. package/src/index.ts +2 -2
  83. package/src/seeders/system-fields-seeder.service.ts +53 -0
  84. package/src/services/authentication.service.ts +4 -4
  85. package/src/services/computed-fields/concat-computed-field-provider.service.ts +3 -0
  86. package/src/services/crud.service.ts +7 -6
  87. package/src/services/model-metadata.service.ts +1 -1
  88. package/src/services/setting.service.ts +30 -1
  89. package/src/solid-core.module.ts +8 -1
  90. package/src/subscribers/audit.subscriber.ts +10 -11
  91. package/src/subscribers/created-by-updated-by.subscriber.ts +54 -0
  92. package/src/subscribers/model.subscriber.ts +16 -70
  93. package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.d.ts.map +0 -1
  94. package/dist/subscribers/softDeleteAwareEventSubscriber.subscriber.js.map +0 -1
  95. package/src/services/pending_import_issues +0 -3
  96. /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.98",
3
+ "version": "1.2.99",
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
- async updateSettings(@Body() updateSettingsDto: UpdateSettingsDto) {
92
- return this.service.updateSettings(updateSettingsDto.settings);
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,7 +1,6 @@
1
- import { IsNotEmpty, IsObject } from 'class-validator';
1
+ import { IsObject, IsOptional } from 'class-validator';
2
2
 
3
3
  export class UpdateSettingsDto {
4
- @IsObject()
5
- @IsNotEmpty()
4
+ @IsOptional()
6
5
  settings: Record<string, any>;
7
6
  }
@@ -1,5 +1,5 @@
1
- import { PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, Index, ManyToOne, JoinColumn } from "typeorm"
2
- import { Locale } from "./locale.entity";
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.users, { cascade: true })
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) ? errors.push({ field: this.options.fieldName, error: `Field: ${this.options.fieldName} is required` }): "no errors";
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 !Object.values(SYSTEM_FIELDS_TO_IGNORE_FOR_CODE_GENERATION).includes(field.name);
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 { ISelectionProvider, ISelectionProviderContext } from "../interfaces";
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/softDeleteAwareEventSubscriber.subscriber' //rename
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 * from './transformers/datetime-transformer'
262
+ export { default as datetimeTransformer } from './transformers/datetime-transformer'
@@ -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);
@@ -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/softDeleteAwareEventSubscriber.subscriber';
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
- private readonly connection: Connection,
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
- connection.subscribers.push(this);
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
  );
@@ -0,0 +1,54 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { InjectDataSource } from "@nestjs/typeorm";
3
+ import { User } from "src/entities/user.entity";
4
+ import { ActiveUserData } from "src/interfaces/active-user-data.interface";
5
+ import { RequestContextService } from "src/services/request-context.service";
6
+ import { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent } from "typeorm";
7
+
8
+ @Injectable()
9
+ @EventSubscriber()
10
+ export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
11
+ constructor(
12
+ @InjectDataSource()
13
+ private readonly dataSource: DataSource,
14
+ private readonly requestContextService: RequestContextService,
15
+ ) {
16
+ this.dataSource.subscribers.push(this);
17
+ }
18
+
19
+ async beforeInsert(event: InsertEvent<any>) {
20
+ await this.stampUserField(event, true);
21
+ }
22
+
23
+ async beforeUpdate(event: UpdateEvent<any>) {
24
+ await this.stampUserField(event, false);
25
+ }
26
+
27
+ private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean){
28
+ if (!event.entity) {
29
+ return;
30
+ }
31
+ // Get the current active user details from the request context
32
+ const activeUserOrUndefined = this.requestContextService.getActiveUser;
33
+ if (!activeUserOrUndefined) {
34
+ return;
35
+ }
36
+
37
+ const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);
38
+ if (isInsert) {
39
+ event.entity.createdBy = loadedUser;
40
+ event.entity.updatedBy = loadedUser; // For insert, we set both createdBy and updatedBy to the same user
41
+ }
42
+ else {
43
+ event.entity.updatedBy = loadedUser;
44
+ }
45
+ }
46
+
47
+ private async loadUser(activeUser: ActiveUserData): Promise<User> {
48
+ const userRepo = this.dataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application
49
+ const loadedUser = await userRepo.findOne({
50
+ where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token
51
+ });
52
+ return loadedUser;;
53
+ }
54
+ }