@oneuptime/common 12.0.5 → 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.
- package/Models/DatabaseModels/AIInsight.ts +1 -1
- package/Models/DatabaseModels/AIRun.ts +33 -0
- package/Models/DatabaseModels/DataSource.ts +744 -0
- package/Models/DatabaseModels/DatabaseBaseModel/FileModel.ts +2 -3
- package/Models/DatabaseModels/DeletedProject.ts +595 -0
- package/Models/DatabaseModels/Index.ts +5 -0
- package/Models/DatabaseModels/Monitor.ts +111 -0
- package/Models/DatabaseModels/Project.ts +3 -3
- package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
- package/Models/DatabaseModels/User.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
- package/Models/DatabaseModels/UserSession.ts +2 -0
- package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
- package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -1
- package/Server/API/AIInsightAPI.ts +47 -4
- package/Server/API/AIInvestigationAPI.ts +25 -0
- package/Server/API/DashboardAPI.ts +176 -492
- package/Server/API/DataSourceAPI.ts +268 -0
- package/Server/API/FileAPI.ts +15 -2
- package/Server/API/MicrosoftTeamsAPI.ts +6 -1
- package/Server/API/ProjectAPI.ts +214 -0
- package/Server/API/StatusPageAPI.ts +50 -7
- package/Server/API/UserAPI.ts +119 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.ts +50 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Infrastructure/Queue.ts +26 -0
- package/Server/Infrastructure/QueueWorker.ts +17 -0
- package/Server/Middleware/TelemetryIngest.ts +8 -3
- package/Server/Services/AIAgentService.ts +20 -0
- package/Server/Services/AIInsightService.ts +132 -12
- package/Server/Services/AIRunService.ts +34 -0
- package/Server/Services/AIService.ts +17 -2
- package/Server/Services/AlertService.ts +38 -32
- package/Server/Services/AnalyticsDatabaseService.ts +8 -4
- package/Server/Services/DashboardDomainService.ts +73 -28
- package/Server/Services/DataSourceService.ts +200 -0
- package/Server/Services/DatabaseService.ts +1 -0
- package/Server/Services/DeletedProjectService.ts +138 -0
- package/Server/Services/FileService.ts +61 -0
- package/Server/Services/HostService.ts +92 -0
- package/Server/Services/IncidentEpisodePublicNoteService.ts +18 -0
- package/Server/Services/IncidentPublicNoteService.ts +18 -0
- package/Server/Services/IncidentService.ts +45 -0
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/MonitorService.ts +360 -0
- package/Server/Services/MonitorStatusService.ts +166 -0
- package/Server/Services/OpenTelemetryIngestService.ts +255 -184
- package/Server/Services/ProbeService.ts +20 -0
- package/Server/Services/ProjectService.ts +297 -1
- package/Server/Services/PushNotificationService.ts +71 -0
- package/Server/Services/RumSessionPinService.ts +7 -21
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +18 -0
- package/Server/Services/StatusPageAnnouncementService.ts +53 -0
- package/Server/Services/StatusPageDomainService.ts +73 -28
- package/Server/Services/StatusPageSubscriberService.ts +13 -0
- package/Server/Services/TeamMemberService.ts +117 -12
- package/Server/Services/TelemetryIngestionKeyService.ts +1 -0
- package/Server/Services/UserService.ts +280 -9
- package/Server/Services/UserWebhookService.ts +34 -109
- package/Server/Services/WorkspaceNotificationRuleService.ts +69 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +37 -1
- package/Server/Static/Vendor/README.md +62 -0
- package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
- package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
- package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
- package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
- package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
- package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
- package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
- package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
- package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
- package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
- package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
- package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
- package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
- package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
- package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
- package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
- package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
- package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
- package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
- package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
- package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
- package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
- package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
- package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
- package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
- package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
- package/Server/Types/Database/DeleteById.ts +2 -0
- package/Server/Types/Database/DeleteOneBy.ts +6 -0
- package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
- package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
- package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
- package/Server/Types/Domain.ts +41 -0
- package/Server/Types/Workflow/Components/API/Delete.ts +2 -1
- package/Server/Types/Workflow/Components/API/Get.ts +2 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +2 -1
- package/Server/Types/Workflow/Components/API/Post.ts +2 -1
- package/Server/Types/Workflow/Components/API/Put.ts +2 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +41 -2
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +25 -19
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +6 -5
- package/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.ts +32 -14
- package/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.ts +31 -13
- package/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.ts +29 -20
- package/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.ts +28 -21
- package/Server/Types/Workflow/Components/BaseModel/LogComponentError.ts +89 -0
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +208 -0
- package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +11 -0
- package/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.ts +41 -18
- package/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.ts +45 -19
- package/Server/Types/Workflow/Components/Discord/SendMessageToChannel.ts +16 -9
- package/Server/Types/Workflow/Components/IncomingWebhookUtils.ts +66 -0
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +15 -9
- package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +17 -0
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +171 -0
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +3 -3
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
- package/Server/Utils/AI/SRE/Insights/FixRouting.ts +3 -2
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
- package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
- package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +1 -1
- package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +6 -6
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +43 -5
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
- package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
- package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
- package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
- package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
- package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
- package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
- package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
- package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
- package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
- package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
- package/Server/Utils/DataSource/EgressGuard.ts +606 -0
- package/Server/Utils/DataSource/HttpFetch.ts +182 -0
- package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
- package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
- package/Server/Utils/DataSource/Types.ts +93 -0
- package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
- package/Server/Utils/Execute.ts +8 -0
- package/Server/Utils/InProcessMemo.ts +141 -0
- package/Server/Utils/InlineImageAccessTokenSync.ts +23 -0
- package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
- package/Server/Utils/LLM/LLMService.ts +57 -12
- package/Server/Utils/Monitor/MonitorAlert.ts +40 -0
- package/Server/Utils/Monitor/MonitorDependencySuppression.ts +192 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +40 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
- package/Server/Utils/Monitor/MonitorResource.ts +50 -0
- package/Server/Utils/Response.ts +154 -7
- package/Server/Utils/SSRFProtection.ts +450 -0
- package/Server/Utils/StartServer.ts +16 -0
- package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
- package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
- package/Server/Utils/Telemetry/Telemetry.ts +112 -1
- package/Server/Utils/Telemetry.ts +104 -3
- package/Server/Utils/VM/VMRunner.ts +88 -1
- package/Server/Utils/VendorAssets.ts +165 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +582 -53
- package/Server/Utils/Workspace/Slack/Slack.ts +30 -2
- package/Server/Utils/Workspace/WorkspaceBase.ts +6 -0
- package/Server/Views/Partials/Head.ejs +21 -7
- package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
- package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
- package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
- package/Tests/App/Dashboard/DangerZone.test.tsx +334 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +539 -0
- package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +678 -0
- package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +706 -0
- package/Tests/App/Dashboard/DashboardVariablesModal.test.tsx +508 -0
- package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
- package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
- package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
- package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
- package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +470 -0
- package/Tests/App/StatusPage/StatusPageDetailPagesNotFound.test.tsx +308 -0
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
- package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
- package/Tests/Models/DatabaseModels/DeletedProjectModel.test.ts +251 -0
- package/Tests/Models/DatabaseModels/SavedViewJSONColumnTuples.test.ts +115 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
- package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
- package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
- package/Tests/Server/API/FileAPIAccessControl.test.ts +316 -0
- package/Tests/Server/API/ProjectAPI.test.ts +299 -1
- package/Tests/Server/API/ProjectAPIAdjustBalance.test.ts +459 -0
- package/Tests/Server/API/ProjectAPIAdjustBalanceBillingDisabled.test.ts +177 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
- package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
- package/Tests/Server/Services/AIInsightService.test.ts +266 -11
- package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
- package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
- package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
- package/Tests/Server/Services/DatabaseServiceDeletionReason.test.ts +123 -0
- package/Tests/Server/Services/DeletedProjectService.test.ts +415 -0
- package/Tests/Server/Services/DeletedProjectServiceSelfHosted.test.ts +70 -0
- package/Tests/Server/Services/DomainSubdomainHooks.test.ts +94 -0
- package/Tests/Server/Services/FileIsPublicBooleanMigration.test.ts +244 -0
- package/Tests/Server/Services/FileServiceMimeType.test.ts +89 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
- package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
- package/Tests/Server/Services/MonitorServiceDependencyValidation.test.ts +702 -0
- package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
- package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
- package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
- package/Tests/Server/Services/ProjectServiceAdjustBalance.test.ts +615 -0
- package/Tests/Server/Services/ProjectServiceDeleteAudit.test.ts +406 -0
- package/Tests/Server/Services/PublishTimeImageVisibility.test.ts +450 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
- package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +669 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
- package/Tests/Server/Services/UserWebhookSSRF.test.ts +174 -0
- package/Tests/Server/Services/WebPushEndpointAllowlist.test.ts +133 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenRepoint.test.ts +618 -0
- package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
- package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +26 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +215 -0
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +742 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ModelArguments.test.ts +371 -0
- package/Tests/Server/Utils/AI/CodeAgentCommandGuard.test.ts +204 -0
- package/Tests/Server/Utils/AI/CodeAgentWriteLimits.test.ts +213 -0
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +52 -5
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
- package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
- package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
- package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +3 -0
- package/Tests/Server/Utils/AI/LLMServiceEgressGuard.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +3 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +17 -5
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +14 -1
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +35 -0
- package/Tests/Server/Utils/AI/StubLLMEgressGuard.ts +25 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +912 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
- package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
- package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +714 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +330 -0
- package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
- package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
- package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
- package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
- package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
- package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
- package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
- package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
- package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
- package/Tests/Server/Utils/GitHubCreatePullRequestWithToken.test.ts +393 -0
- package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
- package/Tests/Server/Utils/InlineImageAccessTokenSync.test.ts +202 -0
- package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppression.test.ts +811 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +604 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
- package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
- package/Tests/Server/Utils/ResponseSendFileResponse.test.ts +192 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +406 -0
- package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
- package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
- package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
- package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
- package/Tests/Server/Utils/Telemetry.test.ts +87 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +194 -0
- package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +1169 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +2 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +1605 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTenantResolution.test.ts +1218 -0
- package/Tests/Server/Views/OfflineRender.test.ts +297 -0
- package/Tests/Types/AI/AIChatMessageStatus.test.ts +79 -0
- package/Tests/Types/AI/AIInsightStatus.test.ts +40 -0
- package/Tests/Types/API/HostnameAuthorityParsing.test.ts +157 -0
- package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +27 -0
- package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
- package/Tests/Types/DataSource/DataSourceLimits.test.ts +71 -0
- package/Tests/Types/DataSource/DataSourceType.test.ts +181 -0
- package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
- package/Tests/Types/DomainSubdomainValidation.test.ts +90 -0
- package/Tests/Types/JSONFunctions.test.ts +252 -1
- package/Tests/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.test.ts +61 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.test.ts +62 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.test.ts +68 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpVersion.test.ts +51 -0
- package/Tests/Types/Password.test.ts +363 -0
- package/Tests/UI/Components/404.test.tsx +1 -1
- package/Tests/UI/Components/Alert.test.tsx +1 -1
- package/Tests/UI/Components/Badge.test.tsx +1 -1
- package/Tests/UI/Components/BasicForm.test.tsx +39 -10
- package/Tests/UI/Components/Button.test.tsx +1 -1
- package/Tests/UI/Components/Card.test.tsx +1 -1
- package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +1 -1
- package/Tests/UI/Components/ColorViewer.test.tsx +1 -1
- package/Tests/UI/Components/ComponentsModal.test.tsx +7 -1
- package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
- package/Tests/UI/Components/DictionaryOfStrings.test.tsx +1 -1
- package/Tests/UI/Components/Dropdown.test.tsx +1 -1
- package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
- package/Tests/UI/Components/FilePicker.test.tsx +1 -1
- package/Tests/UI/Components/HiddenText.test.tsx +1 -1
- package/Tests/UI/Components/Input.test.tsx +1 -1
- package/Tests/UI/Components/Loader.test.tsx +1 -1
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +1 -1
- package/Tests/UI/Components/MasterPage.test.tsx +1 -1
- package/Tests/UI/Components/Modal.test.tsx +281 -1
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +489 -0
- package/Tests/UI/Components/ModelDelete.test.tsx +214 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1 -1
- package/Tests/UI/Components/NavBar.test.tsx +1 -1
- package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
- package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +1 -1
- package/Tests/UI/Components/Pagination.test.tsx +1 -1
- package/Tests/UI/Components/Pill.test.tsx +1 -1
- package/Tests/UI/Components/Probe.test.tsx +1 -1
- package/Tests/UI/Components/ProgressBar.test.tsx +1 -1
- package/Tests/UI/Components/RadioButtons.test.tsx +1 -1
- package/Tests/UI/Components/SideMenuItem.test.tsx +1 -1
- package/Tests/UI/Components/SideOver.test.tsx +500 -6
- package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
- package/Tests/UI/Components/Tabs.test.tsx +1 -1
- package/Tests/UI/Components/TelemetrySearchBar.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +1 -1
- package/Tests/UI/Components/TextArea.test.tsx +1 -1
- package/Tests/UI/Components/Toast.test.tsx +1 -1
- package/Tests/UI/Components/Toggle.test.tsx +1 -1
- package/Tests/UI/Components/TopSection.test.tsx +1 -1
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +1 -1
- package/Tests/UI/Rum/ReplayScrubber.test.tsx +1 -1
- package/Tests/UI/Rum/ReplayStage.test.tsx +1 -1
- package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +1 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/LocalStorageArrayRoundTrip.test.ts +72 -0
- package/Tests/Utils/Analytics.test.ts +23 -0
- package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
- package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +19 -2
- package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +44 -0
- package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +288 -5
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +42 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +165 -0
- package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
- package/Tests/Utils/LegacySerializedArray.test.ts +139 -0
- package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
- package/Tests/Utils/Monitor/MonitorDependencyRule.test.ts +190 -0
- package/Tests/Utils/Realtime.test.ts +78 -0
- package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
- package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
- package/Tests/Utils/Telemetry/SavedViewFilters.test.ts +246 -0
- package/Tests/__mocks__/i18next-browser-languagedetector.js +21 -0
- package/Types/AI/AIInsightEvidence.ts +6 -0
- package/Types/AI/FixPullRequestCiStatus.ts +1 -1
- package/Types/AI/FixVerificationStatus.ts +2 -2
- package/Types/API/Hostname.ts +92 -4
- package/Types/API/URL.ts +43 -25
- package/Types/Analytics/RevenueEvent.ts +31 -0
- package/Types/Billing/BalanceAdjustmentType.ts +14 -0
- package/Types/Billing/ProjectBalanceType.ts +13 -0
- package/Types/Dashboard/DashboardComponentType.ts +31 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
- package/Types/DataSource/DataSourceLimits.ts +62 -0
- package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
- package/Types/DataSource/DataSourceTableResult.ts +38 -0
- package/Types/DataSource/DataSourceType.ts +213 -0
- package/Types/Date.ts +43 -3
- package/Types/Domain.ts +26 -0
- package/Types/File/MimeType.ts +11 -0
- package/Types/JSONFunctions.ts +15 -0
- package/Types/Password.ts +64 -0
- package/Types/Permission.ts +46 -0
- package/Types/Telemetry/TelemetrySavedViewState.ts +9 -1
- package/Types/UserAuthenticationStatus.ts +34 -0
- package/Types/Workflow/Components/BaseModel.ts +92 -28
- package/Typings/Index.d.ts +12 -0
- package/UI/Components/Button/Button.tsx +1 -1
- package/UI/Components/ErrorBoundary.tsx +459 -108
- package/UI/Components/GanttChart/ChartContainer.tsx +4 -3
- package/UI/Components/Icon/Icon.tsx +10 -9
- package/UI/Components/Modal/ConfirmModal.tsx +15 -5
- package/UI/Components/Modal/Modal.tsx +247 -35
- package/UI/Components/Modal/ModalFooter.tsx +6 -1
- package/UI/Components/ModelDelete/ModelDelete.tsx +23 -6
- package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
- package/UI/Components/SideOver/SideOver.tsx +195 -68
- package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
- package/UI/Components/StatusPage/ResourceList.tsx +570 -0
- package/UI/Styles/Theme.css +18 -0
- package/UI/Utils/API/RequestOptions.ts +7 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
- package/UI/Utils/PageScrollLock.ts +66 -0
- package/UI/Utils/TestDataSource.ts +61 -0
- package/UI/esbuild-config.js +45 -0
- package/Utils/Analytics.ts +15 -0
- package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
- package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
- package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
- package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
- package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/Dashboard/HtmlWidgetDocument.ts +4 -2
- package/Utils/Dashboard/VariableInterpolation.ts +19 -1
- package/Utils/DataSource/DataSourceQueryText.ts +94 -0
- package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
- package/Utils/LegacySerializedArray.ts +60 -0
- package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
- package/Utils/Monitor/MonitorDependencyRule.ts +88 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
- package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
- package/Utils/StatusPage/ResourceExplorer.ts +853 -0
- package/Utils/Telemetry/SavedViewFilters.ts +88 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js +1 -1
- package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
- package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js +2 -3
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DeletedProject.js +640 -0
- package/build/dist/Models/DatabaseModels/DeletedProject.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +106 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +3 -3
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +2 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
- package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +34 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +119 -433
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/DataSourceAPI.js +173 -0
- package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
- package/build/dist/Server/API/FileAPI.js +14 -2
- package/build/dist/Server/API/FileAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +6 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +157 -4
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +38 -4
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +94 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +27 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIAgentService.js +17 -0
- package/build/dist/Server/Services/AIAgentService.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +110 -10
- package/build/dist/Server/Services/AIInsightService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +16 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +28 -35
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +55 -18
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/DataSourceService.js +159 -0
- package/build/dist/Server/Services/DataSourceService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseService.js +1 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DeletedProjectService.js +124 -0
- package/build/dist/Server/Services/DeletedProjectService.js.map +1 -0
- package/build/dist/Server/Services/FileService.js +52 -0
- package/build/dist/Server/Services/FileService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +74 -0
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +10 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +35 -24
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +277 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +129 -0
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +17 -0
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +228 -6
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +56 -0
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +7 -15
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +10 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageAnnouncementService.js +54 -0
- package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +55 -18
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +97 -6
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js +1 -0
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +241 -10
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +31 -87
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +65 -0
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +23 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +37 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +35 -2
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +15 -11
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +2 -3
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js +21 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js +21 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js +18 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js +18 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js +43 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +116 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +7 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js +24 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js +27 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js +14 -6
- package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js +42 -0
- package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +15 -6
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +10 -2
- package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +141 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +6 -6
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +39 -5
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +389 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +116 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Types.js +2 -0
- package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
- package/build/dist/Server/Utils/Execute.js +2 -2
- package/build/dist/Server/Utils/Execute.js.map +1 -1
- package/build/dist/Server/Utils/InProcessMemo.js +113 -0
- package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
- package/build/dist/Server/Utils/InlineImageAccessTokenSync.js +13 -0
- package/build/dist/Server/Utils/InlineImageAccessTokenSync.js.map +1 -1
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
- package/build/dist/Server/Utils/LLM/LLMService.js +43 -12
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +44 -24
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js +158 -0
- package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +50 -30
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +46 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +121 -6
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +358 -0
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +14 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +93 -6
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +73 -1
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +121 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +469 -46
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +23 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
- package/build/dist/Types/AI/FixPullRequestCiStatus.js +1 -1
- package/build/dist/Types/AI/FixVerificationStatus.js +2 -2
- package/build/dist/Types/API/Hostname.js +73 -4
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/API/URL.js +42 -24
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
- package/build/dist/Types/Billing/BalanceAdjustmentType.js +15 -0
- package/build/dist/Types/Billing/BalanceAdjustmentType.js.map +1 -0
- package/build/dist/Types/Billing/ProjectBalanceType.js +14 -0
- package/build/dist/Types/Billing/ProjectBalanceType.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceType.js +183 -0
- package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
- package/build/dist/Types/Date.js +38 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Domain.js +22 -0
- package/build/dist/Types/Domain.js.map +1 -1
- package/build/dist/Types/File/MimeType.js +11 -0
- package/build/dist/Types/File/MimeType.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +14 -0
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Password.js +56 -0
- package/build/dist/Types/Password.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserAuthenticationStatus.js +2 -0
- package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
- package/build/dist/Types/Workflow/Components/BaseModel.js +83 -28
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +388 -72
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/ChartContainer.js +4 -3
- package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +10 -4
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +3 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +168 -19
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js +11 -6
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideOver/SideOver.js +105 -30
- package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/PageScrollLock.js +52 -0
- package/build/dist/UI/Utils/PageScrollLock.js.map +1 -0
- package/build/dist/UI/Utils/TestDataSource.js +39 -0
- package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
- package/build/dist/Utils/Analytics.js +4 -0
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +4 -2
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -1
- package/build/dist/Utils/Dashboard/VariableInterpolation.js +19 -1
- package/build/dist/Utils/Dashboard/VariableInterpolation.js.map +1 -1
- package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
- package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
- package/build/dist/Utils/LegacySerializedArray.js +51 -0
- package/build/dist/Utils/LegacySerializedArray.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorDependencyRule.js +47 -0
- package/build/dist/Utils/Monitor/MonitorDependencyRule.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
- package/build/dist/Utils/Telemetry/SavedViewFilters.js +52 -0
- package/build/dist/Utils/Telemetry/SavedViewFilters.js.map +1 -0
- package/jest.config.json +4 -0
- package/package.json +5 -1
- package/tsconfig.json +14 -10
|
@@ -0,0 +1,1858 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import {
|
|
3
|
+
act,
|
|
4
|
+
fireEvent,
|
|
5
|
+
render,
|
|
6
|
+
screen,
|
|
7
|
+
waitFor,
|
|
8
|
+
within,
|
|
9
|
+
} from "@testing-library/react";
|
|
10
|
+
import React from "react";
|
|
11
|
+
import { beforeEach, describe, expect, jest, test } from "@jest/globals";
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Contract under test - the Resources tab of a status page.
|
|
15
|
+
*
|
|
16
|
+
* Two things are being held here at once.
|
|
17
|
+
*
|
|
18
|
+
* The first is the counting invariant from issue #3042. The tab used to render
|
|
19
|
+
* one ModelTable per group, eagerly; a status page with 1506 groups mounted
|
|
20
|
+
* 1506 tables and fired 1506 list requests as soon as the tab opened, and the
|
|
21
|
+
* reporter watched the browser climb past a gigabyte and then fail. The
|
|
22
|
+
* explorer that replaced it loads exactly one group at a time, so the number of
|
|
23
|
+
* requests is fixed no matter how large the hierarchy is - and, unlike the tree
|
|
24
|
+
* that came before it, opening a branch in the navigator costs nothing at all,
|
|
25
|
+
* because opening is not the same act as selecting.
|
|
26
|
+
*
|
|
27
|
+
* The second is that the group being edited is edited in one place: one header
|
|
28
|
+
* naming it, one row of buttons acting on it, one list of its monitors in the
|
|
29
|
+
* order visitors see them. There is no table card nested inside a row of
|
|
30
|
+
* another tree any more, and no way for two groups to be half open at once.
|
|
31
|
+
*
|
|
32
|
+
* ModelAPI is mocked at the module boundary and every call is recorded, so "did
|
|
33
|
+
* not fetch" is asserted directly rather than inferred from the DOM.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const PROJECT_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194162001";
|
|
37
|
+
const STATUS_PAGE_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194162002";
|
|
38
|
+
|
|
39
|
+
jest.mock("Common/UI/Utils/Navigation", () => {
|
|
40
|
+
return {
|
|
41
|
+
__esModule: true,
|
|
42
|
+
default: {
|
|
43
|
+
/*
|
|
44
|
+
* Referenced from inside the function body rather than captured when the
|
|
45
|
+
* factory runs: jest.mock is hoisted above the imports, so ObjectID is
|
|
46
|
+
* only bound by the time anything actually calls this.
|
|
47
|
+
*/
|
|
48
|
+
getLastParamAsObjectID: () => {
|
|
49
|
+
return new ObjectID(STATUS_PAGE_ID);
|
|
50
|
+
},
|
|
51
|
+
getCurrentRoute: () => {
|
|
52
|
+
return {
|
|
53
|
+
toString: () => {
|
|
54
|
+
return "/";
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
navigate: jest.fn(),
|
|
59
|
+
getQueryStringByName: () => {
|
|
60
|
+
return null;
|
|
61
|
+
},
|
|
62
|
+
setQueryStringByName: jest.fn(),
|
|
63
|
+
getCurrentPath: () => {
|
|
64
|
+
return "/";
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
jest.mock("Common/UI/Utils/Permission", () => {
|
|
71
|
+
return {
|
|
72
|
+
__esModule: true,
|
|
73
|
+
default: {
|
|
74
|
+
getAllPermissions: () => {
|
|
75
|
+
return [];
|
|
76
|
+
},
|
|
77
|
+
getProjectPermissions: () => {
|
|
78
|
+
return [];
|
|
79
|
+
},
|
|
80
|
+
getGlobalPermissions: () => {
|
|
81
|
+
return [];
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* Permissions come from two places: the model's own checks against the
|
|
89
|
+
* permission list, and master admin. The permission list is mocked empty, so
|
|
90
|
+
* this switch is what a test flips to look at the page as a viewer who may
|
|
91
|
+
* read a status page but not restructure it.
|
|
92
|
+
*/
|
|
93
|
+
const mockIsMasterAdmin: jest.MockedFunction<any> = jest.fn(() => {
|
|
94
|
+
return true;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
jest.mock("Common/UI/Utils/User", () => {
|
|
98
|
+
return {
|
|
99
|
+
__esModule: true,
|
|
100
|
+
default: {
|
|
101
|
+
isMasterAdmin: () => {
|
|
102
|
+
return mockIsMasterAdmin();
|
|
103
|
+
},
|
|
104
|
+
getUserId: () => {
|
|
105
|
+
return null;
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
jest.mock("Common/UI/Utils/Translation", () => {
|
|
112
|
+
return {
|
|
113
|
+
__esModule: true,
|
|
114
|
+
default: () => {
|
|
115
|
+
return {
|
|
116
|
+
translateString: (value: string | undefined) => {
|
|
117
|
+
return value;
|
|
118
|
+
},
|
|
119
|
+
translateValue: (value: unknown) => {
|
|
120
|
+
return value;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
jest.mock("Common/UI/Utils/Project", () => {
|
|
128
|
+
return {
|
|
129
|
+
__esModule: true,
|
|
130
|
+
default: {
|
|
131
|
+
getCurrentProjectId: () => {
|
|
132
|
+
return new ObjectID(PROJECT_ID);
|
|
133
|
+
},
|
|
134
|
+
getCurrentProject: () => {
|
|
135
|
+
return null;
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
* The resource form is a multi step ModelForm that fetches its own dropdown
|
|
143
|
+
* options. Nothing in this suite is about the form's contents - only about
|
|
144
|
+
* which modal a button opens - so it is reduced to its title.
|
|
145
|
+
*/
|
|
146
|
+
const mockModelFormModalProps: Array<any> = [];
|
|
147
|
+
|
|
148
|
+
jest.mock("Common/UI/Components/ModelFormModal/ModelFormModal", () => {
|
|
149
|
+
return {
|
|
150
|
+
__esModule: true,
|
|
151
|
+
default: (props: { title: string }) => {
|
|
152
|
+
/*
|
|
153
|
+
* Recorded so a test can drive the callbacks a real submit would fire -
|
|
154
|
+
* which is the only way to reach what the page does AFTER a group is
|
|
155
|
+
* written, and that is the half of the merge worth proving.
|
|
156
|
+
*/
|
|
157
|
+
mockModelFormModalProps.push(props);
|
|
158
|
+
|
|
159
|
+
return React.createElement(
|
|
160
|
+
"div",
|
|
161
|
+
{ "data-testid": "model-form-modal" },
|
|
162
|
+
props.title,
|
|
163
|
+
);
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
jest.mock("Common/UI/Utils/ModelAPI/ModelAPI", () => {
|
|
169
|
+
return {
|
|
170
|
+
__esModule: true,
|
|
171
|
+
default: {
|
|
172
|
+
getList: jest.fn(),
|
|
173
|
+
count: jest.fn(),
|
|
174
|
+
deleteItem: jest.fn(),
|
|
175
|
+
updateById: jest.fn(),
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
import StatusPageResources from "../../../../App/FeatureSet/Dashboard/src/Pages/StatusPages/View/Resources";
|
|
181
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
182
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
183
|
+
import StatusPageGroup from "../../../Models/DatabaseModels/StatusPageGroup";
|
|
184
|
+
import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResource";
|
|
185
|
+
import Route from "../../../Types/API/Route";
|
|
186
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
187
|
+
import StatusPageGroupViewMode from "../../../Types/StatusPage/StatusPageGroupViewMode";
|
|
188
|
+
import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
|
|
189
|
+
import { MemoryRouter } from "react-router-dom";
|
|
190
|
+
|
|
191
|
+
const mockGetList: jest.MockedFunction<any> =
|
|
192
|
+
ModelAPI.getList as unknown as jest.MockedFunction<any>;
|
|
193
|
+
const mockUpdateById: jest.MockedFunction<any> =
|
|
194
|
+
ModelAPI.updateById as unknown as jest.MockedFunction<any>;
|
|
195
|
+
const mockDeleteItem: jest.MockedFunction<any> =
|
|
196
|
+
ModelAPI.deleteItem as unknown as jest.MockedFunction<any>;
|
|
197
|
+
|
|
198
|
+
interface GetListCall {
|
|
199
|
+
modelType: unknown;
|
|
200
|
+
query: Record<string, any>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
interface MakeGroupData {
|
|
204
|
+
id: string;
|
|
205
|
+
name: string;
|
|
206
|
+
parentId?: string | undefined;
|
|
207
|
+
order?: number | undefined;
|
|
208
|
+
viewMode?: StatusPageGroupViewMode | undefined;
|
|
209
|
+
/*
|
|
210
|
+
* The three settings that change what a visitor sees and are otherwise only
|
|
211
|
+
* visible three steps into the group form. The pane header is the only place
|
|
212
|
+
* they surface now, so a fixture has to be able to carry them.
|
|
213
|
+
*/
|
|
214
|
+
description?: string | undefined;
|
|
215
|
+
isExpandedByDefault?: boolean | undefined;
|
|
216
|
+
showUptimePercent?: boolean | undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
type MakeGroupFunction = (data: MakeGroupData) => StatusPageGroup;
|
|
220
|
+
|
|
221
|
+
const makeGroup: MakeGroupFunction = (data: MakeGroupData): StatusPageGroup => {
|
|
222
|
+
const group: StatusPageGroup = new StatusPageGroup();
|
|
223
|
+
group._id = data.id;
|
|
224
|
+
group.name = data.name;
|
|
225
|
+
group.order = data.order === undefined ? 1 : data.order;
|
|
226
|
+
|
|
227
|
+
if (data.parentId) {
|
|
228
|
+
group.parentStatusPageGroupId = new ObjectID(data.parentId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (data.viewMode) {
|
|
232
|
+
group.viewMode = data.viewMode;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (data.description !== undefined) {
|
|
236
|
+
group.description = data.description;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (data.isExpandedByDefault !== undefined) {
|
|
240
|
+
group.isExpandedByDefault = data.isExpandedByDefault;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (data.showUptimePercent !== undefined) {
|
|
244
|
+
group.showUptimePercent = data.showUptimePercent;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return group;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
type MakeResourceFunction = (data: {
|
|
251
|
+
id: string;
|
|
252
|
+
groupId?: string | undefined;
|
|
253
|
+
order?: number | undefined;
|
|
254
|
+
monitorName?: string | undefined;
|
|
255
|
+
displayName?: string | undefined;
|
|
256
|
+
rowAxisValue?: string | undefined;
|
|
257
|
+
columnAxisValue?: string | undefined;
|
|
258
|
+
}) => StatusPageResource;
|
|
259
|
+
|
|
260
|
+
const makeResource: MakeResourceFunction = (data: {
|
|
261
|
+
id: string;
|
|
262
|
+
groupId?: string | undefined;
|
|
263
|
+
order?: number | undefined;
|
|
264
|
+
monitorName?: string | undefined;
|
|
265
|
+
displayName?: string | undefined;
|
|
266
|
+
rowAxisValue?: string | undefined;
|
|
267
|
+
columnAxisValue?: string | undefined;
|
|
268
|
+
}): StatusPageResource => {
|
|
269
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
270
|
+
resource._id = data.id;
|
|
271
|
+
resource.order = data.order === undefined ? 1 : data.order;
|
|
272
|
+
|
|
273
|
+
if (data.groupId) {
|
|
274
|
+
resource.statusPageGroupId = new ObjectID(data.groupId);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (data.monitorName) {
|
|
278
|
+
const monitor: Monitor = new Monitor();
|
|
279
|
+
monitor._id = `monitor-${data.id}`;
|
|
280
|
+
monitor.name = data.monitorName;
|
|
281
|
+
resource.monitor = monitor;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (data.displayName) {
|
|
285
|
+
resource.displayName = data.displayName;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (data.rowAxisValue) {
|
|
289
|
+
resource.rowAxisValue = data.rowAxisValue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (data.columnAxisValue) {
|
|
293
|
+
resource.columnAxisValue = data.columnAxisValue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return resource;
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/* Corporate › Region 1000 › Market 1001, plus a grid group beside them. */
|
|
300
|
+
const CORPORATE_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194163001";
|
|
301
|
+
const REGION_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194163002";
|
|
302
|
+
const MARKET_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194163003";
|
|
303
|
+
const GRID_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194163004";
|
|
304
|
+
|
|
305
|
+
type BuildHierarchyFunction = () => Array<StatusPageGroup>;
|
|
306
|
+
|
|
307
|
+
const buildHierarchy: BuildHierarchyFunction = (): Array<StatusPageGroup> => {
|
|
308
|
+
const grid: StatusPageGroup = makeGroup({
|
|
309
|
+
id: GRID_ID,
|
|
310
|
+
name: "Grid Group",
|
|
311
|
+
order: 4,
|
|
312
|
+
viewMode: StatusPageGroupViewMode.Grid,
|
|
313
|
+
});
|
|
314
|
+
grid.rowAxisValues = "Auth, API";
|
|
315
|
+
grid.columnAxisValues = "US-East, EU-West";
|
|
316
|
+
grid.rowAxisLabel = "Service";
|
|
317
|
+
grid.columnAxisLabel = "Region";
|
|
318
|
+
|
|
319
|
+
return [
|
|
320
|
+
makeGroup({ id: CORPORATE_ID, name: "Corporate", order: 1 }),
|
|
321
|
+
makeGroup({
|
|
322
|
+
id: REGION_ID,
|
|
323
|
+
name: "Region 1000",
|
|
324
|
+
parentId: CORPORATE_ID,
|
|
325
|
+
order: 2,
|
|
326
|
+
}),
|
|
327
|
+
makeGroup({
|
|
328
|
+
id: MARKET_ID,
|
|
329
|
+
name: "Market 1001",
|
|
330
|
+
parentId: REGION_ID,
|
|
331
|
+
order: 3,
|
|
332
|
+
}),
|
|
333
|
+
grid,
|
|
334
|
+
];
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
type SetUpApiFunction = (data: {
|
|
338
|
+
groups: Array<StatusPageGroup>;
|
|
339
|
+
resources?: Array<StatusPageResource> | undefined;
|
|
340
|
+
resourceTotalCount?: number | undefined;
|
|
341
|
+
}) => Array<GetListCall>;
|
|
342
|
+
|
|
343
|
+
const setUpApi: SetUpApiFunction = (data: {
|
|
344
|
+
groups: Array<StatusPageGroup>;
|
|
345
|
+
resources?: Array<StatusPageResource> | undefined;
|
|
346
|
+
resourceTotalCount?: number | undefined;
|
|
347
|
+
}): Array<GetListCall> => {
|
|
348
|
+
const calls: Array<GetListCall> = [];
|
|
349
|
+
const resources: Array<StatusPageResource> = data.resources || [];
|
|
350
|
+
|
|
351
|
+
mockGetList.mockImplementation(async (callData: any): Promise<any> => {
|
|
352
|
+
calls.push({ modelType: callData.modelType, query: callData.query || {} });
|
|
353
|
+
|
|
354
|
+
if (callData.modelType === StatusPageGroup) {
|
|
355
|
+
/*
|
|
356
|
+
* A fresh array every time, the way a real response is. Handing back the
|
|
357
|
+
* identical instance the page is already holding makes setGroups a no-op
|
|
358
|
+
* that React drops, so a test that mutated the fixture would be reading
|
|
359
|
+
* its own mutation rather than proving the page re-read anything.
|
|
360
|
+
*/
|
|
361
|
+
return {
|
|
362
|
+
data: [...data.groups],
|
|
363
|
+
count: data.groups.length,
|
|
364
|
+
skip: 0,
|
|
365
|
+
limit: data.groups.length,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const isGroupScoped: boolean = Object.prototype.hasOwnProperty.call(
|
|
370
|
+
callData.query || {},
|
|
371
|
+
"statusPageGroupId",
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
if (!isGroupScoped) {
|
|
375
|
+
return {
|
|
376
|
+
data: [...resources],
|
|
377
|
+
count:
|
|
378
|
+
data.resourceTotalCount === undefined
|
|
379
|
+
? resources.length
|
|
380
|
+
: data.resourceTotalCount,
|
|
381
|
+
skip: 0,
|
|
382
|
+
limit: resources.length,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const wantedGroupId: string | null = callData.query.statusPageGroupId
|
|
387
|
+
? callData.query.statusPageGroupId.toString()
|
|
388
|
+
: null;
|
|
389
|
+
|
|
390
|
+
const inGroup: Array<StatusPageResource> = resources.filter(
|
|
391
|
+
(resource: StatusPageResource) => {
|
|
392
|
+
return (
|
|
393
|
+
(resource.statusPageGroupId?.toString() || null) === wantedGroupId
|
|
394
|
+
);
|
|
395
|
+
},
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
data: inGroup,
|
|
400
|
+
count: inGroup.length,
|
|
401
|
+
skip: 0,
|
|
402
|
+
limit: inGroup.length,
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
mockUpdateById.mockImplementation(async (): Promise<any> => {
|
|
407
|
+
return {};
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
mockDeleteItem.mockImplementation(async (): Promise<any> => {
|
|
411
|
+
return {};
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
return calls;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
type GroupCallsFunction = (calls: Array<GetListCall>) => Array<GetListCall>;
|
|
418
|
+
|
|
419
|
+
const groupCalls: GroupCallsFunction = (
|
|
420
|
+
calls: Array<GetListCall>,
|
|
421
|
+
): Array<GetListCall> => {
|
|
422
|
+
return calls.filter((call: GetListCall) => {
|
|
423
|
+
return call.modelType === StatusPageGroup;
|
|
424
|
+
});
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
type ResourceCallsFunction = (calls: Array<GetListCall>) => Array<GetListCall>;
|
|
428
|
+
|
|
429
|
+
const resourceCalls: ResourceCallsFunction = (
|
|
430
|
+
calls: Array<GetListCall>,
|
|
431
|
+
): Array<GetListCall> => {
|
|
432
|
+
return calls.filter((call: GetListCall) => {
|
|
433
|
+
return call.modelType === StatusPageResource;
|
|
434
|
+
});
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
/*
|
|
438
|
+
* The per-selection fetches - everything except the one pass over all resources
|
|
439
|
+
* that builds the count badges, which deliberately does not filter by group.
|
|
440
|
+
*/
|
|
441
|
+
type ScopedResourceCallsFunction = (
|
|
442
|
+
calls: Array<GetListCall>,
|
|
443
|
+
) => Array<GetListCall>;
|
|
444
|
+
|
|
445
|
+
const scopedResourceCalls: ScopedResourceCallsFunction = (
|
|
446
|
+
calls: Array<GetListCall>,
|
|
447
|
+
): Array<GetListCall> => {
|
|
448
|
+
return resourceCalls(calls).filter((call: GetListCall) => {
|
|
449
|
+
return Object.prototype.hasOwnProperty.call(
|
|
450
|
+
call.query,
|
|
451
|
+
"statusPageGroupId",
|
|
452
|
+
);
|
|
453
|
+
});
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
type SelectedGroupIdsFunction = (calls: Array<GetListCall>) => Array<string>;
|
|
457
|
+
|
|
458
|
+
const selectedGroupIds: SelectedGroupIdsFunction = (
|
|
459
|
+
calls: Array<GetListCall>,
|
|
460
|
+
): Array<string> => {
|
|
461
|
+
return scopedResourceCalls(calls).map((call: GetListCall) => {
|
|
462
|
+
return call.query["statusPageGroupId"]
|
|
463
|
+
? call.query["statusPageGroupId"].toString()
|
|
464
|
+
: "ungrouped";
|
|
465
|
+
});
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
type RenderPageFunction = () => ReturnType<typeof render>;
|
|
469
|
+
|
|
470
|
+
const renderPage: RenderPageFunction = (): ReturnType<typeof render> => {
|
|
471
|
+
const project: Project = new Project();
|
|
472
|
+
project._id = PROJECT_ID;
|
|
473
|
+
project.name = "Test Project";
|
|
474
|
+
|
|
475
|
+
return render(
|
|
476
|
+
<MemoryRouter>
|
|
477
|
+
<StatusPageResources
|
|
478
|
+
pageRoute={new Route("/dashboard/status-pages")}
|
|
479
|
+
currentProject={project}
|
|
480
|
+
hasPaymentMethod={true}
|
|
481
|
+
/>
|
|
482
|
+
</MemoryRouter>,
|
|
483
|
+
);
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
/*
|
|
487
|
+
* The page settles over several commits - groups and counts arrive, the initial
|
|
488
|
+
* selection is chosen, and the pane fetches whatever it landed on. React 18
|
|
489
|
+
* flushes a passive effect at the next act boundary rather than with the commit
|
|
490
|
+
* that scheduled it, so a "how many requests were made" assertion taken the
|
|
491
|
+
* instant a group name appears would be reading a half finished page.
|
|
492
|
+
*/
|
|
493
|
+
type FlushEffectsFunction = () => Promise<void>;
|
|
494
|
+
|
|
495
|
+
const flushEffects: FlushEffectsFunction = async (): Promise<void> => {
|
|
496
|
+
for (let index: number = 0; index < 6; index++) {
|
|
497
|
+
await act(async () => {
|
|
498
|
+
await Promise.resolve();
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
type NavigatorRowsFunction = () => Array<HTMLElement>;
|
|
504
|
+
|
|
505
|
+
const navigatorRows: NavigatorRowsFunction = (): Array<HTMLElement> => {
|
|
506
|
+
return screen.queryAllByTestId("status-page-resource-navigator-row");
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
type FindNavigatorRowFunction = (name: string) => HTMLElement | undefined;
|
|
510
|
+
|
|
511
|
+
/*
|
|
512
|
+
* Group names show up twice once a group is selected - once in the navigator
|
|
513
|
+
* and once as the pane's heading - so every assertion about the hierarchy is
|
|
514
|
+
* made against the navigator's own rows rather than against the page.
|
|
515
|
+
*/
|
|
516
|
+
const findNavigatorRow: FindNavigatorRowFunction = (
|
|
517
|
+
name: string,
|
|
518
|
+
): HTMLElement | undefined => {
|
|
519
|
+
return navigatorRows().find((row: HTMLElement) => {
|
|
520
|
+
return (row.textContent || "").includes(name);
|
|
521
|
+
});
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
type NavigatorRowByNameFunction = (name: string) => HTMLElement;
|
|
525
|
+
|
|
526
|
+
const navigatorRowByName: NavigatorRowByNameFunction = (
|
|
527
|
+
name: string,
|
|
528
|
+
): HTMLElement => {
|
|
529
|
+
const found: HTMLElement | undefined = findNavigatorRow(name);
|
|
530
|
+
|
|
531
|
+
if (!found) {
|
|
532
|
+
throw new Error(`No navigator row called ${name} is on screen`);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return found;
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
type WaitForSettledFunction = () => Promise<void>;
|
|
539
|
+
|
|
540
|
+
/*
|
|
541
|
+
* Nothing on this page is loading any more: not the groups, not the counts, and
|
|
542
|
+
* not the selected group's resources. The pane mounts only once the initial
|
|
543
|
+
* selection is chosen, and then fetches, so "the navigator has rows" is several
|
|
544
|
+
* commits short of "the page is ready to be asserted against".
|
|
545
|
+
*/
|
|
546
|
+
const waitForSettled: WaitForSettledFunction = async (): Promise<void> => {
|
|
547
|
+
await waitFor(
|
|
548
|
+
() => {
|
|
549
|
+
expect(screen.queryAllByTestId("component-loader").length).toBe(0);
|
|
550
|
+
},
|
|
551
|
+
/*
|
|
552
|
+
* Longer than the default second: this suite renders hierarchies of
|
|
553
|
+
* fifteen hundred groups, and a machine busy with the rest of the test run
|
|
554
|
+
* takes its time over them.
|
|
555
|
+
*/
|
|
556
|
+
{ timeout: 15000 },
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
await flushEffects();
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
type WaitForExplorerFunction = () => Promise<void>;
|
|
563
|
+
|
|
564
|
+
/*
|
|
565
|
+
* The tab settles over several commits: the groups arrive, the navigator is
|
|
566
|
+
* drawn, the initial selection is chosen from the counts, and only then does
|
|
567
|
+
* the pane mount and fetch. Every test starts from the settled page.
|
|
568
|
+
*/
|
|
569
|
+
const waitForExplorer: WaitForExplorerFunction = async (): Promise<void> => {
|
|
570
|
+
await waitFor(
|
|
571
|
+
() => {
|
|
572
|
+
expect(navigatorRows().length).toBeGreaterThan(0);
|
|
573
|
+
},
|
|
574
|
+
{ timeout: 15000 },
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
await waitForSettled();
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
type SelectGroupFunction = (name: string) => Promise<void>;
|
|
581
|
+
|
|
582
|
+
/*
|
|
583
|
+
* Selecting remounts the pane on the new group, which then fetches, so every
|
|
584
|
+
* selection is followed by waiting for that fetch to land.
|
|
585
|
+
*/
|
|
586
|
+
const selectGroup: SelectGroupFunction = async (
|
|
587
|
+
name: string,
|
|
588
|
+
): Promise<void> => {
|
|
589
|
+
fireEvent.click(
|
|
590
|
+
within(navigatorRowByName(name)).getByTestId(
|
|
591
|
+
"status-page-resource-navigator-select",
|
|
592
|
+
),
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
await waitForSettled();
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
/* The id a group created during a test comes back with. */
|
|
599
|
+
const NEW_GROUP_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194163099";
|
|
600
|
+
|
|
601
|
+
type LastFormModalFunction = () => any;
|
|
602
|
+
|
|
603
|
+
/*
|
|
604
|
+
* The form that is currently open. Only one is ever mounted, so the last props
|
|
605
|
+
* recorded are its props.
|
|
606
|
+
*/
|
|
607
|
+
const lastFormModal: LastFormModalFunction = (): any => {
|
|
608
|
+
const props: any =
|
|
609
|
+
mockModelFormModalProps[mockModelFormModalProps.length - 1];
|
|
610
|
+
|
|
611
|
+
if (!props) {
|
|
612
|
+
throw new Error("No form modal is open");
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return props;
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
type OpenRowMenuFunction = (name: string) => Promise<void>;
|
|
619
|
+
|
|
620
|
+
/*
|
|
621
|
+
* A navigator row's overflow menu. The cluster it lives in is revealed on hover
|
|
622
|
+
* in a browser and is always in the DOM in jsdom, so the click is enough.
|
|
623
|
+
*/
|
|
624
|
+
const openRowMenu: OpenRowMenuFunction = async (
|
|
625
|
+
name: string,
|
|
626
|
+
): Promise<void> => {
|
|
627
|
+
fireEvent.click(
|
|
628
|
+
within(navigatorRowByName(name)).getByTestId(
|
|
629
|
+
"status-page-resource-navigator-more",
|
|
630
|
+
),
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
await flushEffects();
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
describe("Status Page > Resources", () => {
|
|
637
|
+
beforeEach(() => {
|
|
638
|
+
jest.clearAllMocks();
|
|
639
|
+
mockIsMasterAdmin.mockReturnValue(true);
|
|
640
|
+
mockModelFormModalProps.length = 0;
|
|
641
|
+
window.localStorage.clear();
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
describe("opening the tab", () => {
|
|
645
|
+
test("costs the same handful of requests on a 1506 group status page", async () => {
|
|
646
|
+
const groups: Array<StatusPageGroup> = [];
|
|
647
|
+
|
|
648
|
+
for (let index: number = 0; index < 1506; index++) {
|
|
649
|
+
groups.push(
|
|
650
|
+
makeGroup({
|
|
651
|
+
id: `0198c8ec-2a1d-7f0c-9e75-38419417${index
|
|
652
|
+
.toString()
|
|
653
|
+
.padStart(4, "0")}`,
|
|
654
|
+
name: `Group ${index}`,
|
|
655
|
+
order: index,
|
|
656
|
+
}),
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const calls: Array<GetListCall> = setUpApi({ groups: groups });
|
|
661
|
+
|
|
662
|
+
renderPage();
|
|
663
|
+
|
|
664
|
+
await waitForExplorer();
|
|
665
|
+
|
|
666
|
+
/*
|
|
667
|
+
* The bug was 1506 of these. One is the whole point of the explorer: the
|
|
668
|
+
* pane loads the selection, and there is only ever one selection.
|
|
669
|
+
*/
|
|
670
|
+
expect(scopedResourceCalls(calls).length).toBe(1);
|
|
671
|
+
/* Groups, the one pass for the counts, and the selection. */
|
|
672
|
+
expect(calls.length).toBe(3);
|
|
673
|
+
/*
|
|
674
|
+
* Rendering fifteen hundred groups in jsdom is legitimately slower than
|
|
675
|
+
* jest's five second default. The number this test exists to hold is the
|
|
676
|
+
* request count, not the clock.
|
|
677
|
+
*/
|
|
678
|
+
}, 60000);
|
|
679
|
+
|
|
680
|
+
test("draws the hierarchy in the navigator", async () => {
|
|
681
|
+
setUpApi({ groups: buildHierarchy() });
|
|
682
|
+
|
|
683
|
+
renderPage();
|
|
684
|
+
|
|
685
|
+
await waitForExplorer();
|
|
686
|
+
|
|
687
|
+
expect(navigatorRowByName("Region 1000")).toBeInTheDocument();
|
|
688
|
+
expect(navigatorRowByName("Market 1001")).toBeInTheDocument();
|
|
689
|
+
expect(navigatorRowByName("Grid Group")).toBeInTheDocument();
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
test("opens on the uncategorized bucket when there are resources in it", async () => {
|
|
693
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
694
|
+
groups: buildHierarchy(),
|
|
695
|
+
resources: [
|
|
696
|
+
makeResource({ id: "loose", monitorName: "Loose Monitor" }),
|
|
697
|
+
],
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
renderPage();
|
|
701
|
+
|
|
702
|
+
await waitForExplorer();
|
|
703
|
+
|
|
704
|
+
expect(
|
|
705
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
706
|
+
).toBe("Top of page");
|
|
707
|
+
expect(selectedGroupIds(calls)).toEqual(["ungrouped"]);
|
|
708
|
+
expect(screen.getByText("Loose Monitor")).toBeInTheDocument();
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
/*
|
|
712
|
+
* An empty pane beside a full navigator reads as a page that failed to
|
|
713
|
+
* load.
|
|
714
|
+
*/
|
|
715
|
+
test("opens on the first group when nothing is uncategorized", async () => {
|
|
716
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
717
|
+
groups: buildHierarchy(),
|
|
718
|
+
resources: [
|
|
719
|
+
makeResource({
|
|
720
|
+
id: "a",
|
|
721
|
+
groupId: CORPORATE_ID,
|
|
722
|
+
monitorName: "Corporate Monitor",
|
|
723
|
+
}),
|
|
724
|
+
],
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
renderPage();
|
|
728
|
+
|
|
729
|
+
await waitForExplorer();
|
|
730
|
+
|
|
731
|
+
expect(
|
|
732
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
733
|
+
).toBe("Corporate");
|
|
734
|
+
expect(selectedGroupIds(calls)).toEqual([CORPORATE_ID]);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
test("a status page with no groups is one flat list and no navigator", async () => {
|
|
738
|
+
setUpApi({
|
|
739
|
+
groups: [],
|
|
740
|
+
resources: [makeResource({ id: "a", monitorName: "Only Monitor" })],
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
renderPage();
|
|
744
|
+
|
|
745
|
+
await waitForSettled();
|
|
746
|
+
|
|
747
|
+
expect(screen.getByText("Only Monitor")).toBeInTheDocument();
|
|
748
|
+
|
|
749
|
+
expect(
|
|
750
|
+
screen.queryByTestId("status-page-resource-navigator"),
|
|
751
|
+
).not.toBeInTheDocument();
|
|
752
|
+
expect(
|
|
753
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
754
|
+
).toBe("All resources");
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
describe("moving around the hierarchy", () => {
|
|
759
|
+
test("selecting a group fetches that group and nothing else", async () => {
|
|
760
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
761
|
+
groups: buildHierarchy(),
|
|
762
|
+
resources: [
|
|
763
|
+
makeResource({ id: "loose", monitorName: "Loose Monitor" }),
|
|
764
|
+
makeResource({
|
|
765
|
+
id: "market",
|
|
766
|
+
groupId: MARKET_ID,
|
|
767
|
+
monitorName: "Market Monitor",
|
|
768
|
+
}),
|
|
769
|
+
],
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
renderPage();
|
|
773
|
+
|
|
774
|
+
await waitForExplorer();
|
|
775
|
+
|
|
776
|
+
const before: number = scopedResourceCalls(calls).length;
|
|
777
|
+
|
|
778
|
+
await selectGroup("Market 1001");
|
|
779
|
+
|
|
780
|
+
expect(screen.getByText("Market Monitor")).toBeInTheDocument();
|
|
781
|
+
expect(scopedResourceCalls(calls).length).toBe(before + 1);
|
|
782
|
+
expect(selectedGroupIds(calls).slice(-1)).toEqual([MARKET_ID]);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
/*
|
|
786
|
+
* The tree this replaced fetched a group the moment it was opened, so
|
|
787
|
+
* looking inside a branch cost a request per group on the way down. Opening
|
|
788
|
+
* and selecting are different acts now.
|
|
789
|
+
*/
|
|
790
|
+
test("opening a branch in the navigator fetches nothing", async () => {
|
|
791
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
792
|
+
groups: buildHierarchy(),
|
|
793
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
renderPage();
|
|
797
|
+
|
|
798
|
+
await waitForExplorer();
|
|
799
|
+
|
|
800
|
+
const before: number = calls.length;
|
|
801
|
+
|
|
802
|
+
/* Close Corporate, then open it again. */
|
|
803
|
+
const disclosure: HTMLElement = within(
|
|
804
|
+
navigatorRowByName("Corporate"),
|
|
805
|
+
).getByTestId("status-page-resource-navigator-disclosure");
|
|
806
|
+
|
|
807
|
+
fireEvent.click(disclosure);
|
|
808
|
+
await flushEffects();
|
|
809
|
+
|
|
810
|
+
expect(findNavigatorRow("Region 1000")).toBeUndefined();
|
|
811
|
+
|
|
812
|
+
fireEvent.click(
|
|
813
|
+
within(navigatorRowByName("Corporate")).getByTestId(
|
|
814
|
+
"status-page-resource-navigator-disclosure",
|
|
815
|
+
),
|
|
816
|
+
);
|
|
817
|
+
await flushEffects();
|
|
818
|
+
|
|
819
|
+
expect(navigatorRowByName("Region 1000")).toBeInTheDocument();
|
|
820
|
+
expect(calls.length).toBe(before);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
test("a nested group says where it sits, and the path is clickable", async () => {
|
|
824
|
+
setUpApi({
|
|
825
|
+
groups: buildHierarchy(),
|
|
826
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
renderPage();
|
|
830
|
+
|
|
831
|
+
await waitForExplorer();
|
|
832
|
+
|
|
833
|
+
await selectGroup("Market 1001");
|
|
834
|
+
|
|
835
|
+
const breadcrumb: HTMLElement = screen.getByTestId(
|
|
836
|
+
"status-page-resource-panel-breadcrumb",
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
const steps: Array<HTMLElement> = within(breadcrumb).getAllByTestId(
|
|
840
|
+
"status-page-resource-panel-breadcrumb-step",
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
expect(
|
|
844
|
+
steps.map((step: HTMLElement) => {
|
|
845
|
+
return step.textContent;
|
|
846
|
+
}),
|
|
847
|
+
).toEqual(["Corporate", "Region 1000"]);
|
|
848
|
+
|
|
849
|
+
fireEvent.click(steps[0]!);
|
|
850
|
+
await waitForSettled();
|
|
851
|
+
|
|
852
|
+
expect(
|
|
853
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
854
|
+
).toBe("Corporate");
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
test("finds a group by name and leaves its ancestors as context", async () => {
|
|
858
|
+
setUpApi({
|
|
859
|
+
groups: buildHierarchy(),
|
|
860
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
renderPage();
|
|
864
|
+
|
|
865
|
+
await waitForExplorer();
|
|
866
|
+
|
|
867
|
+
fireEvent.change(
|
|
868
|
+
screen.getByTestId("status-page-resource-group-search"),
|
|
869
|
+
{
|
|
870
|
+
target: { value: "Market" },
|
|
871
|
+
},
|
|
872
|
+
);
|
|
873
|
+
|
|
874
|
+
await flushEffects();
|
|
875
|
+
|
|
876
|
+
expect(navigatorRowByName("Market 1001")).toBeInTheDocument();
|
|
877
|
+
expect(findNavigatorRow("Grid Group")).toBeUndefined();
|
|
878
|
+
/* Its ancestors stay so the match has somewhere to sit. */
|
|
879
|
+
expect(navigatorRowByName("Corporate")).toBeInTheDocument();
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
test("says so when a search matches nothing", async () => {
|
|
883
|
+
setUpApi({ groups: buildHierarchy() });
|
|
884
|
+
|
|
885
|
+
renderPage();
|
|
886
|
+
|
|
887
|
+
await waitForExplorer();
|
|
888
|
+
|
|
889
|
+
fireEvent.change(
|
|
890
|
+
screen.getByTestId("status-page-resource-group-search"),
|
|
891
|
+
{
|
|
892
|
+
target: { value: "nothing matches this" },
|
|
893
|
+
},
|
|
894
|
+
);
|
|
895
|
+
|
|
896
|
+
await flushEffects();
|
|
897
|
+
|
|
898
|
+
expect(
|
|
899
|
+
screen.getByTestId("status-page-resource-navigator-empty"),
|
|
900
|
+
).toBeInTheDocument();
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
describe("the counts beside each group", () => {
|
|
905
|
+
test("show what a group holds, and what is further down when it holds none", async () => {
|
|
906
|
+
setUpApi({
|
|
907
|
+
groups: buildHierarchy(),
|
|
908
|
+
resources: [
|
|
909
|
+
makeResource({ id: "a", groupId: MARKET_ID }),
|
|
910
|
+
makeResource({ id: "b", groupId: MARKET_ID }),
|
|
911
|
+
makeResource({ id: "c", groupId: REGION_ID }),
|
|
912
|
+
],
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
renderPage();
|
|
916
|
+
|
|
917
|
+
await waitForExplorer();
|
|
918
|
+
|
|
919
|
+
expect(
|
|
920
|
+
within(navigatorRowByName("Market 1001")).getByTestId(
|
|
921
|
+
"status-page-resource-navigator-count",
|
|
922
|
+
).textContent,
|
|
923
|
+
).toBe("2");
|
|
924
|
+
|
|
925
|
+
/* Corporate holds nothing itself but three sit below it. */
|
|
926
|
+
expect(
|
|
927
|
+
within(navigatorRowByName("Corporate")).getByTestId(
|
|
928
|
+
"status-page-resource-navigator-count",
|
|
929
|
+
).textContent,
|
|
930
|
+
).toBe("+3");
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
test("stay silent when the count pass came back truncated", async () => {
|
|
934
|
+
setUpApi({
|
|
935
|
+
groups: buildHierarchy(),
|
|
936
|
+
resources: [makeResource({ id: "a", groupId: MARKET_ID })],
|
|
937
|
+
resourceTotalCount: 5000,
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
renderPage();
|
|
941
|
+
|
|
942
|
+
await waitForExplorer();
|
|
943
|
+
|
|
944
|
+
expect(
|
|
945
|
+
screen.queryAllByTestId("status-page-resource-navigator-count").length,
|
|
946
|
+
).toBe(0);
|
|
947
|
+
});
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
describe("the group's resources", () => {
|
|
951
|
+
test("are listed by monitor, with the public name only when it differs", async () => {
|
|
952
|
+
setUpApi({
|
|
953
|
+
groups: buildHierarchy(),
|
|
954
|
+
resources: [
|
|
955
|
+
makeResource({
|
|
956
|
+
id: "a",
|
|
957
|
+
monitorName: "High Node Disk Usage",
|
|
958
|
+
displayName: "Disk",
|
|
959
|
+
}),
|
|
960
|
+
makeResource({
|
|
961
|
+
id: "b",
|
|
962
|
+
monitorName: "API",
|
|
963
|
+
displayName: "API",
|
|
964
|
+
}),
|
|
965
|
+
],
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
renderPage();
|
|
969
|
+
|
|
970
|
+
await waitForExplorer();
|
|
971
|
+
|
|
972
|
+
expect(screen.getByText("High Node Disk Usage")).toBeInTheDocument();
|
|
973
|
+
|
|
974
|
+
expect(screen.queryAllByTestId("status-page-resource-row").length).toBe(
|
|
975
|
+
2,
|
|
976
|
+
);
|
|
977
|
+
|
|
978
|
+
const displayNames: Array<HTMLElement> = screen.queryAllByTestId(
|
|
979
|
+
"status-page-resource-row-display-name",
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
expect(displayNames.length).toBe(1);
|
|
983
|
+
expect(displayNames[0]!.textContent).toContain("Disk");
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
test("offer somewhere to start when the group is empty", async () => {
|
|
987
|
+
setUpApi({
|
|
988
|
+
groups: buildHierarchy(),
|
|
989
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
renderPage();
|
|
993
|
+
|
|
994
|
+
await waitForExplorer();
|
|
995
|
+
|
|
996
|
+
await selectGroup("Market 1001");
|
|
997
|
+
|
|
998
|
+
expect(screen.getByText("No monitors here yet")).toBeInTheDocument();
|
|
999
|
+
expect(
|
|
1000
|
+
screen.getByTestId("status-page-resource-panel-empty-add"),
|
|
1001
|
+
).toBeInTheDocument();
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
test("filter down to what was typed, in the pane rather than the navigator", async () => {
|
|
1005
|
+
setUpApi({
|
|
1006
|
+
groups: buildHierarchy(),
|
|
1007
|
+
resources: [
|
|
1008
|
+
makeResource({ id: "a", monitorName: "API" }),
|
|
1009
|
+
makeResource({ id: "b", monitorName: "Database" }),
|
|
1010
|
+
],
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
renderPage();
|
|
1014
|
+
|
|
1015
|
+
await waitForExplorer();
|
|
1016
|
+
|
|
1017
|
+
expect(screen.getByText("Database")).toBeInTheDocument();
|
|
1018
|
+
|
|
1019
|
+
fireEvent.change(
|
|
1020
|
+
screen.getByTestId("status-page-resource-panel-search"),
|
|
1021
|
+
{
|
|
1022
|
+
target: { value: "data" },
|
|
1023
|
+
},
|
|
1024
|
+
);
|
|
1025
|
+
|
|
1026
|
+
await flushEffects();
|
|
1027
|
+
|
|
1028
|
+
expect(screen.queryAllByTestId("status-page-resource-row").length).toBe(
|
|
1029
|
+
1,
|
|
1030
|
+
);
|
|
1031
|
+
expect(screen.getByText("Database")).toBeInTheDocument();
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
test("a grid group opens into its matrix instead of a list", async () => {
|
|
1035
|
+
setUpApi({
|
|
1036
|
+
groups: buildHierarchy(),
|
|
1037
|
+
resources: [
|
|
1038
|
+
makeResource({ id: "loose", monitorName: "Loose" }),
|
|
1039
|
+
makeResource({
|
|
1040
|
+
id: "grid-a",
|
|
1041
|
+
groupId: GRID_ID,
|
|
1042
|
+
monitorName: "Auth US",
|
|
1043
|
+
rowAxisValue: "Auth",
|
|
1044
|
+
columnAxisValue: "US-East",
|
|
1045
|
+
}),
|
|
1046
|
+
],
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
renderPage();
|
|
1050
|
+
|
|
1051
|
+
await waitForExplorer();
|
|
1052
|
+
|
|
1053
|
+
await selectGroup("Grid Group");
|
|
1054
|
+
|
|
1055
|
+
expect(
|
|
1056
|
+
screen.getByTestId("status-page-resource-grid"),
|
|
1057
|
+
).toBeInTheDocument();
|
|
1058
|
+
expect(
|
|
1059
|
+
screen.queryByTestId("status-page-resource-list"),
|
|
1060
|
+
).not.toBeInTheDocument();
|
|
1061
|
+
expect(screen.getByText("Auth US")).toBeInTheDocument();
|
|
1062
|
+
expect(
|
|
1063
|
+
screen.getByTestId("status-page-resource-panel-grid-badge"),
|
|
1064
|
+
).toBeInTheDocument();
|
|
1065
|
+
});
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
describe("changing a group", () => {
|
|
1069
|
+
test("moving a resource up writes the order of the one it displaced", async () => {
|
|
1070
|
+
setUpApi({
|
|
1071
|
+
groups: buildHierarchy(),
|
|
1072
|
+
resources: [
|
|
1073
|
+
makeResource({ id: "a", order: 1, monitorName: "First" }),
|
|
1074
|
+
makeResource({ id: "b", order: 2, monitorName: "Second" }),
|
|
1075
|
+
],
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
renderPage();
|
|
1079
|
+
|
|
1080
|
+
await waitForExplorer();
|
|
1081
|
+
|
|
1082
|
+
expect(screen.getByText("Second")).toBeInTheDocument();
|
|
1083
|
+
|
|
1084
|
+
const secondRow: HTMLElement = screen
|
|
1085
|
+
.queryAllByTestId("status-page-resource-row")
|
|
1086
|
+
.find((row: HTMLElement) => {
|
|
1087
|
+
return (row.textContent || "").includes("Second");
|
|
1088
|
+
})!;
|
|
1089
|
+
|
|
1090
|
+
fireEvent.click(
|
|
1091
|
+
within(secondRow).getByTestId("status-page-resource-row-more"),
|
|
1092
|
+
);
|
|
1093
|
+
|
|
1094
|
+
await flushEffects();
|
|
1095
|
+
|
|
1096
|
+
fireEvent.click(screen.getByText("Move up"));
|
|
1097
|
+
|
|
1098
|
+
await waitFor(() => {
|
|
1099
|
+
expect(mockUpdateById).toHaveBeenCalled();
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
const updateCall: any = mockUpdateById.mock.calls[0]![0];
|
|
1103
|
+
|
|
1104
|
+
expect(updateCall.id.toString()).toBe("b");
|
|
1105
|
+
expect(updateCall.data).toEqual({ order: 1 });
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
test("removing a resource asks first, then deletes and reloads the group", async () => {
|
|
1109
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
1110
|
+
groups: buildHierarchy(),
|
|
1111
|
+
resources: [makeResource({ id: "a", monitorName: "Doomed" })],
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
renderPage();
|
|
1115
|
+
|
|
1116
|
+
await waitForExplorer();
|
|
1117
|
+
|
|
1118
|
+
expect(screen.getByText("Doomed")).toBeInTheDocument();
|
|
1119
|
+
|
|
1120
|
+
const before: number = scopedResourceCalls(calls).length;
|
|
1121
|
+
|
|
1122
|
+
fireEvent.click(screen.getByTestId("status-page-resource-row-more"));
|
|
1123
|
+
await flushEffects();
|
|
1124
|
+
|
|
1125
|
+
fireEvent.click(screen.getByText("Remove from status page"));
|
|
1126
|
+
await flushEffects();
|
|
1127
|
+
|
|
1128
|
+
expect(
|
|
1129
|
+
screen.getByText("Remove resource from status page"),
|
|
1130
|
+
).toBeInTheDocument();
|
|
1131
|
+
|
|
1132
|
+
fireEvent.click(screen.getByText("Remove"));
|
|
1133
|
+
|
|
1134
|
+
await waitFor(() => {
|
|
1135
|
+
expect(mockDeleteItem).toHaveBeenCalled();
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
/* The group reloads itself rather than the page re-reading everything. */
|
|
1139
|
+
await waitFor(() => {
|
|
1140
|
+
expect(scopedResourceCalls(calls).length).toBe(before + 1);
|
|
1141
|
+
});
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
test("the add button opens the resource form for the selected group", async () => {
|
|
1145
|
+
setUpApi({
|
|
1146
|
+
groups: buildHierarchy(),
|
|
1147
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
renderPage();
|
|
1151
|
+
|
|
1152
|
+
await waitForExplorer();
|
|
1153
|
+
|
|
1154
|
+
await selectGroup("Market 1001");
|
|
1155
|
+
|
|
1156
|
+
fireEvent.click(screen.getByTestId("status-page-resource-panel-add"));
|
|
1157
|
+
await flushEffects();
|
|
1158
|
+
|
|
1159
|
+
expect(screen.getByTestId("model-form-modal").textContent).toBe(
|
|
1160
|
+
"Add a monitor to Market 1001",
|
|
1161
|
+
);
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
/*
|
|
1165
|
+
* The pane holds one group's worth of state - what is loading, which modal
|
|
1166
|
+
* is open, what a failed write said - and none of it means anything for the
|
|
1167
|
+
* next group.
|
|
1168
|
+
*/
|
|
1169
|
+
test("a modal left open does not follow the operator to the next group", async () => {
|
|
1170
|
+
setUpApi({
|
|
1171
|
+
groups: buildHierarchy(),
|
|
1172
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
renderPage();
|
|
1176
|
+
|
|
1177
|
+
await waitForExplorer();
|
|
1178
|
+
|
|
1179
|
+
fireEvent.click(screen.getByTestId("status-page-resource-panel-add"));
|
|
1180
|
+
await flushEffects();
|
|
1181
|
+
|
|
1182
|
+
expect(screen.getByTestId("model-form-modal")).toBeInTheDocument();
|
|
1183
|
+
|
|
1184
|
+
await selectGroup("Market 1001");
|
|
1185
|
+
|
|
1186
|
+
expect(screen.queryByTestId("model-form-modal")).not.toBeInTheDocument();
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
describe("keeping the counts honest after a change", () => {
|
|
1191
|
+
test("a delete moves the group's badge without re-reading the status page", async () => {
|
|
1192
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
1193
|
+
groups: buildHierarchy(),
|
|
1194
|
+
resources: [
|
|
1195
|
+
makeResource({ id: "a", groupId: MARKET_ID, monitorName: "One" }),
|
|
1196
|
+
makeResource({ id: "b", groupId: MARKET_ID, monitorName: "Two" }),
|
|
1197
|
+
],
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
renderPage();
|
|
1201
|
+
|
|
1202
|
+
await waitForExplorer();
|
|
1203
|
+
|
|
1204
|
+
await selectGroup("Market 1001");
|
|
1205
|
+
|
|
1206
|
+
expect(
|
|
1207
|
+
within(navigatorRowByName("Market 1001")).getByTestId(
|
|
1208
|
+
"status-page-resource-navigator-count",
|
|
1209
|
+
).textContent,
|
|
1210
|
+
).toBe("2");
|
|
1211
|
+
|
|
1212
|
+
const unscopedCallsBefore: number =
|
|
1213
|
+
resourceCalls(calls).length - scopedResourceCalls(calls).length;
|
|
1214
|
+
|
|
1215
|
+
/* The next fetch of this group comes back with one fewer resource. */
|
|
1216
|
+
mockGetList.mockImplementation(async (callData: any): Promise<any> => {
|
|
1217
|
+
calls.push({
|
|
1218
|
+
modelType: callData.modelType,
|
|
1219
|
+
query: callData.query || {},
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
if (callData.modelType === StatusPageGroup) {
|
|
1223
|
+
return {
|
|
1224
|
+
data: buildHierarchy(),
|
|
1225
|
+
count: 4,
|
|
1226
|
+
skip: 0,
|
|
1227
|
+
limit: 4,
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
return {
|
|
1232
|
+
data: [
|
|
1233
|
+
makeResource({
|
|
1234
|
+
id: "a",
|
|
1235
|
+
groupId: MARKET_ID,
|
|
1236
|
+
monitorName: "One",
|
|
1237
|
+
}),
|
|
1238
|
+
],
|
|
1239
|
+
count: 1,
|
|
1240
|
+
skip: 0,
|
|
1241
|
+
limit: 1,
|
|
1242
|
+
};
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
fireEvent.click(
|
|
1246
|
+
screen
|
|
1247
|
+
.queryAllByTestId("status-page-resource-row-more")
|
|
1248
|
+
.slice(-1)[0] as HTMLElement,
|
|
1249
|
+
);
|
|
1250
|
+
await flushEffects();
|
|
1251
|
+
|
|
1252
|
+
fireEvent.click(screen.getByText("Remove from status page"));
|
|
1253
|
+
await flushEffects();
|
|
1254
|
+
|
|
1255
|
+
fireEvent.click(screen.getByText("Remove"));
|
|
1256
|
+
|
|
1257
|
+
await waitFor(() => {
|
|
1258
|
+
expect(
|
|
1259
|
+
within(navigatorRowByName("Market 1001")).getByTestId(
|
|
1260
|
+
"status-page-resource-navigator-count",
|
|
1261
|
+
).textContent,
|
|
1262
|
+
).toBe("1");
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
/* Still exactly one unscoped pass over every resource: the one at load. */
|
|
1266
|
+
expect(
|
|
1267
|
+
resourceCalls(calls).length - scopedResourceCalls(calls).length,
|
|
1268
|
+
).toBe(unscopedCallsBefore);
|
|
1269
|
+
});
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
/*
|
|
1273
|
+
* The half of this page that used to be Status Page > Groups. Every one of
|
|
1274
|
+
* these was previously reachable only by leaving the page you were working
|
|
1275
|
+
* on, doing the thing on another page, and coming back to it.
|
|
1276
|
+
*/
|
|
1277
|
+
describe("changing the hierarchy without leaving the page", () => {
|
|
1278
|
+
test("a group is created from the page header, and the pane opens on it", async () => {
|
|
1279
|
+
const groups: Array<StatusPageGroup> = buildHierarchy();
|
|
1280
|
+
const calls: Array<GetListCall> = setUpApi({ groups: groups });
|
|
1281
|
+
|
|
1282
|
+
renderPage();
|
|
1283
|
+
|
|
1284
|
+
await waitForExplorer();
|
|
1285
|
+
|
|
1286
|
+
const groupReadsBefore: number = groupCalls(calls).length;
|
|
1287
|
+
|
|
1288
|
+
fireEvent.click(screen.getByText("New Group"));
|
|
1289
|
+
await flushEffects();
|
|
1290
|
+
|
|
1291
|
+
expect(screen.getByTestId("model-form-modal").textContent).toBe(
|
|
1292
|
+
"Create New Status Page Group",
|
|
1293
|
+
);
|
|
1294
|
+
|
|
1295
|
+
const created: StatusPageGroup = makeGroup({
|
|
1296
|
+
id: NEW_GROUP_ID,
|
|
1297
|
+
name: "Brand New",
|
|
1298
|
+
order: 6,
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
/*
|
|
1302
|
+
* The refetch has to come back holding it, or the pane would open on a
|
|
1303
|
+
* group the page does not know about.
|
|
1304
|
+
*/
|
|
1305
|
+
groups.push(created);
|
|
1306
|
+
|
|
1307
|
+
await act(async () => {
|
|
1308
|
+
await lastFormModal().onSuccess(created);
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
await waitForSettled();
|
|
1312
|
+
|
|
1313
|
+
expect(screen.queryByTestId("model-form-modal")).not.toBeInTheDocument();
|
|
1314
|
+
expect(groupCalls(calls).length).toBe(groupReadsBefore + 1);
|
|
1315
|
+
|
|
1316
|
+
/* The hierarchy on screen is the one that was re-read, not the old one. */
|
|
1317
|
+
expect(findNavigatorRow("Brand New")).toBeDefined();
|
|
1318
|
+
|
|
1319
|
+
/*
|
|
1320
|
+
* A group is created in order to put monitors in it, so the next thing
|
|
1321
|
+
* the operator wants is already on screen.
|
|
1322
|
+
*/
|
|
1323
|
+
expect(
|
|
1324
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
1325
|
+
).toBe("Brand New");
|
|
1326
|
+
expect(selectedGroupIds(calls).slice(-1)).toEqual([NEW_GROUP_ID]);
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
/*
|
|
1330
|
+
* The parent picker offers every group the API would accept, including ones
|
|
1331
|
+
* whose rows are not currently drawn - so a group can be created several
|
|
1332
|
+
* levels inside a collapsed branch. Opening only its immediate parent
|
|
1333
|
+
* leaves it exactly as invisible as opening nothing.
|
|
1334
|
+
*/
|
|
1335
|
+
test("a group created inside a collapsed branch is revealed all the way down", async () => {
|
|
1336
|
+
const groups: Array<StatusPageGroup> = buildHierarchy();
|
|
1337
|
+
|
|
1338
|
+
setUpApi({ groups: groups });
|
|
1339
|
+
|
|
1340
|
+
renderPage();
|
|
1341
|
+
|
|
1342
|
+
await waitForExplorer();
|
|
1343
|
+
|
|
1344
|
+
/*
|
|
1345
|
+
* Closing the root takes its whole subtree off screen, so the group is
|
|
1346
|
+
* about to be created somewhere nobody can see.
|
|
1347
|
+
*/
|
|
1348
|
+
fireEvent.click(
|
|
1349
|
+
within(navigatorRowByName("Corporate")).getByTestId(
|
|
1350
|
+
"status-page-resource-navigator-disclosure",
|
|
1351
|
+
),
|
|
1352
|
+
);
|
|
1353
|
+
await flushEffects();
|
|
1354
|
+
|
|
1355
|
+
expect(findNavigatorRow("Region 1000")).toBeUndefined();
|
|
1356
|
+
expect(findNavigatorRow("Market 1001")).toBeUndefined();
|
|
1357
|
+
|
|
1358
|
+
fireEvent.click(screen.getByText("New Group"));
|
|
1359
|
+
await flushEffects();
|
|
1360
|
+
|
|
1361
|
+
const nested: StatusPageGroup = makeGroup({
|
|
1362
|
+
id: NEW_GROUP_ID,
|
|
1363
|
+
name: "Deep Child",
|
|
1364
|
+
parentId: MARKET_ID,
|
|
1365
|
+
order: 9,
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
groups.push(nested);
|
|
1369
|
+
|
|
1370
|
+
await act(async () => {
|
|
1371
|
+
await lastFormModal().onSuccess(nested);
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
await waitForSettled();
|
|
1375
|
+
|
|
1376
|
+
expect(findNavigatorRow("Corporate")).toBeDefined();
|
|
1377
|
+
expect(findNavigatorRow("Region 1000")).toBeDefined();
|
|
1378
|
+
expect(findNavigatorRow("Market 1001")).toBeDefined();
|
|
1379
|
+
expect(findNavigatorRow("Deep Child")).toBeDefined();
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
/*
|
|
1383
|
+
* The parent is the group whose row was clicked. Without it pre-filled,
|
|
1384
|
+
* "add a sub group" is the create form with extra steps.
|
|
1385
|
+
*/
|
|
1386
|
+
test("a sub group is created from the row of its parent, already nested", async () => {
|
|
1387
|
+
setUpApi({ groups: buildHierarchy() });
|
|
1388
|
+
|
|
1389
|
+
renderPage();
|
|
1390
|
+
|
|
1391
|
+
await waitForExplorer();
|
|
1392
|
+
|
|
1393
|
+
fireEvent.click(
|
|
1394
|
+
within(navigatorRowByName("Region 1000")).getByTestId(
|
|
1395
|
+
"status-page-resource-navigator-add-sub-group",
|
|
1396
|
+
),
|
|
1397
|
+
);
|
|
1398
|
+
await flushEffects();
|
|
1399
|
+
|
|
1400
|
+
expect(screen.getByTestId("model-form-modal")).toBeInTheDocument();
|
|
1401
|
+
expect(lastFormModal().initialValues).toEqual({
|
|
1402
|
+
parentStatusPageGroup: REGION_ID,
|
|
1403
|
+
});
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
test("a group is edited from its own row, in update mode", async () => {
|
|
1407
|
+
setUpApi({ groups: buildHierarchy() });
|
|
1408
|
+
|
|
1409
|
+
renderPage();
|
|
1410
|
+
|
|
1411
|
+
await waitForExplorer();
|
|
1412
|
+
|
|
1413
|
+
await openRowMenu("Market 1001");
|
|
1414
|
+
|
|
1415
|
+
fireEvent.click(screen.getByText("Edit group"));
|
|
1416
|
+
await flushEffects();
|
|
1417
|
+
|
|
1418
|
+
expect(screen.getByTestId("model-form-modal").textContent).toBe(
|
|
1419
|
+
"Edit Status Page Group",
|
|
1420
|
+
);
|
|
1421
|
+
expect(lastFormModal().modelIdToEdit.toString()).toBe(MARKET_ID);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
/*
|
|
1425
|
+
* Re-reading the hierarchy after a group write must not blank the page: a
|
|
1426
|
+
* loader in place of the whole card would unmount the pane, which would
|
|
1427
|
+
* then re-read the selected group's resources for no reason at all.
|
|
1428
|
+
*/
|
|
1429
|
+
test("re-reading the hierarchy leaves the pane where it was", async () => {
|
|
1430
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
1431
|
+
groups: buildHierarchy(),
|
|
1432
|
+
resources: [
|
|
1433
|
+
makeResource({
|
|
1434
|
+
id: "market",
|
|
1435
|
+
groupId: MARKET_ID,
|
|
1436
|
+
monitorName: "Market Monitor",
|
|
1437
|
+
}),
|
|
1438
|
+
],
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
renderPage();
|
|
1442
|
+
|
|
1443
|
+
await waitForExplorer();
|
|
1444
|
+
|
|
1445
|
+
await selectGroup("Market 1001");
|
|
1446
|
+
|
|
1447
|
+
const scopedBefore: number = scopedResourceCalls(calls).length;
|
|
1448
|
+
|
|
1449
|
+
await openRowMenu("Corporate");
|
|
1450
|
+
|
|
1451
|
+
fireEvent.click(screen.getByText("Edit group"));
|
|
1452
|
+
await flushEffects();
|
|
1453
|
+
|
|
1454
|
+
await act(async () => {
|
|
1455
|
+
await lastFormModal().onSuccess(
|
|
1456
|
+
makeGroup({ id: CORPORATE_ID, name: "Corporate", order: 1 }),
|
|
1457
|
+
);
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
await waitForSettled();
|
|
1461
|
+
|
|
1462
|
+
expect(
|
|
1463
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
1464
|
+
).toBe("Market 1001");
|
|
1465
|
+
expect(screen.getByText("Market Monitor")).toBeInTheDocument();
|
|
1466
|
+
expect(scopedResourceCalls(calls).length).toBe(scopedBefore);
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
/*
|
|
1470
|
+
* `order` is one flat sequence across the whole status page and the service
|
|
1471
|
+
* renumbers everything between the two rows, so the write is "take the
|
|
1472
|
+
* place of the sibling above you" rather than an index.
|
|
1473
|
+
*/
|
|
1474
|
+
test("moving a group writes the neighbouring sibling's order", async () => {
|
|
1475
|
+
setUpApi({
|
|
1476
|
+
groups: [
|
|
1477
|
+
makeGroup({ id: CORPORATE_ID, name: "Corporate", order: 1 }),
|
|
1478
|
+
makeGroup({ id: GRID_ID, name: "Second", order: 7 }),
|
|
1479
|
+
],
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
renderPage();
|
|
1483
|
+
|
|
1484
|
+
await waitForExplorer();
|
|
1485
|
+
|
|
1486
|
+
await openRowMenu("Second");
|
|
1487
|
+
|
|
1488
|
+
fireEvent.click(screen.getByText("Move up"));
|
|
1489
|
+
|
|
1490
|
+
await waitFor(() => {
|
|
1491
|
+
expect(mockUpdateById).toHaveBeenCalled();
|
|
1492
|
+
});
|
|
1493
|
+
|
|
1494
|
+
const update: any = mockUpdateById.mock.calls[0]![0];
|
|
1495
|
+
|
|
1496
|
+
expect(update.id.toString()).toBe(GRID_ID);
|
|
1497
|
+
expect(update.data).toEqual({ order: 1 });
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
/*
|
|
1501
|
+
* Deleting a group takes its sub groups, the resources in them, and any
|
|
1502
|
+
* monitor rules pointing at them. Nothing on the row says so, so the
|
|
1503
|
+
* confirmation has to - and a pane still aimed at the deleted branch would
|
|
1504
|
+
* sit there fetching something that no longer exists.
|
|
1505
|
+
*/
|
|
1506
|
+
test("deleting the selected group says what goes with it, then moves the pane out", async () => {
|
|
1507
|
+
const groups: Array<StatusPageGroup> = buildHierarchy();
|
|
1508
|
+
|
|
1509
|
+
setUpApi({
|
|
1510
|
+
groups: groups,
|
|
1511
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
renderPage();
|
|
1515
|
+
|
|
1516
|
+
await waitForExplorer();
|
|
1517
|
+
|
|
1518
|
+
await selectGroup("Region 1000");
|
|
1519
|
+
|
|
1520
|
+
await openRowMenu("Region 1000");
|
|
1521
|
+
|
|
1522
|
+
fireEvent.click(screen.getByText("Delete group"));
|
|
1523
|
+
await flushEffects();
|
|
1524
|
+
|
|
1525
|
+
const confirmation: string =
|
|
1526
|
+
screen.getByTestId("confirm-modal-description").textContent || "";
|
|
1527
|
+
|
|
1528
|
+
expect(confirmation).toContain("1 group nested inside it");
|
|
1529
|
+
expect(confirmation).toContain("monitor rules");
|
|
1530
|
+
expect(confirmation).toContain("This cannot be undone.");
|
|
1531
|
+
|
|
1532
|
+
/* What the refetch will return: the branch is gone. */
|
|
1533
|
+
groups.splice(
|
|
1534
|
+
0,
|
|
1535
|
+
groups.length,
|
|
1536
|
+
makeGroup({ id: CORPORATE_ID, name: "Corporate", order: 1 }),
|
|
1537
|
+
);
|
|
1538
|
+
|
|
1539
|
+
fireEvent.click(screen.getByText("Delete"));
|
|
1540
|
+
|
|
1541
|
+
await waitFor(() => {
|
|
1542
|
+
expect(mockDeleteItem).toHaveBeenCalled();
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
await waitForSettled();
|
|
1546
|
+
|
|
1547
|
+
expect(
|
|
1548
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
1549
|
+
).toBe("Top of page");
|
|
1550
|
+
|
|
1551
|
+
/* And the branch really is gone from the tree, not just from the pane. */
|
|
1552
|
+
expect(findNavigatorRow("Region 1000")).toBeUndefined();
|
|
1553
|
+
expect(findNavigatorRow("Market 1001")).toBeUndefined();
|
|
1554
|
+
});
|
|
1555
|
+
|
|
1556
|
+
/*
|
|
1557
|
+
* The cascade moves numbers nobody counted - every resource in the deleted
|
|
1558
|
+
* group and in every group under it - so this is the one write on the page
|
|
1559
|
+
* where the badges have to be read again rather than adjusted.
|
|
1560
|
+
*/
|
|
1561
|
+
test("a group delete re-reads the counts it could not have worked out", async () => {
|
|
1562
|
+
const groups: Array<StatusPageGroup> = buildHierarchy();
|
|
1563
|
+
const calls: Array<GetListCall> = setUpApi({
|
|
1564
|
+
groups: groups,
|
|
1565
|
+
resources: [
|
|
1566
|
+
makeResource({
|
|
1567
|
+
id: "market",
|
|
1568
|
+
groupId: MARKET_ID,
|
|
1569
|
+
monitorName: "Market Monitor",
|
|
1570
|
+
}),
|
|
1571
|
+
],
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
renderPage();
|
|
1575
|
+
|
|
1576
|
+
await waitForExplorer();
|
|
1577
|
+
|
|
1578
|
+
const unscopedBefore: number =
|
|
1579
|
+
resourceCalls(calls).length - scopedResourceCalls(calls).length;
|
|
1580
|
+
|
|
1581
|
+
await openRowMenu("Market 1001");
|
|
1582
|
+
|
|
1583
|
+
fireEvent.click(screen.getByText("Delete group"));
|
|
1584
|
+
await flushEffects();
|
|
1585
|
+
|
|
1586
|
+
fireEvent.click(screen.getByText("Delete"));
|
|
1587
|
+
|
|
1588
|
+
await waitFor(() => {
|
|
1589
|
+
expect(mockDeleteItem).toHaveBeenCalled();
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
await waitForSettled();
|
|
1593
|
+
|
|
1594
|
+
expect(
|
|
1595
|
+
resourceCalls(calls).length - scopedResourceCalls(calls).length,
|
|
1596
|
+
).toBe(unscopedBefore + 1);
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
/*
|
|
1600
|
+
* The pane names exactly one group, and on a touch screen it is the only
|
|
1601
|
+
* place a group can be acted on - there is no hover to reveal a row's
|
|
1602
|
+
* cluster.
|
|
1603
|
+
*/
|
|
1604
|
+
test("the pane offers the selected group's own actions too", async () => {
|
|
1605
|
+
setUpApi({ groups: buildHierarchy() });
|
|
1606
|
+
|
|
1607
|
+
renderPage();
|
|
1608
|
+
|
|
1609
|
+
await waitForExplorer();
|
|
1610
|
+
|
|
1611
|
+
await selectGroup("Market 1001");
|
|
1612
|
+
|
|
1613
|
+
fireEvent.click(screen.getByTestId("status-page-resource-panel-more"));
|
|
1614
|
+
await flushEffects();
|
|
1615
|
+
|
|
1616
|
+
const labels: Array<string> = within(screen.getByRole("menu"))
|
|
1617
|
+
.getAllByRole("menuitem")
|
|
1618
|
+
.map((item: HTMLElement) => {
|
|
1619
|
+
return item.textContent || "";
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1622
|
+
/*
|
|
1623
|
+
* Everything the tree's hover-revealed row cluster offers, because a
|
|
1624
|
+
* touch screen has no hover and this menu is the only way in.
|
|
1625
|
+
*/
|
|
1626
|
+
expect(labels).toEqual([
|
|
1627
|
+
"Add multiple monitors",
|
|
1628
|
+
"Edit this group",
|
|
1629
|
+
"Add a sub group",
|
|
1630
|
+
"Move group up",
|
|
1631
|
+
"Move group down",
|
|
1632
|
+
"Show group ID",
|
|
1633
|
+
"Refresh",
|
|
1634
|
+
"Delete this group",
|
|
1635
|
+
]);
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
/*
|
|
1639
|
+
* Offered but disabled rather than absent: a menu whose contents change
|
|
1640
|
+
* shape from group to group is a menu you have to read every time.
|
|
1641
|
+
*/
|
|
1642
|
+
test("a move with no sibling that way is offered but disabled", async () => {
|
|
1643
|
+
setUpApi({ groups: buildHierarchy() });
|
|
1644
|
+
|
|
1645
|
+
renderPage();
|
|
1646
|
+
|
|
1647
|
+
await waitForExplorer();
|
|
1648
|
+
|
|
1649
|
+
await selectGroup("Region 1000");
|
|
1650
|
+
|
|
1651
|
+
fireEvent.click(screen.getByTestId("status-page-resource-panel-more"));
|
|
1652
|
+
await flushEffects();
|
|
1653
|
+
|
|
1654
|
+
const menu: HTMLElement = screen.getByRole("menu");
|
|
1655
|
+
|
|
1656
|
+
/* The only group at its level, so neither direction is available. */
|
|
1657
|
+
expect(
|
|
1658
|
+
within(menu).getByText("Move group up").closest("button"),
|
|
1659
|
+
).toBeDisabled();
|
|
1660
|
+
expect(
|
|
1661
|
+
within(menu).getByText("Move group down").closest("button"),
|
|
1662
|
+
).toBeDisabled();
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
/*
|
|
1666
|
+
* The ungrouped bucket is a real place on a status page but it is not a
|
|
1667
|
+
* group, so nothing that acts on a group is offered while it is selected.
|
|
1668
|
+
*/
|
|
1669
|
+
test("the ungrouped bucket is not offered group actions", async () => {
|
|
1670
|
+
setUpApi({
|
|
1671
|
+
groups: buildHierarchy(),
|
|
1672
|
+
resources: [makeResource({ id: "loose", monitorName: "Loose" })],
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
renderPage();
|
|
1676
|
+
|
|
1677
|
+
await waitForExplorer();
|
|
1678
|
+
|
|
1679
|
+
expect(
|
|
1680
|
+
screen.getByTestId("status-page-resource-panel-title").textContent,
|
|
1681
|
+
).toBe("Top of page");
|
|
1682
|
+
|
|
1683
|
+
fireEvent.click(screen.getByTestId("status-page-resource-panel-more"));
|
|
1684
|
+
await flushEffects();
|
|
1685
|
+
|
|
1686
|
+
const labels: Array<string> = within(screen.getByRole("menu"))
|
|
1687
|
+
.getAllByRole("menuitem")
|
|
1688
|
+
.map((item: HTMLElement) => {
|
|
1689
|
+
return item.textContent || "";
|
|
1690
|
+
});
|
|
1691
|
+
|
|
1692
|
+
expect(labels).toEqual([
|
|
1693
|
+
"Add multiple monitors",
|
|
1694
|
+
"Create a group",
|
|
1695
|
+
"Refresh",
|
|
1696
|
+
]);
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
/*
|
|
1700
|
+
* The pane is the whole group-management surface on a touch screen, so its
|
|
1701
|
+
* menu has to be gated exactly as the tree's row actions are. Everything
|
|
1702
|
+
* here would otherwise be an action that can only ever fail at the API.
|
|
1703
|
+
*/
|
|
1704
|
+
test("a viewer who may not change the hierarchy is offered none of it", async () => {
|
|
1705
|
+
mockIsMasterAdmin.mockReturnValue(false);
|
|
1706
|
+
|
|
1707
|
+
setUpApi({ groups: buildHierarchy() });
|
|
1708
|
+
|
|
1709
|
+
renderPage();
|
|
1710
|
+
|
|
1711
|
+
await waitForExplorer();
|
|
1712
|
+
|
|
1713
|
+
await selectGroup("Market 1001");
|
|
1714
|
+
|
|
1715
|
+
/* The hierarchy is still there to get around in, and still selectable. */
|
|
1716
|
+
expect(navigatorRows().length).toBeGreaterThan(0);
|
|
1717
|
+
expect(
|
|
1718
|
+
screen.queryAllByTestId("status-page-resource-navigator-add-sub-group")
|
|
1719
|
+
.length,
|
|
1720
|
+
).toBe(0);
|
|
1721
|
+
|
|
1722
|
+
await openRowMenu("Market 1001");
|
|
1723
|
+
|
|
1724
|
+
/* Reading an id is not a write, so it survives; nothing else does. */
|
|
1725
|
+
expect(
|
|
1726
|
+
within(screen.getByRole("menu"))
|
|
1727
|
+
.getAllByRole("menuitem")
|
|
1728
|
+
.map((item: HTMLElement) => {
|
|
1729
|
+
return item.textContent || "";
|
|
1730
|
+
}),
|
|
1731
|
+
).toEqual(["Show ID"]);
|
|
1732
|
+
|
|
1733
|
+
fireEvent.keyDown(screen.getByRole("menu"), { key: "Escape" });
|
|
1734
|
+
await flushEffects();
|
|
1735
|
+
|
|
1736
|
+
fireEvent.click(screen.getByTestId("status-page-resource-panel-more"));
|
|
1737
|
+
await flushEffects();
|
|
1738
|
+
|
|
1739
|
+
const labels: Array<string> = within(screen.getByRole("menu"))
|
|
1740
|
+
.getAllByRole("menuitem")
|
|
1741
|
+
.map((item: HTMLElement) => {
|
|
1742
|
+
return item.textContent || "";
|
|
1743
|
+
});
|
|
1744
|
+
|
|
1745
|
+
expect(labels).toEqual(["Show group ID", "Refresh"]);
|
|
1746
|
+
});
|
|
1747
|
+
});
|
|
1748
|
+
|
|
1749
|
+
/*
|
|
1750
|
+
* The settings that change what a visitor sees. They live three steps into
|
|
1751
|
+
* the group form, and the old Groups page badged them on every row; the pane
|
|
1752
|
+
* header is the only place they surface now, so this is the only thing
|
|
1753
|
+
* standing between an operator and a group that is quietly published
|
|
1754
|
+
* collapsed.
|
|
1755
|
+
*/
|
|
1756
|
+
describe("what the pane says about the group itself", () => {
|
|
1757
|
+
const GROUP_WITH_SETTINGS: string = "0198c8ec-2a1d-7f0c-9e75-384194163020";
|
|
1758
|
+
const PLAIN_GROUP: string = "0198c8ec-2a1d-7f0c-9e75-384194163021";
|
|
1759
|
+
|
|
1760
|
+
type SetUpSettingsFunction = () => void;
|
|
1761
|
+
|
|
1762
|
+
const setUpSettings: SetUpSettingsFunction = (): void => {
|
|
1763
|
+
setUpApi({
|
|
1764
|
+
groups: [
|
|
1765
|
+
makeGroup({
|
|
1766
|
+
id: GROUP_WITH_SETTINGS,
|
|
1767
|
+
name: "Published Quietly",
|
|
1768
|
+
order: 1,
|
|
1769
|
+
description: "Only the on-call team looks at this one.",
|
|
1770
|
+
isExpandedByDefault: false,
|
|
1771
|
+
showUptimePercent: true,
|
|
1772
|
+
}),
|
|
1773
|
+
makeGroup({ id: PLAIN_GROUP, name: "Plain", order: 2 }),
|
|
1774
|
+
],
|
|
1775
|
+
});
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
test("a group that is published collapsed, with uptime, and has a description says all three", async () => {
|
|
1779
|
+
setUpSettings();
|
|
1780
|
+
|
|
1781
|
+
renderPage();
|
|
1782
|
+
|
|
1783
|
+
await waitForExplorer();
|
|
1784
|
+
|
|
1785
|
+
await selectGroup("Published Quietly");
|
|
1786
|
+
|
|
1787
|
+
expect(
|
|
1788
|
+
screen.getByTestId("status-page-resource-panel-collapsed-by-default"),
|
|
1789
|
+
).toBeInTheDocument();
|
|
1790
|
+
expect(
|
|
1791
|
+
screen.getByTestId("status-page-resource-panel-uptime"),
|
|
1792
|
+
).toBeInTheDocument();
|
|
1793
|
+
expect(
|
|
1794
|
+
screen.getByTestId("status-page-resource-panel-description")
|
|
1795
|
+
.textContent,
|
|
1796
|
+
).toBe("Only the on-call team looks at this one.");
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
test("a group with none of them says none of them", async () => {
|
|
1800
|
+
setUpSettings();
|
|
1801
|
+
|
|
1802
|
+
renderPage();
|
|
1803
|
+
|
|
1804
|
+
await waitForExplorer();
|
|
1805
|
+
|
|
1806
|
+
await selectGroup("Plain");
|
|
1807
|
+
|
|
1808
|
+
expect(
|
|
1809
|
+
screen.queryByTestId("status-page-resource-panel-collapsed-by-default"),
|
|
1810
|
+
).not.toBeInTheDocument();
|
|
1811
|
+
expect(
|
|
1812
|
+
screen.queryByTestId("status-page-resource-panel-uptime"),
|
|
1813
|
+
).not.toBeInTheDocument();
|
|
1814
|
+
expect(
|
|
1815
|
+
screen.queryByTestId("status-page-resource-panel-description"),
|
|
1816
|
+
).not.toBeInTheDocument();
|
|
1817
|
+
});
|
|
1818
|
+
|
|
1819
|
+
/*
|
|
1820
|
+
* A grid group with no axes has nowhere to put a resource - the public page
|
|
1821
|
+
* drops anything whose axis values are not among the group's own - so the
|
|
1822
|
+
* add button is withdrawn. On its own that reads as a broken screen, so the
|
|
1823
|
+
* pane has to say why and hand over the control that fixes it.
|
|
1824
|
+
*/
|
|
1825
|
+
test("a grid group with no axes explains itself and offers the way out", async () => {
|
|
1826
|
+
const grid: StatusPageGroup = makeGroup({
|
|
1827
|
+
id: GRID_ID,
|
|
1828
|
+
name: "Matrix",
|
|
1829
|
+
order: 1,
|
|
1830
|
+
viewMode: StatusPageGroupViewMode.Grid,
|
|
1831
|
+
});
|
|
1832
|
+
|
|
1833
|
+
setUpApi({ groups: [grid] });
|
|
1834
|
+
|
|
1835
|
+
renderPage();
|
|
1836
|
+
|
|
1837
|
+
await waitForExplorer();
|
|
1838
|
+
|
|
1839
|
+
await selectGroup("Matrix");
|
|
1840
|
+
|
|
1841
|
+
expect(
|
|
1842
|
+
screen.getByTestId("status-page-resource-panel-grid-setup").textContent,
|
|
1843
|
+
).toContain("no rows or columns");
|
|
1844
|
+
expect(
|
|
1845
|
+
screen.queryByTestId("status-page-resource-panel-add"),
|
|
1846
|
+
).not.toBeInTheDocument();
|
|
1847
|
+
|
|
1848
|
+
fireEvent.click(
|
|
1849
|
+
screen.getByTestId("status-page-resource-panel-grid-setup-edit"),
|
|
1850
|
+
);
|
|
1851
|
+
await flushEffects();
|
|
1852
|
+
|
|
1853
|
+
expect(screen.getByTestId("model-form-modal").textContent).toBe(
|
|
1854
|
+
"Edit Status Page Group",
|
|
1855
|
+
);
|
|
1856
|
+
});
|
|
1857
|
+
});
|
|
1858
|
+
});
|