@solidstarters/solid-core 1.2.139 → 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 (66) 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/mcp-tool-response-handler-factory.service.d.ts +1 -1
  14. package/dist/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.d.ts.map +1 -1
  15. package/dist/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.js +1 -2
  16. package/dist/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.js.map +1 -1
  17. package/dist/services/mcp-tool-response-handlers/solid-add-field-mcp-tool-response-handler.service.d.ts +16 -0
  18. package/dist/services/mcp-tool-response-handlers/solid-add-field-mcp-tool-response-handler.service.d.ts.map +1 -0
  19. package/dist/services/mcp-tool-response-handlers/solid-add-field-mcp-tool-response-handler.service.js +48 -0
  20. package/dist/services/mcp-tool-response-handlers/solid-add-field-mcp-tool-response-handler.service.js.map +1 -0
  21. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.d.ts +25 -0
  22. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.d.ts.map +1 -0
  23. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.js +114 -0
  24. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.js.map +1 -0
  25. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-mcp-tool-response-handler.service.d.ts +12 -0
  26. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-mcp-tool-response-handler.service.d.ts.map +1 -0
  27. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-mcp-tool-response-handler.service.js +37 -0
  28. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-mcp-tool-response-handler.service.js.map +1 -0
  29. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-sql-dataset-config-mcp-tool-response-handler.service.d.ts +12 -0
  30. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-sql-dataset-config-mcp-tool-response-handler.service.d.ts.map +1 -0
  31. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-sql-dataset-config-mcp-tool-response-handler.service.js +38 -0
  32. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-question-sql-dataset-config-mcp-tool-response-handler.service.js.map +1 -0
  33. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-widget-mcp-tool-response-handler.service.d.ts +12 -0
  34. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-widget-mcp-tool-response-handler.service.d.ts.map +1 -0
  35. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-widget-mcp-tool-response-handler.service.js +38 -0
  36. package/dist/services/mcp-tool-response-handlers/solid-create-dashboard-widget-mcp-tool-response-handler.service.js.map +1 -0
  37. package/dist/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.d.ts +16 -0
  38. package/dist/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.d.ts.map +1 -0
  39. package/dist/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.js +47 -0
  40. package/dist/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.js.map +1 -0
  41. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.d.ts +12 -0
  42. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.d.ts.map +1 -0
  43. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.js +36 -0
  44. package/dist/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.js.map +1 -0
  45. package/dist/services/view-metadata.service.d.ts +3 -2
  46. package/dist/services/view-metadata.service.d.ts.map +1 -1
  47. package/dist/services/view-metadata.service.js +2 -3
  48. package/dist/services/view-metadata.service.js.map +1 -1
  49. package/dist/solid-core.module.d.ts.map +1 -1
  50. package/dist/solid-core.module.js +19 -3
  51. package/dist/solid-core.module.js.map +1 -1
  52. package/dist/tsconfig.tsbuildinfo +1 -1
  53. package/package.json +1 -1
  54. package/src/repository/view-metadata.repository.ts +31 -0
  55. package/src/seeders/module-metadata-seeder.service.ts +23 -17
  56. package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +70 -0
  57. package/src/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.ts +2 -4
  58. package/src/services/mcp-tool-response-handlers/solid-add-field-mcp-tool-response-handler.service.ts +56 -0
  59. package/src/services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service.ts +113 -0
  60. package/src/services/mcp-tool-response-handlers/solid-create-dashboard-question-mcp-tool-response-handler.service.ts +38 -0
  61. package/src/services/mcp-tool-response-handlers/solid-create-dashboard-question-sql-dataset-config-mcp-tool-response-handler.service.ts +40 -0
  62. package/src/services/mcp-tool-response-handlers/solid-create-dashboard-widget-mcp-tool-response-handler.service.ts +39 -0
  63. package/src/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.ts +46 -0
  64. package/src/services/mcp-tool-response-handlers/solid-save-model-layout-mcp-tool-response-handler.service.ts +34 -0
  65. package/src/services/view-metadata.service.ts +3 -2
  66. package/src/solid-core.module.ts +20 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.139",
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,10 +1,8 @@
1
- import { Logger } from '@nestjs/common';
2
- import { Injectable } from '@nestjs/common';
1
+ import { Injectable, Logger } from '@nestjs/common';
3
2
 
