@solidstarters/solid-core 1.2.140 → 1.2.141

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 (36) hide show
  1. package/dist/repository/view-metadata.repository.d.ts +9 -0
  2. package/dist/repository/view-metadata.repository.d.ts.map +1 -0
  3. package/dist/repository/view-metadata.repository.js +42 -0
  4. package/dist/repository/view-metadata.repository.js.map +1 -0
  5. package/dist/seeders/module-metadata-seeder.service.d.ts +1 -1
  6. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  7. package/dist/seeders/module-metadata-seeder.service.js +22 -8
  8. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  9. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts +14 -0
  10. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -0
  11. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +66 -0
  12. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -0
  13. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.d.ts +14 -1
  14. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.d.ts.map +1 -1
  15. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.js +81 -5
  16. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.js.map +1 -1
  17. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.d.ts +12 -0
  18. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.d.ts.map +1 -0
  19. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.js +36 -0
  20. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.js.map +1 -0
  21. package/dist/services/view-metadata.service.d.ts +3 -2
  22. package/dist/services/view-metadata.service.d.ts.map +1 -1
  23. package/dist/services/view-metadata.service.js +2 -3
  24. package/dist/services/view-metadata.service.js.map +1 -1
  25. package/dist/solid-core.module.d.ts.map +1 -1
  26. package/dist/solid-core.module.js +4 -0
  27. package/dist/solid-core.module.js.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +1 -1
  30. package/src/repository/view-metadata.repository.ts +31 -0
  31. package/src/seeders/module-metadata-seeder.service.ts +23 -17
  32. package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +70 -0
  33. package/src/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.ts +83 -9
  34. package/src/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.ts +34 -0
  35. package/src/services/view-metadata.service.ts +3 -2
  36. package/src/solid-core.module.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.140",
3
+ "version": "1.2.141",
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",
@@ -0,0 +1,31 @@
1
+ import { Injectable, Logger } from "@nestjs/common";
2
+ import { Dashboard } from "src/entities/dashboard.entity";
3
+ import { ViewMetadata } from "src/entities/view-metadata.entity";
4
+ import { DataSource, Repository, View } from "typeorm";
5
+
6
+ @Injectable()
7
+ export class ViewMetadataRepository extends Repository<ViewMetadata> {
8
+ private readonly logger = new Logger(this.constructor.name);
9
+
10
+ constructor(
11
+ private dataSource: DataSource,
12
+ ) {
13
+ super(ViewMetadata, dataSource.createEntityManager());
14
+ }
15
+
16
+ // Custom repository methods can be added here if needed
17
+ async findByNameAndModelNameAndModuleName(name: string, modelUserKey: string, moduleUserKey: string): Promise<ViewMetadata | null> {
18
+ const viewMetadata = await this.findOne({
19
+ where: {
20
+ name,
21
+ model: {
22
+ singularName: modelUserKey,
23
+ module: {
24
+ name: moduleUserKey
25
+ }
26
+ }
27
+ }
28
+ });
29
+ return viewMetadata;
30
+ }
31
+ }
@@ -189,7 +189,7 @@ export class ModuleMetadataSeederService {
189
189
  // Email templates
190
190
  this.logger.debug(`[Start] Processing email templates for ${moduleMetadata.name}`);
191
191
  const emailTemplates: CreateEmailTemplateDto[] = overallMetadata.emailTemplates;
192
- await this.seedEmailTemplates(emailTemplates);
192
+ await this.seedEmailTemplates(emailTemplates, moduleMetadata);
193
193
  this.logger.debug(`[End] Processing email templates for ${moduleMetadata.name}`);
194
194
 
195
195
  // Sms templates
@@ -255,10 +255,11 @@ export class ModuleMetadataSeederService {
255
255
  'SavedFiltersController.partialUpdate',
256
256
  'SavedFiltersController.update',
257
257
  'SavedFiltersController.insertMany',
258
- 'SavedFiltersController.create'
258
+ 'SavedFiltersController.create',
259
+ 'AuthenticationController.logout'
259
260
  ]
260
261
  await this.roleService.addPermissionToRole('Internal User', internalRolePermission);
261
- await this.roleService.addPermissionToRole('Public', ['SettingController.wrapSettings']);
262
+ await this.roleService.addPermissionToRole('Public', ['SettingController.wrapSettings','AuthenticationController.logout']);
262
263
  this.logger.log(`All Seeders finished`);
263
264
  this.logger.log(`Newly created username is: ${usersDetail?.length > 0 ? usersDetail[0]?.username : ''} and password is ${usersDetail?.length > 0 ? usersDetail[0]?.password : ''}`);
264
265
  }
