@solidstarters/solid-core 1.2.133 → 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.
Files changed (278) hide show
  1. package/dist/controllers/ai-interaction.controller.d.ts +45 -0
  2. package/dist/controllers/ai-interaction.controller.d.ts.map +1 -0
  3. package/dist/controllers/ai-interaction.controller.js +192 -0
  4. package/dist/controllers/ai-interaction.controller.js.map +1 -0
  5. package/dist/controllers/dashboard-variable.controller.d.ts +43 -0
  6. package/dist/controllers/dashboard-variable.controller.d.ts.map +1 -0
  7. package/dist/controllers/dashboard-variable.controller.js +179 -0
  8. package/dist/controllers/dashboard-variable.controller.js.map +1 -0
  9. package/dist/controllers/dashboard.controller.d.ts +45 -0
  10. package/dist/controllers/dashboard.controller.d.ts.map +1 -0
  11. package/dist/controllers/dashboard.controller.js +192 -0
  12. package/dist/controllers/dashboard.controller.js.map +1 -0
  13. package/dist/controllers/question-sql-dataset-config.controller.d.ts +43 -0
  14. package/dist/controllers/question-sql-dataset-config.controller.d.ts.map +1 -0
  15. package/dist/controllers/question-sql-dataset-config.controller.js +179 -0
  16. package/dist/controllers/question-sql-dataset-config.controller.js.map +1 -0
  17. package/dist/controllers/question.controller.d.ts +45 -0
  18. package/dist/controllers/question.controller.d.ts.map +1 -0
  19. package/dist/controllers/question.controller.js +194 -0
  20. package/dist/controllers/question.controller.js.map +1 -0
  21. package/dist/controllers/test.controller.d.ts.map +1 -1
  22. package/dist/controllers/test.controller.js.map +1 -1
  23. package/dist/decorators/dashboard-question-data-provider.decorator.d.ts +3 -0
  24. package/dist/decorators/dashboard-question-data-provider.decorator.d.ts.map +1 -0
  25. package/dist/decorators/dashboard-question-data-provider.decorator.js +11 -0
  26. package/dist/decorators/dashboard-question-data-provider.decorator.js.map +1 -0
  27. package/dist/decorators/dashboard-selection-provider.decorator.d.ts +3 -0
  28. package/dist/decorators/dashboard-selection-provider.decorator.d.ts.map +1 -0
  29. package/dist/decorators/dashboard-selection-provider.decorator.js +11 -0
  30. package/dist/decorators/dashboard-selection-provider.decorator.js.map +1 -0
  31. package/dist/dtos/create-ai-interaction.dto.d.ts +14 -0
  32. package/dist/dtos/create-ai-interaction.dto.d.ts.map +1 -0
  33. package/dist/dtos/create-ai-interaction.dto.js +90 -0
  34. package/dist/dtos/create-ai-interaction.dto.js.map +1 -0
  35. package/dist/dtos/create-dashboard-variable.dto.d.ts +18 -0
  36. package/dist/dtos/create-dashboard-variable.dto.d.ts.map +1 -0
  37. package/dist/dtos/create-dashboard-variable.dto.js +97 -0
  38. package/dist/dtos/create-dashboard-variable.dto.js.map +1 -0
  39. package/dist/dtos/create-dashboard.dto.d.ts +15 -0
  40. package/dist/dtos/create-dashboard.dto.d.ts.map +1 -0
  41. package/dist/dtos/create-dashboard.dto.js +90 -0
  42. package/dist/dtos/create-dashboard.dto.js.map +1 -0
  43. package/dist/dtos/create-question-sql-dataset-config.dto.d.ts +12 -0
  44. package/dist/dtos/create-question-sql-dataset-config.dto.d.ts.map +1 -0
  45. package/dist/dtos/create-question-sql-dataset-config.dto.js +77 -0
  46. package/dist/dtos/create-question-sql-dataset-config.dto.js.map +1 -0
  47. package/dist/dtos/create-question.dto.d.ts +16 -0
  48. package/dist/dtos/create-question.dto.d.ts.map +1 -0
  49. package/dist/dtos/create-question.dto.js +99 -0
  50. package/dist/dtos/create-question.dto.js.map +1 -0
  51. package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.d.ts +8 -0
  52. package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.d.ts.map +1 -0
  53. package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.js +52 -0
  54. package/dist/dtos/dashboard-variable-selection-dynamic-query.dto.js.map +1 -0
  55. package/dist/dtos/invoke-ai-prompt.dto.d.ts +4 -0
  56. package/dist/dtos/invoke-ai-prompt.dto.d.ts.map +1 -0
  57. package/dist/dtos/invoke-ai-prompt.dto.js +25 -0
  58. package/dist/dtos/invoke-ai-prompt.dto.js.map +1 -0
  59. package/dist/dtos/update-ai-interaction.dto.d.ts +15 -0
  60. package/dist/dtos/update-ai-interaction.dto.d.ts.map +1 -0
  61. package/dist/dtos/update-ai-interaction.dto.js +96 -0
  62. package/dist/dtos/update-ai-interaction.dto.js.map +1 -0
  63. package/dist/dtos/update-dashboard-variable.dto.d.ts +15 -0
  64. package/dist/dtos/update-dashboard-variable.dto.d.ts.map +1 -0
  65. package/dist/dtos/update-dashboard-variable.dto.js +95 -0
  66. package/dist/dtos/update-dashboard-variable.dto.js.map +1 -0
  67. package/dist/dtos/update-dashboard.dto.d.ts +16 -0
  68. package/dist/dtos/update-dashboard.dto.d.ts.map +1 -0
  69. package/dist/dtos/update-dashboard.dto.js +96 -0
  70. package/dist/dtos/update-dashboard.dto.js.map +1 -0
  71. package/dist/dtos/update-question-sql-dataset-config.dto.d.ts +13 -0
  72. package/dist/dtos/update-question-sql-dataset-config.dto.d.ts.map +1 -0
  73. package/dist/dtos/update-question-sql-dataset-config.dto.js +86 -0
  74. package/dist/dtos/update-question-sql-dataset-config.dto.js.map +1 -0
  75. package/dist/dtos/update-question.dto.d.ts +17 -0
  76. package/dist/dtos/update-question.dto.d.ts.map +1 -0
  77. package/dist/dtos/update-question.dto.js +106 -0
  78. package/dist/dtos/update-question.dto.js.map +1 -0
  79. package/dist/entities/ai-interaction.entity.d.ts +15 -0
  80. package/dist/entities/ai-interaction.entity.d.ts.map +1 -0
  81. package/dist/entities/ai-interaction.entity.js +70 -0
  82. package/dist/entities/ai-interaction.entity.js.map +1 -0
  83. package/dist/entities/dashboard-variable.entity.d.ts +15 -0
  84. package/dist/entities/dashboard-variable.entity.d.ts.map +1 -0
  85. package/dist/entities/dashboard-variable.entity.js +73 -0
  86. package/dist/entities/dashboard-variable.entity.js.map +1 -0
  87. package/dist/entities/dashboard.entity.d.ts +12 -0
  88. package/dist/entities/dashboard.entity.d.ts.map +1 -0
  89. package/dist/entities/dashboard.entity.js +50 -0
  90. package/dist/entities/dashboard.entity.js.map +1 -0
  91. package/dist/entities/question-sql-dataset-config.entity.d.ts +13 -0
  92. package/dist/entities/question-sql-dataset-config.entity.d.ts.map +1 -0
  93. package/dist/entities/question-sql-dataset-config.entity.js +60 -0
  94. package/dist/entities/question-sql-dataset-config.entity.js.map +1 -0
  95. package/dist/entities/question.entity.d.ts +15 -0
  96. package/dist/entities/question.entity.d.ts.map +1 -0
  97. package/dist/entities/question.entity.js +67 -0
  98. package/dist/entities/question.entity.js.map +1 -0
  99. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.d.ts +1 -0
  100. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.d.ts.map +1 -1
  101. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js +32 -7
  102. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js.map +1 -1
  103. package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.d.ts +1 -0
  104. package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.d.ts.map +1 -1
  105. package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.js +32 -7
  106. package/dist/helpers/field-crud-managers/SelectionStaticFieldCrudManager.js.map +1 -1
  107. package/dist/helpers/solid-registry.d.ts +9 -1
  108. package/dist/helpers/solid-registry.d.ts.map +1 -1
  109. package/dist/helpers/solid-registry.js +32 -0
  110. package/dist/helpers/solid-registry.js.map +1 -1
  111. package/dist/index.d.ts +5 -0
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +5 -0
  114. package/dist/index.js.map +1 -1
  115. package/dist/interfaces.d.ts +12 -1
  116. package/dist/interfaces.d.ts.map +1 -1
  117. package/dist/interfaces.js.map +1 -1
  118. package/dist/mappers/dashboard-mapper.d.ts +6 -0
  119. package/dist/mappers/dashboard-mapper.d.ts.map +1 -0
  120. package/dist/mappers/dashboard-mapper.js +60 -0
  121. package/dist/mappers/dashboard-mapper.js.map +1 -0
  122. package/dist/repository/dashboard.repository.d.ts +11 -0
  123. package/dist/repository/dashboard.repository.d.ts.map +1 -0
  124. package/dist/repository/dashboard.repository.js +93 -0
  125. package/dist/repository/dashboard.repository.js.map +1 -0
  126. package/dist/seeders/module-metadata-seeder.service.d.ts +5 -1
  127. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  128. package/dist/seeders/module-metadata-seeder.service.js +18 -2
  129. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  130. package/dist/seeders/seed-data/solid-core-metadata.json +4222 -2418
  131. package/dist/services/ai-interaction.service.d.ts +23 -0
  132. package/dist/services/ai-interaction.service.d.ts.map +1 -0
  133. package/dist/services/ai-interaction.service.js +147 -0
  134. package/dist/services/ai-interaction.service.js.map +1 -0
  135. package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.d.ts +12 -0
  136. package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.d.ts.map +1 -0
  137. package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.js +55 -0
  138. package/dist/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.js.map +1 -0
  139. package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.d.ts +11 -0
  140. package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.d.ts.map +1 -0
  141. package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.js +45 -0
  142. package/dist/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.js.map +1 -0
  143. package/dist/services/dashboard-variable.service.d.ts +23 -0
  144. package/dist/services/dashboard-variable.service.d.ts.map +1 -0
  145. package/dist/services/dashboard-variable.service.js +57 -0
  146. package/dist/services/dashboard-variable.service.js.map +1 -0
  147. package/dist/services/dashboard.service.d.ts +38 -0
  148. package/dist/services/dashboard.service.d.ts.map +1 -0
  149. package/dist/services/dashboard.service.js +179 -0
  150. package/dist/services/dashboard.service.js.map +1 -0
  151. package/dist/services/import-transaction.service.d.ts.map +1 -1
  152. package/dist/services/import-transaction.service.js +2 -1
  153. package/dist/services/import-transaction.service.js.map +1 -1
  154. package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
  155. package/dist/services/menu-item-metadata.service.js +1 -1
  156. package/dist/services/menu-item-metadata.service.js.map +1 -1
  157. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts +36 -0
  158. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.d.ts.map +1 -0
  159. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js +85 -0
  160. package/dist/services/question-data-providers/chartjs-sql-data-provider.service.js.map +1 -0
  161. package/dist/services/question-data-providers/helpers.d.ts +4 -0
  162. package/dist/services/question-data-providers/helpers.d.ts.map +1 -0
  163. package/dist/services/question-data-providers/helpers.js +10 -0
  164. package/dist/services/question-data-providers/helpers.js.map +1 -0
  165. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts +17 -0
  166. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.d.ts.map +1 -0
  167. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js +64 -0
  168. package/dist/services/question-data-providers/prime-react-datatable-sql-data-provider.service.js.map +1 -0
  169. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts +19 -0
  170. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.d.ts.map +1 -0
  171. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js +84 -0
  172. package/dist/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.js.map +1 -0
  173. package/dist/services/question-sql-dataset-config.service.d.ts +22 -0
  174. package/dist/services/question-sql-dataset-config.service.d.ts.map +1 -0
  175. package/dist/services/question-sql-dataset-config.service.js +56 -0
  176. package/dist/services/question-sql-dataset-config.service.js.map +1 -0
  177. package/dist/services/question.service.d.ts +29 -0
  178. package/dist/services/question.service.d.ts.map +1 -0
  179. package/dist/services/question.service.js +117 -0
  180. package/dist/services/question.service.js.map +1 -0
  181. package/dist/services/scheduled-jobs/scheduler.service.d.ts.map +1 -1
  182. package/dist/services/scheduled-jobs/scheduler.service.js +22 -11
  183. package/dist/services/scheduled-jobs/scheduler.service.js.map +1 -1
  184. package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.d.ts +11 -0
  185. package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.d.ts.map +1 -0
  186. package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.js +46 -0
  187. package/dist/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.js.map +1 -0
  188. package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.d.ts +11 -0
  189. package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.d.ts.map +1 -0
  190. package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.js +47 -0
  191. package/dist/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.js.map +1 -0
  192. package/dist/services/solid-introspect.service.d.ts +2 -0
  193. package/dist/services/solid-introspect.service.d.ts.map +1 -1
  194. package/dist/services/solid-introspect.service.js +28 -0
  195. package/dist/services/solid-introspect.service.js.map +1 -1
  196. package/dist/services/sql-expression-resolver.service.d.ts +9 -0
  197. package/dist/services/sql-expression-resolver.service.d.ts.map +1 -0
  198. package/dist/services/sql-expression-resolver.service.js +105 -0
  199. package/dist/services/sql-expression-resolver.service.js.map +1 -0
  200. package/dist/solid-core.module.d.ts.map +1 -1
  201. package/dist/solid-core.module.js +61 -0
  202. package/dist/solid-core.module.js.map +1 -1
  203. package/dist/subscribers/dashboard-variable.subscriber.d.ts +16 -0
  204. package/dist/subscribers/dashboard-variable.subscriber.d.ts.map +1 -0
  205. package/dist/subscribers/dashboard-variable.subscriber.js +72 -0
  206. package/dist/subscribers/dashboard-variable.subscriber.js.map +1 -0
  207. package/dist/subscribers/dashboard.subscriber.d.ts +15 -0
  208. package/dist/subscribers/dashboard.subscriber.d.ts.map +1 -0
  209. package/dist/subscribers/dashboard.subscriber.js +56 -0
  210. package/dist/subscribers/dashboard.subscriber.js.map +1 -0
  211. package/dist/subscribers/question-sql-dataset-config.subscriber.d.ts +16 -0
  212. package/dist/subscribers/question-sql-dataset-config.subscriber.d.ts.map +1 -0
  213. package/dist/subscribers/question-sql-dataset-config.subscriber.js +72 -0
  214. package/dist/subscribers/question-sql-dataset-config.subscriber.js.map +1 -0
  215. package/dist/subscribers/question.subscriber.d.ts +16 -0
  216. package/dist/subscribers/question.subscriber.d.ts.map +1 -0
  217. package/dist/subscribers/question.subscriber.js +72 -0
  218. package/dist/subscribers/question.subscriber.js.map +1 -0
  219. package/dist/tsconfig.tsbuildinfo +1 -1
  220. package/package.json +1 -1
  221. package/src/controllers/ai-interaction.controller.ts +98 -0
  222. package/src/controllers/dashboard-variable.controller.ts +93 -0
  223. package/src/controllers/dashboard.controller.ts +99 -0
  224. package/src/controllers/question-sql-dataset-config.controller.ts +93 -0
  225. package/src/controllers/question.controller.ts +104 -0
  226. package/src/controllers/test.controller.ts +1 -2
  227. package/src/decorators/dashboard-question-data-provider.decorator.ts +7 -0
  228. package/src/decorators/dashboard-selection-provider.decorator.ts +7 -0
  229. package/src/dtos/create-ai-interaction.dto.ts +60 -0
  230. package/src/dtos/create-dashboard-variable.dto.ts +56 -0
  231. package/src/dtos/create-dashboard.dto.ts +53 -0
  232. package/src/dtos/create-question-sql-dataset-config.dto.ts +42 -0
  233. package/src/dtos/create-question.dto.ts +58 -0
  234. package/src/dtos/dashboard-variable-selection-dynamic-query.dto.ts +29 -0
  235. package/src/dtos/invoke-ai-prompt.dto.ts +6 -0
  236. package/src/dtos/update-ai-interaction.dto.ts +65 -0
  237. package/src/dtos/update-dashboard-variable.dto.ts +54 -0
  238. package/src/dtos/update-dashboard.dto.ts +57 -0
  239. package/src/dtos/update-question-sql-dataset-config.dto.ts +49 -0
  240. package/src/dtos/update-question.dto.ts +63 -0
  241. package/src/entities/ai-interaction.entity.ts +39 -0
  242. package/src/entities/dashboard-variable.entity.ts +30 -0
  243. package/src/entities/dashboard.entity.ts +21 -0
  244. package/src/entities/question-sql-dataset-config.entity.ts +25 -0
  245. package/src/entities/question.entity.ts +30 -0
  246. package/src/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.ts +44 -18
  247. package/src/helpers/field-crud-managers/SelectionStaticFieldCrudManager.ts +43 -15
  248. package/src/helpers/solid-registry.ts +44 -2
  249. package/src/index.ts +5 -0
  250. package/src/interfaces.ts +41 -29
  251. package/src/mappers/dashboard-mapper.ts +52 -0
  252. package/src/repository/dashboard.repository.ts +100 -0
  253. package/src/seeders/module-metadata-seeder.service.ts +21 -1
  254. package/src/seeders/seed-data/solid-core-metadata.json +4225 -2421
  255. package/src/services/1. Create a context menu option i.py +12 -0
  256. package/src/services/ai-interaction.service.ts +127 -0
  257. package/src/services/dashboard-selection-providers/dashboard-variable-sql-dynamic-provider.service.ts +56 -0
  258. package/src/services/dashboard-selection-providers/dashboard-variable-test-dynamic-provider.service.ts +37 -0
  259. package/src/services/dashboard-variable.service.ts +36 -0
  260. package/src/services/dashboard.service.ts +147 -0
  261. package/src/services/import-transaction.service.ts +2 -1
  262. package/src/services/menu-item-metadata.service.ts +2 -1
  263. package/src/services/question-data-providers/chartjs-sql-data-provider.service.ts +121 -0
  264. package/src/services/question-data-providers/helpers.ts +11 -0
  265. package/src/services/question-data-providers/prime-react-datatable-sql-data-provider.service.ts +72 -0
  266. package/src/services/question-data-providers/prime-react-meter-group-sql-data-provider.service.ts +110 -0
  267. package/src/services/question-sql-dataset-config.service.ts +34 -0
  268. package/src/services/question.service.ts +115 -0
  269. package/src/services/scheduled-jobs/scheduler.service.ts +32 -64
  270. package/src/services/selection-providers/list-of-dashboard-question-providers-selection-provider.service.ts +41 -0
  271. package/src/services/selection-providers/list-of-dashboard-variable-providers-selection-provider.service.ts +41 -0
  272. package/src/services/solid-introspect.service.ts +42 -0
  273. package/src/services/sql-expression-resolver.service.ts +125 -0
  274. package/src/solid-core.module.ts +61 -1
  275. package/src/subscribers/dashboard-variable.subscriber.ts +63 -0
  276. package/src/subscribers/dashboard.subscriber.ts +43 -0
  277. package/src/subscribers/question-sql-dataset-config.subscriber.ts +63 -0
  278. package/src/subscribers/question.subscriber.ts +65 -0
