@solidstarters/solid-core 1.2.135 → 1.2.137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/ai-interaction.controller.d.ts +3 -1
- package/dist/controllers/ai-interaction.controller.d.ts.map +1 -1
- package/dist/controllers/ai-interaction.controller.js +27 -3
- package/dist/controllers/ai-interaction.controller.js.map +1 -1
- package/dist/controllers/dashboard-question-sql-dataset-config.controller.d.ts +43 -0
- package/dist/controllers/dashboard-question-sql-dataset-config.controller.d.ts.map +1 -0
- package/dist/controllers/{question-sql-dataset-config.controller.js → dashboard-question-sql-dataset-config.controller.js} +30 -30
- package/dist/controllers/dashboard-question-sql-dataset-config.controller.js.map +1 -0
- package/dist/controllers/dashboard-question.controller.d.ts +45 -0
- package/dist/controllers/dashboard-question.controller.d.ts.map +1 -0
- package/dist/controllers/{question.controller.js → dashboard-question.controller.js} +31 -31
- package/dist/controllers/dashboard-question.controller.js.map +1 -0
- package/dist/dtos/{create-question-sql-dataset-config.dto.d.ts → create-dashboard-question-sql-dataset-config.dto.d.ts} +2 -2
- package/dist/dtos/create-dashboard-question-sql-dataset-config.dto.d.ts.map +1 -0
- package/dist/dtos/{create-question-sql-dataset-config.dto.js → create-dashboard-question-sql-dataset-config.dto.js} +13 -13
- package/dist/dtos/create-dashboard-question-sql-dataset-config.dto.js.map +1 -0
- package/dist/dtos/create-dashboard-question.dto.d.ts +17 -0
- package/dist/dtos/create-dashboard-question.dto.d.ts.map +1 -0
- package/dist/dtos/{create-question.dto.js → create-dashboard-question.dto.js} +25 -19
- package/dist/dtos/create-dashboard-question.dto.js.map +1 -0
- package/dist/dtos/create-dashboard.dto.d.ts +2 -2
- package/dist/dtos/create-dashboard.dto.d.ts.map +1 -1
- package/dist/dtos/create-dashboard.dto.js +3 -3
- package/dist/dtos/create-dashboard.dto.js.map +1 -1
- package/dist/dtos/{update-question-sql-dataset-config.dto.d.ts → update-dashboard-question-sql-dataset-config.dto.d.ts} +2 -2
- package/dist/dtos/update-dashboard-question-sql-dataset-config.dto.d.ts.map +1 -0
- package/dist/dtos/{update-question-sql-dataset-config.dto.js → update-dashboard-question-sql-dataset-config.dto.js} +14 -14
- package/dist/dtos/update-dashboard-question-sql-dataset-config.dto.js.map +1 -0
- package/dist/dtos/update-dashboard-question.dto.d.ts +18 -0
- package/dist/dtos/update-dashboard-question.dto.d.ts.map +1 -0
- package/dist/dtos/{update-question.dto.js → update-dashboard-question.dto.js} +26 -20
- package/dist/dtos/update-dashboard-question.dto.js.map +1 -0
- package/dist/dtos/update-dashboard.dto.d.ts +2 -2
- package/dist/dtos/update-dashboard.dto.d.ts.map +1 -1
- package/dist/dtos/update-dashboard.dto.js +3 -3
- package/dist/dtos/update-dashboard.dto.js.map +1 -1
- package/dist/entities/dashboard-question-sql-dataset-config.entity.d.ts +13 -0
- package/dist/entities/dashboard-question-sql-dataset-config.entity.d.ts.map +1 -0
- package/dist/entities/{question-sql-dataset-config.entity.js → dashboard-question-sql-dataset-config.entity.js} +19 -19
- package/dist/entities/dashboard-question-sql-dataset-config.entity.js.map +1 -0
- package/dist/entities/dashboard-question.entity.d.ts +16 -0
- package/dist/entities/dashboard-question.entity.d.ts.map +1 -0
- package/dist/entities/{question.entity.js → dashboard-question.entity.js} +23 -19
- package/dist/entities/dashboard-question.entity.js.map +1 -0
- package/dist/entities/dashboard.entity.d.ts +2 -2
- package/dist/entities/dashboard.entity.d.ts.map +1 -1
- package/dist/entities/dashboard.entity.js +3 -3
- package/dist/entities/dashboard.entity.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +21 -2
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-publisher-database.service.d.ts +11 -0
- package/dist/jobs/database/trigger-mcp-client-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/trigger-mcp-client-publisher-database.service.js +39 -0
- package/dist/jobs/database/trigger-mcp-client-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/trigger-mcp-client-queue-options.d.ts +8 -0
- package/dist/jobs/database/trigger-mcp-client-queue-options.d.ts.map +1 -0
- package/dist/jobs/database/trigger-mcp-client-queue-options.js +10 -0
- package/dist/jobs/database/trigger-mcp-client-queue-options.js.map +1 -0
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts +16 -0
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +91 -0
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -0
- package/dist/repository/dashboard.repository.d.ts +1 -3
- package/dist/repository/dashboard.repository.d.ts.map +1 -1
- package/dist/repository/dashboard.repository.js +42 -37
- package/dist/repository/dashboard.repository.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +88 -131
- package/dist/services/ai-interaction.service.d.ts +13 -2
- package/dist/services/ai-interaction.service.d.ts.map +1 -1
- package/dist/services/ai-interaction.service.js +84 -19
- package/dist/services/ai-interaction.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +8 -6
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/{question-sql-dataset-config.service.d.ts → dashboard-question-sql-dataset-config.service.d.ts} +5 -5
- package/dist/services/dashboard-question-sql-dataset-config.service.d.ts.map +1 -0
- package/dist/services/{question-sql-dataset-config.service.js → dashboard-question-sql-dataset-config.service.js} +9 -9
- package/dist/services/dashboard-question-sql-dataset-config.service.js.map +1 -0
- package/dist/services/{question.service.d.ts → dashboard-question.service.d.ts} +5 -5
- package/dist/services/dashboard-question.service.d.ts.map +1 -0
- package/dist/services/{question.service.js → dashboard-question.service.js} +10 -10
- package/dist/services/dashboard-question.service.js.map +1 -0
- package/dist/services/field-metadata.service.d.ts.map +1 -1
- package/dist/services/field-metadata.service.js +1 -0
- package/dist/services/field-metadata.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts +3 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +22 -0
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/list-of-values.service.d.ts +1 -0
- package/dist/services/list-of-values.service.d.ts.map +1 -1
- package/dist/services/list-of-values.service.js +3 -0
- package/dist/services/list-of-values.service.js.map +1 -1
- package/dist/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.d.ts +9 -0
- package/dist/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.d.ts.map +1 -0
- package/dist/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.js +40 -0
- package/dist/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.js.map +1 -0
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.d.ts +11 -0
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.d.ts.map +1 -0
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.js +46 -0
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.js.map +1 -0
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts +2 -2
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts.map +1 -1
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js +11 -7
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js.map +1 -1
- package/dist/services/question-data-providers/helpers.d.ts +5 -2
- package/dist/services/question-data-providers/helpers.d.ts.map +1 -1
- package/dist/services/question-data-providers/helpers.js +18 -3
- package/dist/services/question-data-providers/helpers.js.map +1 -1
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts +2 -2
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts.map +1 -1
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js +10 -4
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js.map +1 -1
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts +2 -2
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts.map +1 -1
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js +11 -3
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js.map +1 -1
- package/dist/services/queues/publisher-factory.service.js +1 -1
- package/dist/services/queues/publisher-factory.service.js.map +1 -1
- package/dist/services/scheduled-jobs/scheduled-job.interface.d.ts +1 -1
- package/dist/services/scheduled-jobs/scheduled-job.interface.d.ts.map +1 -1
- package/dist/services/scheduled-jobs/scheduled-job.interface.js.map +1 -1
- package/dist/services/scheduled-jobs/scheduler.service.js +1 -6
- package/dist/services/scheduled-jobs/scheduler.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +56 -39
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/{question-sql-dataset-config.subscriber.d.ts → dashboard-question-sql-dataset-config.subscriber.d.ts} +6 -6
- package/dist/subscribers/dashboard-question-sql-dataset-config.subscriber.d.ts.map +1 -0
- package/dist/subscribers/{question-sql-dataset-config.subscriber.js → dashboard-question-sql-dataset-config.subscriber.js} +10 -10
- package/dist/subscribers/dashboard-question-sql-dataset-config.subscriber.js.map +1 -0
- package/dist/subscribers/{question.subscriber.d.ts → dashboard-question.subscriber.d.ts} +7 -7
- package/dist/subscribers/dashboard-question.subscriber.d.ts.map +1 -0
- package/dist/subscribers/{question.subscriber.js → dashboard-question.subscriber.js} +8 -8
- package/dist/subscribers/dashboard-question.subscriber.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/controllers/ai-interaction.controller.ts +14 -2
- package/src/controllers/{question-sql-dataset-config.controller.ts → dashboard-question-sql-dataset-config.controller.ts} +10 -10
- package/src/controllers/{question.controller.ts → dashboard-question.controller.ts} +10 -10
- package/src/dtos/{create-question-sql-dataset-config.dto.ts → create-dashboard-question-sql-dataset-config.dto.ts} +1 -1
- package/src/dtos/{create-question.dto.ts → create-dashboard-question.dto.ts} +9 -4
- package/src/dtos/create-dashboard.dto.ts +3 -3
- package/src/dtos/{update-question-sql-dataset-config.dto.ts → update-dashboard-question-sql-dataset-config.dto.ts} +1 -1
- package/src/dtos/{update-question.dto.ts → update-dashboard-question.dto.ts} +9 -4
- package/src/dtos/update-dashboard.dto.ts +3 -3
- package/src/entities/{question-sql-dataset-config.entity.ts → dashboard-question-sql-dataset-config.entity.ts} +5 -5
- package/src/entities/{question.entity.ts → dashboard-question.entity.ts} +8 -5
- package/src/entities/dashboard.entity.ts +3 -3
- package/src/index.ts +7 -4
- package/src/interfaces.ts +24 -2
- package/src/jobs/database/trigger-mcp-client-publisher-database.service.ts +22 -0
- package/src/jobs/database/trigger-mcp-client-queue-options.ts +9 -0
- package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +89 -0
- package/src/repository/dashboard.repository.ts +54 -53
- package/src/seeders/seed-data/solid-core-metadata.json +88 -131
- package/src/services/ai-interaction.service.ts +117 -33
- package/src/services/chatter-message.service.ts +12 -6
- package/src/services/{question-sql-dataset-config.service.ts → dashboard-question-sql-dataset-config.service.ts} +5 -5
- package/src/services/{question.service.ts → dashboard-question.service.ts} +6 -6
- package/src/services/field-metadata.service.ts +1 -0
- package/src/services/import-transaction.service.ts +29 -1
- package/src/services/list-of-values.service.ts +5 -0
- package/src/services/mcp-tool-response-handlers/mcp-tool-response-handler-factory.service.ts +36 -0
- package/src/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.ts +52 -0
- package/src/services/module-metadata.service.ts +0 -5
- package/src/services/question-data-providers/chartjs-sql-data-provider.service.ts +14 -10
- package/src/services/question-data-providers/helpers.ts +23 -4
- package/src/services/question-data-providers/prime-react-datatable-sql-data-provider.service.ts +12 -6
- package/src/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.ts +14 -5
- package/src/services/question-data-providers/test.sql +1 -0
- package/src/services/queues/publisher-factory.service.ts +1 -1
- package/src/services/scheduled-jobs/scheduled-job.interface.ts +1 -1
- package/src/services/scheduled-jobs/scheduler.service.ts +6 -6
- package/src/solid-core.module.ts +59 -39
- package/src/subscribers/{question-sql-dataset-config.subscriber.ts → dashboard-question-sql-dataset-config.subscriber.ts} +8 -10
- package/src/subscribers/{question.subscriber.ts → dashboard-question.subscriber.ts} +7 -10
- package/dist/controllers/question-sql-dataset-config.controller.d.ts +0 -43
- package/dist/controllers/question-sql-dataset-config.controller.d.ts.map +0 -1
- package/dist/controllers/question-sql-dataset-config.controller.js.map +0 -1
- package/dist/controllers/question.controller.d.ts +0 -45
- package/dist/controllers/question.controller.d.ts.map +0 -1
- package/dist/controllers/question.controller.js.map +0 -1
- package/dist/dtos/create-question-sql-dataset-config.dto.d.ts.map +0 -1
- package/dist/dtos/create-question-sql-dataset-config.dto.js.map +0 -1
- package/dist/dtos/create-question.dto.d.ts +0 -16
- package/dist/dtos/create-question.dto.d.ts.map +0 -1
- package/dist/dtos/create-question.dto.js.map +0 -1
- package/dist/dtos/update-question-sql-dataset-config.dto.d.ts.map +0 -1
- package/dist/dtos/update-question-sql-dataset-config.dto.js.map +0 -1
- package/dist/dtos/update-question.dto.d.ts +0 -17
- package/dist/dtos/update-question.dto.d.ts.map +0 -1
- package/dist/dtos/update-question.dto.js.map +0 -1
- package/dist/entities/question-sql-dataset-config.entity.d.ts +0 -13
- package/dist/entities/question-sql-dataset-config.entity.d.ts.map +0 -1
- package/dist/entities/question-sql-dataset-config.entity.js.map +0 -1
- package/dist/entities/question.entity.d.ts +0 -15
- package/dist/entities/question.entity.d.ts.map +0 -1
- package/dist/entities/question.entity.js.map +0 -1
- package/dist/services/question-sql-dataset-config.service.d.ts.map +0 -1
- package/dist/services/question-sql-dataset-config.service.js.map +0 -1
- package/dist/services/question.service.d.ts.map +0 -1
- package/dist/services/question.service.js.map +0 -1
- package/dist/subscribers/question-sql-dataset-config.subscriber.d.ts.map +0 -1
- package/dist/subscribers/question-sql-dataset-config.subscriber.js.map +0 -1
- package/dist/subscribers/question.subscriber.d.ts.map +0 -1
- package/dist/subscribers/question.subscriber.js.map +0 -1
- package/src/services/1. Create a context menu option i.py +0 -12
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
1
|
+
import { BadRequestException, Logger, Injectable } from '@nestjs/common';
|
|
2
2
|
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
|
3
3
|
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
4
4
|
import { EntityManager, Repository } from 'typeorm';
|
|
@@ -12,22 +12,16 @@ import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
|
12
12
|
import { spawn } from 'child_process';
|
|
13
13
|
import { AiInteraction } from '../entities/ai-interaction.entity';
|
|
14
14
|
import * as fs from 'fs/promises';
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
response: string;
|
|
21
|
-
model?: string;
|
|
22
|
-
tools_invoked?: string[];
|
|
23
|
-
tool_calls?: any[];
|
|
24
|
-
duration_ms?: number;
|
|
25
|
-
errors?: string[];
|
|
26
|
-
trace?: string[];
|
|
27
|
-
}
|
|
15
|
+
import { McpResponse, TriggerMcpClientOptions } from 'src/interfaces';
|
|
16
|
+
import { PublisherFactory } from './queues/publisher-factory.service';
|
|
17
|
+
import { RequestContextService } from './request-context.service';
|
|
18
|
+
import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
|
|
19
|
+
import { McpToolResponseHandlerFactory } from './mcp-tool-response-handlers/mcp-tool-response-handler-factory.service';
|
|
28
20
|
|
|
29
21
|
@Injectable()
|
|
30
22
|
export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
23
|
+
private readonly logger = new Logger(AiInteractionService.name);
|
|
24
|
+
|
|
31
25
|
constructor(
|
|
32
26
|
readonly modelMetadataService: ModelMetadataService,
|
|
33
27
|
readonly moduleMetadataService: ModuleMetadataService,
|
|
@@ -39,18 +33,46 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
|
39
33
|
readonly entityManager: EntityManager,
|
|
40
34
|
@InjectRepository(AiInteraction, 'default')
|
|
41
35
|
readonly repo: Repository<AiInteraction>,
|
|
42
|
-
readonly moduleRef: ModuleRef
|
|
36
|
+
readonly moduleRef: ModuleRef,
|
|
37
|
+
readonly publisherFactory: PublisherFactory<TriggerMcpClientOptions>,
|
|
38
|
+
readonly requestContextService: RequestContextService,
|
|
39
|
+
readonly mcpToolResponseHandlerFactory: McpToolResponseHandlerFactory,
|
|
43
40
|
|
|
44
41
|
) {
|
|
45
42
|
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'aiInteraction', 'solid-core', moduleRef);
|
|
46
43
|
}
|
|
47
44
|
|
|
45
|
+
async triggerMcpClientJob(prompt: string): Promise<string> {
|
|
46
|
+
const activeUser: ActiveUserData = this.requestContextService.getActiveUser();
|
|
47
|
+
|
|
48
|
+
const aiInteraction = await this.create({
|
|
49
|
+
userId: activeUser.sub,
|
|
50
|
+
threadId: `thread-${activeUser.sub}`,
|
|
51
|
+
role: 'human',
|
|
52
|
+
message: prompt,
|
|
53
|
+
contentType: '',
|
|
54
|
+
errorMessage: '',
|
|
55
|
+
modelUsed: '',
|
|
56
|
+
responseTimeMs: 0,
|
|
57
|
+
metadata: ''
|
|
58
|
+
});
|
|
59
|
+
const m = {
|
|
60
|
+
payload: {
|
|
61
|
+
aiInteractionId: aiInteraction.id,
|
|
62
|
+
},
|
|
63
|
+
parentEntity: 'aiInteraction',
|
|
64
|
+
parentEntityId: aiInteraction.id,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return await this.publisherFactory.publish(m, 'TriggerMcpClientPublisher');
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
/**
|
|
49
71
|
* Runs the Python MCP client with a prompt and returns the parsed JSON embedded in the 'response'.
|
|
50
72
|
* @param prompt - The question or instruction to send to the MCP client.
|
|
51
73
|
* @returns The parsed object inside the 'response' field of the JSON output.
|
|
52
74
|
*/
|
|
53
|
-
async runMcpPrompt(prompt: string): Promise<
|
|
75
|
+
async runMcpPrompt(prompt: string): Promise<McpResponse> {
|
|
54
76
|
const pythonExecutable = process.env.MCP_PYTHON_EXECUTABLE;
|
|
55
77
|
const mcpClient = process.env.MCP_CLIENT;
|
|
56
78
|
|
|
@@ -78,9 +100,12 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
|
78
100
|
throw new BadRequestException(`Invalid MCP executable or client path: ${err.message}`);
|
|
79
101
|
}
|
|
80
102
|
|
|
103
|
+
// TODO: Refactor to use the command.service.ts instead...
|
|
81
104
|
return new Promise((resolve, reject) => {
|
|
105
|
+
this.logger.log(`Attempting to run command:`)
|
|
106
|
+
this.logger.log(`${pythonExecutable} ${mcpClient} "${prompt}"`);
|
|
82
107
|
|
|
83
|
-
const python = spawn(pythonExecutable, [mcpClient, prompt]);
|
|
108
|
+
const python = spawn(pythonExecutable, [mcpClient, `"${prompt}"`]);
|
|
84
109
|
|
|
85
110
|
let stdout = '';
|
|
86
111
|
let stderr = '';
|
|
@@ -94,34 +119,93 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
|
94
119
|
});
|
|
95
120
|
|
|
96
121
|
python.on('close', (code) => {
|
|
122
|
+
this.logger.log(`Python script exited with code ${code}`);
|
|
123
|
+
|
|
97
124
|
if (code !== 0) {
|
|
98
|
-
|
|
125
|
+
this.logger.error(`Python script exited with a non-zero exit code: ${stderr}`);
|
|
126
|
+
return reject(new Error(`Python script exited with a non-zero exit code: ${stderr}`));
|
|
99
127
|
}
|
|
100
128
|
|
|
101
129
|
try {
|
|
130
|
+
this.logger.log(`Python script exited with zero exit code: ${stdout}`);
|
|
102
131
|
const raw: McpResponse = JSON.parse(stdout);
|
|
103
132
|
|
|
104
|
-
if (!raw.success) {
|
|
105
|
-
|
|
106
|
-
}
|
|
133
|
+
// if (!raw.success) {
|
|
134
|
+
// return reject(new Error(`MCP error: ${raw.errors?.join(', ')}`));
|
|
135
|
+
// }
|
|
136
|
+
// let cleaned = raw.response.trim();
|
|
107
137
|
|
|
108
|
-
|
|
138
|
+
// Don't need to re-parse this...
|
|
139
|
+
// const parsed = JSON.parse(cleaned);
|
|
140
|
+
// resolve(cleaned);
|
|
109
141
|
|
|
110
|
-
|
|
111
|
-
if (cleaned.startsWith('```json')) {
|
|
112
|
-
cleaned = cleaned.replace(/^```json/, '').trim();
|
|
113
|
-
}
|
|
114
|
-
if (cleaned.endsWith('```')) {
|
|
115
|
-
cleaned = cleaned.replace(/```$/, '').trim();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const parsed = JSON.parse(cleaned);
|
|
119
|
-
resolve(parsed);
|
|
142
|
+
resolve(raw);
|
|
120
143
|
} catch (err: any) {
|
|
121
|
-
reject(new Error(`
|
|
144
|
+
reject(new Error(`Mcp Invocation Failed: ${err.message}`));
|
|
122
145
|
}
|
|
123
146
|
});
|
|
124
147
|
});
|
|
125
148
|
}
|
|
126
149
|
|
|
150
|
+
cleanResponse(response: string) {
|
|
151
|
+
this.logger.log(`mcp server response is: ${response}`);
|
|
152
|
+
|
|
153
|
+
// Remove markdown-style code block wrapper
|
|
154
|
+
if (response.startsWith('```json')) {
|
|
155
|
+
response = response.replace(/^```json/, '').trim();
|
|
156
|
+
}
|
|
157
|
+
if (response.endsWith('```')) {
|
|
158
|
+
response = response.replace(/```$/, '').trim();
|
|
159
|
+
}
|
|
160
|
+
this.logger.log(`mcp server response after removing doc tags is: ${response}`);
|
|
161
|
+
|
|
162
|
+
return response;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async applySolidAiInteraction(id: number) {
|
|
166
|
+
// Fetch the aiInteraction
|
|
167
|
+
const aiInteraction = await this.findOne(id, {
|
|
168
|
+
populate: ['user']
|
|
169
|
+
});
|
|
170
|
+
if (!aiInteraction) {
|
|
171
|
+
const m = `Unable to identified the aiInteraction entry that triggered this job... using id: ${id}`
|
|
172
|
+
|
|
173
|
+
// TODO: RESPONSE SHAPE ALERT Check if we want to control the shape of the response....
|
|
174
|
+
throw new Error(m);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// TODO: Validation: Check if JSON.parse(metadata).tools_invoked starts with solid_
|
|
178
|
+
let metadata = {};
|
|
179
|
+
try {
|
|
180
|
+
metadata = JSON.parse(aiInteraction.metadata);
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
// TODO: RESPONSE SHAPE ALERT Check if we want to control the shape of the response....
|
|
184
|
+
throw new Error(e);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const toolsInvoked = metadata['tools_invoked'];
|
|
188
|
+
if (!toolsInvoked) {
|
|
189
|
+
// TODO: RESPONSE SHAPE ALERT Check if we want to control the shape of the response....
|
|
190
|
+
throw new Error('Unable to resolve a solid_ command that was used to come up with this response.');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// TODO: OPTIMISATION for chained tool invocation, for now we are assuming only 1 tool was used.
|
|
194
|
+
const toolInvoked = toolsInvoked[0];
|
|
195
|
+
|
|
196
|
+
// TODO: use the toolInvoked to identify a service using some convention.
|
|
197
|
+
// TODO: Eg. if toolInvoked is solid_create_module <> SolidCreateModuleMcpToolHandler ... create a factory class to do this mapping and identify the relevant provider.
|
|
198
|
+
const mcpToolHandler = this.mcpToolResponseHandlerFactory.getInstance(toolInvoked);
|
|
199
|
+
if (!mcpToolHandler) {
|
|
200
|
+
// TODO: RESPONSE SHAPE ALERT Check if we want to control the shape of the response....
|
|
201
|
+
throw new Error('Unable to resolve a mcp tool handler.');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const handlerApplicationResponse = await mcpToolHandler.apply(aiInteraction);
|
|
205
|
+
|
|
206
|
+
// TODO: This provider to implement an interface - IMcpToolResponseHandler ... apply(aiInteraction: AiInteraction)
|
|
207
|
+
// throw new Error('Method not implemented.');
|
|
208
|
+
|
|
209
|
+
return handlerApplicationResponse;
|
|
210
|
+
}
|
|
127
211
|
}
|
|
@@ -125,7 +125,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage>{
|
|
|
125
125
|
|
|
126
126
|
for (const field of auditFields) {
|
|
127
127
|
const fieldValue = entity[field.name];
|
|
128
|
-
if (fieldValue !== undefined && fieldValue !== null) {
|
|
128
|
+
if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
|
|
129
129
|
const messageDetail = new ChatterMessageDetails();
|
|
130
130
|
messageDetail.chatterMessage = savedMessage;
|
|
131
131
|
messageDetail.fieldName = field.name;
|
|
@@ -289,19 +289,25 @@ private formatFieldValueDisplay(field: any, value: any): string {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
private hasValueChanged(newValue: any, oldValue: any): boolean {
|
|
292
|
-
if (
|
|
292
|
+
if (
|
|
293
|
+
(newValue === null || newValue === undefined) &&
|
|
294
|
+
(oldValue === null || oldValue === undefined)
|
|
295
|
+
) {
|
|
293
296
|
return false;
|
|
294
297
|
}
|
|
295
298
|
|
|
296
|
-
if (newValue ===
|
|
299
|
+
if (newValue === oldValue) {
|
|
297
300
|
return false;
|
|
298
301
|
}
|
|
299
302
|
|
|
300
|
-
if (newValue
|
|
301
|
-
return
|
|
303
|
+
if (Array.isArray(newValue) && Array.isArray(oldValue)) {
|
|
304
|
+
return JSON.stringify(newValue) !== JSON.stringify(oldValue);
|
|
302
305
|
}
|
|
303
306
|
|
|
304
|
-
if (
|
|
307
|
+
if (
|
|
308
|
+
typeof newValue === 'object' && newValue !== null &&
|
|
309
|
+
typeof oldValue === 'object' && oldValue !== null
|
|
310
|
+
) {
|
|
305
311
|
return JSON.stringify(newValue) !== JSON.stringify(oldValue);
|
|
306
312
|
}
|
|
307
313
|
|
|
@@ -11,10 +11,10 @@ import { FileService } from 'src/services/file.service';
|
|
|
11
11
|
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { DashboardQuestionSqlDatasetConfig } from '../entities/dashboard-question-sql-dataset-config.entity';
|
|
15
15
|
|
|
16
16
|
@Injectable()
|
|
17
|
-
export class
|
|
17
|
+
export class DashboardQuestionSqlDatasetConfigService extends CRUDService<DashboardQuestionSqlDatasetConfig>{
|
|
18
18
|
constructor(
|
|
19
19
|
readonly modelMetadataService: ModelMetadataService,
|
|
20
20
|
readonly moduleMetadataService: ModuleMetadataService,
|
|
@@ -24,11 +24,11 @@ export class QuestionSqlDatasetConfigService extends CRUDService<QuestionSqlData
|
|
|
24
24
|
readonly crudHelperService: CrudHelperService,
|
|
25
25
|
@InjectEntityManager()
|
|
26
26
|
readonly entityManager: EntityManager,
|
|
27
|
-
@InjectRepository(
|
|
28
|
-
readonly repo: Repository<
|
|
27
|
+
@InjectRepository(DashboardQuestionSqlDatasetConfig, 'default')
|
|
28
|
+
readonly repo: Repository<DashboardQuestionSqlDatasetConfig>,
|
|
29
29
|
readonly moduleRef: ModuleRef
|
|
30
30
|
|
|
31
31
|
) {
|
|
32
|
-
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, '
|
|
32
|
+
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, 'dashboardQuestionSqlDatasetConfig', 'solid-core', moduleRef);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -13,7 +13,7 @@ import { ModuleMetadataService } from 'src/services/module-metadata.service';
|
|
|
13
13
|
|
|
14
14
|
import { DashboardVariable } from 'src/entities/dashboard-variable.entity';
|
|
15
15
|
import { SolidRegistry } from 'src/helpers/solid-registry';
|
|
16
|
-
import {
|
|
16
|
+
import { DashboardQuestion } from '../entities/dashboard-question.entity';
|
|
17
17
|
import { SqlExpression, SqlExpressionOperator } from './question-data-providers/chartjs-sql-data-provider.service';
|
|
18
18
|
import { DashboardService } from './dashboard.service';
|
|
19
19
|
import { Dashboard } from 'src/entities/dashboard.entity';
|
|
@@ -28,7 +28,7 @@ const PRIME_REACT_METER_GROUP_SQL_DATA_PROVIDER_NAME = 'PrimeReactMeterGroupSqlD
|
|
|
28
28
|
const PRIME_REACT_DATATABLE_SQL_DATA_PROVIDER_NAME = 'PrimeReactDatatableSqlDataProvider';
|
|
29
29
|
|
|
30
30
|
@Injectable()
|
|
31
|
-
export class
|
|
31
|
+
export class DashboardQuestionService extends CRUDService<DashboardQuestion> {
|
|
32
32
|
private readonly logger = new Logger(this.constructor.name);
|
|
33
33
|
constructor(
|
|
34
34
|
readonly modelMetadataService: ModelMetadataService,
|
|
@@ -39,12 +39,12 @@ export class QuestionService extends CRUDService<Question> {
|
|
|
39
39
|
readonly crudHelperService: CrudHelperService,
|
|
40
40
|
@InjectEntityManager()
|
|
41
41
|
readonly entityManager: EntityManager,
|
|
42
|
-
@InjectRepository(
|
|
43
|
-
readonly repo: Repository<
|
|
42
|
+
@InjectRepository(DashboardQuestion, 'default')
|
|
43
|
+
readonly repo: Repository<DashboardQuestion>,
|
|
44
44
|
readonly moduleRef: ModuleRef,
|
|
45
45
|
readonly solidRegistry: SolidRegistry, // Assuming solidRegistry is injected for data providers
|
|
46
46
|
) {
|
|
47
|
-
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, '
|
|
47
|
+
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'dashboardQuestion', 'solid-core', moduleRef);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// Get the data for a specific question
|
|
@@ -100,7 +100,7 @@ export class QuestionService extends CRUDService<Question> {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
private async loadQuestion(id: number) {
|
|
103
|
-
const repo = this.entityManager.getRepository(
|
|
103
|
+
const repo = this.entityManager.getRepository(DashboardQuestion);
|
|
104
104
|
|
|
105
105
|
// Load the dashboard record using the field
|
|
106
106
|
const question = await repo.findOne({
|
|
@@ -496,7 +496,7 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
496
496
|
};
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
-
|
|
499
|
+
async createErrorLogEntry(importTransaction: ImportTransaction, record: Record<string, any>, error: any) {
|
|
500
500
|
const importTransactionRepo = this.entityManager.getRepository(ImportTransactionErrorLog);
|
|
501
501
|
// Create a new ImportTransactionErrorLog entry
|
|
502
502
|
const rowNumber = uuidv4(); // Generate a unique row number or use page.rowNumber if available
|
|
@@ -574,11 +574,39 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
574
574
|
return this.populateDtoForNumber(dtoRecord, fieldMetadata, record, key);
|
|
575
575
|
case SolidFieldType.boolean:
|
|
576
576
|
return this.populateDtoForBoolean(dtoRecord, fieldMetadata, record, key);
|
|
577
|
+
case SolidFieldType.selectionStatic:
|
|
578
|
+
case SolidFieldType.selectionDynamic:
|
|
579
|
+
return this.populateDtoForSelectionValues(dtoRecord, fieldMetadata, record, key);
|
|
577
580
|
default:
|
|
578
581
|
dtoRecord[fieldMetadata.name] = record[key];
|
|
579
582
|
return dtoRecord;
|
|
580
583
|
}
|
|
581
584
|
}
|
|
585
|
+
|
|
586
|
+
private populateDtoForSelectionValues(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
587
|
+
const rawValue = record[key];
|
|
588
|
+
|
|
589
|
+
if (rawValue == null) {
|
|
590
|
+
dtoRecord[fieldMetadata.name] = null;
|
|
591
|
+
return dtoRecord;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const isMultipleSelection = fieldMetadata.isMultiSelect;
|
|
595
|
+
|
|
596
|
+
if (isMultipleSelection) {
|
|
597
|
+
const selectionValues = String(rawValue)
|
|
598
|
+
.split(',')
|
|
599
|
+
.map(value => value.trim())
|
|
600
|
+
.filter(value => value !== '');
|
|
601
|
+
|
|
602
|
+
dtoRecord[fieldMetadata.name] = JSON.stringify(selectionValues);
|
|
603
|
+
} else {
|
|
604
|
+
dtoRecord[fieldMetadata.name] = rawValue; // Single select: assign directly
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return dtoRecord;
|
|
608
|
+
}
|
|
609
|
+
|
|
582
610
|
|
|
583
611
|
private populateDtoForBoolean(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
584
612
|
const booleanValue = Boolean(record[key]);
|
|
@@ -39,6 +39,11 @@ export class ListOfValuesService extends CRUDService<ListOfValues> {
|
|
|
39
39
|
},
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
async findAll(): Promise<ListOfValues[]> {
|
|
44
|
+
return await this.repo.find();
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
async upsert(updateListOfValuesDto: any) {
|
|
43
48
|
// First check if module already exists using name
|
|
44
49
|
const existingListOfValue = await this.repo.findOne({
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
|
|
4
|
+
import { QueueMessage, QueuePublisher } from 'src/interfaces/mq';
|
|
5
|
+
import { classify } from '@angular-devkit/core/src/utils/strings';
|
|
6
|
+
import { SolidIntrospectService } from '../solid-introspect.service';
|
|
7
|
+
import { IMcpToolResponseHandler } from 'src/interfaces';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class McpToolResponseHandlerFactory {
|
|
12
|
+
private readonly logger = new Logger(McpToolResponseHandlerFactory.name);
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
private readonly solidIntrospectionService: SolidIntrospectService
|
|
16
|
+
) {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getInstance(toolInvoked: string): IMcpToolResponseHandler {
|
|
20
|
+
toolInvoked = classify(toolInvoked);
|
|
21
|
+
|
|
22
|
+
let resolvedHandlerName = `${toolInvoked}McpToolResponseHandler`;
|
|
23
|
+
|
|
24
|
+
// Register all ISolidDatabaseModules implementations
|
|
25
|
+
let actualHandler = this.solidIntrospectionService.getProvider(resolvedHandlerName);
|
|
26
|
+
if (!actualHandler) {
|
|
27
|
+
throw new Error(`Unable to locate mcp tool handler with name ${resolvedHandlerName}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// type safe
|
|
31
|
+
const actualHandlerInstance: IMcpToolResponseHandler = actualHandler.instance;
|
|
32
|
+
this.logger.error(`Resolved mcp tool response handler with name ${actualHandler.name}`);
|
|
33
|
+
|
|
34
|
+
return actualHandlerInstance;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { IMcpToolResponseHandler } from "../../interfaces";
|
|
3
|
+
import { AiInteraction } from "src/entities/ai-interaction.entity";
|
|
4
|
+
import { ModuleMetadataService } from "../module-metadata.service";
|
|
5
|
+
import { CreateModuleMetadataDto } from "src/dtos/create-module-metadata.dto";
|
|
6
|
+
import { SolidRegistry } from "src/helpers/solid-registry";
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class SolidCreateModuleMcpToolResponseHandler implements IMcpToolResponseHandler {
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly moduleMetadataService: ModuleMetadataService,
|
|
13
|
+
private readonly solidRegistry: SolidRegistry,
|
|
14
|
+
|
|
15
|
+
) {
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async apply(aiInteraction: AiInteraction) {
|
|
19
|
+
const aiResponse = JSON.parse(aiInteraction.message);
|
|
20
|
+
|
|
21
|
+
const moduleMetadata = aiResponse['moduleMetadata'];
|
|
22
|
+
|
|
23
|
+
// TODO: Validate if another module with same name exists, if it does then raise an error...
|
|
24
|
+
|
|
25
|
+
const createDto: CreateModuleMetadataDto = {
|
|
26
|
+
defaultDataSource: 'default',
|
|
27
|
+
description: moduleMetadata['description'],
|
|
28
|
+
displayName: moduleMetadata['displayName'],
|
|
29
|
+
isSystem: false,
|
|
30
|
+
menuIconUrl: '',
|
|
31
|
+
models: [],
|
|
32
|
+
name: moduleMetadata['name'],
|
|
33
|
+
menuSequenceNumber: 1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// This creates the module-metadata.json file....
|
|
37
|
+
const moduleObj = await this.moduleMetadataService.create(createDto);
|
|
38
|
+
|
|
39
|
+
const seeder = this.solidRegistry.getSeeders().filter((seeder) => seeder.name === 'ModuleMetadataSeederService').map((seeder) => seeder.instance).pop();
|
|
40
|
+
|
|
41
|
+
// Now we need to run solid seed & then solid refresh-model --name <module-name>
|
|
42
|
+
await this.moduleMetadataService.generateCode({ moduleId: moduleObj.id });
|
|
43
|
+
|
|
44
|
+
// solid seed ... this has to be run after reboot from the UI...
|
|
45
|
+
// await new Promise(resolve => setTimeout(resolve, 1000));
|
|
46
|
+
// await seeder.seed();
|
|
47
|
+
|
|
48
|
+
// TODO: decide on some shape to return hre...
|
|
49
|
+
return {}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}
|
|
@@ -213,7 +213,6 @@ export class ModuleMetadataService {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
|
|
217
216
|
async updateInDB(manager: EntityManager, id: number, updateModuleMetadataDto: UpdateModuleMetadataDto, files: Express.Multer.File[] = []) {
|
|
218
217
|
|
|
219
218
|
const module = await this.moduleMetadataRepo.preload({
|
|
@@ -286,7 +285,6 @@ export class ModuleMetadataService {
|
|
|
286
285
|
}
|
|
287
286
|
}
|
|
288
287
|
|
|
289
|
-
|
|
290
288
|
async upsert(updateModuleMetadataDto: UpdateModuleMetadataDto) {
|
|
291
289
|
this.logger.log(`Module Upsert called for : ${updateModuleMetadataDto.name}`);
|
|
292
290
|
// First check if module already exists using name
|
|
@@ -308,9 +306,6 @@ export class ModuleMetadataService {
|
|
|
308
306
|
}
|
|
309
307
|
}
|
|
310
308
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
309
|
async removeByName(name: string) {
|
|
315
310
|
const entity = await this.findOneByUserKey(name);
|
|
316
311
|
if (entity) {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { Injectable } from "@nestjs/common";
|
|
1
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
2
2
|
import { DashboardQuestionDataProvider } from "src/decorators/dashboard-question-data-provider.decorator";
|
|
3
|
-
import {
|
|
3
|
+
import { DashboardQuestion } from "src/entities/dashboard-question.entity";
|
|
4
4
|
import { IDashboardQuestionDataProvider } from "src/interfaces";
|
|
5
5
|
import { EntityManager } from "typeorm";
|
|
6
6
|
import { SqlExpressionResolverService } from "../sql-expression-resolver.service";
|
|
7
|
-
import {
|
|
8
|
-
import { getLabels } from "./helpers";
|
|
7
|
+
import { getKpi, getLabels } from "./helpers";
|
|
9
8
|
|
|
10
9
|
export interface QuestionSqlDataProviderContext {
|
|
11
10
|
// questionSqlDatasetConfig: QuestionSqlDatasetConfig;
|
|
@@ -50,7 +49,7 @@ export class ChartJsSqlDataProvider implements IDashboardQuestionDataProvider<Qu
|
|
|
50
49
|
return "ChartJsSqlDataProvider";
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
async getData(question:
|
|
52
|
+
async getData(question: DashboardQuestion, expressions?: SqlExpression[], context?: QuestionSqlDataProviderContext): Promise<any> {
|
|
54
53
|
// TODO: put some validation to check if the results of each SQL in each dataset returns the same number of rows
|
|
55
54
|
|
|
56
55
|
// This is what we have to return.
|
|
@@ -80,7 +79,8 @@ export class ChartJsSqlDataProvider implements IDashboardQuestionDataProvider<Qu
|
|
|
80
79
|
let datasetIdx = 0;
|
|
81
80
|
const datasets = [];
|
|
82
81
|
|
|
83
|
-
const labels: string[] = await getLabels(question, this.entityManager);
|
|
82
|
+
const labels: string[] = await getLabels(question, expressions, this.entityManager, this.sqlExpressionResolver);
|
|
83
|
+
const kpi: string = await getKpi(question, expressions, this.entityManager, this.sqlExpressionResolver);
|
|
84
84
|
|
|
85
85
|
// const question = context.question;
|
|
86
86
|
for (const questionSqlDatasetConfig of question.questionSqlDatasetConfigs) {
|
|
@@ -91,8 +91,8 @@ export class ChartJsSqlDataProvider implements IDashboardQuestionDataProvider<Qu
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
const sqlReplacementResult = this.sqlExpressionResolver.resolveSqlWithExpressions(sql, expressions || []);
|
|
94
|
-
this.logger.
|
|
95
|
-
this.logger.
|
|
94
|
+
this.logger.debug(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is query=[${sqlReplacementResult.rawSql}]`);
|
|
95
|
+
this.logger.debug(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is parameters=[${JSON.stringify(sqlReplacementResult.parameters)}]`);
|
|
96
96
|
const results = await this.entityManager.query(sqlReplacementResult.rawSql, sqlReplacementResult.parameters);
|
|
97
97
|
|
|
98
98
|
// Also for each data set we create the dataset object as is expected by ChartJs.
|
|
@@ -111,8 +111,12 @@ export class ChartJsSqlDataProvider implements IDashboardQuestionDataProvider<Qu
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
return {
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
kpi,
|
|
115
|
+
visualizedAs: question.visualisedAs,
|
|
116
|
+
visualizationData: {
|
|
117
|
+
labels,
|
|
118
|
+
datasets
|
|
119
|
+
}
|
|
116
120
|
};
|
|
117
121
|
|
|
118
122
|
}
|
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DashboardQuestion } from "src/entities/dashboard-question.entity";
|
|
2
2
|
import { EntityManager } from "typeorm";
|
|
3
|
+
import { SqlExpression } from "./chartjs-sql-data-provider.service";
|
|
4
|
+
import { SqlExpressionResolverService } from "../sql-expression-resolver.service";
|
|
3
5
|
|
|
4
|
-
export async function getLabels(question:
|
|
5
|
-
const
|
|
6
|
-
|
|
6
|
+
export async function getLabels(question: DashboardQuestion, expressions: SqlExpression[], entityManager: EntityManager, sqlExpressionResolver: SqlExpressionResolverService): Promise<string[]> {
|
|
7
|
+
const sql = question.labelSql;
|
|
8
|
+
if (!sql) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
const sqlReplacementResult = sqlExpressionResolver.resolveSqlWithExpressions(sql, expressions || []);
|
|
12
|
+
|
|
13
|
+
const labelResults = await entityManager.query(sqlReplacementResult.rawSql, sqlReplacementResult.parameters);
|
|
14
|
+
|
|
7
15
|
// Assuming labelResults has a single row with a 'label' field
|
|
8
16
|
// Map the label results to the labels array
|
|
9
17
|
const labels: string[] = labelResults.map((result: { [x: string]: string; }) => result['label']);
|
|
10
18
|
return labels;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getKpi(question: DashboardQuestion, expressions: SqlExpression[], entityManager: EntityManager, sqlExpressionResolver: SqlExpressionResolverService): Promise<string> {
|
|
22
|
+
const sql = question.kpiSql;
|
|
23
|
+
if (!sql) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
const sqlReplacementResult = sqlExpressionResolver.resolveSqlWithExpressions(sql, expressions || []);
|
|
27
|
+
const result = await entityManager.query(sqlReplacementResult.rawSql, sqlReplacementResult.parameters);
|
|
28
|
+
const kpiResult = result.pop();
|
|
29
|
+
return kpiResult?.kpi || "";
|
|
11
30
|
}
|
package/src/services/question-data-providers/prime-react-datatable-sql-data-provider.service.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Injectable } from "@nestjs/common";
|
|
2
2
|
import { DashboardQuestionDataProvider } from "src/decorators/dashboard-question-data-provider.decorator";
|
|
3
|
-
import {
|
|
3
|
+
import { DashboardQuestion } from "src/entities/dashboard-question.entity";
|
|
4
4
|
import { IDashboardQuestionDataProvider } from "src/interfaces";
|
|
5
5
|
import { EntityManager } from "typeorm";
|
|
6
6
|
import { SqlExpressionResolverService } from "../sql-expression-resolver.service";
|
|
7
7
|
import { Logger } from '@nestjs/common';
|
|
8
8
|
import { SqlExpression } from "./chartjs-sql-data-provider.service";
|
|
9
|
+
import { getKpi } from "./helpers";
|
|
9
10
|
|
|
10
11
|
export interface QuestionSqlDataProviderContext {
|
|
11
12
|
// questionSqlDatasetConfig: QuestionSqlDatasetConfig;
|
|
@@ -28,11 +29,12 @@ export class PrimeReactDatatableSqlDataProvider implements IDashboardQuestionDat
|
|
|
28
29
|
return "PrimeReactDatatableSqlDataProvider";
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
async getData(question:
|
|
32
|
+
async getData(question: DashboardQuestion, expressions?: SqlExpression[], context?: QuestionSqlDataProviderContext): Promise<any> {
|
|
32
33
|
// TODO: put some validation to check if the results of each SQL in each dataset returns the same number of rows
|
|
33
34
|
|
|
34
35
|
// Check the expected response for prime react data tables to understand what is going on here...
|
|
35
36
|
|
|
37
|
+
const kpi: string = await getKpi(question, expressions, this.entityManager, this.sqlExpressionResolver);
|
|
36
38
|
// TODO: Load the set of labels by using a separate field on the question entity.
|
|
37
39
|
const labelSql = question.labelSql;
|
|
38
40
|
const labelResults = await this.entityManager.query(labelSql);
|
|
@@ -59,13 +61,17 @@ export class PrimeReactDatatableSqlDataProvider implements IDashboardQuestionDat
|
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
const sqlReplacementResult = this.sqlExpressionResolver.resolveSqlWithExpressions(sql, expressions || []);
|
|
62
|
-
this.logger.
|
|
63
|
-
this.logger.
|
|
64
|
+
this.logger.debug(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is query=[${sqlReplacementResult.rawSql}]`);
|
|
65
|
+
this.logger.debug(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is parameters=[${JSON.stringify(sqlReplacementResult.parameters)}]`);
|
|
64
66
|
const results = await this.entityManager.query(sqlReplacementResult.rawSql, sqlReplacementResult.parameters);
|
|
65
67
|
|
|
66
68
|
return {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
kpi,
|
|
70
|
+
visualisedAs: question.visualisedAs,
|
|
71
|
+
visualizationData: {
|
|
72
|
+
columns,
|
|
73
|
+
rows: results,
|
|
74
|
+
}
|
|
69
75
|
};
|
|
70
76
|
|
|
71
77
|
}
|