@solidstarters/solid-core 1.2.134 → 1.2.135
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 +45 -0
- package/dist/controllers/ai-interaction.controller.d.ts.map +1 -0
- package/dist/controllers/ai-interaction.controller.js +192 -0
- package/dist/controllers/ai-interaction.controller.js.map +1 -0
- package/dist/controllers/dashboard-variable.controller.d.ts +43 -0
- package/dist/controllers/dashboard-variable.controller.d.ts.map +1 -0
- package/dist/controllers/dashboard-variable.controller.js +179 -0
- package/dist/controllers/dashboard-variable.controller.js.map +1 -0
- package/dist/controllers/dashboard.controller.d.ts +45 -0
- package/dist/controllers/dashboard.controller.d.ts.map +1 -0
- package/dist/controllers/dashboard.controller.js +192 -0
- package/dist/controllers/dashboard.controller.js.map +1 -0
- package/dist/controllers/question-sql-dataset-config.controller.d.ts +43 -0
- package/dist/controllers/question-sql-dataset-config.controller.d.ts.map +1 -0
- package/dist/controllers/question-sql-dataset-config.controller.js +179 -0
- package/dist/controllers/question-sql-dataset-config.controller.js.map +1 -0
- package/dist/controllers/question.controller.d.ts +45 -0
- package/dist/controllers/question.controller.d.ts.map +1 -0
- package/dist/controllers/question.controller.js +194 -0
- package/dist/controllers/question.controller.js.map +1 -0
- package/dist/controllers/test.controller.d.ts.map +1 -1
- package/dist/controllers/test.controller.js.map +1 -1
- package/dist/decorators/dashboard-question-data-provider.decorator.d.ts +3 -0
- package/dist/decorators/dashboard-question-data-provider.decorator.d.ts.map +1 -0
- package/dist/decorators/dashboard-question-data-provider.decorator.js +11 -0
- package/dist/decorators/dashboard-question-data-provider.decorator.js.map +1 -0
- package/dist/decorators/dashboard-selection-provider.decorator.d.ts +3 -0
- package/dist/decorators/dashboard-selection-provider.decorator.d.ts.map +1 -0
- package/dist/decorators/dashboard-selection-provider.decorator.js +11 -0
- package/dist/decorators/dashboard-selection-provider.decorator.js.map +1 -0
- package/dist/dtos/create-ai-interaction.dto.d.ts +14 -0
- package/dist/dtos/create-ai-interaction.dto.d.ts.map +1 -0
- package/dist/dtos/create-ai-interaction.dto.js +90 -0
- package/dist/dtos/create-ai-interaction.dto.js.map +1 -0
- package/dist/dtos/create-dashboard-variable.dto.d.ts +18 -0
- package/dist/dtos/create-dashboard-variable.dto.d.ts.map +1 -0
- package/dist/dtos/create-dashboard-variable.dto.js +97 -0
- package/dist/dtos/create-dashboard-variable.dto.js.map +1 -0
- package/dist/dtos/create-dashboard.dto.d.ts +15 -0
- package/dist/dtos/create-dashboard.dto.d.ts.map +1 -0
- package/dist/dtos/create-dashboard.dto.js +90 -0
- package/dist/dtos/create-dashboard.dto.js.map +1 -0
- package/dist/dtos/create-question-sql-dataset-config.dto.d.ts +12 -0
- package/dist/dtos/create-question-sql-dataset-config.dto.d.ts.map +1 -0
- package/dist/dtos/create-question-sql-dataset-config.dto.js +77 -0
- package/dist/dtos/create-question-sql-dataset-config.dto.js.map +1 -0
- package/dist/dtos/create-question.dto.d.ts +16 -0
- package/dist/dtos/create-question.dto.d.ts.map +1 -0
- package/dist/dtos/create-question.dto.js +99 -0
- package/dist/dtos/create-question.dto.js.map +1 -0
- package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.d.ts +8 -0
- package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.d.ts.map +1 -0
- package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.js +52 -0
- package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.js.map +1 -0
- package/dist/dtos/invoke-ai-prompt.dto.d.ts +4 -0
- package/dist/dtos/invoke-ai-prompt.dto.d.ts.map +1 -0
- package/dist/dtos/invoke-ai-prompt.dto.js +25 -0
- package/dist/dtos/invoke-ai-prompt.dto.js.map +1 -0
- package/dist/dtos/update-ai-interaction.dto.d.ts +15 -0
- package/dist/dtos/update-ai-interaction.dto.d.ts.map +1 -0
- package/dist/dtos/update-ai-interaction.dto.js +96 -0
- package/dist/dtos/update-ai-interaction.dto.js.map +1 -0
- package/dist/dtos/update-dashboard-variable.dto.d.ts +15 -0
- package/dist/dtos/update-dashboard-variable.dto.d.ts.map +1 -0
- package/dist/dtos/update-dashboard-variable.dto.js +95 -0
- package/dist/dtos/update-dashboard-variable.dto.js.map +1 -0
- package/dist/dtos/update-dashboard.dto.d.ts +16 -0
- package/dist/dtos/update-dashboard.dto.d.ts.map +1 -0
- package/dist/dtos/update-dashboard.dto.js +96 -0
- package/dist/dtos/update-dashboard.dto.js.map +1 -0
- package/dist/dtos/update-question-sql-dataset-config.dto.d.ts +13 -0
- package/dist/dtos/update-question-sql-dataset-config.dto.d.ts.map +1 -0
- package/dist/dtos/update-question-sql-dataset-config.dto.js +86 -0
- package/dist/dtos/update-question-sql-dataset-config.dto.js.map +1 -0
- package/dist/dtos/update-question.dto.d.ts +17 -0
- package/dist/dtos/update-question.dto.d.ts.map +1 -0
- package/dist/dtos/update-question.dto.js +106 -0
- package/dist/dtos/update-question.dto.js.map +1 -0
- package/dist/entities/ai-interaction.entity.d.ts +15 -0
- package/dist/entities/ai-interaction.entity.d.ts.map +1 -0
- package/dist/entities/ai-interaction.entity.js +70 -0
- package/dist/entities/ai-interaction.entity.js.map +1 -0
- package/dist/entities/dashboard-variable.entity.d.ts +15 -0
- package/dist/entities/dashboard-variable.entity.d.ts.map +1 -0
- package/dist/entities/dashboard-variable.entity.js +73 -0
- package/dist/entities/dashboard-variable.entity.js.map +1 -0
- package/dist/entities/dashboard.entity.d.ts +12 -0
- package/dist/entities/dashboard.entity.d.ts.map +1 -0
- package/dist/entities/dashboard.entity.js +50 -0
- package/dist/entities/dashboard.entity.js.map +1 -0
- package/dist/entities/question-sql-dataset-config.entity.d.ts +13 -0
- package/dist/entities/question-sql-dataset-config.entity.d.ts.map +1 -0
- package/dist/entities/question-sql-dataset-config.entity.js +60 -0
- package/dist/entities/question-sql-dataset-config.entity.js.map +1 -0
- package/dist/entities/question.entity.d.ts +15 -0
- package/dist/entities/question.entity.d.ts.map +1 -0
- package/dist/entities/question.entity.js +67 -0
- package/dist/entities/question.entity.js.map +1 -0
- package/dist/helpers/solid-registry.d.ts +9 -1
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js +32 -0
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +12 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/mappers/dashboard-mapper.d.ts +6 -0
- package/dist/mappers/dashboard-mapper.d.ts.map +1 -0
- package/dist/mappers/dashboard-mapper.js +60 -0
- package/dist/mappers/dashboard-mapper.js.map +1 -0
- package/dist/repository/dashboard.repository.d.ts +11 -0
- package/dist/repository/dashboard.repository.d.ts.map +1 -0
- package/dist/repository/dashboard.repository.js +93 -0
- package/dist/repository/dashboard.repository.js.map +1 -0
- package/dist/seeders/module-metadata-seeder.service.d.ts +5 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +18 -2
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +4222 -2418
- package/dist/services/ai-interaction.service.d.ts +23 -0
- package/dist/services/ai-interaction.service.d.ts.map +1 -0
- package/dist/services/ai-interaction.service.js +147 -0
- package/dist/services/ai-interaction.service.js.map +1 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.d.ts +12 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.d.ts.map +1 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.js +55 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.js.map +1 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.d.ts +11 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.d.ts.map +1 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.js +45 -0
- package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.js.map +1 -0
- package/dist/services/dashboard-variable.service.d.ts +23 -0
- package/dist/services/dashboard-variable.service.d.ts.map +1 -0
- package/dist/services/dashboard-variable.service.js +57 -0
- package/dist/services/dashboard-variable.service.js.map +1 -0
- package/dist/services/dashboard.service.d.ts +38 -0
- package/dist/services/dashboard.service.d.ts.map +1 -0
- package/dist/services/dashboard.service.js +179 -0
- package/dist/services/dashboard.service.js.map +1 -0
- package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
- package/dist/services/menu-item-metadata.service.js +1 -1
- package/dist/services/menu-item-metadata.service.js.map +1 -1
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts +36 -0
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts.map +1 -0
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js +85 -0
- package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js.map +1 -0
- package/dist/services/question-data-providers/helpers.d.ts +4 -0
- package/dist/services/question-data-providers/helpers.d.ts.map +1 -0
- package/dist/services/question-data-providers/helpers.js +10 -0
- package/dist/services/question-data-providers/helpers.js.map +1 -0
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts +17 -0
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts.map +1 -0
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js +64 -0
- package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js.map +1 -0
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts +19 -0
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts.map +1 -0
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js +84 -0
- package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js.map +1 -0
- package/dist/services/question-sql-dataset-config.service.d.ts +22 -0
- package/dist/services/question-sql-dataset-config.service.d.ts.map +1 -0
- package/dist/services/question-sql-dataset-config.service.js +56 -0
- package/dist/services/question-sql-dataset-config.service.js.map +1 -0
- package/dist/services/question.service.d.ts +29 -0
- package/dist/services/question.service.d.ts.map +1 -0
- package/dist/services/question.service.js +117 -0
- package/dist/services/question.service.js.map +1 -0
- package/dist/services/scheduled-jobs/scheduler.service.d.ts.map +1 -1
- package/dist/services/scheduled-jobs/scheduler.service.js +22 -11
- package/dist/services/scheduled-jobs/scheduler.service.js.map +1 -1
- package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.d.ts +11 -0
- package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.d.ts.map +1 -0
- package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.js +46 -0
- package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.js.map +1 -0
- package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.d.ts +11 -0
- package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.d.ts.map +1 -0
- package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.js +47 -0
- package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.js.map +1 -0
- package/dist/services/solid-introspect.service.d.ts +2 -0
- package/dist/services/solid-introspect.service.d.ts.map +1 -1
- package/dist/services/solid-introspect.service.js +28 -0
- package/dist/services/solid-introspect.service.js.map +1 -1
- package/dist/services/sql-expression-resolver.service.d.ts +9 -0
- package/dist/services/sql-expression-resolver.service.d.ts.map +1 -0
- package/dist/services/sql-expression-resolver.service.js +105 -0
- package/dist/services/sql-expression-resolver.service.js.map +1 -0
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +61 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/dashboard-variable.subscriber.d.ts +16 -0
- package/dist/subscribers/dashboard-variable.subscriber.d.ts.map +1 -0
- package/dist/subscribers/dashboard-variable.subscriber.js +72 -0
- package/dist/subscribers/dashboard-variable.subscriber.js.map +1 -0
- package/dist/subscribers/dashboard.subscriber.d.ts +15 -0
- package/dist/subscribers/dashboard.subscriber.d.ts.map +1 -0
- package/dist/subscribers/dashboard.subscriber.js +56 -0
- package/dist/subscribers/dashboard.subscriber.js.map +1 -0
- package/dist/subscribers/question-sql-dataset-config.subscriber.d.ts +16 -0
- package/dist/subscribers/question-sql-dataset-config.subscriber.d.ts.map +1 -0
- package/dist/subscribers/question-sql-dataset-config.subscriber.js +72 -0
- package/dist/subscribers/question-sql-dataset-config.subscriber.js.map +1 -0
- package/dist/subscribers/question.subscriber.d.ts +16 -0
- package/dist/subscribers/question.subscriber.d.ts.map +1 -0
- package/dist/subscribers/question.subscriber.js +72 -0
- package/dist/subscribers/question.subscriber.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/controllers/ai-interaction.controller.ts +98 -0
- package/src/controllers/dashboard-variable.controller.ts +93 -0
- package/src/controllers/dashboard.controller.ts +99 -0
- package/src/controllers/question-sql-dataset-config.controller.ts +93 -0
- package/src/controllers/question.controller.ts +104 -0
- package/src/controllers/test.controller.ts +1 -2
- package/src/decorators/dashboard-question-data-provider.decorator.ts +7 -0
- package/src/decorators/dashboard-selection-provider.decorator.ts +7 -0
- package/src/dtos/create-ai-interaction.dto.ts +60 -0
- package/src/dtos/create-dashboard-variable.dto.ts +56 -0
- package/src/dtos/create-dashboard.dto.ts +53 -0
- package/src/dtos/create-question-sql-dataset-config.dto.ts +42 -0
- package/src/dtos/create-question.dto.ts +58 -0
- package/src/dtos/dashboard-variable-selection-dynamic-query.dto.ts +29 -0
- package/src/dtos/invoke-ai-prompt.dto.ts +6 -0
- package/src/dtos/update-ai-interaction.dto.ts +65 -0
- package/src/dtos/update-dashboard-variable.dto.ts +54 -0
- package/src/dtos/update-dashboard.dto.ts +57 -0
- package/src/dtos/update-question-sql-dataset-config.dto.ts +49 -0
- package/src/dtos/update-question.dto.ts +63 -0
- package/src/entities/ai-interaction.entity.ts +39 -0
- package/src/entities/dashboard-variable.entity.ts +30 -0
- package/src/entities/dashboard.entity.ts +21 -0
- package/src/entities/question-sql-dataset-config.entity.ts +25 -0
- package/src/entities/question.entity.ts +30 -0
- package/src/helpers/solid-registry.ts +44 -2
- package/src/index.ts +5 -0
- package/src/interfaces.ts +41 -29
- package/src/mappers/dashboard-mapper.ts +52 -0
- package/src/repository/dashboard.repository.ts +100 -0
- package/src/seeders/module-metadata-seeder.service.ts +21 -1
- package/src/seeders/seed-data/solid-core-metadata.json +4225 -2421
- package/src/services/1. Create a context menu option i.py +12 -0
- package/src/services/ai-interaction.service.ts +127 -0
- package/src/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.ts +56 -0
- package/src/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.ts +37 -0
- package/src/services/dashboard-variable.service.ts +36 -0
- package/src/services/dashboard.service.ts +147 -0
- package/src/services/menu-item-metadata.service.ts +2 -1
- package/src/services/question-data-providers/chartjs-sql-data-provider.service.ts +121 -0
- package/src/services/question-data-providers/helpers.ts +11 -0
- package/src/services/question-data-providers/prime-react-datatable-sql-data-provider.service.ts +72 -0
- package/src/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.ts +110 -0
- package/src/services/question-sql-dataset-config.service.ts +34 -0
- package/src/services/question.service.ts +115 -0
- package/src/services/scheduled-jobs/scheduler.service.ts +32 -64
- package/src/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.ts +41 -0
- package/src/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.ts +41 -0
- package/src/services/solid-introspect.service.ts +42 -0
- package/src/services/sql-expression-resolver.service.ts +125 -0
- package/src/solid-core.module.ts +61 -1
- package/src/subscribers/dashboard-variable.subscriber.ts +63 -0
- package/src/subscribers/dashboard.subscriber.ts +43 -0
- package/src/subscribers/question-sql-dataset-config.subscriber.ts +63 -0
- package/src/subscribers/question.subscriber.ts +65 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
1. Create a context menu option i.e Custom Import
|
|
2
|
+
2. On clicking custom import, we will show the wizard i.e (which will show the sample format links)
|
|
3
|
+
2.1 No need to add any description for now
|
|
4
|
+
2.1 For the sample import endpoint, we will create endpoint like import-template/custom/:format in qb controller, which will just return the saved template file from the media-storage-folder.
|
|
5
|
+
3. On clicking continue on step 2, we will show the upload dialog
|
|
6
|
+
4. On clicking upload, we will call the custom import endpoint
|
|
7
|
+
4.1 start-import/custom/sync (body -> file)
|
|
8
|
+
4.2 basis the mime type, we will call the call appropriate method to read the records from the file
|
|
9
|
+
-> refer importFromFileToDB
|
|
10
|
+
4.3 After reading the records, we will have the json for a particular records
|
|
11
|
+
4.4. We will use this json to create the appropriate entities and save the record.
|
|
12
|
+
4.5 You can use entity manager for saving the entity accordingly.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
2
|
+
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
|
3
|
+
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
4
|
+
import { EntityManager, Repository } from 'typeorm';
|
|
5
|
+
|
|
6
|
+
import { CRUDService } from 'src/services/crud.service';
|
|
7
|
+
import { ModelMetadataService } from 'src/services/model-metadata.service';
|
|
8
|
+
import { ModuleMetadataService } from 'src/services/module-metadata.service';
|
|
9
|
+
import { ConfigService } from '@nestjs/config';
|
|
10
|
+
import { FileService } from 'src/services/file.service';
|
|
11
|
+
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
12
|
+
import { spawn } from 'child_process';
|
|
13
|
+
import { AiInteraction } from '../entities/ai-interaction.entity';
|
|
14
|
+
import * as fs from 'fs/promises';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
|
|
17
|
+
interface McpResponse {
|
|
18
|
+
success: boolean;
|
|
19
|
+
request: string;
|
|
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
|
+
}
|
|
28
|
+
|
|
29
|
+
@Injectable()
|
|
30
|
+
export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
31
|
+
constructor(
|
|
32
|
+
readonly modelMetadataService: ModelMetadataService,
|
|
33
|
+
readonly moduleMetadataService: ModuleMetadataService,
|
|
34
|
+
readonly configService: ConfigService,
|
|
35
|
+
readonly fileService: FileService,
|
|
36
|
+
readonly discoveryService: DiscoveryService,
|
|
37
|
+
readonly crudHelperService: CrudHelperService,
|
|
38
|
+
@InjectEntityManager()
|
|
39
|
+
readonly entityManager: EntityManager,
|
|
40
|
+
@InjectRepository(AiInteraction, 'default')
|
|
41
|
+
readonly repo: Repository<AiInteraction>,
|
|
42
|
+
readonly moduleRef: ModuleRef
|
|
43
|
+
|
|
44
|
+
) {
|
|
45
|
+
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'aiInteraction', 'solid-core', moduleRef);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Runs the Python MCP client with a prompt and returns the parsed JSON embedded in the 'response'.
|
|
50
|
+
* @param prompt - The question or instruction to send to the MCP client.
|
|
51
|
+
* @returns The parsed object inside the 'response' field of the JSON output.
|
|
52
|
+
*/
|
|
53
|
+
async runMcpPrompt(prompt: string): Promise<any> {
|
|
54
|
+
const pythonExecutable = process.env.MCP_PYTHON_EXECUTABLE;
|
|
55
|
+
const mcpClient = process.env.MCP_CLIENT;
|
|
56
|
+
|
|
57
|
+
// TODO: We can return an error if the above env variables are not properly setup...
|
|
58
|
+
if (!pythonExecutable || !mcpClient) {
|
|
59
|
+
throw new BadRequestException('SolidX AI MCP python executable or client path not configured.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check if both paths are valid and accessible
|
|
63
|
+
try {
|
|
64
|
+
const [pyStat, clientStat] = await Promise.all([
|
|
65
|
+
fs.stat(pythonExecutable),
|
|
66
|
+
fs.stat(mcpClient),
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
if (!pyStat.isFile()) {
|
|
70
|
+
throw new BadRequestException(`MCP_PYTHON_EXECUTABLE path is not a file: ${pythonExecutable}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!clientStat.isFile()) {
|
|
74
|
+
throw new BadRequestException(`MCP_CLIENT path is not a file: ${mcpClient}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} catch (err: any) {
|
|
78
|
+
throw new BadRequestException(`Invalid MCP executable or client path: ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
|
|
83
|
+
const python = spawn(pythonExecutable, [mcpClient, prompt]);
|
|
84
|
+
|
|
85
|
+
let stdout = '';
|
|
86
|
+
let stderr = '';
|
|
87
|
+
|
|
88
|
+
python.stdout.on('data', (data) => {
|
|
89
|
+
stdout += data.toString();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
python.stderr.on('data', (data) => {
|
|
93
|
+
stderr += data.toString();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
python.on('close', (code) => {
|
|
97
|
+
if (code !== 0) {
|
|
98
|
+
return reject(new Error(`Python script exited with code ${code}: ${stderr}`));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const raw: McpResponse = JSON.parse(stdout);
|
|
103
|
+
|
|
104
|
+
if (!raw.success) {
|
|
105
|
+
return reject(new Error(`MCP error: ${raw.errors?.join(', ')}`));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let cleaned = raw.response.trim();
|
|
109
|
+
|
|
110
|
+
// Remove markdown-style code block wrapper
|
|
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);
|
|
120
|
+
} catch (err: any) {
|
|
121
|
+
reject(new Error(`Failed to parse JSON: ${err.message}`));
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { IDashboardVariableSelectionProvider, ISelectionProviderContext, ISelectionProviderValues } from "../../interfaces";
|
|
3
|
+
// import localeCodes from 'locale-codes';
|
|
4
|
+
import { EntityManager } from "typeorm";
|
|
5
|
+
import { DashboardVariableSelectionProvider } from "src/decorators/dashboard-selection-provider.decorator";
|
|
6
|
+
|
|
7
|
+
@DashboardVariableSelectionProvider()
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class DashboardVariableSQLDynamicProvider implements IDashboardVariableSelectionProvider<ISelectionProviderContext> {
|
|
10
|
+
|
|
11
|
+
constructor(private readonly entityManager: EntityManager) {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
help(): string {
|
|
15
|
+
return "# Get the dashboard variable after executing the SQL query configured for the dashboard variable.\n";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
name(): string {
|
|
19
|
+
return 'DashboardVariableSQLDynamicProvider';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async value(optionValue: string, ctxt: ISelectionProviderContext): Promise<ISelectionProviderValues | any> {
|
|
23
|
+
throw new Error("DashboardVariableSQLDynamicProvider does not support value method. Use values method instead.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
|
|
27
|
+
const { sql, limit, offset } = ctxt as unknown as { sql: string, limit?: number, offset?: number };
|
|
28
|
+
if (!sql) {
|
|
29
|
+
throw new Error("DashboardVariableSQLDynamicProvider requires a SQL query to be provided in the context.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Here you would execute the SQL query against your database
|
|
33
|
+
// For demonstration, let's assume we have a mock database function that executes the SQL query
|
|
34
|
+
const results = await this.entityManager.query(this.appendLimitOffset(sql), [limit, offset]);
|
|
35
|
+
|
|
36
|
+
// Transform the results into the expected format
|
|
37
|
+
return results.map((result: any) => {
|
|
38
|
+
const transformedResult: ISelectionProviderValues = {
|
|
39
|
+
value: result.value, // Assuming the result has a 'value' field
|
|
40
|
+
label: result.label, // Assuming the result has a 'label' field
|
|
41
|
+
// Add any other fields you need to transform here
|
|
42
|
+
};
|
|
43
|
+
return transformedResult;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
appendLimitOffset(sql: string): string {
|
|
48
|
+
// Strip trailing semicolon if present
|
|
49
|
+
const trimmedSql = sql.trim().replace(/;$/, '');
|
|
50
|
+
|
|
51
|
+
// Append the LIMIT/OFFSET
|
|
52
|
+
const finalSql = `${trimmedSql} LIMIT $1 OFFSET $2`; // FIXME This works with PostgreSQL. for mysql use ?. For this we will need to identify the datasource using the model for the particular dashboard variable
|
|
53
|
+
|
|
54
|
+
return finalSql;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { IDashboardVariableSelectionProvider, ISelectionProviderContext, ISelectionProviderValues } from "../../interfaces";
|
|
3
|
+
// import localeCodes from 'locale-codes';
|
|
4
|
+
import { EntityManager } from "typeorm";
|
|
5
|
+
import { DashboardVariableSelectionProvider } from "src/decorators/dashboard-selection-provider.decorator";
|
|
6
|
+
|
|
7
|
+
@DashboardVariableSelectionProvider()
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class DasbhoardVariableTestDynamicProvider implements IDashboardVariableSelectionProvider<ISelectionProviderContext> {
|
|
10
|
+
|
|
11
|
+
constructor(private readonly entityManager: EntityManager) {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
help(): string {
|
|
15
|
+
return "# Get the dashboard variable values.\n";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
name(): string {
|
|
19
|
+
return 'DasbhoardVariableTestDynamicProvider';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async value(optionValue: string, ctxt: ISelectionProviderContext): Promise<ISelectionProviderValues | any> {
|
|
23
|
+
throw new Error("DasbhoardVariableTestDynamicProvider does not support value method. Use values method instead.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
|
|
27
|
+
// Return some dummy data for testing
|
|
28
|
+
const { sql, limit, offset } = ctxt as unknown as { sql: string, limit?: number, offset?: number };
|
|
29
|
+
return [
|
|
30
|
+
{ value: '1', label: 'Option 1' },
|
|
31
|
+
{ value: '2', label: 'Option 2' },
|
|
32
|
+
{ value: '3', label: 'Option 3' },
|
|
33
|
+
{ value: '4', label: 'Option 4' },
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
3
|
+
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
|
|
4
|
+
import { EntityManager, Repository } from 'typeorm';
|
|
5
|
+
|
|
6
|
+
import { ConfigService } from '@nestjs/config';
|
|
7
|
+
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
8
|
+
import { CRUDService } from 'src/services/crud.service';
|
|
9
|
+
import { FileService } from 'src/services/file.service';
|
|
10
|
+
import { ModelMetadataService } from 'src/services/model-metadata.service';
|
|
11
|
+
import { ModuleMetadataService } from 'src/services/module-metadata.service';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import { DashboardVariable } from '../entities/dashboard-variable.entity';
|
|
15
|
+
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class DashboardVariableService extends CRUDService<DashboardVariable> {
|
|
18
|
+
private readonly logger = new Logger(this.constructor.name);
|
|
19
|
+
constructor(
|
|
20
|
+
readonly modelMetadataService: ModelMetadataService,
|
|
21
|
+
readonly moduleMetadataService: ModuleMetadataService,
|
|
22
|
+
readonly configService: ConfigService,
|
|
23
|
+
readonly fileService: FileService,
|
|
24
|
+
readonly discoveryService: DiscoveryService,
|
|
25
|
+
readonly crudHelperService: CrudHelperService,
|
|
26
|
+
@InjectEntityManager()
|
|
27
|
+
readonly entityManager: EntityManager,
|
|
28
|
+
@InjectRepository(DashboardVariable, 'default')
|
|
29
|
+
readonly repo: Repository<DashboardVariable>,
|
|
30
|
+
readonly moduleRef: ModuleRef,
|
|
31
|
+
) {
|
|
32
|
+
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'dashboardVariable', 'solid-core', moduleRef);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
|
2
|
+
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
3
|
+
import { InjectEntityManager } from '@nestjs/typeorm';
|
|
4
|
+
import { EntityManager } from 'typeorm';
|
|
5
|
+
|
|
6
|
+
import { ConfigService } from '@nestjs/config';
|
|
7
|
+
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
8
|
+
import { CRUDService } from 'src/services/crud.service';
|
|
9
|
+
import { FileService } from 'src/services/file.service';
|
|
10
|
+
import { ModelMetadataService } from 'src/services/model-metadata.service';
|
|
11
|
+
import { ModuleMetadataService } from 'src/services/module-metadata.service';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import * as fs from 'fs/promises'; // Use the Promise-based version of fs for async/await
|
|
15
|
+
import { SelectionDynamicSourceType } from 'src/dtos/create-dashboard-variable.dto';
|
|
16
|
+
import { DashboardVariableSelectionDynamicQueryDto } from 'src/dtos/dashboard-variable-selection-dynamic-query.dto';
|
|
17
|
+
import { DashboardVariable } from 'src/entities/dashboard-variable.entity';
|
|
18
|
+
import { ModuleMetadataHelperService } from 'src/helpers/module-metadata-helper.service';
|
|
19
|
+
import { SolidRegistry } from 'src/helpers/solid-registry';
|
|
20
|
+
import { DashboardMapper } from 'src/mappers/dashboard-mapper';
|
|
21
|
+
import { DashboardRepository } from 'src/repository/dashboard.repository';
|
|
22
|
+
import { Dashboard } from '../entities/dashboard.entity';
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
export const SQL_DYNAMIC_PROVIDER_NAME = 'DashboardVariableSQLDynamicProvider';
|
|
26
|
+
@Injectable()
|
|
27
|
+
export class DashboardService extends CRUDService<Dashboard> {
|
|
28
|
+
private readonly logger = new Logger(this.constructor.name);
|
|
29
|
+
constructor(
|
|
30
|
+
readonly modelMetadataService: ModelMetadataService,
|
|
31
|
+
readonly moduleMetadataService: ModuleMetadataService,
|
|
32
|
+
readonly configService: ConfigService,
|
|
33
|
+
readonly fileService: FileService,
|
|
34
|
+
readonly discoveryService: DiscoveryService,
|
|
35
|
+
readonly crudHelperService: CrudHelperService,
|
|
36
|
+
@InjectEntityManager()
|
|
37
|
+
readonly entityManager: EntityManager,
|
|
38
|
+
readonly repo: DashboardRepository, // Assuming you have a DashboardRepository for custom queries
|
|
39
|
+
readonly moduleRef: ModuleRef,
|
|
40
|
+
readonly solidRegistry: SolidRegistry, // Assuming solidRegistry is injected for selection providers
|
|
41
|
+
readonly moduleMetadataHelperService: ModuleMetadataHelperService,
|
|
42
|
+
readonly dashboardMapper: DashboardMapper,
|
|
43
|
+
) {
|
|
44
|
+
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'dashboard', 'solid-core', moduleRef);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async getSelectionDynamicValues(query: DashboardVariableSelectionDynamicQueryDto) {
|
|
48
|
+
// Get the dashboard variable repo
|
|
49
|
+
const dashboardVariable = await this.loadDashboardVariable(query.variableId);
|
|
50
|
+
|
|
51
|
+
// Get the providerName and context for the dashboard variable
|
|
52
|
+
const [providerName, context] = this.getProviderNameAndContext(dashboardVariable, query);
|
|
53
|
+
|
|
54
|
+
// Get hold of the provider instance from the SolidRegistry
|
|
55
|
+
const selectionProviderInstance = this.solidRegistry.getDashboardVariableSelectionProviderInstance(providerName);
|
|
56
|
+
if (!selectionProviderInstance) {
|
|
57
|
+
throw new NotFoundException(`Field incorrectly configured. No provider with name ${providerName} registered in backend.`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 4. Call the provider's getSelectionDynamicValues method
|
|
61
|
+
return selectionProviderInstance.values(query.query, context);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
private getProviderNameAndContext(dashboardVariable: DashboardVariable, query: DashboardVariableSelectionDynamicQueryDto): [string, any] {
|
|
66
|
+
const sourceType = dashboardVariable.selectionDynamicSourceType;
|
|
67
|
+
|
|
68
|
+
// Get the appropriate provide name based on the source type
|
|
69
|
+
let providerName: string;
|
|
70
|
+
const context = { limit: query.limit, offset: query.offset };
|
|
71
|
+
switch (sourceType) {
|
|
72
|
+
case SelectionDynamicSourceType.SQL:
|
|
73
|
+
providerName = SQL_DYNAMIC_PROVIDER_NAME;
|
|
74
|
+
context['sql'] = dashboardVariable.selectionDynamicSQL;
|
|
75
|
+
break;
|
|
76
|
+
case SelectionDynamicSourceType.PROVIDER:
|
|
77
|
+
providerName = dashboardVariable.selectionDynamicProviderName;
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
throw new Error(`Unsupported selection dynamic source type: ${sourceType}`);
|
|
81
|
+
}
|
|
82
|
+
return [providerName, context];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private async loadDashboardVariable(variableId: number) {
|
|
86
|
+
const dashboardVariableRepo = this.entityManager.getRepository(DashboardVariable);
|
|
87
|
+
|
|
88
|
+
// Load the dashboard record using the field
|
|
89
|
+
const dashboardVariable = await dashboardVariableRepo.findOne({
|
|
90
|
+
where: {
|
|
91
|
+
id: variableId,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return dashboardVariable;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async saveDashboardToConfig(entity: Dashboard) {
|
|
98
|
+
if (!entity) {
|
|
99
|
+
this.logger.debug('No entity found in the DashboardSubscriber saveDashboardToConfig method');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Validate dashboard details
|
|
104
|
+
const dashboard = entity as Dashboard;
|
|
105
|
+
const moduleMetadata = entity.module;
|
|
106
|
+
if (!moduleMetadata) {
|
|
107
|
+
throw new Error(`Module metadata not found for dashboard id ${entity.id}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Get config file details
|
|
111
|
+
const { filePath, metaData } = await this.getConfigFileDetails(moduleMetadata.name);
|
|
112
|
+
if (!filePath || !metaData) {
|
|
113
|
+
throw new Error(`Configuration details not found for module: ${moduleMetadata.name}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Write the dashboard to the config file
|
|
117
|
+
await this.writeToConfig(metaData, dashboard, filePath);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async getConfigFileDetails(moduleName: string): Promise<{ filePath: string; metaData: any }> {
|
|
121
|
+
const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
|
|
122
|
+
try {
|
|
123
|
+
await fs.access(filePath);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
throw new Error(`Configuration file not found for module: ${moduleName}`);
|
|
126
|
+
}
|
|
127
|
+
const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
|
|
128
|
+
return { filePath, metaData };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private async writeToConfig(metaData: any, dashboard: Dashboard, filePath: string) {
|
|
132
|
+
if (metaData.dashboards) {
|
|
133
|
+
const dashboardIndex = metaData.dashboards?.findIndex((dashboardFromFile: { name: string; }) => dashboardFromFile.name === dashboard.name);
|
|
134
|
+
const dto = await this.dashboardMapper.toDto(dashboard);
|
|
135
|
+
metaData.dashboards[dashboardIndex] = dto;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const dashboards = [];
|
|
139
|
+
const dto = await this.dashboardMapper.toDto(dashboard);
|
|
140
|
+
dashboards.push(dto);
|
|
141
|
+
metaData.dashboards = dashboards;
|
|
142
|
+
}
|
|
143
|
+
// Write the updated object back to the file
|
|
144
|
+
const updatedContent = JSON.stringify(metaData, null, 2);
|
|
145
|
+
await fs.writeFile(filePath, updatedContent);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -169,7 +169,8 @@ export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
|
|
|
169
169
|
// TODO: Here we are assuming that we will always take the user to collection view of a model.
|
|
170
170
|
// We can make provision to take them other views also in the future.
|
|
171
171
|
// path = `/admin/core/${rootItem.module.name}/${rootItem.action.model.singularName}/${rootItem.action.view.name}`;
|
|
172
|
-
path = `/admin/core/${rootItem.module.name}/${dasherize(rootItem.action
|
|
172
|
+
path = `/admin/core/${rootItem.module.name}/${dasherize(rootItem.action?.model?.singularName ?? 'unknown')}/${rootItem.action?.view?.type ?? 'list'}`;
|
|
173
|
+
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
176
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { DashboardQuestionDataProvider } from "src/decorators/dashboard-question-data-provider.decorator";
|
|
3
|
+
import { Question } from "src/entities/question.entity";
|
|
4
|
+
import { IDashboardQuestionDataProvider } from "src/interfaces";
|
|
5
|
+
import { EntityManager } from "typeorm";
|
|
6
|
+
import { SqlExpressionResolverService } from "../sql-expression-resolver.service";
|
|
7
|
+
import { Logger } from '@nestjs/common';
|
|
8
|
+
import { getLabels } from "./helpers";
|
|
9
|
+
|
|
10
|
+
export interface QuestionSqlDataProviderContext {
|
|
11
|
+
// questionSqlDatasetConfig: QuestionSqlDatasetConfig;
|
|
12
|
+
// questionId: number;
|
|
13
|
+
// question: Question;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export enum SqlExpressionOperator {
|
|
17
|
+
EQUALS = '$equals',
|
|
18
|
+
NOT_EQUALS = '$notEquals',
|
|
19
|
+
CONTAINS = '$contains',
|
|
20
|
+
NOT_CONTAINS = '$notContains',
|
|
21
|
+
STARTS_WITH = '$startsWith',
|
|
22
|
+
ENDS_WITH = '$endsWith',
|
|
23
|
+
IN = '$in',
|
|
24
|
+
NOT_IN = '$notIn',
|
|
25
|
+
BETWEEN = '$between',
|
|
26
|
+
LT = '$lt',
|
|
27
|
+
LTE = '$lte',
|
|
28
|
+
GT = '$gt',
|
|
29
|
+
GTE = '$gte'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SqlExpression {
|
|
33
|
+
variableName: string; // The name of the variable in the SQL query
|
|
34
|
+
operator: SqlExpressionOperator; // The operator to use for the replacement (e.g., '=', '>', '<', etc.)
|
|
35
|
+
value: string[]; // The value to replace the variable with
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@DashboardQuestionDataProvider()
|
|
39
|
+
@Injectable()
|
|
40
|
+
export class ChartJsSqlDataProvider implements IDashboardQuestionDataProvider<QuestionSqlDataProviderContext, any> {
|
|
41
|
+
private readonly logger = new Logger(ChartJsSqlDataProvider.name);
|
|
42
|
+
|
|
43
|
+
constructor(private readonly entityManager: EntityManager, private readonly sqlExpressionResolver: SqlExpressionResolverService) { }
|
|
44
|
+
|
|
45
|
+
help(): string {
|
|
46
|
+
return "Provides data for dashboard questions using a SQL dataset configuration. Configure your SQL dataset in the admin panel, then reference it in your dashboard question to fetch data.";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
name(): string {
|
|
50
|
+
return "ChartJsSqlDataProvider";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async getData(question: Question, expressions?: SqlExpression[], context?: QuestionSqlDataProviderContext): Promise<any> {
|
|
54
|
+
// TODO: put some validation to check if the results of each SQL in each dataset returns the same number of rows
|
|
55
|
+
|
|
56
|
+
// This is what we have to return.
|
|
57
|
+
// const data = {
|
|
58
|
+
// labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
|
|
59
|
+
// datasets: [
|
|
60
|
+
// {
|
|
61
|
+
// label: 'Dataset 1',
|
|
62
|
+
// data: labels.map(() => faker.number.int({ min: 0, max: 1000 })),
|
|
63
|
+
// backgroundColor: 'rgba(255, 99, 132, 0.5)',
|
|
64
|
+
// },
|
|
65
|
+
// {
|
|
66
|
+
// label: 'Dataset 2',
|
|
67
|
+
// data: labels.map(() => faker.number.int({ min: 0, max: 1000 })),
|
|
68
|
+
// backgroundColor: 'rgba(53, 162, 235, 0.5)',
|
|
69
|
+
// },
|
|
70
|
+
// {
|
|
71
|
+
// label: 'Dataset 3',
|
|
72
|
+
// data: labels.map(() => faker.number.int({ min: 0, max: 1000 })),
|
|
73
|
+
// backgroundColor: 'rgba(53, 235, 162, 0.5)',
|
|
74
|
+
// },
|
|
75
|
+
// ],
|
|
76
|
+
// };
|
|
77
|
+
|
|
78
|
+
// TODO: Load the set of labels by using a separate field on the question entity.
|
|
79
|
+
|
|
80
|
+
let datasetIdx = 0;
|
|
81
|
+
const datasets = [];
|
|
82
|
+
|
|
83
|
+
const labels: string[] = await getLabels(question, this.entityManager);
|
|
84
|
+
|
|
85
|
+
// const question = context.question;
|
|
86
|
+
for (const questionSqlDatasetConfig of question.questionSqlDatasetConfigs) {
|
|
87
|
+
|
|
88
|
+
const sql = questionSqlDatasetConfig.sql;
|
|
89
|
+
if (!sql) {
|
|
90
|
+
throw new Error(`SQL dataset ${questionSqlDatasetConfig.datasetName} configuration does not contain a valid SQL query.`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const sqlReplacementResult = this.sqlExpressionResolver.resolveSqlWithExpressions(sql, expressions || []);
|
|
94
|
+
this.logger.log(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is query=[${sqlReplacementResult.rawSql}]`);
|
|
95
|
+
this.logger.log(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is parameters=[${JSON.stringify(sqlReplacementResult.parameters)}]`);
|
|
96
|
+
const results = await this.entityManager.query(sqlReplacementResult.rawSql, sqlReplacementResult.parameters);
|
|
97
|
+
|
|
98
|
+
// Also for each data set we create the dataset object as is expected by ChartJs.
|
|
99
|
+
const data = [];
|
|
100
|
+
for (let i = 0; i < results.length; i++) {
|
|
101
|
+
const result = results[i];
|
|
102
|
+
data.push(result[questionSqlDatasetConfig.valueColumnName]);
|
|
103
|
+
}
|
|
104
|
+
datasets.push({
|
|
105
|
+
label: questionSqlDatasetConfig.datasetDisplayName,
|
|
106
|
+
data: data,
|
|
107
|
+
...JSON.parse(questionSqlDatasetConfig.options || '{}'),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
datasetIdx++;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
labels,
|
|
115
|
+
datasets
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Question } from "src/entities/question.entity";
|
|
2
|
+
import { EntityManager } from "typeorm";
|
|
3
|
+
|
|
4
|
+
export async function getLabels(question: Question, entityManager: EntityManager): Promise<string[]> {
|
|
5
|
+
const labelSql = question.labelSql;
|
|
6
|
+
const labelResults = await this.entityManager.query(labelSql);
|
|
7
|
+
// Assuming labelResults has a single row with a 'label' field
|
|
8
|
+
// Map the label results to the labels array
|
|
9
|
+
const labels: string[] = labelResults.map((result: { [x: string]: string; }) => result['label']);
|
|
10
|
+
return labels;
|
|
11
|
+
}
|
package/src/services/question-data-providers/prime-react-datatable-sql-data-provider.service.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { DashboardQuestionDataProvider } from "src/decorators/dashboard-question-data-provider.decorator";
|
|
3
|
+
import { Question } from "src/entities/question.entity";
|
|
4
|
+
import { IDashboardQuestionDataProvider } from "src/interfaces";
|
|
5
|
+
import { EntityManager } from "typeorm";
|
|
6
|
+
import { SqlExpressionResolverService } from "../sql-expression-resolver.service";
|
|
7
|
+
import { Logger } from '@nestjs/common';
|
|
8
|
+
import { SqlExpression } from "./chartjs-sql-data-provider.service";
|
|
9
|
+
|
|
10
|
+
export interface QuestionSqlDataProviderContext {
|
|
11
|
+
// questionSqlDatasetConfig: QuestionSqlDatasetConfig;
|
|
12
|
+
// questionId: number;
|
|
13
|
+
// question: Question;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@DashboardQuestionDataProvider()
|
|
17
|
+
@Injectable()
|
|
18
|
+
export class PrimeReactDatatableSqlDataProvider implements IDashboardQuestionDataProvider<QuestionSqlDataProviderContext, any> {
|
|
19
|
+
private readonly logger = new Logger(PrimeReactDatatableSqlDataProvider.name);
|
|
20
|
+
|
|
21
|
+
constructor(private readonly entityManager: EntityManager, private readonly sqlExpressionResolver: SqlExpressionResolverService) { }
|
|
22
|
+
|
|
23
|
+
help(): string {
|
|
24
|
+
return "Provides data for dashboard questions using a SQL dataset configuration. Configure your SQL dataset in the admin panel, then reference it in your dashboard question to fetch data.";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
name(): string {
|
|
28
|
+
return "PrimeReactDatatableSqlDataProvider";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getData(question: Question, expressions?: SqlExpression[], context?: QuestionSqlDataProviderContext): Promise<any> {
|
|
32
|
+
// TODO: put some validation to check if the results of each SQL in each dataset returns the same number of rows
|
|
33
|
+
|
|
34
|
+
// Check the expected response for prime react data tables to understand what is going on here...
|
|
35
|
+
|
|
36
|
+
// TODO: Load the set of labels by using a separate field on the question entity.
|
|
37
|
+
const labelSql = question.labelSql;
|
|
38
|
+
const labelResults = await this.entityManager.query(labelSql);
|
|
39
|
+
const columns = [];
|
|
40
|
+
for (let i = 0; i < labelResults.length; i++) {
|
|
41
|
+
const labelResult = labelResults[i];
|
|
42
|
+
columns.push({
|
|
43
|
+
field: labelResult['field'],
|
|
44
|
+
header: labelResult['header'],
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Load the chart options as a JSON
|
|
49
|
+
// const chartOptions = JSON.parse(question.barChartLabelOptions || '{}');
|
|
50
|
+
|
|
51
|
+
const values = []
|
|
52
|
+
|
|
53
|
+
// For meter group we can assume that we only have one sql dataset config.
|
|
54
|
+
const questionSqlDatasetConfig = question.questionSqlDatasetConfigs[0];
|
|
55
|
+
|
|
56
|
+
const sql = questionSqlDatasetConfig.sql;
|
|
57
|
+
if (!sql) {
|
|
58
|
+
throw new Error(`SQL dataset ${questionSqlDatasetConfig.datasetName} configuration does not contain a valid SQL query.`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sqlReplacementResult = this.sqlExpressionResolver.resolveSqlWithExpressions(sql, expressions || []);
|
|
62
|
+
this.logger.log(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is query=[${sqlReplacementResult.rawSql}]`);
|
|
63
|
+
this.logger.log(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is parameters=[${JSON.stringify(sqlReplacementResult.parameters)}]`);
|
|
64
|
+
const results = await this.entityManager.query(sqlReplacementResult.rawSql, sqlReplacementResult.parameters);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
columns,
|
|
68
|
+
data: results,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
}
|