@@ -313,7 +314,7 @@ export class ModuleMetadataSeederService {
313
314
  }
314
315
  }
315
316
 
316
- async seedEmailTemplates(emailTemplates: CreateEmailTemplateDto[]) {
317
+ async seedEmailTemplates(emailTemplates: CreateEmailTemplateDto[], moduleMetadata: CreateModuleMetadataDto) {
317
318
  if (!emailTemplates) {
318
319
  return;
319
320
  }
@@ -323,19 +324,24 @@ export class ModuleMetadataSeederService {
323
324
  this.logger.log(`Found ${emailTemplate.name} email template`);
324
325
 
325
326
  // We need to load the actual template contents.
326
- // const emailTemplateFilePath = path.join(process.cwd(), emailTemplate.body);
327
-
328
- // emailTemplate.body = fs.readFileSync(emailTemplateFilePath, 'utf-8').toString()
329
- const modulePath = path.dirname(require.resolve('@solidstarters/solid-core'));
330
-
331
- // Resolve the `src` folder
332
- const seedDataPath = path.join(modulePath, '../src/seeders/seed-data/email-templates');
333
-
334
- // Example usage
335
- const filePath = path.join(seedDataPath, emailTemplate.body);
336
-
337
-
338
- emailTemplate.body = fs.readFileSync(filePath, 'utf-8').toString();
327
+ if (moduleMetadata.name === 'solid-core') {
328
+ const modulePath = path.dirname(require.resolve('@solidstarters/solid-core'));
329
+ const seedDataPath = path.join(modulePath, '../src/seeders/seed-data/email-templates');
330
+ const filePath = path.join(seedDataPath, emailTemplate.body);
331
+ this.logger.log(`Seeding email template from solid-core at path: ${filePath}`);
332
+ if (fs.existsSync(filePath)) {
333
+ emailTemplate.body = fs.readFileSync(filePath, 'utf-8').toString();
334
+ }
335
+ }
336
+ else {
337
+ // Check if file exists
338
+ const emailTemplateHandlebar = `module-metadata/${moduleMetadata.name}/email-templates/${emailTemplate.body}`
339
+ const fullPath = path.join(process.cwd(), emailTemplateHandlebar);
340
+ this.logger.log(`Seeding custom email template from consuming model at path: ${fullPath}`);
341
+ if (fs.existsSync(fullPath)) {
342
+ emailTemplate.body = fs.readFileSync(fullPath, 'utf-8').toString();
343
+ }
344
+ }
339
345
 
340
346
  // Save to DB.
341
347
  await this.emailTemplateService.removeByName(emailTemplate.name);
@@ -0,0 +1,70 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectEntityManager } from '@nestjs/typeorm';
3
+ import { ComputedFieldProvider } from 'src/decorators/computed-field-provider.decorator';
4
+ import { ComputedFieldMetadata } from 'src/helpers/solid-registry';
5
+ import { IEntityPreComputeFieldProvider } from 'src/interfaces';
6
+ // import {
7
+ // ComputedFieldMetadata,
8
+ // ComputedFieldProvider,
9
+ // IEntityPreComputeFieldProvider,
10
+ // } from '@solidstarters/solid-core';
11
+ import { EntityManager } from 'typeorm';
12
+ // import { Agreement } from '../entities/agreement.entity';
13
+
14
+ @ComputedFieldProvider()
15
+ @Injectable()
16
+ export class AgreementIdComputationProvider implements IEntityPreComputeFieldProvider<any, any, string> {
17
+ constructor(
18
+ @InjectEntityManager()
19
+ private readonly entityManager: EntityManager
20
+ ) { }
21
+
22
+ generateRandomCode(length = 5): string {
23
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
24
+ let result = '';
25
+ for (let i = 0; i < length; i++) {
26
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
27
+ }
28
+ return result;
29
+ }
30
+
31
+ async isAgreementIdUnique(agreementId: string): Promise<boolean> {
32
+ // const count = await this.entityManager.count(Agreement, {
33
+ // where: { agreementId },
34
+ // });
35
+ // return count === 0;
36
+ return false;
37
+ }
38
+
39
+ async generateUniqueAgreementId(codeLength: number): Promise<string> {
40
+ const maxAttempts = 10;
41
+
42
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
43
+ const newId = this.generateRandomCode(codeLength);
44
+
45
+ const isUnique = await this.isAgreementIdUnique(newId);
46
+
47
+ if (isUnique) {
48
+ return newId;
49
+ }
50
+ }
51
+
52
+ throw new Error('Failed to generate a unique agreement ID after multiple attempts');
53
+ }
54
+
55
+ async preComputeValue(triggerEntity: any, computedFieldMetadata: ComputedFieldMetadata<any>): Promise<string> {
56
+ const prefix = computedFieldMetadata.computedFieldValueProviderCtxt['prefix'];
57
+ const codeLength = computedFieldMetadata.computedFieldValueProviderCtxt['length'] || 5;
58
+ const uniqueCode = await this.generateUniqueAgreementId(codeLength);
59
+ return `${prefix}-${uniqueCode}`;
60
+ }
61
+
62
+ name(): string {
63
+ return 'AgreementIdComputationProvider';
64
+ }
65
+
66
+ help(): string {
67
+ return 'Provider used to compute values for the agreement id field on save.';
68
+ }
69
+
70
+ }
@@ -1,15 +1,27 @@
1
1
  import { Injectable } from "@nestjs/common";
2
2
  import { plainToInstance } from "class-transformer";
3
3
  import { CreateDashboardDto } from "src/dtos/create-dashboard.dto";
4
+ import { UpdateMenuItemMetadataDto } from "src/dtos/update-menu-item-metadata.dto";
4
5
  import { AiInteraction } from "src/entities/ai-interaction.entity";
6
+ import { Dashboard } from "src/entities/dashboard.entity";
5
7
  import { IMcpToolResponseHandler } from "../../interfaces";
8
+ import { ActionMetadataService } from "../action-metadata.service";
6
9
  import { DashboardService } from "../dashboard.service";
10
+ import { MenuItemMetadataService } from "../menu-item-metadata.service";
11
+ import { ModelMetadataService } from "../model-metadata.service";
12
+ import { ModuleMetadataService } from "../module-metadata.service";
13
+ import { RoleMetadataService } from "../role-metadata.service";
7
14
 
8
15
  @Injectable()
9
16
  export class SolidCreateDashboardMcpToolResponseHandler implements IMcpToolResponseHandler {
10
17
 
11
18
  constructor(
12
19
  private readonly dashboardService: DashboardService,
20
+ private readonly actionMetadataService: ActionMetadataService,
21
+ private readonly menuItemMetadataService: MenuItemMetadataService,
22
+ private readonly moduleMetadataService: ModuleMetadataService,
23
+ private readonly modelMetadataService: ModelMetadataService,
24
+ private readonly roleService: RoleMetadataService, // Assuming roleService is a Mongoose model, adjust as necessary
13
25
  ) {
14
26
  }
15
27
 
@@ -17,17 +29,11 @@ export class SolidCreateDashboardMcpToolResponseHandler implements IMcpToolRespo
17
29
  const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
18
30
  const aiResponseMessage = JSON.parse(escapedMessage);
19
31
 
20
- // const aiResponse = JSON.parse(aiInteraction.message);
32
+ const { dashboardDto, dashboard } = await this.createDashboard(aiResponseMessage);
21
33
 
22
- //FIXME: Replace \' with ' in the response, since the AI response seems to contain \' which is invalid JSON.
23
- // This is a workaround for now, until we find a better solution.
24
- // const aiResponseMessageReplaced = aiResponse['message'].replace(/\\'/g, "'");
25
-
26
- const dashboardDto = plainToInstance(CreateDashboardDto, aiResponseMessage);
27
- dashboardDto['layoutJson'] = JSON.stringify(dashboardDto['layoutJson']);
34
+ const { moduleUserKey, actionMetadataEntity } = await this.createActionMetadataEntry(dashboardDto, dashboard);
28
35
 
29
-
30
- const dashboard = await this.dashboardService.create(dashboardDto, []);
36
+ await this.createMenuItemEntry(dashboard, moduleUserKey, actionMetadataEntity);
31
37
 
32
38
  // TODO: decide on some shape to return hre...
33
39
  return {
@@ -36,4 +42,72 @@ export class SolidCreateDashboardMcpToolResponseHandler implements IMcpToolRespo
36
42
  }
37
43
  }
38
44
 
45
+
46
+ private async createMenuItemEntry(dashboard: Dashboard, moduleUserKey: string, actionMetadataEntity: any) {
47
+ const menuData = {
48
+ displayName: dashboard.displayName || dashboard.name,
49
+ name: `${moduleUserKey}-${dashboard.name}-dashboard-menu-item`,
50
+ sequenceNumber: 1,
51
+ actionUserKey: actionMetadataEntity.name,
52
+ moduleUserKey: moduleUserKey,
53
+ parentMenuItemUserKey: '',
54
+ };
55
+
56
+ const adminRole = await this.roleService.findRoleByName('Admin');
57
+ const specifiedRoles = menuData['roles'];
58
+
59
+ // If the developer has specified roles, then resolve them, making sure that admin role is also given.
60
+ const specifiedRoleObjects = [adminRole];
61
+ if (specifiedRoles) {
62
+ for (let i = 0; i < specifiedRoles.length; i++) {
63
+ const specifiedRole = specifiedRoles[i];
64
+ const specifiedRoleObject = await this.roleService.findRoleByName(specifiedRole);
65
+ if (!specifiedRoleObject) {
66
+ throw new Error(`Invalid role: (${specifiedRole}) specified against menu with display name ${menuData.displayName}.`);
67
+ }
68
+ specifiedRoleObjects.push(specifiedRoleObject);
69
+ }
70
+ }
71
+
72
+ menuData['roles'] = specifiedRoleObjects;
73
+ menuData['action'] = await this.actionMetadataService.findOneByUserKey(menuData.actionUserKey);
74
+ menuData['module'] = await this.moduleMetadataService.findOneByUserKey(menuData.moduleUserKey);
75
+
76
+ if (menuData.parentMenuItemUserKey) {
77
+ menuData['parentMenuItem'] = await this.menuItemMetadataService.findOneByUserKey(menuData.parentMenuItemUserKey);
78
+ } else {
79
+ menuData['parentMenuItem'] = null;
80
+ }
81
+ await this.menuItemMetadataService.upsert(menuData as unknown as UpdateMenuItemMetadataDto);
82
+ }
83
+
84
+ private async createActionMetadataEntry(dashboardDto: CreateDashboardDto, dashboard: Dashboard) {
85
+ const moduleUserKey = dashboardDto.moduleUserKey;
86
+ const actionMetadata = {
87
+ name: `${dashboard.name}-view`,
88
+ displayName: dashboard.displayName || dashboard.name,
89
+ type: 'custom',
90
+ domain: "{}",
91
+ context: "{}",
92
+ customComponent: `/admin/core/${moduleUserKey}/dashboards?dashboardName=${dashboard.name}`,
93
+ customIsModal: true,
94
+ serverEndpoint: '',
95
+ moduleUserKey: moduleUserKey,
96
+ modelUserKey: '',
97
+ viewUserKey: `${dashboard.name}-view`,
98
+ };
99
+ actionMetadata['module'] = await this.moduleMetadataService.findOneByUserKey(actionMetadata.moduleUserKey);
100
+ if (actionMetadata.modelUserKey) {
101
+ actionMetadata['model'] = await this.modelMetadataService.findOneByUserKey(actionMetadata.modelUserKey);
102
+ }
103
+ const actionMetadataEntity = await this.actionMetadataService.upsert(actionMetadata);
104
+ return { moduleUserKey, actionMetadataEntity };
105
+ }
106
+
107
+ private async createDashboard(aiResponseMessage: any) {
108
+ const dashboardDto = plainToInstance(CreateDashboardDto, aiResponseMessage);
109
+ dashboardDto['layoutJson'] = JSON.stringify(dashboardDto['layoutJson']);
110
+ const dashboard = await this.dashboardService.create(dashboardDto, []);
111
+ return { dashboardDto, dashboard };
112
+ }
39
113
  }
