@oneuptime/common 12.0.22 → 12.0.24

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 (345) hide show
  1. package/Models/DatabaseModels/DetectionRule.ts +55 -0
  2. package/Models/DatabaseModels/Index.ts +2 -0
  3. package/Models/DatabaseModels/NetworkDevice.ts +44 -0
  4. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
  5. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
  6. package/Models/DatabaseModels/Project.ts +68 -0
  7. package/Models/DatabaseModels/User.ts +68 -0
  8. package/Server/API/TelemetryAPI.ts +29 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  16. package/Server/Middleware/GzipRequestBody.ts +311 -0
  17. package/Server/Middleware/MultipartFormData.ts +87 -3
  18. package/Server/Services/DetectionRuleService.ts +73 -6
  19. package/Server/Services/Index.ts +207 -0
  20. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
  21. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -0
  22. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
  23. package/Server/Services/NetworkDeviceService.ts +148 -0
  24. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  25. package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
  26. package/Server/Services/ProjectService.ts +29 -19
  27. package/Server/Services/SecurityEventService.ts +90 -13
  28. package/Server/Services/StatusPageResourceService.ts +223 -0
  29. package/Server/Services/TelemetryAttributeService.ts +17 -0
  30. package/Server/Services/UserService.ts +4 -6
  31. package/Server/Types/Markdown.ts +161 -67
  32. package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
  33. package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
  34. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
  35. package/Server/Utils/Attribution.ts +4 -0
  36. package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
  37. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +39 -1
  38. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
  39. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
  40. package/Server/Utils/StartServer.ts +12 -32
  41. package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
  42. package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
  43. package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
  44. package/Server/Views/Partials/Head.ejs +2 -1
  45. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
  46. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
  47. package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
  48. package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
  49. package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
  50. package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
  51. package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
  52. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
  53. package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
  54. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
  55. package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
  56. package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
  57. package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
  58. package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
  59. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
  60. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
  61. package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
  62. package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
  63. package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
  64. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
  65. package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
  66. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
  67. package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
  68. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
  69. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
  70. package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
  71. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
  72. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
  73. package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
  74. package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
  75. package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
  76. package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -0
  77. package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
  78. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
  79. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
  80. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -0
  81. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
  82. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
  83. package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
  84. package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
  85. package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
  86. package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
  87. package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
  88. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
  89. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
  91. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +97 -1
  92. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
  93. package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
  94. package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
  95. package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
  96. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
  97. package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
  98. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
  99. package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
  100. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
  101. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
  102. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
  103. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
  104. package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
  105. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
  106. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
  107. package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
  108. package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
  109. package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
  110. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
  111. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
  112. package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
  113. package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
  114. package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
  115. package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
  116. package/Tests/UI/Components/SideOver.test.tsx +1 -1
  117. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
  118. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
  119. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
  120. package/Tests/UI/Components/Toggle.test.tsx +25 -0
  121. package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
  122. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
  123. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +516 -0
  124. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
  125. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
  126. package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
  127. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
  128. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
  129. package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
  130. package/Types/Code/YamlSyntax.ts +396 -0
  131. package/Types/Exception/ExceptionCode.ts +1 -0
  132. package/Types/Exception/PayloadTooLargeException.ts +8 -0
  133. package/Types/Marketing/Attribution.ts +10 -0
  134. package/Types/Marketing/MarketingEvent.ts +4 -0
  135. package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
  136. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
  137. package/Types/NetworkAutomation/RuleRunResult.ts +84 -0
  138. package/Types/Permission.ts +47 -0
  139. package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
  140. package/Types/Telemetry/LlmMetricConventions.ts +124 -0
  141. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
  142. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
  143. package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
  144. package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
  145. package/UI/Components/Detail/Detail.tsx +10 -0
  146. package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
  147. package/UI/Components/Forms/Fields/FormField.tsx +31 -0
  148. package/UI/Components/Forms/Types/Field.ts +8 -0
  149. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  150. package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
  151. package/UI/Components/Forms/Validation.ts +46 -0
  152. package/UI/Components/Input/Input.tsx +3 -0
  153. package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
  154. package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
  155. package/UI/Components/ModelTable/Column.ts +8 -0
  156. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
  157. package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
  158. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
  159. package/UI/Components/SideOver/SideOver.tsx +5 -5
  160. package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
  161. package/UI/Components/StatusPage/ResourceList.tsx +197 -38
  162. package/UI/Components/Toggle/Toggle.tsx +9 -1
  163. package/UI/Components/Types/FieldType.ts +1 -0
  164. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
  165. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +257 -0
  166. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
  167. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
  168. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
  169. package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
  170. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
  171. package/Utils/StatusPage/ResourceExplorer.ts +258 -0
  172. package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
  173. package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
  174. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  175. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  176. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  177. package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
  178. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  179. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
  180. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
  181. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -0
  182. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/Project.js +76 -0
  184. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/User.js +76 -0
  186. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  187. package/build/dist/Server/API/TelemetryAPI.js +17 -0
  188. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
  190. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
  191. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
  192. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
  193. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
  194. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
  195. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
  196. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
  197. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
  198. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
  199. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
  200. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
  201. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  202. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  203. package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
  204. package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
  205. package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
  206. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  207. package/build/dist/Server/Services/DetectionRuleService.js +49 -4
  208. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  209. package/build/dist/Server/Services/Index.js +206 -0
  210. package/build/dist/Server/Services/Index.js.map +1 -1
  211. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
  212. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
  213. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
  214. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
  215. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
  216. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  217. package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
  218. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  219. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
  220. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  221. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
  222. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
  223. package/build/dist/Server/Services/ProjectService.js +25 -45
  224. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  225. package/build/dist/Server/Services/SecurityEventService.js +57 -5
  226. package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
  227. package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
  228. package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
  229. package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
  230. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  231. package/build/dist/Server/Services/UserService.js +2 -12
  232. package/build/dist/Server/Services/UserService.js.map +1 -1
  233. package/build/dist/Server/Types/Markdown.js +147 -61
  234. package/build/dist/Server/Types/Markdown.js.map +1 -1
  235. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
  236. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
  237. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
  238. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  239. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
  240. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  241. package/build/dist/Server/Utils/Attribution.js.map +1 -1
  242. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
  243. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +26 -0
  245. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  246. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
  247. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  248. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
  249. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  250. package/build/dist/Server/Utils/StartServer.js +12 -21
  251. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  252. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
  253. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
  254. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
  255. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
  256. package/build/dist/Types/Code/YamlSyntax.js +212 -0
  257. package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
  258. package/build/dist/Types/Exception/ExceptionCode.js +1 -0
  259. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  260. package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
  261. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
  262. package/build/dist/Types/Marketing/Attribution.js +10 -0
  263. package/build/dist/Types/Marketing/Attribution.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
  265. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  266. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
  267. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
  268. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +24 -0
  269. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  270. package/build/dist/Types/Permission.js +41 -0
  271. package/build/dist/Types/Permission.js.map +1 -1
  272. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
  273. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  274. package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
  275. package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
  276. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  277. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  278. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
  279. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  280. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
  281. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  282. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
  283. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
  284. package/build/dist/UI/Components/Detail/Detail.js +9 -0
  285. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  286. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
  287. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  288. package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
  289. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  290. package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
  291. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  292. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  293. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
  294. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
  295. package/build/dist/UI/Components/Forms/Validation.js +27 -0
  296. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  297. package/build/dist/UI/Components/Input/Input.js +1 -1
  298. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  299. package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
  300. package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
  301. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
  302. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  303. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
  304. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  305. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
  306. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
  307. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
  308. package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
  309. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
  310. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  311. package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
  312. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  313. package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
  314. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  315. package/build/dist/UI/Components/Types/FieldType.js +1 -0
  316. package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
  317. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
  318. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  319. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +149 -0
  320. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
  321. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
  322. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
  323. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
  324. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
  325. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
  326. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
  327. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
  328. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
  329. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
  330. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  331. package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
  332. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
  333. package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
  334. package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
  335. package/jest.config.json +2 -0
  336. package/package.json +1 -1
  337. package/tsconfig.json +2 -1
  338. package/Tests/UI/Components/Toast.test.tsx +0 -197
  339. package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
  340. package/UI/Components/Toast/Toast.tsx +0 -189
  341. package/UI/Components/Toast/ToastInit.tsx +0 -110
  342. package/build/dist/UI/Components/Toast/Toast.js +0 -123
  343. package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
  344. package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
  345. package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
