@oneuptime/common 12.0.6 → 12.0.7

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 (424) hide show
  1. package/Models/DatabaseModels/AIInsight.ts +1 -1
  2. package/Models/DatabaseModels/DatabaseBaseModel/FileModel.ts +2 -3
  3. package/Models/DatabaseModels/DeletedProject.ts +595 -0
  4. package/Models/DatabaseModels/Index.ts +2 -0
  5. package/Models/DatabaseModels/Monitor.ts +111 -0
  6. package/Models/DatabaseModels/Project.ts +3 -3
  7. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -1
  8. package/Server/API/FileAPI.ts +15 -2
  9. package/Server/API/MicrosoftTeamsAPI.ts +6 -1
  10. package/Server/API/ProjectAPI.ts +214 -0
  11. package/Server/API/StatusPageAPI.ts +39 -6
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.ts +50 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.ts +67 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.ts +39 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  16. package/Server/Services/AIAgentService.ts +20 -0
  17. package/Server/Services/DashboardDomainService.ts +73 -28
  18. package/Server/Services/DatabaseService.ts +1 -0
  19. package/Server/Services/DeletedProjectService.ts +138 -0
  20. package/Server/Services/FileService.ts +61 -0
  21. package/Server/Services/IncidentEpisodePublicNoteService.ts +18 -0
  22. package/Server/Services/IncidentPublicNoteService.ts +18 -0
  23. package/Server/Services/Index.ts +2 -0
  24. package/Server/Services/MonitorService.ts +311 -0
  25. package/Server/Services/ProbeService.ts +20 -0
  26. package/Server/Services/ProjectService.ts +297 -1
  27. package/Server/Services/PushNotificationService.ts +71 -0
  28. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +18 -0
  29. package/Server/Services/StatusPageAnnouncementService.ts +53 -0
  30. package/Server/Services/StatusPageDomainService.ts +73 -28
  31. package/Server/Services/TeamMemberService.ts +117 -12
  32. package/Server/Services/TelemetryIngestionKeyService.ts +1 -0
  33. package/Server/Services/UserWebhookService.ts +34 -109
  34. package/Server/Services/WorkspaceNotificationRuleService.ts +69 -0
  35. package/Server/Services/WorkspaceProjectAuthTokenService.ts +37 -1
  36. package/Server/Types/Database/DeleteById.ts +2 -0
  37. package/Server/Types/Database/DeleteOneBy.ts +6 -0
  38. package/Server/Types/Domain.ts +41 -0
  39. package/Server/Types/Workflow/Components/API/Delete.ts +2 -1
  40. package/Server/Types/Workflow/Components/API/Get.ts +2 -1
  41. package/Server/Types/Workflow/Components/API/Patch.ts +2 -1
  42. package/Server/Types/Workflow/Components/API/Post.ts +2 -1
  43. package/Server/Types/Workflow/Components/API/Put.ts +2 -1
  44. package/Server/Types/Workflow/Components/API/Utils.ts +41 -2
  45. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +25 -19
  46. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +6 -5
  47. package/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.ts +32 -14
  48. package/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.ts +31 -13
  49. package/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.ts +29 -20
  50. package/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.ts +28 -21
  51. package/Server/Types/Workflow/Components/BaseModel/LogComponentError.ts +89 -0
  52. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +208 -0
  53. package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +11 -0
  54. package/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.ts +41 -18
  55. package/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.ts +45 -19
  56. package/Server/Types/Workflow/Components/Discord/SendMessageToChannel.ts +16 -9
  57. package/Server/Types/Workflow/Components/IncomingWebhookUtils.ts +66 -0
  58. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +15 -9
  59. package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +17 -0
  60. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
  61. package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +171 -0
  62. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +3 -3
  63. package/Server/Utils/AI/SRE/Insights/FixRouting.ts +3 -2
  64. package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +1 -1
  65. package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +6 -6
  66. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +43 -5
  67. package/Server/Utils/DataSource/EgressGuard.ts +176 -16
  68. package/Server/Utils/DataSource/HttpFetch.ts +6 -45
  69. package/Server/Utils/Execute.ts +8 -0
  70. package/Server/Utils/InlineImageAccessTokenSync.ts +23 -0
  71. package/Server/Utils/LLM/LLMService.ts +57 -12
  72. package/Server/Utils/Monitor/MonitorAlert.ts +40 -0
  73. package/Server/Utils/Monitor/MonitorDependencySuppression.ts +192 -0
  74. package/Server/Utils/Monitor/MonitorIncident.ts +40 -0
  75. package/Server/Utils/Monitor/MonitorResource.ts +36 -0
  76. package/Server/Utils/Response.ts +142 -6
  77. package/Server/Utils/SSRFProtection.ts +273 -60
  78. package/Server/Utils/StartServer.ts +8 -0
  79. package/Server/Utils/VM/VMRunner.ts +88 -1
  80. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +555 -49
  81. package/Server/Utils/Workspace/Slack/Slack.ts +25 -2
  82. package/Server/Utils/Workspace/WorkspaceBase.ts +6 -0
  83. package/Tests/App/Dashboard/DangerZone.test.tsx +334 -0
  84. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +539 -0
  85. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +678 -0
  86. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +706 -0
  87. package/Tests/App/Dashboard/DashboardVariablesModal.test.tsx +508 -0
  88. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +470 -0
  89. package/Tests/App/StatusPage/StatusPageDetailPagesNotFound.test.tsx +308 -0
  90. package/Tests/Models/DatabaseModels/DeletedProjectModel.test.ts +251 -0
  91. package/Tests/Models/DatabaseModels/SavedViewJSONColumnTuples.test.ts +115 -0
  92. package/Tests/Server/API/FileAPIAccessControl.test.ts +316 -0
  93. package/Tests/Server/API/ProjectAPI.test.ts +299 -1
  94. package/Tests/Server/API/ProjectAPIAdjustBalance.test.ts +459 -0
  95. package/Tests/Server/API/ProjectAPIAdjustBalanceBillingDisabled.test.ts +177 -0
  96. package/Tests/Server/Services/DatabaseServiceDeletionReason.test.ts +123 -0
  97. package/Tests/Server/Services/DeletedProjectService.test.ts +415 -0
  98. package/Tests/Server/Services/DeletedProjectServiceSelfHosted.test.ts +70 -0
  99. package/Tests/Server/Services/DomainSubdomainHooks.test.ts +94 -0
  100. package/Tests/Server/Services/FileIsPublicBooleanMigration.test.ts +244 -0
  101. package/Tests/Server/Services/FileServiceMimeType.test.ts +89 -0
  102. package/Tests/Server/Services/MonitorServiceDependencyValidation.test.ts +702 -0
  103. package/Tests/Server/Services/ProjectServiceAdjustBalance.test.ts +615 -0
  104. package/Tests/Server/Services/ProjectServiceDeleteAudit.test.ts +406 -0
  105. package/Tests/Server/Services/PublishTimeImageVisibility.test.ts +450 -0
  106. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +669 -0
  107. package/Tests/Server/Services/UserWebhookSSRF.test.ts +174 -0
  108. package/Tests/Server/Services/WebPushEndpointAllowlist.test.ts +133 -0
  109. package/Tests/Server/Services/WorkspaceProjectAuthTokenRepoint.test.ts +618 -0
  110. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +26 -1
  111. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +215 -0
  112. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +742 -0
  113. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +256 -0
  114. package/Tests/Server/Types/Workflow/Components/ModelArguments.test.ts +371 -0
  115. package/Tests/Server/Utils/AI/CodeAgentCommandGuard.test.ts +204 -0
  116. package/Tests/Server/Utils/AI/CodeAgentWriteLimits.test.ts +213 -0
  117. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +52 -5
  118. package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +3 -0
  119. package/Tests/Server/Utils/AI/LLMServiceEgressGuard.test.ts +315 -0
  120. package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +3 -0
  121. package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +17 -5
  122. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +14 -1
  123. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +35 -0
  124. package/Tests/Server/Utils/AI/StubLLMEgressGuard.ts +25 -0
  125. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +62 -7
  126. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +69 -0
  127. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +330 -0
  128. package/Tests/Server/Utils/GitHubCreatePullRequestWithToken.test.ts +393 -0
  129. package/Tests/Server/Utils/InlineImageAccessTokenSync.test.ts +202 -0
  130. package/Tests/Server/Utils/Monitor/MonitorDependencySuppression.test.ts +811 -0
  131. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +604 -0
  132. package/Tests/Server/Utils/ResponseSendFileResponse.test.ts +192 -0
  133. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +406 -0
  134. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +194 -0
  135. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +1169 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +2 -0
  137. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +1605 -0
  138. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTenantResolution.test.ts +1218 -0
  139. package/Tests/Types/AI/AIChatMessageStatus.test.ts +79 -0
  140. package/Tests/Types/AI/AIInsightStatus.test.ts +40 -0
  141. package/Tests/Types/API/HostnameAuthorityParsing.test.ts +157 -0
  142. package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +27 -0
  143. package/Tests/Types/DataSource/DataSourceLimits.test.ts +71 -0
  144. package/Tests/Types/DataSource/DataSourceType.test.ts +181 -0
  145. package/Tests/Types/DomainSubdomainValidation.test.ts +90 -0
  146. package/Tests/Types/JSONFunctions.test.ts +252 -1
  147. package/Tests/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.test.ts +61 -0
  148. package/Tests/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.test.ts +62 -0
  149. package/Tests/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.test.ts +68 -0
  150. package/Tests/Types/Monitor/SnmpMonitor/SnmpVersion.test.ts +51 -0
  151. package/Tests/UI/Components/404.test.tsx +1 -1
  152. package/Tests/UI/Components/Alert.test.tsx +1 -1
  153. package/Tests/UI/Components/Badge.test.tsx +1 -1
  154. package/Tests/UI/Components/BasicForm.test.tsx +39 -10
  155. package/Tests/UI/Components/Button.test.tsx +1 -1
  156. package/Tests/UI/Components/Card.test.tsx +1 -1
  157. package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +1 -1
  158. package/Tests/UI/Components/ColorViewer.test.tsx +1 -1
  159. package/Tests/UI/Components/ComponentsModal.test.tsx +7 -1
  160. package/Tests/UI/Components/DictionaryOfStrings.test.tsx +1 -1
  161. package/Tests/UI/Components/Dropdown.test.tsx +1 -1
  162. package/Tests/UI/Components/FilePicker.test.tsx +1 -1
  163. package/Tests/UI/Components/HiddenText.test.tsx +1 -1
  164. package/Tests/UI/Components/Input.test.tsx +1 -1
  165. package/Tests/UI/Components/Loader.test.tsx +1 -1
  166. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +1 -1
  167. package/Tests/UI/Components/MasterPage.test.tsx +1 -1
  168. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +489 -0
  169. package/Tests/UI/Components/ModelDelete.test.tsx +214 -0
  170. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1 -1
  171. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +1 -1
  172. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +1 -1
  173. package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +1 -1
  174. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1 -1
  175. package/Tests/UI/Components/NavBar.test.tsx +1 -1
  176. package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +1 -1
  177. package/Tests/UI/Components/Pagination.test.tsx +1 -1
  178. package/Tests/UI/Components/Pill.test.tsx +1 -1
  179. package/Tests/UI/Components/Probe.test.tsx +1 -1
  180. package/Tests/UI/Components/ProgressBar.test.tsx +1 -1
  181. package/Tests/UI/Components/RadioButtons.test.tsx +1 -1
  182. package/Tests/UI/Components/SideMenuItem.test.tsx +1 -1
  183. package/Tests/UI/Components/SideOver.test.tsx +500 -6
  184. package/Tests/UI/Components/Tabs.test.tsx +1 -1
  185. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +1 -1
  186. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +1 -1
  187. package/Tests/UI/Components/TextArea.test.tsx +1 -1
  188. package/Tests/UI/Components/Toast.test.tsx +1 -1
  189. package/Tests/UI/Components/Toggle.test.tsx +1 -1
  190. package/Tests/UI/Components/TopSection.test.tsx +1 -1
  191. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +1 -1
  192. package/Tests/UI/Rum/ReplayScrubber.test.tsx +1 -1
  193. package/Tests/UI/Rum/ReplayStage.test.tsx +1 -1
  194. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +1 -1
  195. package/Tests/UI/Utils/LocalStorageArrayRoundTrip.test.ts +72 -0
  196. package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +19 -2
  197. package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +44 -0
  198. package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +288 -5
  199. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +42 -0
  200. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +27 -0
  201. package/Tests/Utils/LegacySerializedArray.test.ts +139 -0
  202. package/Tests/Utils/Monitor/MonitorDependencyRule.test.ts +190 -0
  203. package/Tests/Utils/Realtime.test.ts +78 -0
  204. package/Tests/Utils/Telemetry/SavedViewFilters.test.ts +246 -0
  205. package/Tests/__mocks__/i18next-browser-languagedetector.js +21 -0
  206. package/Types/AI/FixPullRequestCiStatus.ts +1 -1
  207. package/Types/AI/FixVerificationStatus.ts +2 -2
  208. package/Types/API/Hostname.ts +92 -4
  209. package/Types/API/URL.ts +43 -25
  210. package/Types/Billing/BalanceAdjustmentType.ts +14 -0
  211. package/Types/Billing/ProjectBalanceType.ts +13 -0
  212. package/Types/Domain.ts +26 -0
  213. package/Types/File/MimeType.ts +11 -0
  214. package/Types/JSONFunctions.ts +15 -0
  215. package/Types/Telemetry/TelemetrySavedViewState.ts +9 -1
  216. package/Types/Workflow/Components/BaseModel.ts +92 -28
  217. package/Typings/Index.d.ts +12 -0
  218. package/UI/Components/GanttChart/ChartContainer.tsx +4 -3
  219. package/UI/Components/Modal/ConfirmModal.tsx +10 -5
  220. package/UI/Components/Modal/Modal.tsx +27 -47
  221. package/UI/Components/ModelDelete/ModelDelete.tsx +23 -6
  222. package/UI/Components/SideOver/SideOver.tsx +195 -68
  223. package/UI/Utils/PageScrollLock.ts +66 -0
  224. package/Utils/Dashboard/HtmlWidgetDocument.ts +4 -2
  225. package/Utils/Dashboard/VariableInterpolation.ts +19 -1
  226. package/Utils/LegacySerializedArray.ts +60 -0
  227. package/Utils/Monitor/MonitorDependencyRule.ts +88 -0
  228. package/Utils/Telemetry/SavedViewFilters.ts +88 -0
  229. package/build/dist/Models/DatabaseModels/AIInsight.js +1 -1
  230. package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
  231. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js +2 -3
  232. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js.map +1 -1
  233. package/build/dist/Models/DatabaseModels/DeletedProject.js +640 -0
  234. package/build/dist/Models/DatabaseModels/DeletedProject.js.map +1 -0
  235. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  236. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  237. package/build/dist/Models/DatabaseModels/Monitor.js +106 -0
  238. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  239. package/build/dist/Models/DatabaseModels/Project.js +3 -3
  240. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  241. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  242. package/build/dist/Server/API/FileAPI.js +14 -2
  243. package/build/dist/Server/API/FileAPI.js.map +1 -1
  244. package/build/dist/Server/API/MicrosoftTeamsAPI.js +6 -1
  245. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  246. package/build/dist/Server/API/ProjectAPI.js +157 -4
  247. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  248. package/build/dist/Server/API/StatusPageAPI.js +27 -3
  249. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  250. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js +34 -0
  251. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js.map +1 -0
  252. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js +30 -0
  253. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js.map +1 -0
  254. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js +20 -0
  255. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js.map +1 -0
  256. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  257. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  258. package/build/dist/Server/Services/AIAgentService.js +17 -0
  259. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  260. package/build/dist/Server/Services/DashboardDomainService.js +55 -18
  261. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  262. package/build/dist/Server/Services/DatabaseService.js +1 -0
  263. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  264. package/build/dist/Server/Services/DeletedProjectService.js +124 -0
  265. package/build/dist/Server/Services/DeletedProjectService.js.map +1 -0
  266. package/build/dist/Server/Services/FileService.js +52 -0
  267. package/build/dist/Server/Services/FileService.js.map +1 -1
  268. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +10 -1
  269. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  270. package/build/dist/Server/Services/IncidentPublicNoteService.js +10 -1
  271. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  272. package/build/dist/Server/Services/Index.js +2 -0
  273. package/build/dist/Server/Services/Index.js.map +1 -1
  274. package/build/dist/Server/Services/MonitorService.js +232 -0
  275. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  276. package/build/dist/Server/Services/ProbeService.js +17 -0
  277. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  278. package/build/dist/Server/Services/ProjectService.js +228 -6
  279. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  280. package/build/dist/Server/Services/PushNotificationService.js +56 -0
  281. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  282. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +10 -1
  283. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  284. package/build/dist/Server/Services/StatusPageAnnouncementService.js +54 -0
  285. package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
  286. package/build/dist/Server/Services/StatusPageDomainService.js +55 -18
  287. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  288. package/build/dist/Server/Services/TeamMemberService.js +97 -6
  289. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  290. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +1 -0
  291. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  292. package/build/dist/Server/Services/UserWebhookService.js +31 -87
  293. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  294. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +65 -0
  295. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  296. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +23 -0
  297. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  298. package/build/dist/Server/Types/Domain.js +37 -0
  299. package/build/dist/Server/Types/Domain.js.map +1 -1
  300. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +2 -1
  301. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  302. package/build/dist/Server/Types/Workflow/Components/API/Get.js +2 -1
  303. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  304. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +2 -1
  305. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  306. package/build/dist/Server/Types/Workflow/Components/API/Post.js +2 -1
  307. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  308. package/build/dist/Server/Types/Workflow/Components/API/Put.js +2 -1
  309. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  310. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +35 -2
  311. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  312. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +15 -11
  313. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  314. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +2 -3
  315. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  316. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js +21 -10
  317. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js.map +1 -1
  318. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js +21 -10
  319. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js.map +1 -1
  320. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js +18 -13
  321. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js.map +1 -1
  322. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js +18 -13
  323. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js.map +1 -1
  324. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js +43 -0
  325. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js.map +1 -0
  326. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +116 -0
  327. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -0
  328. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +7 -0
  329. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
  330. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js +24 -13
  331. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js.map +1 -1
  332. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js +27 -13
  333. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js.map +1 -1
  334. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js +14 -6
  335. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js.map +1 -1
  336. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js +42 -0
  337. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js.map +1 -0
  338. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +15 -6
  339. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  340. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +10 -2
  341. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
  342. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
  343. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +141 -0
  344. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -1
  345. package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
  346. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +1 -1
  347. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +6 -6
  348. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -1
  349. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +39 -5
  350. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  351. package/build/dist/Server/Utils/DataSource/EgressGuard.js +94 -10
  352. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  353. package/build/dist/Server/Utils/DataSource/HttpFetch.js +1 -19
  354. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  355. package/build/dist/Server/Utils/Execute.js +2 -2
  356. package/build/dist/Server/Utils/Execute.js.map +1 -1
  357. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js +13 -0
  358. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js.map +1 -1
  359. package/build/dist/Server/Utils/LLM/LLMService.js +43 -12
  360. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  361. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +44 -24
  362. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  363. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js +158 -0
  364. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js.map +1 -0
  365. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +50 -30
  366. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  367. package/build/dist/Server/Utils/Monitor/MonitorResource.js +32 -0
  368. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  369. package/build/dist/Server/Utils/Response.js +112 -5
  370. package/build/dist/Server/Utils/Response.js.map +1 -1
  371. package/build/dist/Server/Utils/SSRFProtection.js +219 -53
  372. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  373. package/build/dist/Server/Utils/StartServer.js +7 -0
  374. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  375. package/build/dist/Server/Utils/VM/VMRunner.js +73 -1
  376. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  377. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +445 -43
  378. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  379. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +18 -1
  380. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  381. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  382. package/build/dist/Types/AI/FixPullRequestCiStatus.js +1 -1
  383. package/build/dist/Types/AI/FixVerificationStatus.js +2 -2
  384. package/build/dist/Types/API/Hostname.js +73 -4
  385. package/build/dist/Types/API/Hostname.js.map +1 -1
  386. package/build/dist/Types/API/URL.js +42 -24
  387. package/build/dist/Types/API/URL.js.map +1 -1
  388. package/build/dist/Types/Billing/BalanceAdjustmentType.js +15 -0
  389. package/build/dist/Types/Billing/BalanceAdjustmentType.js.map +1 -0
  390. package/build/dist/Types/Billing/ProjectBalanceType.js +14 -0
  391. package/build/dist/Types/Billing/ProjectBalanceType.js.map +1 -0
  392. package/build/dist/Types/Domain.js +22 -0
  393. package/build/dist/Types/Domain.js.map +1 -1
  394. package/build/dist/Types/File/MimeType.js +11 -0
  395. package/build/dist/Types/File/MimeType.js.map +1 -1
  396. package/build/dist/Types/JSONFunctions.js +14 -0
  397. package/build/dist/Types/JSONFunctions.js.map +1 -1
  398. package/build/dist/Types/Workflow/Components/BaseModel.js +83 -28
  399. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  400. package/build/dist/UI/Components/GanttChart/ChartContainer.js +4 -3
  401. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +1 -1
  402. package/build/dist/UI/Components/Modal/ConfirmModal.js +3 -1
  403. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  404. package/build/dist/UI/Components/Modal/Modal.js +24 -34
  405. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  406. package/build/dist/UI/Components/ModelDelete/ModelDelete.js +11 -6
  407. package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
  408. package/build/dist/UI/Components/SideOver/SideOver.js +105 -30
  409. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  410. package/build/dist/UI/Utils/PageScrollLock.js +52 -0
  411. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -0
  412. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +4 -2
  413. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -1
  414. package/build/dist/Utils/Dashboard/VariableInterpolation.js +19 -1
  415. package/build/dist/Utils/Dashboard/VariableInterpolation.js.map +1 -1
  416. package/build/dist/Utils/LegacySerializedArray.js +51 -0
  417. package/build/dist/Utils/LegacySerializedArray.js.map +1 -0
  418. package/build/dist/Utils/Monitor/MonitorDependencyRule.js +47 -0
  419. package/build/dist/Utils/Monitor/MonitorDependencyRule.js.map +1 -0
  420. package/build/dist/Utils/Telemetry/SavedViewFilters.js +52 -0
  421. package/build/dist/Utils/Telemetry/SavedViewFilters.js.map +1 -0
  422. package/jest.config.json +3 -0
  423. package/package.json +1 -1
  424. package/tsconfig.json +3 -1