@@ -0,0 +1,110 @@
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 PrimeReactMeterGroupSqlDataProvider implements IDashboardQuestionDataProvider<QuestionSqlDataProviderContext, any> {
19
+ private readonly logger = new Logger(PrimeReactMeterGroupSqlDataProvider.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 "PrimeReactMeterGroupSqlDataProvider";
29
+ }
30
+
31
+ hslToHex(h: number, s: number, l: number): string {
32
+ l /= 100;
33
+ s /= 100;
34
+
35
+ const k = (n: number) => (n + h / 30) % 12;
36
+ const a = s * Math.min(l, 1 - l);
37
+ const f = (n: number) =>
38
+ Math.round(255 * (l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))));
39
+
40
+ return `#${f(0).toString(16).padStart(2, '0')}${f(8).toString(16).padStart(2, '0')}${f(4)
41
+ .toString(16)
42
+ .padStart(2, '0')}`;
43
+ }
44
+
45
+ generateDistinctColors(count: number): string[] {
46
+ const colors: string[] = [];
47
+
48
+ const hueStep = 360 / count;
49
+ const saturation = 65; // keep it vibrant
50
+ const lightness = 55; // balanced for both light/dark themes
51
+
52
+ for (let i = 0; i < count; i++) {
53
+ const hue = Math.round(i * hueStep);
54
+ colors.push(this.hslToHex(hue, saturation, lightness));
55
+ }
56
+
57
+ return colors;
58
+ }
59
+
60
+ async getData(question: Question, expressions?: SqlExpression[], context?: QuestionSqlDataProviderContext): Promise<any> {
61
+ // TODO: put some validation to check if the results of each SQL in each dataset returns the same number of rows
62
+
63
+ // This is what we have to return.
64
+ // const values = [
65
+ // { label: 'Apps', color: '#34d399', value: 16 },
66
+ // { label: 'Messages', color: '#fbbf24', value: 8 },
67
+ // { label: 'Media', color: '#60a5fa', value: 24 },
68
+ // { label: 'System', color: '#c084fc', value: 10 }
69
+ // ];
70
+
71
+ // TODO: Load the set of labels by using a separate field on the question entity.
72
+
73
+ // Load the chart options as a JSON
74
+ const chartOptions = JSON.parse(question.chartOptions || '{}');
75
+
76
+ const values = []
77
+
78
+ // For meter group we can assume that we only have one sql dataset config.
79
+ const questionSqlDatasetConfig = question.questionSqlDatasetConfigs[0];
80
+
81
+ const sql = questionSqlDatasetConfig.sql;
82
+ if (!sql) {
83
+ throw new Error(`SQL dataset ${questionSqlDatasetConfig.datasetName} configuration does not contain a valid SQL query.`);
84
+ }
85
+
86
+ const sqlReplacementResult = this.sqlExpressionResolver.resolveSqlWithExpressions(sql, expressions || []);
87
+ this.logger.log(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is query=[${sqlReplacementResult.rawSql}]`);
88
+ this.logger.log(`Final Sql query for dataset [${questionSqlDatasetConfig.datasetName}] is parameters=[${JSON.stringify(sqlReplacementResult.parameters)}]`);
89
+ const results = await this.entityManager.query(sqlReplacementResult.rawSql, sqlReplacementResult.parameters);
90
+
91
+ const colors = this.generateDistinctColors(results.length);
92
+
93
+ // Also for each data set we create the dataset object as is expected by ChartJs.
94
+ for (let i = 0; i < results.length; i++) {
95
+ const result = results[i];
96
+
97
+ const colorFromChartOptions = chartOptions?.colors?.[result[questionSqlDatasetConfig.labelColumnName]];
98
+ const color = typeof colorFromChartOptions === 'string' ? colorFromChartOptions : colors[i];
99
+
100
+ values.push({
101
+ label: result[questionSqlDatasetConfig.labelColumnName],
102
+ color: color,
103
+ value: result[questionSqlDatasetConfig.valueColumnName]
104
+ })
105
+ }
106
+
107
+ return values;
108
+
109
+ }
110
+ }
@@ -0,0 +1,34 @@
1
+ import { 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
+
13
+
14
+ import { QuestionSqlDatasetConfig } from '../entities/question-sql-dataset-config.entity';
15
+
16
+ @Injectable()
17
+ export class QuestionSqlDatasetConfigService extends CRUDService<QuestionSqlDatasetConfig>{
18
+ constructor(
19
+ readonly modelMetadataService: ModelMetadataService,
20
+ readonly moduleMetadataService: ModuleMetadataService,
21
+ readonly configService: ConfigService,
22
+ readonly fileService: FileService,
23
+ readonly discoveryService: DiscoveryService,
24
+ readonly crudHelperService: CrudHelperService,
25
+ @InjectEntityManager()
26
+ readonly entityManager: EntityManager,
27
+ @InjectRepository(QuestionSqlDatasetConfig, 'default')
28
+ readonly repo: Repository<QuestionSqlDatasetConfig>,
29
+ readonly moduleRef: ModuleRef
30
+
31
+ ) {
32
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, 'questionSqlDatasetConfig', 'solid-core', moduleRef);
33
+ }
34
+ }
@@ -0,0 +1,115 @@
1
+ import { BadRequestException, Injectable, Logger, NotImplementedException } 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 'src/entities/dashboard-variable.entity';
15
+ import { SolidRegistry } from 'src/helpers/solid-registry';
16
+ import { Question } from '../entities/question.entity';
17
+ import { SqlExpression, SqlExpressionOperator } from './question-data-providers/chartjs-sql-data-provider.service';
18
+ import { DashboardService } from './dashboard.service';
19
+ import { Dashboard } from 'src/entities/dashboard.entity';
20
+
21
+ enum SOURCE_TYPE {
22
+ SQL = 'sql',
23
+ PROVIDER = 'provider',
24
+ }
25
+
26
+ const CHARTJS_SQL_DATA_PROVIDER_NAME = 'ChartJsSqlDataProvider';
27
+ const PRIME_REACT_METER_GROUP_SQL_DATA_PROVIDER_NAME = 'PrimeReactMeterGroupSqlDataProvider';
28
+ const PRIME_REACT_DATATABLE_SQL_DATA_PROVIDER_NAME = 'PrimeReactDatatableSqlDataProvider';
29
+
30
+ @Injectable()
31
+ export class QuestionService extends CRUDService<Question> {
32
+ private readonly logger = new Logger(this.constructor.name);
33
+ constructor(
34
+ readonly modelMetadataService: ModelMetadataService,
35
+ readonly moduleMetadataService: ModuleMetadataService,
36
+ readonly configService: ConfigService,
37
+ readonly fileService: FileService,
38
+ readonly discoveryService: DiscoveryService,
39
+ readonly crudHelperService: CrudHelperService,
40
+ @InjectEntityManager()
41
+ readonly entityManager: EntityManager,
42
+ @InjectRepository(Question, 'default')
43
+ readonly repo: Repository<Question>,
44
+ readonly moduleRef: ModuleRef,
45
+ readonly solidRegistry: SolidRegistry, // Assuming solidRegistry is injected for data providers
46
+ ) {
47
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'question', 'solid-core', moduleRef);
48
+ }
49
+
50
+ // Get the data for a specific question
51
+ async getData(id: number, inputExpressions: SqlExpression[] = [], isPreview = false): Promise<any> {
52
+ // Load the question
53
+ const question = await this.loadQuestion(id);
54
+ if (!question) {
55
+ throw new BadRequestException(`Question with id ${id} not found`);
56
+ }
57
+
58
+ // Get the dashbbard variables from the question
59
+ const dashboardVariables = question.dashboard?.dashboardVariables || [];
60
+ const expressions: SqlExpression[] = this.getExpressions(isPreview, dashboardVariables, inputExpressions);
61
+
62
+ // Try to resolve the dataProvider based on a combination of sourceType and visualisedAs
63
+ let dataProvider = null;
64
+
65
+ if (question.sourceType === SOURCE_TYPE.SQL && ['bar', 'pie', 'line', 'donut'].includes(question.visualisedAs)) {
66
+ dataProvider = this.solidRegistry.getDashboardQuestionDataProviderInstance(CHARTJS_SQL_DATA_PROVIDER_NAME);
67
+ }
68
+ if (question.sourceType === SOURCE_TYPE.SQL && ['prime-meter-group'].includes(question.visualisedAs)) {
69
+ dataProvider = this.solidRegistry.getDashboardQuestionDataProviderInstance(PRIME_REACT_METER_GROUP_SQL_DATA_PROVIDER_NAME);
70
+ }
71
+ if (question.sourceType === SOURCE_TYPE.SQL && ['prime-datatable'].includes(question.visualisedAs)) {
72
+ dataProvider = this.solidRegistry.getDashboardQuestionDataProviderInstance(PRIME_REACT_DATATABLE_SQL_DATA_PROVIDER_NAME);
73
+ }
74
+
75
+ if (!dataProvider) {
76
+ throw new NotImplementedException(`Invalid data source type ${question.sourceType}`);
77
+ }
78
+
79
+ return await dataProvider.getData(question, expressions);
80
+
81
+ }
82
+
83
+ private getExpressions(isPreview: boolean, dashboardVariables: DashboardVariable[], inputExpressions: SqlExpression[]) {
84
+ const expressions: SqlExpression[] = [];
85
+ if (isPreview) {
86
+ // Convert the dashboard variables into objects of interface type SqlExpression using the default value, default operator and the variable name
87
+ const expr: SqlExpression[] = dashboardVariables.map(variable => {
88
+ return {
89
+ variableName: variable.variableName,
90
+ operator: variable.defaultOperator as SqlExpressionOperator, // Assuming defaultOperator is a valid SqlExpressionOperator
91
+ value: JSON.parse(variable.defaultValue || '[]'), // Assuming defaultValue is a string or can be converted to a string array
92
+ };
93
+ });
94
+ expressions.push(...expr);
95
+ }
96
+ else {
97
+ expressions.push(...inputExpressions);
98
+ }
99
+ return expressions;
100
+ }
101
+
102
+ private async loadQuestion(id: number) {
103
+ const repo = this.entityManager.getRepository(Question);
104
+
105
+ // Load the dashboard record using the field
106
+ const question = await repo.findOne({
107
+ where: {
108
+ id,
109
+ },
110
+ relations: ['questionSqlDatasetConfigs', 'dashboard', 'dashboard.dashboardVariables'],
111
+ });
112
+ return question;
113
+ }
114
+
115
+ }
@@ -18,88 +18,54 @@ export class SchedulerServiceImpl implements ISchedulerService {
18
18
  private readonly solidRegistry: SolidRegistry,
19
19
  ) { }
20
20
 
21
- // @Cron(CronExpression.EVERY_MINUTE)
22
- // async runScheduledJobs(): Promise<void> {
23
- // const now = new Date();
24
-
25
- // const dueScheduledJobs = await this.scheduledJobRepo.find({
26
- // where: {
27
- // isActive: true,
28
- // nextRunAt: LessThanOrEqual(now),
29
- // },
30
- // });
31
-
32
- // for (const dueScheduledJob of dueScheduledJobs) {
33
- // try {
34
- // const jobName = dueScheduledJob.job;
35
- // // @ts-ignore
36
- // // const jobHandler = this.jobMap[jobName];
37
- // // const jobHandler = '';
38
- // const jobHandler: IScheduledJob | undefined = this.solidRegistry.getScheduledJobProviderInstance(jobName)
39
-
40
- // if (!jobHandler) {
41
- // this.logger.warn(`No job service found for: ${jobName}`);
42
- // continue;
43
- // }
44
-
45
- // await jobHandler.executeReminder(dueScheduledJob);
46
-
47
- // // Update nextRunAt and lastRunAt based on frequency
48
- // dueScheduledJob.lastRunAt = now;
49
- // dueScheduledJob.nextRunAt = this.computeNextRunAt(dueScheduledJob);
50
- // await this.scheduledJobRepo.save(dueScheduledJob);
51
-
52
- // this.logger.log(`Successfully ran job: ${jobName}`);
53
- // } catch (err) {
54
- // this.logger.error(`Failed to run job for reminder ${dueScheduledJob.id}`, err.stack);
55
- // }
56
- // }
57
- // }
58
-
59
- // private computeNextRunAt(reminder: ScheduledJob): Date {
60
- // const now = new Date();
61
- // switch (reminder.frequency) {
62
- // case 'daily':
63
- // return new Date(now.getTime() + 24 * 60 * 60 * 1000);
64
- // case 'weekly':
65
- // return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
66
- // case 'monthly':
67
- // return new Date(now.setMonth(now.getMonth() + 1));
68
- // default:
69
- // return new Date(now.getTime() + 24 * 60 * 60 * 1000); // default fallback
70
- // }
71
- // }
72
-
73
21
  @Cron(CronExpression.EVERY_MINUTE)
74
22
  async runScheduledJobs(): Promise<void> {
75
23
  const now = new Date();
24
+
25
+ this.logger.log(`[${now.getTime()}]: scheduler service started run...`);
76
26
  const dueJobs = await this.scheduledJobRepo.find({
77
- where: {
78
- isActive: true,
79
- nextRunAt: LessThanOrEqual(now),
80
- },
27
+ where: [
28
+ {
29
+ isActive: true,
30
+ nextRunAt: LessThanOrEqual(now),
31
+ },
32
+ // Newly created jobs are also picked for examination
33
+ {
34
+ isActive: true,
35
+ nextRunAt: null,
36
+ },
37
+ ],
81
38
  });
82
39
 
40
+ this.logger.log(`[${now.getTime()}]: scheduler service identified ${dueJobs.length} jobs to run...`);
41
+
83
42
  for (const job of dueJobs) {
43
+ this.logger.log(`[${now.getTime()}]: scheduler service attempting to run job ${job.job}`);
84
44
  try {
85
45
  if (!this.shouldRunNow(job, now)) {
46
+ this.logger.log(`[${now.getTime()}]: scheduler service skipping job ${job.job}`);
86
47
  continue;
87
48
  }
88
49
 
89
50
  const handler = this.solidRegistry.getScheduledJobProviderInstance(job.job);
90
51
  if (!handler) {
91
- this.logger.warn(`No job handler found for job: ${job.job}`);
52
+ this.logger.warn(`[${now.getTime()}]: scheduler service skipping because job handler not found: ${job.job}`);
92
53
  continue;
93
54
  }
94
55
 
56
+ this.logger.log(`[${now.getTime()}]: scheduler service about to run job ${job.job}`);
95
57
  await handler.executeReminder(job);
58
+ this.logger.log(`[${now.getTime()}]: scheduler service finished running job ${job.job}`);
59
+
96
60
  job.isActive = true;
97
61
  job.lastRunAt = now;
98
62
  job.nextRunAt = this.computeNextRunAt(job, now);
63
+ this.logger.log(`[${now.getTime()}]: scheduler service coomputed next run for ${job.job} as ${job.nextRunAt}`);
64
+
99
65
  await this.scheduledJobRepo.save(job);
100
- this.logger.log(`Successfully ran job: ${job.job}`);
66
+ this.logger.log(`[${now.getTime()}]: scheduler service finished running job: ${job.job}`);
101
67
  } catch (err) {
102
- this.logger.error(`Failed to run job ${job.job}`, err.stack);
68
+ this.logger.error(`[${now.getTime()}]: scheduler service failed to run job ${job.job}`, err.stack);
103
69
  }
104
70
  }
105
71
  }
@@ -143,8 +109,10 @@ export class SchedulerServiceImpl implements ISchedulerService {
143
109
  const base = new Date(from);
144
110
 
145
111
  switch (job.frequency.toLowerCase()) {
146
- case 'once':
147
- return null; // don't reschedule
112
+ // case 'once':
113
+ // return null; // don't reschedule
114
+ case 'every minute':
115
+ return new Date(base.getTime() + 1 * 60 * 1000);
148
116
  case 'hourly':
149
117
  return new Date(base.getTime() + 60 * 60 * 1000);
150
118
  case 'daily':
@@ -155,9 +123,9 @@ export class SchedulerServiceImpl implements ISchedulerService {
155
123
  const next = new Date(base);
156
124
  next.setMonth(base.getMonth() + 1);
157
125
  return next;
158
- case 'custom':
159
- // Optional: let job handler decide via metadata or registry
160
- return new Date(base.getTime() + 24 * 60 * 60 * 1000);
126
+ // case 'custom':
127
+ // // Optional: let job handler decide via metadata or registry
128
+ // return new Date(base.getTime() + 24 * 60 * 60 * 1000);
161
129
  default:
162
130
  return new Date(base.getTime() + 24 * 60 * 60 * 1000);
163
131
  }
@@ -0,0 +1,41 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { SelectionProvider } from "src/decorators/selection-provider.decorator";
3
+ import { SolidRegistry } from "src/helpers/solid-registry";
4
+ import { IDashboardQuestionDataProvider, IDashboardVariableSelectionProvider, ISelectionProvider, ISelectionProviderContext, ISelectionProviderValues } from "../../interfaces";
5
+ import { SQL_DYNAMIC_PROVIDER_NAME } from "../dashboard.service";
6
+
7
+
8
+ @SelectionProvider()
9
+ @Injectable()
10
+ export class ListOfDashboardQuestionProvidersSelectionProvider implements ISelectionProvider<ISelectionProviderContext> {
11
+
12
+ constructor(
13
+ private readonly solidRegistry: SolidRegistry,
14
+ ) {
15
+ }
16
+
17
+ help(): string {
18
+ return "# Allows one to dynamically fetch all the dashboard providers that are registered in the system. ";
19
+ }
20
+
21
+ name(): string {
22
+ return 'ListOfDashboardQuestionProvidersSelectionProvider';
23
+ }
24
+
25
+ async value(optionValue: string, ctxt: ISelectionProviderContext): Promise<ISelectionProviderValues | any> {
26
+ const dashboardSelectionProvider: IDashboardQuestionDataProvider<any, any> | undefined = this.solidRegistry.getDashboardQuestionDataProviderInstance(optionValue);
27
+ if (!dashboardSelectionProvider) {
28
+ return null;
29
+ }
30
+
31
+ return { label: dashboardSelectionProvider.name(), value: dashboardSelectionProvider.name() };
32
+ }
33
+
34
+ async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
35
+ const dashboardSelectionProviders = this.solidRegistry.getDashboardQuestionDataProviders()
36
+ //Exclude the SQL dynamic provider from the list, (since although it is a dashboard selection provider, it is not a valid option for the user to select)
37
+ return dashboardSelectionProviders.map(i => {
38
+ return { label: i.name, value: i.name };
39
+ });
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { SelectionProvider } from "src/decorators/selection-provider.decorator";
3
+ import { SolidRegistry } from "src/helpers/solid-registry";
4
+ import { IDashboardVariableSelectionProvider, ISelectionProvider, ISelectionProviderContext, ISelectionProviderValues } from "../../interfaces";
5
+ import { SQL_DYNAMIC_PROVIDER_NAME } from "../dashboard.service";
6
+
7
+
8
+ @SelectionProvider()
9
+ @Injectable()
10
+ export class ListOfDashboardVariableProvidersSelectionProvider implements ISelectionProvider<ISelectionProviderContext> {
11
+
12
+ constructor(
13
+ private readonly solidRegistry: SolidRegistry,
14
+ ) {
15
+ }
16
+
17
+ help(): string {
18
+ return "# Allows one to dynamically fetch all the dashboard providers that are registered in the system. ";
19
+ }
20
+
21
+ name(): string {
22
+ return 'ListOfDashboardVariableProvidersSelectionProvider';
23
+ }
24
+
25
+ async value(optionValue: string, ctxt: ISelectionProviderContext): Promise<ISelectionProviderValues | any> {
26
+ const dashboardSelectionProvider: IDashboardVariableSelectionProvider<ISelectionProviderContext> | undefined = this.solidRegistry.getDashboardVariableSelectionProviderInstance(optionValue);
27
+ if (!dashboardSelectionProvider) {
28
+ return null;
29
+ }
30
+
31
+ return { label: dashboardSelectionProvider.name(), value: dashboardSelectionProvider.name() };
32
+ }
33
+
34
+ async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
35
+ const dashboardSelectionProviders = this.solidRegistry.getDashboardVariableSelectionProviders()
36
+ //Exclude the SQL dynamic provider from the list, (since although it is a dashboard selection provider, it is not a valid option for the user to select)
37
+ return dashboardSelectionProviders.filter(i => (i.name !== SQL_DYNAMIC_PROVIDER_NAME)).map(i => {
38
+ return { label: i.name, value: i.name };
39
+ });
40
+ }
41
+ }
@@ -8,6 +8,8 @@ import { IS_SOLID_DATABASE_MODULE } from 'src/decorators/solid-database-module.d
8
8
  import { SolidRegistry } from 'src/helpers/solid-registry';
9
9
  import { CRUDService } from './crud.service';
10
10
  import { IS_SCHEDULED_JOB_PROVIDER } from 'src/decorators/scheduled-job-provider.decorator';
11
+ import { IS_DASHBOARD_VARIABLE_SELECTION_PROVIDER } from 'src/decorators/dashboard-selection-provider.decorator';
12
+ import { IS_DASHBOARD_QUESTION_DATA_PROVIDER } from 'src/decorators/dashboard-question-data-provider.decorator';
11
13
 
12
14
  @Injectable()
13
15
  export class SolidIntrospectService implements OnApplicationBootstrap {
@@ -42,6 +44,26 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
42
44
  this.solidRegistry.registerSelectionProvider(selectionProvider);
43
45
  });
44
46
 
47
+ // Register all IDashboardSelectionProvider implementations
48
+ const dashboardVariableSelectionProviders = this.discoveryService
49
+ .getProviders()
50
+ .filter((provider) => this.isDashboardVariableSelectionProvider(provider));
51
+
52
+ dashboardVariableSelectionProviders.forEach((dashboardSelectionProvider) => {
53
+ // @ts-ignore
54
+ this.solidRegistry.registerDashboardVariableSelectionProvider(dashboardSelectionProvider);
55
+ });
56
+
57
+ // Register all IDashboardSelectionProvider implementations
58
+ const dashboardQuestionDataProviders = this.discoveryService
59
+ .getProviders()
60
+ .filter((provider) => this.isDashboardQuestionDataProvider(provider));
61
+
62
+ dashboardQuestionDataProviders.forEach((provider) => {
63
+ // @ts-ignore
64
+ this.solidRegistry.registerDashboardQuestionDataProvider(provider);
65
+ });
66
+
45
67
 
46
68
  // Register all IComputedProvider implementations
47
69
  const computedFieldProviders = this.discoveryService
@@ -94,6 +116,16 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
94
116
 
95
117
  }
96
118
 
119
+ isDashboardQuestionDataProvider(providerWrapper: InstanceWrapper<any>) {
120
+ const { instance } = providerWrapper;
121
+ if (!instance) return false;
122
+ const provider = this.reflector.get<boolean>(
123
+ IS_DASHBOARD_QUESTION_DATA_PROVIDER,
124
+ instance.constructor,
125
+ );
126
+ return !!provider;
127
+ }
128
+
97
129
  // This method identifies a provider as a seeder if it has a seed method i.e duck typing
98
130
  private isSeeder(provider: InstanceWrapper) {
99
131
  const { instance } = provider;
@@ -118,6 +150,16 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
118
150
  return !!isSelectionProvider;
119
151
  }
120
152
 
153
+ private isDashboardVariableSelectionProvider(provider: InstanceWrapper) {
154
+ const { instance } = provider;
155
+ if (!instance) return false;
156
+ const isDashboardSelectionProvider = this.reflector.get<boolean>(
157
+ IS_DASHBOARD_VARIABLE_SELECTION_PROVIDER,
158
+ instance.constructor,
159
+ );
160
+ return !!isDashboardSelectionProvider;
161
+ }
162
+
121
163
  private isComputedFieldProvider(provider: InstanceWrapper) {
122
164
  const { instance } = provider;
123
165
  if (!instance) return false;
@@ -0,0 +1,125 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { SqlExpression, SqlExpressionOperator } from "./question-data-providers/chartjs-sql-data-provider.service";
3
+
4
+ export interface SqlReplacementResult {
5
+ rawSql: string;
6
+ parameters: any[]; // Positional parameters
7
+ }
8
+
9
+ @Injectable()
10
+ export class SqlExpressionResolverService {
11
+ resolveSqlWithExpressions(sql: string, expressions: SqlExpression[]): SqlReplacementResult {
12
+ const variableToColumnMap: Record<string, string> = {};
13
+ const variablePattern = /{{\s*(\w+)\s*\[\s*([\w.]+)\s*\]\s*}}/g;
14
+
15
+ // --- Pass 1: extract variable -> column mappings ---
16
+ let simplifiedSql = sql.replace(variablePattern, (_, variableName, columnName) => {
17
+ variableToColumnMap[variableName] = columnName;
18
+ return `{{${variableName}}}`;
19
+ });
20
+
21
+ // --- Pass 2: Replace each variable with positional fragment ---
22
+ let paramIndex = 1;
23
+ const parameters: any[] = [];
24
+
25
+ for (const expr of expressions) {
26
+ const column = variableToColumnMap[expr.variableName];
27
+ if (!column) continue;
28
+
29
+ let sqlFragment = '';
30
+ const placeholder = `{{${expr.variableName}}}`;
31
+
32
+ switch (expr.operator) {
33
+ case SqlExpressionOperator.EQUALS:
34
+ sqlFragment = `${column} = $${paramIndex++}`;
35
+ parameters.push(expr.value[0]);
36
+ break;
37
+
38
+ case SqlExpressionOperator.NOT_EQUALS:
39
+ sqlFragment = `${column} != $${paramIndex++}`;
40
+ parameters.push(expr.value[0]);
41
+ break;
42
+
43
+ case SqlExpressionOperator.CONTAINS:
44
+ sqlFragment = `${column} LIKE $${paramIndex++}`;
45
+ parameters.push(`%${expr.value[0]}%`);
46
+ break;
47
+
48
+ case SqlExpressionOperator.NOT_CONTAINS:
49
+ sqlFragment = `${column} NOT LIKE $${paramIndex++}`;
50
+ parameters.push(`%${expr.value[0]}%`);
51
+ break;
52
+
53
+ case SqlExpressionOperator.STARTS_WITH:
54
+ sqlFragment = `${column} LIKE $${paramIndex++}`;
55
+ parameters.push(`${expr.value[0]}%`);
56
+ break;
57
+
58
+ case SqlExpressionOperator.ENDS_WITH:
59
+ sqlFragment = `${column} LIKE $${paramIndex++}`;
60
+ parameters.push(`%${expr.value[0]}`);
61
+ break;
62
+
63
+ case SqlExpressionOperator.IN:
64
+ const inParams = expr.value.map(val => {
65
+ parameters.push(val);
66
+ return `$${paramIndex++}`;
67
+ });
68
+ sqlFragment = `${column} IN (${inParams.join(", ")})`;
69
+ break;
70
+
71
+ case SqlExpressionOperator.NOT_IN:
72
+ const notInParams = expr.value.map(val => {
73
+ parameters.push(val);
74
+ return `$${paramIndex++}`;
75
+ });
76
+ sqlFragment = `${column} NOT IN (${notInParams.join(", ")})`;
77
+ break;
78
+
79
+ case SqlExpressionOperator.BETWEEN:
80
+ sqlFragment = `${column} BETWEEN $${paramIndex} AND $${paramIndex + 1}`;
81
+ parameters.push(expr.value[0], expr.value[1]);
82
+ paramIndex += 2;
83
+ break;
84
+
85
+ case SqlExpressionOperator.LT:
86
+ sqlFragment = `${column} < $${paramIndex++}`;
87
+ parameters.push(expr.value[0]);
88
+ break;
89
+
90
+ case SqlExpressionOperator.LTE:
91
+ sqlFragment = `${column} <= $${paramIndex++}`;
92
+ parameters.push(expr.value[0]);
93
+ break;
94
+
95
+ case SqlExpressionOperator.GT:
96
+ sqlFragment = `${column} > $${paramIndex++}`;
97
+ parameters.push(expr.value[0]);
98
+ break;
99
+
100
+ case SqlExpressionOperator.GTE:
101
+ sqlFragment = `${column} >= $${paramIndex++}`;
102
+ parameters.push(expr.value[0]);
103
+ break;
104
+
105
+ default:
106
+ throw new Error(`Unsupported SQL operator: ${expr.operator}`);
107
+ }
108
+ simplifiedSql = simplifiedSql.replace(placeholder, sqlFragment);
109
+ }
110
+
111
+ // --- Final cleanup: remove any remaining placeholders ---
112
+ simplifiedSql = simplifiedSql.replace(/{{\s*\w+\s*}}/g, '');
113
+
114
+ // Remove dangling where clause if no expressions were applied
115
+ simplifiedSql = simplifiedSql.replace(/\bwhere\b\s*$/i, '').trim();
116
+
117
+ // Need to handle scenarios of complex expression i.e with and / or clauses. probably need to have this logic in the sql expression object itself
118
+
119
+
120
+ return {
121
+ rawSql: simplifiedSql,
122
+ parameters
123
+ };
124
+ }
125
+ }