4
- import { QueueMessage, QueuePublisher } from 'src/interfaces/mq';
5
3
  import { classify } from '@angular-devkit/core/src/utils/strings';
6
- import { SolidIntrospectService } from '../solid-introspect.service';
7
4
  import { IMcpToolResponseHandler } from 'src/interfaces';
5
+ import { SolidIntrospectService } from '../solid-introspect.service';
8
6
 
9
7
 
10
8
  @Injectable()
@@ -0,0 +1,56 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { UpdateModelMetaDataDto } from "src/dtos/update-model-metadata.dto";
3
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
4
+ import { FieldMetadata } from "src/entities/field-metadata.entity";
5
+ import { SolidRegistry } from "src/helpers/solid-registry";
6
+ import { IMcpToolResponseHandler } from "../../interfaces";
7
+ import { FieldMetadataService } from "../field-metadata.service";
8
+ import { ModelMetadataService } from "../model-metadata.service";
9
+
10
+ @Injectable()
11
+ // solid_add_field
12
+ export class SolidAddFieldMcpToolResponseHandler implements IMcpToolResponseHandler {
13
+
14
+ constructor(
15
+ private readonly modelMetadataService: ModelMetadataService,
16
+ private readonly fieldMetadataService: FieldMetadataService,
17
+ private readonly solidRegistry: SolidRegistry,
18
+ ) {
19
+ }
20
+
21
+ async apply(aiInteraction: AiInteraction) {
22
+ // const aiResponse = JSON.parse(aiInteraction.message);
23
+ const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
24
+ const aiResponse = JSON.parse(escapedMessage);
25
+
26
+ const { modelUserKey, fieldSchema } = aiResponse;
27
+
28
+ // TODO: Validate if another field with same name exists, if it does then raise an error...
29
+
30
+ // TODO: load the model with the fields.
31
+ const modelMetadata = await this.modelMetadataService.findOneByUserKey(modelUserKey, ['fields']);
32
+ if (!modelMetadata) {
33
+ throw new Error(`Model with user key ${modelUserKey} not found.`);
34
+ }
35
+
36
+ // Add the fieldSchema to the model fields array
37
+ fieldSchema['modelId'] = modelMetadata.id;
38
+ modelMetadata.fields.push(fieldSchema as FieldMetadata);
39
+
40
+ const modelObj = await this.modelMetadataService.update(modelMetadata.id, modelMetadata as unknown as UpdateModelMetaDataDto);
41
+
42
+
43
+ // This creates the module-metadata.json file....
44
+ // const modelObj = await this.fieldMetadataService.create(fieldSchema as CreateFieldMetadataDto);
45
+
46
+ // Now we need to run solid seed & then solid refresh-model --name <module-name>
47
+ await this.modelMetadataService.handleGenerateCode({ modelId: modelMetadata.id });
48
+
49
+ // TODO: decide on some shape to return hre...
50
+ return {
51
+ seedingRequired: true,
52
+ serverRebooting: true,
53
+ }
54
+ }
55
+
56
+ }
@@ -0,0 +1,113 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { plainToInstance } from "class-transformer";
3
+ import { CreateDashboardDto } from "src/dtos/create-dashboard.dto";
4
+ import { UpdateMenuItemMetadataDto } from "src/dtos/update-menu-item-metadata.dto";
5
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
6
+ import { Dashboard } from "src/entities/dashboard.entity";
7
+ import { IMcpToolResponseHandler } from "../../interfaces";
8
+ import { ActionMetadataService } from "../action-metadata.service";
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";
14
+
15
+ @Injectable()
16
+ export class SolidCreateDashboardMcpToolResponseHandler implements IMcpToolResponseHandler {
17
+
18
+ constructor(
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
25
+ ) {
26
+ }
27
+
28
+ async apply(aiInteraction: AiInteraction) {
29
+ const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
30
+ const aiResponseMessage = JSON.parse(escapedMessage);
31
+
32
+ const { dashboardDto, dashboard } = await this.createDashboard(aiResponseMessage);
33
+
34
+ const { moduleUserKey, actionMetadataEntity } = await this.createActionMetadataEntry(dashboardDto, dashboard);
35
+
36
+ await this.createMenuItemEntry(dashboard, moduleUserKey, actionMetadataEntity);
37
+
38
+ // TODO: decide on some shape to return hre...
39
+ return {
40
+ seedingRequired: false,
41
+ serverRebooting: false,
42
+ }
43
+ }
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
+ }
113
+ }
@@ -0,0 +1,38 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { plainToInstance } from "class-transformer";
3
+ import { CreateDashboardQuestionDto } from "src/dtos/create-dashboard-question.dto";
4
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
5
+ import { IMcpToolResponseHandler } from "../../interfaces";
6
+ import { DashboardQuestionService } from "../dashboard-question.service";
7
+
8
+ @Injectable()
9
+ export class SolidCreateDashboardQuestionMcpToolResponseHandler implements IMcpToolResponseHandler {
10
+
11
+ constructor(
12
+ private readonly dashboardQuestionService: DashboardQuestionService,
13
+ ) {
14
+ }
15
+
16
+ async apply(aiInteraction: AiInteraction) {
17
+ const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
18
+ const aiResponseMessage = JSON.parse(escapedMessage);
19
+
20
+ //FIXME: Replace \' with ' in the response, since the AI response seems to contain \' which is invalid JSON.
21
+ // This is a workaround for now, until we find a better solution.
22
+ // const aiResponseMessageReplaced = aiResponseMessage['message'].replace(/\\'/g, "'");
23
+ // const dashboardUserKey = aiResponseMessageReplaced['dashboardUserKey'];
24
+ // if (!dashboardUserKey) {
25
+ // throw new Error("Dashboard User Key is required to create a Dashboard Question.");
26
+ // }
27
+ const dashboardQuestionDto = plainToInstance(CreateDashboardQuestionDto, aiResponseMessage);
28
+
29
+ const dashboardQuestion = await this.dashboardQuestionService.create(dashboardQuestionDto, []);
30
+
31
+ // TODO: decide on some shape to return hre...
32
+ return {
33
+ seedingRequired: false,
34
+ serverRebooting: false,
35
+ }
36
+ }
37
+
38
+ }
@@ -0,0 +1,40 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { plainToInstance } from "class-transformer";
3
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
4
+ import { IMcpToolResponseHandler } from "../../interfaces";
5
+ import { DashboardQuestionSqlDatasetConfigService } from "../dashboard-question-sql-dataset-config.service";
6
+ import { CreateDashboardQuestionSqlDatasetConfigDto } from "src/dtos/create-dashboard-question-sql-dataset-config.dto";
7
+
8
+ @Injectable()
9
+ export class SolidCreateDashboardQuestionSqlDatasetConfigMcpToolResponseHandler implements IMcpToolResponseHandler {
10
+
11
+ constructor(
12
+ private readonly dashboardQuestionSqlDatasetConfigService: DashboardQuestionSqlDatasetConfigService,
13
+ ) {
14
+ }
15
+
16
+ async apply(aiInteraction: AiInteraction) {
17
+ // const aiResponse = JSON.parse(aiInteraction.message);
18
+ const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
19
+ const aiResponseMessage = JSON.parse(escapedMessage);
20
+
21
+ // FIXME: Replace \' with ' in the response, since the AI response seems to contain \' which is invalid JSON.
22
+ // This is a workaround for now, until we find a better solution.
23
+ // const aiResponseMessageReplaced = aiResponse['message'].replace(/\\'/g, "'");
24
+ // const dashboardUserKey = aiResponseMessageReplaced['dashboardUserKey'];
25
+ // if (!dashboardUserKey) {
26
+ // throw new Error("Dashboard User Key is required to create a Dashboard Question.");
27
+ // }
28
+ const dto = plainToInstance(CreateDashboardQuestionSqlDatasetConfigDto, aiResponseMessage);
29
+ dto['options'] = JSON.stringify(dto['options']);
30
+
31
+ const dashboardQuestion = await this.dashboardQuestionSqlDatasetConfigService.create(dto, []);
32
+
33
+ // TODO: decide on some shape to return hre...
34
+ return {
35
+ seedingRequired: false,
36
+ serverRebooting: false,
37
+ }
38
+ }
39
+
40
+ }
@@ -0,0 +1,39 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { plainToInstance } from "class-transformer";
3
+ import { CreateDashboardQuestionDto } from "src/dtos/create-dashboard-question.dto";
4
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
5
+ import { IMcpToolResponseHandler } from "../../interfaces";
6
+ import { DashboardQuestionService } from "../dashboard-question.service";
7
+
8
+ @Injectable()
9
+ export class SolidCreateDashboardWidgetMcpToolResponseHandler implements IMcpToolResponseHandler {
10
+
11
+ constructor(
12
+ private readonly dashboardQuestionService: DashboardQuestionService,
13
+ ) {
14
+ }
15
+
16
+ async apply(aiInteraction: AiInteraction) {
17
+ const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
18
+ const aiResponseMessage = JSON.parse(escapedMessage);
19
+
20
+ //FIXME: Replace \' with ' in the response, since the AI response seems to contain \' which is invalid JSON.
21
+ // This is a workaround for now, until we find a better solution.
22
+ // const aiResponseMessageReplaced = aiResponseMessage['message'].replace(/\\'/g, "'");
23
+ // const dashboardUserKey = aiResponseMessageReplaced['dashboardUserKey'];
24
+ // if (!dashboardUserKey) {
25
+ // throw new Error("Dashboard User Key is required to create a Dashboard Question.");
26
+ // }
27
+ const dashboardQuestionDto = plainToInstance(CreateDashboardQuestionDto, aiResponseMessage);
28
+ dashboardQuestionDto['questionSqlDatasetConfigsCommand'] = "update";
29
+
30
+ const dashboardQuestion = await this.dashboardQuestionService.create(dashboardQuestionDto, []);
31
+
32
+ // TODO: decide on some shape to return hre...
33
+ return {
34
+ seedingRequired: false,
35
+ serverRebooting: false,
36
+ }
37
+ }
38
+
39
+ }
@@ -0,0 +1,46 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { IMcpToolResponseHandler } from "../../interfaces";
3
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
4
+ import { SolidRegistry } from "src/helpers/solid-registry";
5
+ import { ModelMetadataService } from "../model-metadata.service";
6
+ import { CreateModelMetadataDto } from "src/dtos/create-model-metadata.dto";
7
+ import { ModuleMetadataService } from "../module-metadata.service";
8
+
9
+ @Injectable()
10
+ export class SolidCreateModelWithFieldsMcpToolResponseHandler implements IMcpToolResponseHandler {
11
+
12
+ constructor(
13
+ private readonly moduleMetadataService: ModuleMetadataService,
14
+ private readonly modelMetadataService: ModelMetadataService,
15
+ private readonly solidRegistry: SolidRegistry,
16
+ ) {
17
+ }
18
+
19
+ async apply(aiInteraction: AiInteraction) {
20
+ // const aiResponse = JSON.parse(aiInteraction.message);
21
+ const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
22
+ const aiResponse = JSON.parse(escapedMessage);
23
+
24
+ const { moduleUserKey, modelSchema } = aiResponse;
25
+ const moduleMetadata = await this.moduleMetadataService.findOneByUserKey(moduleUserKey);
26
+ if (!moduleMetadata) {
27
+ throw new Error(`Module with user key ${moduleUserKey} not found.`);
28
+ }
29
+ modelSchema['moduleId'] = moduleMetadata.id;
30
+
31
+ // TODO: Validate if another model with same name exists, if it does then raise an error...
32
+
33
+ // This creates the module-metadata.json file....
34
+ const modelObj = await this.modelMetadataService.create(modelSchema as CreateModelMetadataDto);
35
+
36
+ // Now we need to run solid seed & then solid refresh-model --name <module-name>
37
+ await this.modelMetadataService.handleGenerateCode({ modelId: modelObj.id });
38
+
39
+ // TODO: decide on some shape to return hre...
40
+ return {
41
+ seedingRequired: true,
42
+ serverRebooting: true,
43
+ }
44
+ }
45
+
46
+ }
@@ -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)
@@ -203,6 +203,7 @@ import { ChatterMessageDetailsService } from './services/chatter-message-details
203
203
  import { ChatterMessageService } from './services/chatter-message.service';