@@ -0,0 +1,678 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ render,
15
+ screen,
16
+ waitFor,
17
+ } from "@testing-library/react";
18
+ import * as React from "react";
19
+ import getJestMockFunction, { MockFunction } from "../../MockType";
20
+
21
+ /*
22
+ * A dashboard variable has two readers that must never disagree: the toolbar
23
+ * control, which tells the user what the board is scoped to, and the query
24
+ * interpolation in Common/Utils/Dashboard/VariableInterpolation.ts, which
25
+ * decides what the widgets actually fetch.
26
+ *
27
+ * Picking "All" emits selectedValue: "" — an explicit choice, distinct from
28
+ * "the user has not touched this yet" (undefined). The interpolation side has
29
+ * always read it that way (`??`), so "" applies no filter. The toolbar used
30
+ * `||`, which folds "" back into defaultValue: the dropdown snapped back to
31
+ * "prod" while the widgets went right on showing every cluster, and no amount
32
+ * of clicking could get out of it.
33
+ *
34
+ * So these tests assert both halves together — what the control displays AND
35
+ * what the same variable resolves to for the query — because a selector that
36
+ * merely looks right while the widgets are filtered differently is the exact
37
+ * bug being pinned here.
38
+ */
39
+
40
+ const getTelemetryAttributeValuesMock: MockFunction = getJestMockFunction();
41
+
42
+ /*
43
+ * The arrow wrapper is load bearing: jest.mock is hoisted above the compiled
44
+ * requires, so the mock variable above is still unassigned when the factory
45
+ * runs. Dereferencing it lazily, at call time, is what makes this work.
46
+ */
47
+ jest.mock(
48
+ "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/Utils/Metrics",
49
+ () => {
50
+ return {
51
+ __esModule: true,
52
+ default: {
53
+ getTelemetryAttributeValues: (...args: Array<any>): unknown => {
54
+ return getTelemetryAttributeValuesMock(...args);
55
+ },
56
+ },
57
+ };
58
+ },
59
+ );
60
+
61
+ import DashboardVariableSelector, {
62
+ VariableValueChange,
63
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/Dashboard/Toolbar/DashboardVariableSelector";
64
+ import DashboardVariable, {
65
+ DashboardVariableType,
66
+ } from "../../../Types/Dashboard/DashboardVariable";
67
+ import DashboardVariableInterpolation, {
68
+ ResolvedVariableValue,
69
+ } from "../../../Utils/Dashboard/VariableInterpolation";
70
+
71
+ const ATTRIBUTE_KEY: string = "k8s.cluster.name";
72
+ const OPTIONS: Array<string> = ["prod", "staging"];
73
+
74
+ type MakeVariableFunction = (
75
+ overrides?: Partial<DashboardVariable>,
76
+ ) => DashboardVariable;
77
+
78
+ const telemetryVariable: MakeVariableFunction = (
79
+ overrides: Partial<DashboardVariable> = {},
80
+ ): DashboardVariable => {
81
+ return {
82
+ id: "cluster",
83
+ name: "cluster",
84
+ label: "Cluster",
85
+ type: DashboardVariableType.TelemetryAttribute,
86
+ attributeKey: ATTRIBUTE_KEY,
87
+ ...overrides,
88
+ };
89
+ };
90
+
91
+ const customListVariable: MakeVariableFunction = (
92
+ overrides: Partial<DashboardVariable> = {},
93
+ ): DashboardVariable => {
94
+ return {
95
+ id: "region",
96
+ name: "region",
97
+ label: "Region",
98
+ type: DashboardVariableType.CustomList,
99
+ customListValues: "eu-west-1, us-east-1",
100
+ ...overrides,
101
+ };
102
+ };
103
+
104
+ const textVariable: MakeVariableFunction = (
105
+ overrides: Partial<DashboardVariable> = {},
106
+ ): DashboardVariable => {
107
+ return {
108
+ id: "service",
109
+ name: "service",
110
+ label: "Service",
111
+ type: DashboardVariableType.TextInput,
112
+ ...overrides,
113
+ };
114
+ };
115
+
116
+ /*
117
+ * Verbatim from DashboardView.tsx's onVariableValueChange. The bug only
118
+ * appears on the render AFTER the parent stores the change, so a harness that
119
+ * did not round-trip through the owner's state could not see it.
120
+ */
121
+ type ApplyVariableChangeFunction = (
122
+ variables: Array<DashboardVariable>,
123
+ variableId: string,
124
+ change: VariableValueChange,
125
+ ) => Array<DashboardVariable>;
126
+
127
+ const applyVariableChange: ApplyVariableChangeFunction = (
128
+ variables: Array<DashboardVariable>,
129
+ variableId: string,
130
+ change: VariableValueChange,
131
+ ): Array<DashboardVariable> => {
132
+ return variables.map((v: DashboardVariable): DashboardVariable => {
133
+ if (v.id !== variableId) {
134
+ return v;
135
+ }
136
+
137
+ if (change.selectedValues !== undefined) {
138
+ return {
139
+ ...v,
140
+ selectedValues: change.selectedValues,
141
+ selectedValue: undefined,
142
+ };
143
+ }
144
+
145
+ return { ...v, selectedValue: change.selectedValue };
146
+ });
147
+ };
148
+
149
+ interface RecordedChange {
150
+ variableId: string;
151
+ change: VariableValueChange;
152
+ }
153
+
154
+ /** What the dashboard owner is holding after the user's interactions. */
155
+ interface ToolbarState {
156
+ variables: Array<DashboardVariable>;
157
+ changes: Array<RecordedChange>;
158
+ }
159
+
160
+ interface HarnessProps {
161
+ state: ToolbarState;
162
+ }
163
+
164
+ const ToolbarHarness: React.FunctionComponent<HarnessProps> = (
165
+ props: HarnessProps,
166
+ ): React.ReactElement => {
167
+ const [variables, setVariables] = React.useState<Array<DashboardVariable>>(
168
+ props.state.variables,
169
+ );
170
+
171
+ return (
172
+ <DashboardVariableSelector
173
+ variables={variables}
174
+ onVariableValueChange={(
175
+ variableId: string,
176
+ change: VariableValueChange,
177
+ ) => {
178
+ const updated: Array<DashboardVariable> = applyVariableChange(
179
+ variables,
180
+ variableId,
181
+ change,
182
+ );
183
+ props.state.changes.push({ variableId: variableId, change: change });
184
+ props.state.variables = updated;
185
+ setVariables(updated);
186
+ }}
187
+ />
188
+ );
189
+ };
190
+
191
+ type RenderToolbarFunction = (
192
+ variables: Array<DashboardVariable>,
193
+ ) => ToolbarState;
194
+
195
+ const renderToolbar: RenderToolbarFunction = (
196
+ variables: Array<DashboardVariable>,
197
+ ): ToolbarState => {
198
+ const state: ToolbarState = { variables: variables, changes: [] };
199
+ render(<ToolbarHarness state={state} />);
200
+ return state;
201
+ };
202
+
203
+ type GetSelectFunction = (index?: number) => HTMLSelectElement;
204
+
205
+ const getSelect: GetSelectFunction = (index: number = 0): HTMLSelectElement => {
206
+ return screen.getAllByRole("combobox")[index] as HTMLSelectElement;
207
+ };
208
+
209
+ type GetTextInputFunction = (index?: number) => HTMLInputElement;
210
+
211
+ const getTextInput: GetTextInputFunction = (
212
+ index: number = 0,
213
+ ): HTMLInputElement => {
214
+ return screen.getAllByRole("textbox")[index] as HTMLInputElement;
215
+ };
216
+
217
+ /*
218
+ * A TelemetryAttribute select is disabled, and holds only the "All" option,
219
+ * until its values come back — so every assertion about what it displays has
220
+ * to wait for that.
221
+ */
222
+ type WaitForOptionsFunction = (index?: number) => Promise<HTMLSelectElement>;
223
+
224
+ const waitForOptions: WaitForOptionsFunction = async (
225
+ index: number = 0,
226
+ ): Promise<HTMLSelectElement> => {
227
+ await waitFor((): void => {
228
+ expect(getSelect(index)).not.toBeDisabled();
229
+ });
230
+ return getSelect(index);
231
+ };
232
+
233
+ type SelectValueFunction = (element: HTMLElement, value: string) => void;
234
+
235
+ const selectValue: SelectValueFunction = (
236
+ element: HTMLElement,
237
+ value: string,
238
+ ): void => {
239
+ fireEvent.change(element, { target: { value: value } });
240
+ };
241
+
242
+ type OptionLabelsFunction = (select: HTMLSelectElement) => Array<string>;
243
+
244
+ const optionLabels: OptionLabelsFunction = (
245
+ select: HTMLSelectElement,
246
+ ): Array<string> => {
247
+ return Array.from(select.options).map((option: HTMLOptionElement): string => {
248
+ return option.textContent || "";
249
+ });
250
+ };
251
+
252
+ /** What the widgets would be filtered by, given what the toolbar is holding. */
253
+ type ResolveFunction = (
254
+ variable: DashboardVariable,
255
+ ) => ResolvedVariableValue | undefined;
256
+
257
+ const resolvedFilter: ResolveFunction = (
258
+ variable: DashboardVariable,
259
+ ): ResolvedVariableValue | undefined => {
260
+ return DashboardVariableInterpolation.resolveValue(variable);
261
+ };
262
+
263
+ beforeEach((): void => {
264
+ getTelemetryAttributeValuesMock.mockReset();
265
+ getTelemetryAttributeValuesMock.mockResolvedValue(OPTIONS);
266
+ });
267
+
268
+ afterEach((): void => {
269
+ cleanup();
270
+ });
271
+
272
+ describe("DashboardVariableSelector", () => {
273
+ describe('picking "All" on a variable that has a default', () => {
274
+ test('the select stays on "All" instead of snapping back to the default', async () => {
275
+ const state: ToolbarState = renderToolbar([
276
+ telemetryVariable({ defaultValue: "prod" }),
277
+ ]);
278
+
279
+ const select: HTMLSelectElement = await waitForOptions();
280
+
281
+ // Untouched, the variable shows its default. That part was never broken.
282
+ expect(select).toHaveValue("prod");
283
+ expect(select).toHaveDisplayValue("prod");
284
+
285
+ selectValue(select, "");
286
+
287
+ /*
288
+ * The whole report in two assertions: the value is the empty string the
289
+ * user picked, and what they read is "All" — not "prod".
290
+ */
291
+ expect(getSelect()).toHaveValue("");
292
+ expect(getSelect()).toHaveDisplayValue("All");
293
+ expect(state.variables[0]!.selectedValue).toBe("");
294
+ });
295
+
296
+ test("the change handed to the dashboard is an empty string, not undefined", async () => {
297
+ const state: ToolbarState = renderToolbar([
298
+ telemetryVariable({ defaultValue: "prod" }),
299
+ ]);
300
+
301
+ selectValue(await waitForOptions(), "");
302
+
303
+ expect(state.changes).toEqual([
304
+ { variableId: "cluster", change: { selectedValue: "" } },
305
+ ]);
306
+ });
307
+
308
+ test("what the toolbar shows and what the widgets query agree", async () => {
309
+ const state: ToolbarState = renderToolbar([
310
+ telemetryVariable({ defaultValue: "prod" }),
311
+ ]);
312
+
313
+ selectValue(await waitForOptions(), "");
314
+
315
+ /*
316
+ * The damage the display bug did was not cosmetic: the toolbar claimed
317
+ * "prod" while the interpolation, reading the same variable, applied no
318
+ * filter at all and the widgets showed every cluster.
319
+ */
320
+ expect(getSelect()).toHaveDisplayValue("All");
321
+ expect(resolvedFilter(state.variables[0]!)).toBeUndefined();
322
+ expect(
323
+ DashboardVariableInterpolation.applyToAttributes({}, state.variables),
324
+ ).toEqual({});
325
+ });
326
+
327
+ test('a pre-existing filter on the attribute key is dropped when "All" is picked', async () => {
328
+ const state: ToolbarState = renderToolbar([
329
+ telemetryVariable({ defaultValue: "prod" }),
330
+ ]);
331
+
332
+ selectValue(await waitForOptions(), "");
333
+
334
+ expect(
335
+ DashboardVariableInterpolation.applyToAttributes(
336
+ { [ATTRIBUTE_KEY]: "prod", "service.name": "checkout" },
337
+ state.variables,
338
+ ),
339
+ ).toEqual({ "service.name": "checkout" });
340
+ });
341
+
342
+ test("the user can pick a real value again afterwards", async () => {
343
+ const state: ToolbarState = renderToolbar([
344
+ telemetryVariable({ defaultValue: "prod" }),
345
+ ]);
346
+
347
+ selectValue(await waitForOptions(), "");
348
+ expect(getSelect()).toHaveDisplayValue("All");
349
+
350
+ selectValue(getSelect(), "staging");
351
+
352
+ expect(getSelect()).toHaveValue("staging");
353
+ expect(resolvedFilter(state.variables[0]!)).toEqual({
354
+ scalar: "staging",
355
+ });
356
+ });
357
+
358
+ test('"All" survives a round trip through a concrete value', async () => {
359
+ const state: ToolbarState = renderToolbar([
360
+ telemetryVariable({ defaultValue: "prod" }),
361
+ ]);
362
+
363
+ selectValue(await waitForOptions(), "");
364
+ selectValue(getSelect(), "staging");
365
+ selectValue(getSelect(), "");
366
+
367
+ expect(getSelect()).toHaveValue("");
368
+ expect(getSelect()).toHaveDisplayValue("All");
369
+ expect(state.variables[0]!.selectedValue).toBe("");
370
+ expect(resolvedFilter(state.variables[0]!)).toBeUndefined();
371
+ });
372
+
373
+ test('a variable restored with an empty selection opens on "All"', async () => {
374
+ /*
375
+ * An explicit "All" that is already in place on the very first render,
376
+ * with no change event to reveal it. DashboardVariableUrlState.
377
+ * parseFromSearch produces exactly this from `var-cluster=`, and the
378
+ * public dashboard's server-side variable policy passes an empty
379
+ * string straight through.
380
+ *
381
+ * Note the asymmetry it implies: writeToBrowserUrl skips an empty
382
+ * selectedValue (Common/Utils/Dashboard/VariableUrlState.ts:117), so
383
+ * the URL cannot yet record an "All" the reader would happily restore.
384
+ * That gap is untouched here — this pins only the render.
385
+ */
386
+ renderToolbar([
387
+ telemetryVariable({ defaultValue: "prod", selectedValue: "" }),
388
+ ]);
389
+
390
+ const select: HTMLSelectElement = await waitForOptions();
391
+
392
+ expect(select).toHaveValue("");
393
+ expect(select).toHaveDisplayValue("All");
394
+ });
395
+ });
396
+
397
+ describe("the default value still works", () => {
398
+ test("an untouched variable shows and queries its default", async () => {
399
+ const state: ToolbarState = renderToolbar([
400
+ telemetryVariable({ defaultValue: "prod" }),
401
+ ]);
402
+
403
+ expect(await waitForOptions()).toHaveValue("prod");
404
+ expect(resolvedFilter(state.variables[0]!)).toEqual({ scalar: "prod" });
405
+ });
406
+
407
+ test("a selected value takes precedence over the default", async () => {
408
+ renderToolbar([
409
+ telemetryVariable({ defaultValue: "prod", selectedValue: "staging" }),
410
+ ]);
411
+
412
+ expect(await waitForOptions()).toHaveValue("staging");
413
+ });
414
+
415
+ test('a variable with no default and no selection opens on "All"', async () => {
416
+ const state: ToolbarState = renderToolbar([telemetryVariable({})]);
417
+
418
+ const select: HTMLSelectElement = await waitForOptions();
419
+
420
+ expect(select).toHaveValue("");
421
+ expect(select).toHaveDisplayValue("All");
422
+ expect(resolvedFilter(state.variables[0]!)).toBeUndefined();
423
+ });
424
+
425
+ test("a default the attribute no longer reports does not select some other value", async () => {
426
+ /*
427
+ * The attribute stopped reporting "prod" in this time range. There is no
428
+ * option to select, so the control lands on "All" — the point being that
429
+ * it does not silently scope the board to a different cluster.
430
+ */
431
+ getTelemetryAttributeValuesMock.mockResolvedValue(["staging"]);
432
+
433
+ renderToolbar([telemetryVariable({ defaultValue: "prod" })]);
434
+
435
+ const select: HTMLSelectElement = await waitForOptions();
436
+
437
+ expect(optionLabels(select)).toEqual(["All", "staging"]);
438
+ expect(select).toHaveValue("");
439
+ });
440
+ });
441
+
442
+ describe("fetching the options", () => {
443
+ test("the variable's attribute key is what gets asked for", async () => {
444
+ renderToolbar([telemetryVariable({ defaultValue: "prod" })]);
445
+
446
+ await waitForOptions();
447
+
448
+ expect(getTelemetryAttributeValuesMock).toHaveBeenCalledWith({
449
+ attributeKey: ATTRIBUTE_KEY,
450
+ });
451
+ });
452
+
453
+ test('while values load the select is disabled and reads "Loading…"', async () => {
454
+ let releaseValues: (values: Array<string>) => void = (): void => {
455
+ // Replaced synchronously by the executor below.
456
+ };
457
+
458
+ getTelemetryAttributeValuesMock.mockImplementation((): unknown => {
459
+ return new Promise<Array<string>>(
460
+ (resolve: (values: Array<string>) => void): void => {
461
+ releaseValues = resolve;
462
+ },
463
+ );
464
+ });
465
+
466
+ renderToolbar([telemetryVariable({ defaultValue: "prod" })]);
467
+
468
+ expect(getSelect()).toBeDisabled();
469
+ expect(optionLabels(getSelect())).toEqual(["Loading…"]);
470
+
471
+ await act(async (): Promise<void> => {
472
+ releaseValues(OPTIONS);
473
+ });
474
+
475
+ expect(getSelect()).not.toBeDisabled();
476
+ expect(optionLabels(getSelect())).toEqual(["All", "prod", "staging"]);
477
+ expect(getSelect()).toHaveValue("prod");
478
+ });
479
+
480
+ test('a failed fetch leaves the control usable and on "All"', async () => {
481
+ getTelemetryAttributeValuesMock.mockRejectedValue(
482
+ new Error("attribute values unavailable"),
483
+ );
484
+
485
+ renderToolbar([telemetryVariable({ defaultValue: "prod" })]);
486
+
487
+ const select: HTMLSelectElement = await waitForOptions();
488
+
489
+ expect(optionLabels(select)).toEqual(["All"]);
490
+ expect(select).toHaveValue("");
491
+ });
492
+ });
493
+
494
+ describe("custom list variables", () => {
495
+ test("options come from the comma-separated list, trimmed", () => {
496
+ renderToolbar([customListVariable({ defaultValue: "eu-west-1" })]);
497
+
498
+ expect(optionLabels(getSelect())).toEqual([
499
+ "All",
500
+ "eu-west-1",
501
+ "us-east-1",
502
+ ]);
503
+ expect(getSelect()).toHaveValue("eu-west-1");
504
+ });
505
+
506
+ test('picking "All" stays on "All"', () => {
507
+ const state: ToolbarState = renderToolbar([
508
+ customListVariable({ defaultValue: "eu-west-1" }),
509
+ ]);
510
+
511
+ selectValue(getSelect(), "");
512
+
513
+ expect(getSelect()).toHaveValue("");
514
+ expect(getSelect()).toHaveDisplayValue("All");
515
+ expect(state.variables[0]!.selectedValue).toBe("");
516
+ });
517
+
518
+ test("no network call is made for a custom list", () => {
519
+ renderToolbar([customListVariable({})]);
520
+
521
+ expect(getTelemetryAttributeValuesMock).not.toHaveBeenCalled();
522
+ });
523
+ });
524
+
525
+ describe("free-text variables", () => {
526
+ test("the default is shown until the user types", () => {
527
+ renderToolbar([textVariable({ defaultValue: "checkout" })]);
528
+
529
+ expect(getTextInput()).toHaveValue("checkout");
530
+ });
531
+
532
+ test("clearing the box leaves it empty instead of restoring the default", () => {
533
+ const state: ToolbarState = renderToolbar([
534
+ textVariable({ defaultValue: "checkout" }),
535
+ ]);
536
+
537
+ fireEvent.change(getTextInput(), { target: { value: "" } });
538
+
539
+ /*
540
+ * Same defect, other control: with `||` the field refilled itself with
541
+ * "checkout" the instant it was emptied, so the value could never be
542
+ * cleared.
543
+ */
544
+ expect(getTextInput()).toHaveValue("");
545
+ expect(state.variables[0]!.selectedValue).toBe("");
546
+ expect(resolvedFilter(state.variables[0]!)).toBeUndefined();
547
+ });
548
+
549
+ test("typing replaces the default and is stored", () => {
550
+ const state: ToolbarState = renderToolbar([
551
+ textVariable({ defaultValue: "checkout" }),
552
+ ]);
553
+
554
+ fireEvent.change(getTextInput(), { target: { value: "payments" } });
555
+
556
+ expect(getTextInput()).toHaveValue("payments");
557
+ expect(state.variables[0]!.selectedValue).toBe("payments");
558
+ });
559
+
560
+ test("a cleared box can be typed into again", () => {
561
+ renderToolbar([textVariable({ defaultValue: "checkout" })]);
562
+
563
+ fireEvent.change(getTextInput(), { target: { value: "" } });
564
+ fireEvent.change(getTextInput(), { target: { value: "payments" } });
565
+
566
+ expect(getTextInput()).toHaveValue("payments");
567
+ });
568
+ });
569
+
570
+ describe("several variables on one toolbar", () => {
571
+ test("each is labelled and rendered with its own control", async () => {
572
+ renderToolbar([
573
+ telemetryVariable({ defaultValue: "prod" }),
574
+ customListVariable({ defaultValue: "eu-west-1" }),
575
+ textVariable({ defaultValue: "checkout" }),
576
+ ]);
577
+
578
+ await waitForOptions();
579
+
580
+ expect(screen.getByText("Cluster")).toBeInTheDocument();
581
+ expect(screen.getByText("Region")).toBeInTheDocument();
582
+ expect(screen.getByText("Service")).toBeInTheDocument();
583
+ expect(screen.getAllByRole("combobox")).toHaveLength(2);
584
+ expect(screen.getAllByRole("textbox")).toHaveLength(1);
585
+ });
586
+
587
+ test('picking "All" on one leaves the others on their defaults', async () => {
588
+ const state: ToolbarState = renderToolbar([
589
+ telemetryVariable({ defaultValue: "prod" }),
590
+ customListVariable({ defaultValue: "eu-west-1" }),
591
+ ]);
592
+
593
+ selectValue(await waitForOptions(0), "");
594
+
595
+ expect(getSelect(0)).toHaveDisplayValue("All");
596
+ expect(getSelect(1)).toHaveValue("eu-west-1");
597
+ expect(state.variables[1]!.selectedValue).toBeUndefined();
598
+ expect(resolvedFilter(state.variables[1]!)).toEqual({
599
+ scalar: "eu-west-1",
600
+ });
601
+ });
602
+
603
+ test("a second variable changing does not resurrect the first one's default", async () => {
604
+ const state: ToolbarState = renderToolbar([
605
+ telemetryVariable({ defaultValue: "prod" }),
606
+ customListVariable({ defaultValue: "eu-west-1" }),
607
+ ]);
608
+
609
+ selectValue(await waitForOptions(0), "");
610
+ selectValue(getSelect(1), "us-east-1");
611
+
612
+ /*
613
+ * The re-render that the second variable forces is exactly where the
614
+ * first one used to jump back to "prod".
615
+ */
616
+ expect(getSelect(0)).toHaveValue("");
617
+ expect(getSelect(0)).toHaveDisplayValue("All");
618
+ expect(getSelect(1)).toHaveValue("us-east-1");
619
+ expect(state.variables[0]!.selectedValue).toBe("");
620
+ });
621
+
622
+ test("the variable label falls back to the name when unset", () => {
623
+ renderToolbar([customListVariable({ label: undefined })]);
624
+
625
+ expect(screen.getByText("region")).toBeInTheDocument();
626
+ });
627
+ });
628
+
629
+ describe("multi-select variables", () => {
630
+ test('the button reads "All" while nothing is picked, default or not', async () => {
631
+ renderToolbar([
632
+ telemetryVariable({ isMultiSelect: true, defaultValue: "prod" }),
633
+ ]);
634
+
635
+ await waitFor((): void => {
636
+ expect(screen.getByRole("button")).not.toBeDisabled();
637
+ });
638
+
639
+ expect(screen.getByRole("button")).toHaveTextContent("All");
640
+ });
641
+
642
+ test("picking and then clearing values returns the button to All", async () => {
643
+ const state: ToolbarState = renderToolbar([
644
+ telemetryVariable({ isMultiSelect: true, defaultValue: "prod" }),
645
+ ]);
646
+
647
+ await waitFor((): void => {
648
+ expect(screen.getByRole("button")).not.toBeDisabled();
649
+ });
650
+
651
+ fireEvent.click(screen.getByRole("button"));
652
+ fireEvent.click(screen.getByRole("checkbox", { name: "staging" }));
653
+
654
+ expect(state.variables[0]!.selectedValues).toEqual(["staging"]);
655
+ expect(
656
+ screen.getByRole("button", { name: /staging/ }),
657
+ ).toBeInTheDocument();
658
+
659
+ fireEvent.click(screen.getByRole("button", { name: "Clear" }));
660
+
661
+ expect(state.variables[0]!.selectedValues).toEqual([]);
662
+ expect(screen.getAllByRole("button")[0]).toHaveTextContent("All");
663
+ });
664
+ });
665
+
666
+ test("no variables renders nothing at all", () => {
667
+ const { container }: { container: HTMLElement } = render(
668
+ <DashboardVariableSelector
669
+ variables={[]}
670
+ onVariableValueChange={(): void => {
671
+ // Never called: there is nothing to change.
672
+ }}
673
+ />,
674
+ );
675
+
676
+ expect(container).toBeEmptyDOMElement();
677
+ });
678
+ });