@@ -0,0 +1,34 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
3
+ import { ViewMetadataRepository } from "src/repository/view-metadata.repository";
4
+ import { IMcpToolResponseHandler } from "../../interfaces";
5
+
6
+ @Injectable()
7
+ export class SolidCreateModelLayoutMcpToolResponseHandler implements IMcpToolResponseHandler {
8
+
9
+ constructor(
10
+ private readonly viewMetadataRepository: ViewMetadataRepository,
11
+ ) {
12
+ }
13
+
14
+ async apply(aiInteraction: AiInteraction) {
15
+ const aiResponse = JSON.parse(aiInteraction.message);
16
+
17
+ // Get the data for resolving the view metadata
18
+ const {name, modelUserKey, moduleUserKey, layout} = aiResponse;
19
+
20
+ // Fetch the view metadata for the given model and module and the view name
21
+ const viewMetadata = await this.viewMetadataRepository.findByNameAndModelNameAndModuleName(name, modelUserKey, moduleUserKey);
22
+
23
+ viewMetadata.layout = JSON.stringify(layout);
24
+ // Save the updated view metadata
25
+ await this.viewMetadataRepository.save(viewMetadata);
26
+
27
+ // TODO: decide on some shape to return hre...
28
+ return {
29
+ seedingRequired: false,
30
+ serverRebooting: false,
31
+ }
32
+ }
33
+
34
+ }
@@ -20,6 +20,7 @@ import { ViewMetadata } from '../entities/view-metadata.entity';
20
20
  import { ActionMetadataService } from './action-metadata.service';