204
204
  import { ConcatComputedFieldProvider } from './services/computed-fields/concat-computed-field-provider.service';
205
205
  import { ConcatEntityComputedFieldProvider } from './services/computed-fields/entity/concat-entity-computed-field-provider.service';
206
+ import { CRUDService } from './services/crud.service';
206
207
  import { CsvService } from './services/csv.service';
207
208
  import { DashboardQuestionSqlDatasetConfigService } from './services/dashboard-question-sql-dataset-config.service';
208
209
  import { DashboardQuestionService } from './services/dashboard-question.service';
@@ -216,6 +217,11 @@ import { ExportTransactionService } from './services/export-transaction.service'
216
217
  import { ImportTransactionErrorLogService } from './services/import-transaction-error-log.service';
217
218
  import { ImportTransactionService } from './services/import-transaction.service';
218
219
  import { LocaleService } from './services/locale.service';
220
+ import { McpToolResponseHandlerFactory } from './services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service';
221
+ import { SolidCreateDashboardMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-dashboard-mcp-tool-response-handler.service';
222
+ import { SolidCreateDashboardQuestionMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-dashboard-question-mcp-tool-response-handler.service';
223
+ import { SolidCreateDashboardQuestionSqlDatasetConfigMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-dashboard-question-sql-dataset-config-mcp-tool-response-handler.service';
224
+ import { SolidCreateModuleMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service';
219
225
  import { FileS3StorageProvider } from './services/mediaStorageProviders/file-s3-storage-provider';
220
226
  import { FileStorageProvider } from './services/mediaStorageProviders/file-storage-provider';
221
227
  import { ChartJsSqlDataProvider } from './services/question-data-providers/chartjs-sql-data-provider.service';
@@ -246,9 +252,11 @@ import { DashboardVariableSubscriber } from './subscribers/dashboard-variable.su
246
252
  import { DashboardSubscriber } from './subscribers/dashboard.subscriber';
247
253
  import { SecurityRuleSubscriber } from './subscribers/security-rule.subscriber';
248
254
  import { ViewMetadataSubsciber } from './subscribers/view-metadata.subscriber';
249
- import { CRUDService } from './services/crud.service';
250
- import { McpToolResponseHandlerFactory } from './services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service';
251
- import { SolidCreateModuleMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service';
255
+ import { SolidCreateDashboardWidgetMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-dashboard-widget-mcp-tool-response-handler.service';
256
+ import { SolidCreateModelWithFieldsMcpToolResponseHandler } from './services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service';
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';
252
260
 
253
261
 
254
262
  @Global()
@@ -419,7 +427,7 @@ import { SolidCreateModuleMcpToolResponseHandler } from './services/mcp-tool-res
419
427
  SmsTemplateService,
420
428
  EmailTemplateService,
421
429
  PublisherFactory,
422
-
430
+
423
431
  McpToolResponseHandlerFactory,
424
432
  SolidCreateModuleMcpToolResponseHandler,
425
433
 
@@ -528,6 +536,14 @@ import { SolidCreateModuleMcpToolResponseHandler } from './services/mcp-tool-res
528
536
  DashboardVariableSubscriber,
529
537
  DashboardQuestionSubscriber,
530
538
  DashboardQuestionSqlDatasetConfigSubscriber,
539
+ SolidCreateDashboardMcpToolResponseHandler,
540
+ SolidCreateDashboardQuestionMcpToolResponseHandler,
541
+ SolidCreateDashboardQuestionSqlDatasetConfigMcpToolResponseHandler,
542
+ SolidCreateDashboardWidgetMcpToolResponseHandler,
543
+ SolidCreateModelWithFieldsMcpToolResponseHandler,
544
+ SolidAddFieldMcpToolResponseHandler,
545
+ ViewMetadataRepository,
546
+ SolidCreateModelLayoutMcpToolResponseHandler,
531
547
  ],
532
548
  exports: [
533
549
  ModuleMetadataService,