@@ -0,0 +1,256 @@
1
+ import { TelemetryAttributeService } from "../../../Server/Services/TelemetryAttributeService";
2
+ import { Statement } from "../../../Server/Utils/AnalyticsDatabase/Statement";
3
+ import SecurityEvent from "../../../Models/AnalyticsModels/SecurityEvent";
4
+ import AnalyticsTableName from "../../../Types/AnalyticsDatabase/AnalyticsTableName";
5
+ import TelemetryType from "../../../Types/Telemetry/TelemetryType";
6
+ import ObjectID from "../../../Types/ObjectID";
7
+ import { describe, expect, test } from "@jest/globals";
8
+
9
+ /*
10
+ * Security events are the fourth signal to grow an attribute picker, and the
11
+ * one where it matters most: the OCSF schema only has typed columns for the
12
+ * common fields, so everything else a SIEM sent - device.hostname,
13
+ * finding_info.title, metadata.product.name - lives in the `attributes` map.
14
+ * The security events table offers those as columns, and this is the query
15
+ * that tells it which ones exist.
16
+ *
17
+ * TelemetryType.SecurityEvent was in the enum long before there was a source
18
+ * for it, and getTelemetrySource returns null for anything it does not
19
+ * recognise - which makes a missing case a silently empty picker rather than
20
+ * an error. That is what the first test here exists to catch.
21
+ */
22
+
23
+ type SourceShape = {
24
+ tableName: string;
25
+ attributesColumn: string;
26
+ attributeKeysColumn?: string | undefined;
27
+ timeColumn: string;
28
+ isMutableMetricSource?: boolean | undefined;
29
+ };
30
+
31
+ type GetSourceFunction = (telemetryType: TelemetryType) => SourceShape | null;
32
+
33
+ const getSource: GetSourceFunction = (
34
+ telemetryType: TelemetryType,
35
+ ): SourceShape | null => {
36
+ return (
37
+ new TelemetryAttributeService() as unknown as {
38
+ getTelemetrySource: (type: TelemetryType) => SourceShape | null;
39
+ }
40
+ ).getTelemetrySource(telemetryType);
41
+ };
42
+
43
+ describe("TelemetryAttributeService - security event source", () => {
44
+ test("security events resolve to a source at all", () => {
45
+ expect(getSource(TelemetryType.SecurityEvent)).not.toBeNull();
46
+ });
47
+
48
+ test("points at the security event table", () => {
49
+ const source: SourceShape = getSource(
50
+ TelemetryType.SecurityEvent,
51
+ ) as SourceShape;
52
+
53
+ expect(source.tableName).toBe(AnalyticsTableName.SecurityEvent);
54
+ expect(source.tableName).toBe(new SecurityEvent().tableName);
55
+ });
56
+
57
+ /*
58
+ * The names are bound into the statement as identifiers, so a typo here is
59
+ * not a type error anywhere - it is a query that fails at runtime against a
60
+ * table that does exist.
61
+ */
62
+ test("names columns that are actually on the model", () => {
63
+ const source: SourceShape = getSource(
64
+ TelemetryType.SecurityEvent,
65
+ ) as SourceShape;
66
+
67
+ const model: SecurityEvent = new SecurityEvent();
68
+
69
+ expect(model.hasColumn(source.attributesColumn)).toBe(true);
70
+ expect(model.hasColumn(source.attributeKeysColumn as string)).toBe(true);
71
+ expect(model.hasColumn(source.timeColumn)).toBe(true);
72
+ });
73
+
74
+ /*
75
+ * The sidecar array column is what keeps this cheap: without it the SQL
76
+ * falls back to mapKeys(attributes), which reads the whole map off every
77
+ * row instead of an already-denormalized array.
78
+ */
79
+ test("uses the attributeKeys sidecar rather than expanding the map", () => {
80
+ const source: SourceShape = getSource(
81
+ TelemetryType.SecurityEvent,
82
+ ) as SourceShape;
83
+
84
+ expect(source.attributeKeysColumn).toBe("attributeKeys");
85
+ expect(source.attributesColumn).toBe("attributes");
86
+ expect(source.timeColumn).toBe("time");
87
+ });
88
+
89
+ test("is not treated as a mutable metric source", () => {
90
+ const source: SourceShape = getSource(
91
+ TelemetryType.SecurityEvent,
92
+ ) as SourceShape;
93
+
94
+ expect(source.isMutableMetricSource).toBeFalsy();
95
+ });
96
+
97
+ // A signal with no source still degrades to "no attributes", not an error.
98
+ test("a telemetry type with no source is still null", () => {
99
+ expect(getSource(TelemetryType.Profile)).toBeNull();
100
+ });
101
+ });
102
+
103
+ describe("TelemetryAttributeService - security event attribute keys query", () => {
104
+ type BuildAttributesInput = {
105
+ projectId: ObjectID;
106
+ tableName: string;
107
+ attributesColumn: string;
108
+ attributeKeysColumn?: string | undefined;
109
+ timeColumn: string;
110
+ metricName?: string | undefined;
111
+ isMutableMetricSource?: boolean | undefined;
112
+ };
113
+
114
+ type BuildStatementFunction = () => Statement;
115
+
116
+ const buildStatement: BuildStatementFunction = (): Statement => {
117
+ const source: SourceShape = getSource(
118
+ TelemetryType.SecurityEvent,
119
+ ) as SourceShape;
120
+
121
+ return (
122
+ TelemetryAttributeService as unknown as {
123
+ buildAttributesStatement: (data: BuildAttributesInput) => Statement;
124
+ }
125
+ ).buildAttributesStatement({
126
+ projectId: ObjectID.generate(),
127
+ tableName: source.tableName,
128
+ attributesColumn: source.attributesColumn,
129
+ attributeKeysColumn: source.attributeKeysColumn,
130
+ timeColumn: source.timeColumn,
131
+ });
132
+ };
133
+
134
+ /*
135
+ * Table and column names ride in as {pN:Identifier} bindings rather than as
136
+ * SQL text, so everything below asserts against the bound parameters. That
137
+ * is the point of the check as much as a quirk of it: nothing
138
+ * caller-influenced is ever concatenated into these statements.
139
+ */
140
+ test("aggregates the sidecar array over the security event table", () => {
141
+ const statement: Statement = buildStatement();
142
+
143
+ expect(Object.values(statement.query_params)).toContain(
144
+ AnalyticsTableName.SecurityEvent,
145
+ );
146
+ expect(statement.query).toContain("groupUniqArrayArray(");
147
+ expect(Object.values(statement.query_params)).toContain("attributeKeys");
148
+
149
+ /*
150
+ * The mapKeys() branch is the fallback for tables with no sidecar column;
151
+ * taking it here would read the whole map off every row.
152
+ */
153
+ expect(statement.query).not.toContain("mapKeys(");
154
+ });
155
+
156
+ test("scopes to one project, as a bound parameter", () => {
157
+ const statement: Statement = buildStatement();
158
+
159
+ expect(statement.query).toContain("projectId = ");
160
+ /*
161
+ * A tenant id interpolated into SQL would be both an injection surface
162
+ * and a cache-buster; every value in these statements is parameterized.
163
+ */
164
+ expect(statement.query).toMatch(/projectId = \{p\d+:String\}/);
165
+ });
166
+
167
+ test("bounds the scan by time and by execution budget", () => {
168
+ const statement: Statement = buildStatement();
169
+
170
+ expect(Object.values(statement.query_params)).toContain("time");
171
+ expect(statement.query).toMatch(/>= \{p\d+:DateTime\}/);
172
+ expect(statement.query).toContain("max_execution_time = 45");
173
+ expect(statement.query).toContain("timeout_overflow_mode = 'break'");
174
+ });
175
+
176
+ test("skips rows with no attributes at all", () => {
177
+ const statement: Statement = buildStatement();
178
+
179
+ expect(statement.query).toMatch(/NOT empty\(\{p\d+:Identifier\}\)/);
180
+ });
181
+
182
+ // metricName is a Metric-only filter; nothing here should mention `name`.
183
+ test("does not filter by metric name", () => {
184
+ const statement: Statement = buildStatement();
185
+
186
+ expect(statement.query).not.toContain("AND name = ");
187
+ });
188
+ });
189
+
190
+ describe("TelemetryAttributeService - security event attribute values query", () => {
191
+ type BuildValuesInput = {
192
+ projectId: ObjectID;
193
+ source: SourceShape;
194
+ metricName?: string | undefined;
195
+ attributeKey: string;
196
+ searchText?: string | undefined;
197
+ };
198
+
199
+ type BuildValuesFunction = (searchText?: string | undefined) => Statement;
200
+
201
+ const buildValues: BuildValuesFunction = (
202
+ searchText?: string | undefined,
203
+ ): Statement => {
204
+ return (
205
+ TelemetryAttributeService as unknown as {
206
+ buildAttributeValuesStatement: (data: BuildValuesInput) => Statement;
207
+ }
208
+ ).buildAttributeValuesStatement({
209
+ projectId: ObjectID.generate(),
210
+ source: getSource(TelemetryType.SecurityEvent) as SourceShape,
211
+ attributeKey: "device.hostname",
212
+ ...(searchText === undefined ? {} : { searchText }),
213
+ });
214
+ };
215
+
216
+ test("reads the requested key out of the map on the security event table", () => {
217
+ const statement: Statement = buildValues();
218
+
219
+ const params: Array<unknown> = Object.values(statement.query_params);
220
+
221
+ expect(params).toContain(AnalyticsTableName.SecurityEvent);
222
+ expect(params).toContain("attributes");
223
+ expect(params).toContain("device.hostname");
224
+ expect(statement.query).toContain("mapContains(");
225
+ });
226
+
227
+ /*
228
+ * The key is caller-supplied, so it must never reach the SQL text - an
229
+ * attribute key can contain quotes, brackets, anything a payload had.
230
+ */
231
+ test("never inlines the attribute key into the SQL text", () => {
232
+ const statement: Statement = buildValues();
233
+
234
+ expect(statement.query).not.toContain("device.hostname");
235
+ });
236
+
237
+ test("narrows server-side when the picker sends search text", () => {
238
+ const statement: Statement = buildValues("web");
239
+
240
+ expect(statement.query).toContain("ILIKE");
241
+ expect(Object.values(statement.query_params)).toContain("%web%");
242
+ });
243
+
244
+ test("whitespace-only search text is not a filter", () => {
245
+ const statement: Statement = buildValues(" ");
246
+
247
+ expect(statement.query).not.toContain("ILIKE");
248
+ });
249
+
250
+ test("caps the number of values returned", () => {
251
+ const statement: Statement = buildValues();
252
+
253
+ expect(statement.query).toContain("ORDER BY attributeValue ASC");
254
+ expect(statement.query).toContain("LIMIT ");
255
+ });
256
+ });
@@ -0,0 +1,417 @@
1
+ /*
2
+ * The runtime component registry reaches the native isolated-vm addon through
3
+ * the JavaScript component's sandbox (Components/JavaScript -> VMRunner).
4
+ * Nothing under test here runs a sandbox, and the prebuilt binary cannot
5
+ * always dlopen in the test environment - so stub the module out before
6
+ * anything imports it.
7
+ */
8
+ jest.mock("isolated-vm", () => {
9
+ return {};
10
+ });
11
+
12
+ import DatabaseBaseModel from "../../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
13
+ import Entities from "../../../../../Models/DatabaseModels/Index";
14
+ import DatabaseService from "../../../../../Server/Services/DatabaseService";
15
+ import Services from "../../../../../Server/Services/Index";
16
+ import ComponentCode from "../../../../../Server/Types/Workflow/ComponentCode";
17
+ import RuntimeComponents from "../../../../../Server/Types/Workflow/Components/Index";
18
+ import BaseService from "../../../../../Server/Services/BaseService";
19
+ import EnableWorkflowOn from "../../../../../Types/BaseDatabase/EnableWorkflowOn";
20
+ import Dictionary from "../../../../../Types/Dictionary";
21
+ import Text from "../../../../../Types/Text";
22
+ import ComponentMetadata from "../../../../../Types/Workflow/Component";
23
+ import NonModelComponents from "../../../../../Types/Workflow/Components";
24
+ import BaseModelComponentFactory from "../../../../../Types/Workflow/Components/BaseModel";
25
+
26
+ /*
27
+ * A workflow step is resolved twice on the worker, against two registries that
28
+ * are built from two different sources:
29
+ *
30
+ * metadata - App/FeatureSet/Workflow/Utils/ComponentMetadata.ts walks
31
+ * Models/DatabaseModels/Index.ts and emits components for every
32
+ * model carrying @EnableWorkflow. This is also what fills the
33
+ * editor palette, so it decides what a user can drag onto a
34
+ * workflow and save.
35
+ * code - Server/Types/Workflow/Components/Index.ts walks
36
+ * Server/Services/Index.ts and emits components for every
37
+ * REGISTERED DatabaseService whose model carries @EnableWorkflow.
38
+ * This is what RunWorkflow actually executes.
39
+ *
40
+ * A model that is workflow-enabled but whose service was never added to
41
+ * Server/Services/Index.ts satisfies the first registry and not the second. Its
42
+ * components appear in the palette, save into a workflow without complaint, and
43
+ * then fail at execution time with
44
+ * `BadDataException("Component <id> not found")` from RunWorkflow - the failure
45
+ * that Dashboard shipped with, where `dashboard-update-one` was offered by the
46
+ * editor while DashboardService was absent from Server/Services/Index.ts.
47
+ *
48
+ * Nothing in the type system ties the two lists together, so these tests are
49
+ * the thing that keeps them in step.
50
+ */
51
+
52
+ interface ModelComponents {
53
+ modelName: string;
54
+ tableName: string;
55
+ enableWorkflowOn: EnableWorkflowOn;
56
+ componentIds: Array<string>;
57
+ }
58
+
59
+ /*
60
+ * Every model carrying @EnableWorkflow, whether or not it ends up contributing
61
+ * components. Filtering the empty ones out here instead would make any
62
+ * assertion phrased as "every workflow-enabled model ..." quietly exempt them,
63
+ * which is how MetricType and TeamComplianceSetting - both annotated with all
64
+ * four flags false - stay invisible to the palette while still being
65
+ * workflow-annotated models.
66
+ */
67
+ const workflowAnnotatedModels: Array<ModelComponents> = [];
68
+
69
+ for (const model of Entities) {
70
+ const instance: DatabaseBaseModel = new model();
71
+
72
+ if (!instance.enableWorkflowOn) {
73
+ continue;
74
+ }
75
+
76
+ // Mirrors loadAllComponentMetadata() in App/FeatureSet/Workflow/Utils/ComponentMetadata.ts.
77
+ const components: Array<ComponentMetadata> =
78
+ BaseModelComponentFactory.getComponents(instance);
79
+
80
+ workflowAnnotatedModels.push({
81
+ modelName: model.name,
82
+ tableName: instance.tableName!,
83
+ enableWorkflowOn: instance.enableWorkflowOn,
84
+ componentIds: components.map((component: ComponentMetadata) => {
85
+ return component.id;
86
+ }),
87
+ });
88
+ }
89
+
90
+ // The subset the editor actually puts in front of a user.
91
+ const editorModelComponents: Array<ModelComponents> =
92
+ workflowAnnotatedModels.filter((entry: ModelComponents) => {
93
+ return entry.componentIds.length > 0;
94
+ });
95
+
96
+ const allEditorModelComponentIds: Array<string> = editorModelComponents.flatMap(
97
+ (entry: ModelComponents) => {
98
+ return entry.componentIds;
99
+ },
100
+ );
101
+
102
+ const registeredDatabaseServices: Array<DatabaseService<DatabaseBaseModel>> =
103
+ Services.filter((service: BaseService) => {
104
+ return service instanceof DatabaseService;
105
+ }) as Array<DatabaseService<DatabaseBaseModel>>;
106
+
107
+ describe("Workflow component registry parity", () => {
108
+ describe("editor palette vs worker runtime", () => {
109
+ test("every model component the editor offers is runnable by the worker", () => {
110
+ /*
111
+ * Reported per model rather than per component id: one unregistered
112
+ * service costs eleven component ids, and the model name is what points
113
+ * at the missing Server/Services/Index.ts entry.
114
+ */
115
+ const modelsWithMissingComponents: Array<string> = editorModelComponents
116
+ .filter((entry: ModelComponents) => {
117
+ return entry.componentIds.some((id: string) => {
118
+ return !RuntimeComponents[id];
119
+ });
120
+ })
121
+ .map((entry: ModelComponents) => {
122
+ return entry.modelName;
123
+ });
124
+
125
+ expect(modelsWithMissingComponents).toEqual([]);
126
+ });
127
+
128
+ test("every non-model component the editor offers is runnable by the worker", () => {
129
+ const missing: Array<string> = NonModelComponents.filter(
130
+ (component: ComponentMetadata) => {
131
+ return !RuntimeComponents[component.id];
132
+ },
133
+ ).map((component: ComponentMetadata) => {
134
+ return component.id;
135
+ });
136
+
137
+ expect(missing).toEqual([]);
138
+ });
139
+
140
+ test("the worker runs no model component the editor cannot produce", () => {
141
+ /*
142
+ * The other direction. A component the worker can run but the editor
143
+ * never offers is not a user-visible failure, but it means the two id
144
+ * derivations have drifted apart - which is the same defect pointed the
145
+ * other way, and would hide the next real gap.
146
+ */
147
+ const editorIds: Set<string> = new Set(allEditorModelComponentIds);
148
+ const nonModelIds: Set<string> = new Set(
149
+ NonModelComponents.map((component: ComponentMetadata) => {
150
+ return component.id;
151
+ }),
152
+ );
153
+
154
+ const runtimeOnly: Array<string> = Object.keys(RuntimeComponents).filter(
155
+ (id: string) => {
156
+ return !editorIds.has(id) && !nonModelIds.has(id);
157
+ },
158
+ );
159
+
160
+ expect(runtimeOnly).toEqual([]);
161
+ });
162
+
163
+ test("the two registries agree on the exact component id set", () => {
164
+ const editorIds: Array<string> = [
165
+ ...allEditorModelComponentIds,
166
+ ...NonModelComponents.map((component: ComponentMetadata) => {
167
+ return component.id;
168
+ }),
169
+ ].sort();
170
+
171
+ expect(Object.keys(RuntimeComponents).sort()).toEqual(editorIds);
172
+ });
173
+ });
174
+
175
+ describe("the palette is actually populated", () => {
176
+ /*
177
+ * Guards the parity assertions above against passing because both sides
178
+ * are empty - an import that silently resolves to nothing would otherwise
179
+ * turn this whole file green.
180
+ */
181
+ test("the editor offers components for many models", () => {
182
+ expect(editorModelComponents.length).toBeGreaterThan(100);
183
+ });
184
+
185
+ test("the editor offers the non-model components too", () => {
186
+ expect(NonModelComponents.length).toBeGreaterThan(0);
187
+ });
188
+
189
+ test("a model contributes components exactly when one of its flags is on", () => {
190
+ /*
191
+ * The invariant the palette actually rests on. Phrasing it as "every
192
+ * workflow-enabled model contributes a component" would be false -
193
+ * @EnableWorkflow with every flag false is legal and contributes nothing
194
+ * - and phrasing it over the already-filtered list would make it
195
+ * unfailable. Comparing the two conditions catches both a flag that
196
+ * stops emitting components and a component emitted for a flag that is
197
+ * switched off.
198
+ */
199
+ const disagreeing: Array<string> = workflowAnnotatedModels
200
+ .filter((entry: ModelComponents) => {
201
+ const anyFlagOn: boolean = Boolean(
202
+ entry.enableWorkflowOn.create ||
203
+ entry.enableWorkflowOn.read ||
204
+ entry.enableWorkflowOn.update ||
205
+ entry.enableWorkflowOn.delete,
206
+ );
207
+
208
+ const contributesComponents: boolean = entry.componentIds.length > 0;
209
+
210
+ return anyFlagOn !== contributesComponents;
211
+ })
212
+ .map((entry: ModelComponents) => {
213
+ return entry.modelName;
214
+ });
215
+
216
+ expect(disagreeing).toEqual([]);
217
+ });
218
+
219
+ test("the models annotated but inert are exactly the ones we know about", () => {
220
+ /*
221
+ * @EnableWorkflow with every flag false is legal and contributes nothing,
222
+ * so such a model is outside the parity requirement further down. Naming
223
+ * them rather than looping over whatever happens to be inert keeps that
224
+ * exemption deliberate: a new inert model fails here and has to be
225
+ * acknowledged, instead of quietly joining a list nothing asserts on.
226
+ */
227
+ const inert: Array<string> = workflowAnnotatedModels
228
+ .filter((entry: ModelComponents) => {
229
+ return entry.componentIds.length === 0;
230
+ })
231
+ .map((entry: ModelComponents) => {
232
+ return entry.modelName;
233
+ })
234
+ .sort();
235
+
236
+ expect(inert).toEqual(["MetricType", "TeamComplianceSetting"]);
237
+ });
238
+ });
239
+
240
+ describe("Dashboard, the model this parity check was written for", () => {
241
+ /*
242
+ * Dashboard is workflow-enabled and exported from
243
+ * Models/DatabaseModels/Index.ts, but DashboardService was missing from
244
+ * Server/Services/Index.ts - only DashboardDomainService was there. Every
245
+ * dashboard component was therefore offered by the editor and unrunnable.
246
+ */
247
+ test("dashboard-update-one is runnable", () => {
248
+ expect(RuntimeComponents["dashboard-update-one"]).toBeDefined();
249
+ });
250
+
251
+ test("the whole dashboard component family is runnable", () => {
252
+ const dashboard: ModelComponents | undefined = editorModelComponents.find(
253
+ (entry: ModelComponents) => {
254
+ return entry.modelName === "Dashboard";
255
+ },
256
+ );
257
+
258
+ expect(dashboard).toBeDefined();
259
+
260
+ const missing: Array<string> = dashboard!.componentIds.filter(
261
+ (id: string) => {
262
+ return !RuntimeComponents[id];
263
+ },
264
+ );
265
+
266
+ expect(missing).toEqual([]);
267
+ });
268
+
269
+ test("the dashboard components cover create, read, update and delete", () => {
270
+ for (const id of [
271
+ "dashboard-create-one",
272
+ "dashboard-create-many",
273
+ "dashboard-find-one",
274
+ "dashboard-find-many",
275
+ "dashboard-update-one",
276
+ "dashboard-update-many",
277
+ "dashboard-delete-one",
278
+ "dashboard-delete-many",
279
+ "dashboard-on-create",
280
+ "dashboard-on-update",
281
+ "dashboard-on-delete",
282
+ ]) {
283
+ expect(RuntimeComponents[id]).toBeDefined();
284
+ }
285
+ });
286
+ });
287
+
288
+ describe("the service registry the runtime is built from", () => {
289
+ test("every model the palette offers has a service registered in Server/Services/Index.ts", () => {
290
+ const servicedTableNames: Set<string> = new Set(
291
+ registeredDatabaseServices.map(
292
+ (service: DatabaseService<DatabaseBaseModel>) => {
293
+ return service.getModel().tableName!;
294
+ },
295
+ ),
296
+ );
297
+
298
+ const unserviced: Array<string> = editorModelComponents
299
+ .filter((entry: ModelComponents) => {
300
+ return !servicedTableNames.has(entry.tableName);
301
+ })
302
+ .map((entry: ModelComponents) => {
303
+ return entry.modelName;
304
+ });
305
+
306
+ expect(unserviced).toEqual([]);
307
+ });
308
+
309
+ test("no service is registered twice", () => {
310
+ /*
311
+ * Services are singletons, so a duplicated entry is the same object
312
+ * appearing twice - harmless to the registry, but it means an edit landed
313
+ * in the array twice and the second copy will rot.
314
+ */
315
+ const seen: Set<BaseService> = new Set();
316
+ const duplicated: Array<string> = [];
317
+
318
+ for (const service of Services) {
319
+ if (seen.has(service)) {
320
+ duplicated.push(service.constructor.name);
321
+ }
322
+ seen.add(service);
323
+ }
324
+
325
+ expect(duplicated).toEqual([]);
326
+ });
327
+
328
+ test("no two registered services claim the same model", () => {
329
+ const byTableName: Dictionary<Array<string>> = {};
330
+
331
+ for (const service of registeredDatabaseServices) {
332
+ const tableName: string = service.getModel().tableName!;
333
+ byTableName[tableName] = byTableName[tableName] || [];
334
+ byTableName[tableName]!.push(service.constructor.name);
335
+ }
336
+
337
+ const contested: Array<string> = Object.keys(byTableName).filter(
338
+ (tableName: string) => {
339
+ return byTableName[tableName]!.length > 1;
340
+ },
341
+ );
342
+
343
+ expect(contested).toEqual([]);
344
+ });
345
+ });
346
+
347
+ describe("runtime registry integrity", () => {
348
+ test("every registered component is a ComponentCode", () => {
349
+ const notComponentCode: Array<string> = Object.keys(
350
+ RuntimeComponents,
351
+ ).filter((id: string) => {
352
+ return !(RuntimeComponents[id] instanceof ComponentCode);
353
+ });
354
+
355
+ expect(notComponentCode).toEqual([]);
356
+ });
357
+
358
+ test("every component is registered under the id its own metadata declares", () => {
359
+ /*
360
+ * Each model component derives its metadata from the service it was
361
+ * constructed with, while the registry key is derived separately at the
362
+ * call site. A component wired to the wrong service still registers
363
+ * cleanly and then acts on the wrong table, so the two have to match.
364
+ */
365
+ const mismatched: Array<string> = [];
366
+
367
+ for (const id of Object.keys(RuntimeComponents)) {
368
+ const component: ComponentCode | undefined = RuntimeComponents[id];
369
+ const declaredId: string = component!.getMetadata().id;
370
+
371
+ if (declaredId !== id) {
372
+ mismatched.push(`${id} -> ${declaredId}`);
373
+ }
374
+ }
375
+
376
+ expect(mismatched).toEqual([]);
377
+ });
378
+
379
+ test("model component ids are derived from the table name", () => {
380
+ for (const entry of editorModelComponents) {
381
+ const prefix: string = Text.pascalCaseToDashes(entry.tableName);
382
+
383
+ const wrongPrefix: Array<string> = entry.componentIds.filter(
384
+ (id: string) => {
385
+ return !id.startsWith(`${prefix}-`);
386
+ },
387
+ );
388
+
389
+ expect(wrongPrefix).toEqual([]);
390
+ }
391
+ });
392
+
393
+ test("no two models derive the same component id", () => {
394
+ /*
395
+ * Two models whose table names collapse to the same dashed id would
396
+ * silently overwrite each other in the registry, leaving one model's
397
+ * components pointing at the other model's service.
398
+ */
399
+ const owners: Dictionary<Array<string>> = {};
400
+
401
+ for (const entry of editorModelComponents) {
402
+ for (const id of entry.componentIds) {
403
+ owners[id] = owners[id] || [];
404
+ owners[id]!.push(entry.modelName);
405
+ }
406
+ }
407
+
408
+ const collisions: Array<string> = Object.keys(owners).filter(
409
+ (id: string) => {
410
+ return owners[id]!.length > 1;
411
+ },
412
+ );
413
+
414
+ expect(collisions).toEqual([]);
415
+ });
416
+ });
417
+ });