21
21
  import { SolidIntrospectService } from './solid-introspect.service';
22
22
  import { UserViewMetadataService } from './user-view-metadata.service';
23
+ import { ViewMetadataRepository } from 'src/repository/view-metadata.repository';
23
24
 
24
25
  @Injectable()
25
26
  export class ViewMetadataService extends CRUDService<ViewMetadata> {
@@ -35,8 +36,8 @@ export class ViewMetadataService extends CRUDService<ViewMetadata> {
35
36
  readonly userViewMetadataService: UserViewMetadataService,
36
37
  @InjectEntityManager()
37
38
  readonly entityManager: EntityManager,
38
- @InjectRepository(ViewMetadata, 'default')
39
- readonly repo: Repository<ViewMetadata>,
39
+ // @InjectRepository(ViewMetadata, 'default')
40
+ readonly repo: ViewMetadataRepository,
40
41
  @InjectRepository(FieldMetadata)
41
42
  private readonly fieldMetadataRepo: Repository<FieldMetadata>,
42
43
  @InjectRepository(ModelMetadata)
@@ -255,6 +255,8 @@ import { ViewMetadataSubsciber } from './subscribers/view-metadata.subscriber';
255
255
  import { SolidCreateDashboardWidgetMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-dashboard-widget-mcp-tool-response-handler.service';
256
256
  import { SolidCreateModelWithFieldsMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service';
257
257
  import { SolidAddFieldMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-add-field-mcp-tool-response-handler.service';
258
+ import { ViewMetadataRepository } from './repository/view-metadata.repository';
259
+ import { SolidCreateModelLayoutMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service';
258
260
 
259
261
 
260
262
  @Global()
@@ -540,6 +542,8 @@ import { SolidAddFieldMcpToolResponseHandler } from './services/mcp-tool-respons
540
542
  SolidCreateDashboardWidgetMcpToolResponseHandler,
541
543
  SolidCreateModelWithFieldsMcpToolResponseHandler,
542
544
  SolidAddFieldMcpToolResponseHandler,
545
+ ViewMetadataRepository,
546
+ SolidCreateModelLayoutMcpToolResponseHandler,
543
547
  ],
544
548
  exports: [
545
549
  ModuleMetadataService,