@oneuptime/common 13.0.8 → 14.0.1
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/AnalyticsModels/AuditLog.ts +23 -15
- package/Models/DatabaseModels/GlobalConfig.ts +24 -0
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/Monitor.ts +3 -2
- package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
- package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
- package/Scripts/generate-service-worker.js +65 -4
- package/Server/API/BaseAPI.ts +26 -5
- package/Server/API/GlobalConfigAPI.ts +228 -462
- package/Server/API/ProjectOIDC.ts +25 -16
- package/Server/API/ProjectSSO.ts +24 -16
- package/Server/API/StatusPageAPI.ts +100 -42
- package/Server/API/TeamMemberAPI.ts +10 -1
- package/Server/Enterprise/EnterpriseEdition.ts +649 -0
- package/Server/Enterprise/EnterpriseFeature.ts +48 -0
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
- package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
- package/Server/EnvironmentConfig.ts +147 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/UserAuthorization.ts +38 -16
- package/Server/Services/AuditLogService.ts +84 -1079
- package/Server/Services/DatabaseService.ts +10 -0
- package/Server/Services/EnterpriseLicenseService.ts +38 -0
- package/Server/Services/GlobalConfigService.ts +80 -1
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/MonitorProbeService.ts +116 -20
- package/Server/Services/MonitorService.ts +59 -6
- package/Server/Services/MonitorStatusTimelineService.ts +284 -0
- package/Server/Services/MonitorTemplateService.ts +26 -0
- package/Server/Services/ProjectService.ts +284 -0
- package/Server/Services/RumSessionPinService.ts +25 -6
- package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
- package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
- package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
- package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
- package/Server/Services/StatusPagePrivateUserService.ts +7 -3
- package/Server/Services/StatusPageService.ts +78 -1
- package/Server/Services/TeamMemberService.ts +11 -0
- package/Server/Services/TeamService.ts +10 -0
- package/Server/Services/UserService.ts +10 -17
- package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
- package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
- package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
- package/Server/Types/Markdown.ts +41 -0
- package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
- package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
- package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
- package/Server/Utils/EditionEnforcement.ts +233 -0
- package/Server/Utils/FrontendEnvironment.ts +41 -2
- package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
- package/Server/Utils/Monitor/AffectedResourceList.ts +148 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +720 -465
- package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
- package/Server/Utils/Monitor/RootCauseList.ts +139 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
- package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
- package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
- package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
- package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
- package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
- package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
- package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
- package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
- package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
- package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
- package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
- package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
- package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
- package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
- package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
- package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
- package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
- package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
- package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
- package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
- package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
- package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
- package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
- package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
- package/Tests/Server/Services/AuditLogService.test.ts +460 -990
- package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
- package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
- package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
- package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
- package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
- package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
- package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
- package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
- package/Tests/Server/Types/Markdown.test.ts +183 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
- package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
- package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
- package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
- package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
- package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +1339 -96
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
- package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
- package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
- package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
- package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
- package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
- package/Tests/Types/API/Route.test.ts +56 -0
- package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
- package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
- package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
- package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
- package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
- package/Tests/UI/Components/Icon.test.tsx +26 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
- package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
- package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
- package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
- package/Tests/Utils/HostAddressUtil.test.ts +138 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
- package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
- package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
- package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
- package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
- package/Types/API/Hostname.ts +63 -16
- package/Types/API/Route.ts +19 -2
- package/Types/API/URL.ts +26 -2
- package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
- package/Types/Database/CanonicalModelInstance.ts +102 -0
- package/Types/Database/TableColumn.ts +20 -6
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
- package/Types/IP/IPv6.ts +6 -1
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
- package/Types/Monitor/MonitorStep.ts +6 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
- package/Types/Permission.ts +90 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
- package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
- package/UI/Components/Feed/FeedOptions.ts +427 -0
- package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
- package/UI/Components/Feed/useFeedItems.ts +28 -9
- package/UI/Components/Feed/useFeedOptions.ts +225 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/ModelList/ModelList.tsx +11 -1
- package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
- package/UI/esbuild-enterprise.js +263 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
- package/Utils/HostAddressUtil.ts +94 -0
- package/Utils/LabelRuleImportExport.ts +27 -9
- package/Utils/Monitor/MetricValueFormatter.ts +2 -2
- package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
- package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
- package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
- package/Utils/Slo/SloDuration.ts +22 -6
- package/Utils/Slo/SloHealth.ts +8 -7
- package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
- package/Utils/Slo/SloOverviewText.ts +33 -0
- package/Utils/Slo/SloProjection.ts +4 -3
- package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
- package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
- package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- 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 +2 -2
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
- package/build/dist/Server/API/BaseAPI.js +26 -5
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectOIDC.js +24 -16
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +24 -16
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +93 -43
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +8 -1
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +84 -2
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AuditLogService.js +71 -740
- package/build/dist/Server/Services/AuditLogService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +7 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +63 -0
- package/build/dist/Server/Services/GlobalConfigService.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/MonitorProbeService.js +90 -17
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +48 -6
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +214 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +24 -6
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +71 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +10 -0
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamService.js +9 -0
- package/build/dist/Server/Services/TeamService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -16
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +31 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
- package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +67 -0
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +585 -367
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +59 -11
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/API/Route.js +18 -2
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/API/URL.js +23 -2
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
- package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
- package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js +19 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
- package/build/dist/Types/IP/IPv6.js +6 -1
- package/build/dist/Types/IP/IPv6.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +5 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
- package/build/dist/Types/Permission.js +84 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
- package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
- package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
- package/build/dist/Utils/HostAddressUtil.js +80 -0
- package/build/dist/Utils/HostAddressUtil.js.map +1 -0
- package/build/dist/Utils/LabelRuleImportExport.js +22 -6
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
- package/build/dist/Utils/Slo/SloDuration.js +13 -5
- package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
- package/build/dist/Utils/Slo/SloHealth.js +8 -7
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
- package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
- package/jest.config.json +3 -1
- package/package.json +1 -1
- package/test-setup.sh +103 -34
- package/tsconfig.json +13 -0
- package/Server/API/EnterpriseLicenseAPI.ts +0 -925
- package/Server/API/TeamComplianceAPI.ts +0 -158
- package/Server/Middleware/SCIMAuthorization.ts +0 -141
- package/Server/Services/TeamComplianceService.ts +0 -733
- package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
- package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
- package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
- package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
- package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
- package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
- package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
- package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
- package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
- package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
- package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
- package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
- package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
- package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
- package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
- package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
- package/build/dist/Server/Services/TeamComplianceService.js +0 -537
- package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
render,
|
|
15
15
|
screen,
|
|
16
16
|
waitFor,
|
|
17
|
+
within,
|
|
17
18
|
} from "@testing-library/react";
|
|
18
19
|
import * as React from "react";
|
|
19
20
|
|
|
@@ -65,6 +66,27 @@ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
|
65
66
|
};
|
|
66
67
|
});
|
|
67
68
|
|
|
69
|
+
/*
|
|
70
|
+
* The real Icon draws an SVG that says nothing about which icon it is. This
|
|
71
|
+
* one names the icon, so the checklist's icons and the trigger's glyph can be
|
|
72
|
+
* read back off the page. Everything else the module exports (SizeProp,
|
|
73
|
+
* ThickProp, IconType) stays real - components across the feed read those at
|
|
74
|
+
* render time.
|
|
75
|
+
*/
|
|
76
|
+
jest.mock("../../../UI/Components/Icon/Icon", () => {
|
|
77
|
+
const actualIconModule: Record<string, unknown> = jest.requireActual(
|
|
78
|
+
"../../../UI/Components/Icon/Icon",
|
|
79
|
+
) as Record<string, unknown>;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...actualIconModule,
|
|
83
|
+
__esModule: true,
|
|
84
|
+
default: (props: { icon: string }): React.ReactElement => {
|
|
85
|
+
return <span data-icon={props.icon} aria-hidden="true" />;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
|
|
68
90
|
import ResourceFeed, {
|
|
69
91
|
GetResourceFeedIconFunction,
|
|
70
92
|
getIconForEventType,
|
|
@@ -77,7 +99,16 @@ import IconProp from "../../../Types/Icon/IconProp";
|
|
|
77
99
|
import ObjectID from "../../../Types/ObjectID";
|
|
78
100
|
import { Green500 } from "../../../Types/BrandColors";
|
|
79
101
|
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
|
|
102
|
+
import Includes from "../../../Types/BaseDatabase/Includes";
|
|
80
103
|
import { DEFAULT_LIMIT } from "../../../Types/Database/LimitMax";
|
|
104
|
+
import {
|
|
105
|
+
DEFAULT_FEED_OPTIONS,
|
|
106
|
+
FEED_OPTIONS_TEXT,
|
|
107
|
+
FILTERED_FEED_NO_ITEMS_MESSAGE,
|
|
108
|
+
FeedOptions,
|
|
109
|
+
getFeedOptionsSummary,
|
|
110
|
+
} from "../../../UI/Components/Feed/FeedOptions";
|
|
111
|
+
import { getSortOrderStorageKey } from "../../../UI/Components/Feed/useFeedOptions";
|
|
81
112
|
|
|
82
113
|
/*
|
|
83
114
|
* One component serves all ten resource feeds, parameterised by the two
|
|
@@ -90,6 +121,13 @@ import { DEFAULT_LIMIT } from "../../../Types/Database/LimitMax";
|
|
|
90
121
|
|
|
91
122
|
const CLUSTER_ID: ObjectID = ObjectID.generate();
|
|
92
123
|
|
|
124
|
+
const ALL_EVENT_TYPES: Array<string> = Object.values(
|
|
125
|
+
KubernetesClusterFeedEventType,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const NO_ITEMS_MESSAGE: string =
|
|
129
|
+
"No activity has been recorded for this Kubernetes cluster yet.";
|
|
130
|
+
|
|
93
131
|
function feedItem(
|
|
94
132
|
eventType: KubernetesClusterFeedEventType,
|
|
95
133
|
markdown: string,
|
|
@@ -112,9 +150,10 @@ function getFeedElement(resourceId: ObjectID = CLUSTER_ID): React.ReactElement {
|
|
|
112
150
|
resourceIdColumn="kubernetesClusterId"
|
|
113
151
|
resourceId={resourceId}
|
|
114
152
|
eventTypeColumn="kubernetesClusterFeedEventType"
|
|
153
|
+
eventTypes={ALL_EVENT_TYPES}
|
|
115
154
|
title="Kubernetes Cluster Feed"
|
|
116
155
|
description="Everything that has happened to this Kubernetes cluster."
|
|
117
|
-
noItemsMessage=
|
|
156
|
+
noItemsMessage={NO_ITEMS_MESSAGE}
|
|
118
157
|
/>
|
|
119
158
|
);
|
|
120
159
|
}
|
|
@@ -125,11 +164,130 @@ function renderFeed(
|
|
|
125
164
|
return render(getFeedElement(resourceId));
|
|
126
165
|
}
|
|
127
166
|
|
|
167
|
+
const RESOURCE_ID_COLUMN: string = "kubernetesClusterId";
|
|
168
|
+
|
|
169
|
+
const EVENT_TYPE_COLUMN: string = "kubernetesClusterFeedEventType";
|
|
170
|
+
|
|
171
|
+
type GetQuery = (request: GetListRequest) => Record<string, unknown>;
|
|
172
|
+
|
|
173
|
+
const getQuery: GetQuery = (
|
|
174
|
+
request: GetListRequest,
|
|
175
|
+
): Record<string, unknown> => {
|
|
176
|
+
return request["query"] as Record<string, unknown>;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
type GetRequestsFor = (resourceId: ObjectID) => Array<GetListRequest>;
|
|
180
|
+
|
|
181
|
+
// Every request made so far for this cluster's feed, oldest first.
|
|
182
|
+
const getRequestsFor: GetRequestsFor = (
|
|
183
|
+
resourceId: ObjectID,
|
|
184
|
+
): Array<GetListRequest> => {
|
|
185
|
+
return mockGetListCalls.filter((request: GetListRequest) => {
|
|
186
|
+
return (
|
|
187
|
+
String(getQuery(request)[RESOURCE_ID_COLUMN]) === resourceId.toString()
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
type WaitForFeed = (requestCount: number) => Promise<void>;
|
|
193
|
+
|
|
194
|
+
// The feed has asked the API `requestCount` times and drawn the last answer.
|
|
195
|
+
const waitForFeed: WaitForFeed = async (
|
|
196
|
+
requestCount: number,
|
|
197
|
+
): Promise<void> => {
|
|
198
|
+
await waitFor(() => {
|
|
199
|
+
expect(mockGetListCalls).toHaveLength(requestCount);
|
|
200
|
+
expect(screen.queryByTestId("component-loader")).not.toBeInTheDocument();
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
type GetFilterAndSortButton = () => HTMLElement;
|
|
205
|
+
|
|
206
|
+
/*
|
|
207
|
+
* Found the way a screen reader finds it: by its label. What the feed is
|
|
208
|
+
* showing is the button's description, not part of its name, so the name
|
|
209
|
+
* stays "Filter & Sort" whatever is chosen - and the count badge, which is
|
|
210
|
+
* aria-hidden, never joins it either.
|
|
211
|
+
*/
|
|
212
|
+
const getFilterAndSortButton: GetFilterAndSortButton = (): HTMLElement => {
|
|
213
|
+
return screen.getByRole("button", { name: "Filter & Sort" });
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
type OpenFilterAndSort = () => HTMLElement;
|
|
217
|
+
|
|
218
|
+
// Presses Filter & Sort and returns the panel it opens.
|
|
219
|
+
const openFilterAndSort: OpenFilterAndSort = (): HTMLElement => {
|
|
220
|
+
fireEvent.click(getFilterAndSortButton());
|
|
221
|
+
|
|
222
|
+
return screen.getByRole("dialog", { name: FEED_OPTIONS_TEXT.panelLabel });
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
type CloseFilterAndSort = () => void;
|
|
226
|
+
|
|
227
|
+
// A press anywhere outside the panel - on the way to another page, say.
|
|
228
|
+
const closeFilterAndSort: CloseFilterAndSort = (): void => {
|
|
229
|
+
fireEvent.mouseDown(document.body);
|
|
230
|
+
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
type GetExpectedSummary = (options: FeedOptions) => string;
|
|
234
|
+
|
|
235
|
+
/*
|
|
236
|
+
* The sentence the trigger is described by, for this feed's event types.
|
|
237
|
+
* Built by the same function the button uses, so the wording can change
|
|
238
|
+
* without this suite noticing - what is pinned is that the description
|
|
239
|
+
* follows the feed's live order and filter, counted over this feed's own
|
|
240
|
+
* event types.
|
|
241
|
+
*/
|
|
242
|
+
const getExpectedSummary: GetExpectedSummary = (
|
|
243
|
+
options: FeedOptions,
|
|
244
|
+
): string => {
|
|
245
|
+
return getFeedOptionsSummary({
|
|
246
|
+
options: options,
|
|
247
|
+
eventTypeCount: ALL_EVENT_TYPES.length,
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
type GetTriggerIcon = () => string | null;
|
|
252
|
+
|
|
253
|
+
// The glyph in front of the trigger's label: a funnel, or the sort arrows.
|
|
254
|
+
const getTriggerIcon: GetTriggerIcon = (): string | null => {
|
|
255
|
+
return (
|
|
256
|
+
getFilterAndSortButton()
|
|
257
|
+
.querySelector("[data-icon]")
|
|
258
|
+
?.getAttribute("data-icon") || null
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
type GetStoredSortOrderKeys = () => Array<string>;
|
|
263
|
+
|
|
264
|
+
/*
|
|
265
|
+
* Every remembered feed sort order in this browser, whichever feed wrote it.
|
|
266
|
+
* The prefix is whatever getSortOrderStorageKey puts in front of a feed's
|
|
267
|
+
* name, so it is read from there rather than copied.
|
|
268
|
+
*/
|
|
269
|
+
const getStoredSortOrderKeys: GetStoredSortOrderKeys = (): Array<string> => {
|
|
270
|
+
const prefix: string = getSortOrderStorageKey("");
|
|
271
|
+
const keys: Array<string> = [];
|
|
272
|
+
|
|
273
|
+
for (let index: number = 0; index < window.localStorage.length; index++) {
|
|
274
|
+
const key: string | null = window.localStorage.key(index);
|
|
275
|
+
|
|
276
|
+
if (key && key.startsWith(prefix)) {
|
|
277
|
+
keys.push(key);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return keys;
|
|
282
|
+
};
|
|
283
|
+
|
|
128
284
|
beforeEach(() => {
|
|
129
285
|
mockGetListCalls.length = 0;
|
|
130
286
|
mockGetListResponse = (): Promise<unknown> => {
|
|
131
287
|
return Promise.resolve({ data: [], count: 0 });
|
|
132
288
|
};
|
|
289
|
+
// The chosen sort order is remembered, so one test's choice must not leak.
|
|
290
|
+
window.localStorage.clear();
|
|
133
291
|
});
|
|
134
292
|
|
|
135
293
|
afterEach(() => {
|
|
@@ -725,9 +883,10 @@ describe("ResourceFeed - a feed's own icons", () => {
|
|
|
725
883
|
resourceIdColumn="kubernetesClusterId"
|
|
726
884
|
resourceId={CLUSTER_ID}
|
|
727
885
|
eventTypeColumn="kubernetesClusterFeedEventType"
|
|
886
|
+
eventTypes={ALL_EVENT_TYPES}
|
|
728
887
|
title="Kubernetes Cluster Feed"
|
|
729
888
|
description="Everything that has happened to this Kubernetes cluster."
|
|
730
|
-
noItemsMessage=
|
|
889
|
+
noItemsMessage={NO_ITEMS_MESSAGE}
|
|
731
890
|
getIcon={getIcon}
|
|
732
891
|
/>,
|
|
733
892
|
);
|
|
@@ -744,4 +903,455 @@ describe("ResourceFeed - a feed's own icons", () => {
|
|
|
744
903
|
KubernetesClusterFeedEventType.OwnerUserAdded,
|
|
745
904
|
);
|
|
746
905
|
});
|
|
906
|
+
|
|
907
|
+
test("gives the Filter & Sort checklist the same icons, one entry per event type", async () => {
|
|
908
|
+
const askedEventTypes: Array<string> = [];
|
|
909
|
+
|
|
910
|
+
/*
|
|
911
|
+
* One event type gets an icon of the feed's own that the shared rules
|
|
912
|
+
* would never pick for it, so its row can only show it if the checklist
|
|
913
|
+
* really goes through getIcon.
|
|
914
|
+
*/
|
|
915
|
+
const ownIconEventType: string =
|
|
916
|
+
KubernetesClusterFeedEventType.OwnerUserAdded;
|
|
917
|
+
const ownIcon: IconProp = IconProp.Fire;
|
|
918
|
+
|
|
919
|
+
expect(getIconForEventType(ownIconEventType)).not.toBe(ownIcon);
|
|
920
|
+
|
|
921
|
+
const getIcon: GetResourceFeedIconFunction = (
|
|
922
|
+
eventType: string,
|
|
923
|
+
): IconProp | undefined => {
|
|
924
|
+
askedEventTypes.push(eventType);
|
|
925
|
+
return eventType === ownIconEventType ? ownIcon : undefined;
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
render(
|
|
929
|
+
<ResourceFeed<KubernetesClusterFeed>
|
|
930
|
+
modelType={KubernetesClusterFeed}
|
|
931
|
+
resourceIdColumn="kubernetesClusterId"
|
|
932
|
+
resourceId={CLUSTER_ID}
|
|
933
|
+
eventTypeColumn="kubernetesClusterFeedEventType"
|
|
934
|
+
eventTypes={ALL_EVENT_TYPES}
|
|
935
|
+
title="Kubernetes Cluster Feed"
|
|
936
|
+
description="Everything that has happened to this Kubernetes cluster."
|
|
937
|
+
noItemsMessage={NO_ITEMS_MESSAGE}
|
|
938
|
+
getIcon={getIcon}
|
|
939
|
+
/>,
|
|
940
|
+
);
|
|
941
|
+
|
|
942
|
+
await waitFor(() => {
|
|
943
|
+
expect(screen.getByText(NO_ITEMS_MESSAGE)).toBeInTheDocument();
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
// The feed is empty, so only the checklist can have asked.
|
|
947
|
+
for (const eventType of ALL_EVENT_TYPES) {
|
|
948
|
+
expect(askedEventTypes).toContain(eventType);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const panel: HTMLElement = openFilterAndSort();
|
|
952
|
+
|
|
953
|
+
/*
|
|
954
|
+
* Exactly one box per event type of this feed's own enum: none missing,
|
|
955
|
+
* none repeated, and nothing from another feed.
|
|
956
|
+
*/
|
|
957
|
+
const checkboxTestIds: Array<string> = within(panel)
|
|
958
|
+
.getAllByRole("checkbox")
|
|
959
|
+
.map((checkbox: HTMLElement): string => {
|
|
960
|
+
return checkbox.getAttribute("data-testid") || "";
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
expect([...checkboxTestIds].sort()).toEqual(
|
|
964
|
+
ALL_EVENT_TYPES.map((eventType: string): string => {
|
|
965
|
+
return `feed-options-event-type-${eventType}`;
|
|
966
|
+
}).sort(),
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
type GetRowIcon = (eventType: string) => string | null;
|
|
970
|
+
|
|
971
|
+
const getRowIcon: GetRowIcon = (eventType: string): string | null => {
|
|
972
|
+
return (
|
|
973
|
+
within(panel)
|
|
974
|
+
.getByTestId(`feed-options-event-type-${eventType}`)
|
|
975
|
+
.closest("label")
|
|
976
|
+
?.querySelector("[data-icon]")
|
|
977
|
+
?.getAttribute("data-icon") || null
|
|
978
|
+
);
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// The feed's own answer wins...
|
|
982
|
+
expect(getRowIcon(ownIconEventType)).toBe(ownIcon);
|
|
983
|
+
|
|
984
|
+
// ...and "no answer" falls back to the shared rules, row by row.
|
|
985
|
+
for (const eventType of ALL_EVENT_TYPES) {
|
|
986
|
+
if (eventType === ownIconEventType) {
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
expect(getRowIcon(eventType)).toBe(getIconForEventType(eventType));
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
/*
|
|
996
|
+
* Filter & Sort is applied by the API, not to the rows already loaded, and
|
|
997
|
+
* the event type filter is keyed by the same string column as the icon - so
|
|
998
|
+
* a typo there would filter on a column that does not exist.
|
|
999
|
+
*/
|
|
1000
|
+
describe("ResourceFeed - Filter & Sort", () => {
|
|
1001
|
+
test("puts Filter & Sort first in the header, and an untouched feed sends exactly the query it always sent", async () => {
|
|
1002
|
+
renderFeed();
|
|
1003
|
+
|
|
1004
|
+
await waitFor(() => {
|
|
1005
|
+
expect(mockGetListCalls.length).toBe(1);
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
expect(
|
|
1009
|
+
Object.keys(mockGetListCalls[0]!["query"] as Record<string, unknown>),
|
|
1010
|
+
).toEqual(["kubernetesClusterId"]);
|
|
1011
|
+
|
|
1012
|
+
const optionsButton: HTMLElement = getFilterAndSortButton();
|
|
1013
|
+
const refreshButton: HTMLElement = screen.getByRole("button", {
|
|
1014
|
+
name: "Refresh",
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
expect(
|
|
1018
|
+
optionsButton.compareDocumentPosition(refreshButton) &
|
|
1019
|
+
Node.DOCUMENT_POSITION_FOLLOWING,
|
|
1020
|
+
).toBeTruthy();
|
|
1021
|
+
|
|
1022
|
+
/*
|
|
1023
|
+
* The state behind the button is read out as its description - and shown
|
|
1024
|
+
* as its tooltip - so the reader need not open it to know what the feed
|
|
1025
|
+
* is showing.
|
|
1026
|
+
*/
|
|
1027
|
+
const summary: string = getExpectedSummary(DEFAULT_FEED_OPTIONS);
|
|
1028
|
+
|
|
1029
|
+
expect(summary.length).toBeGreaterThan(0);
|
|
1030
|
+
expect(optionsButton).toHaveAccessibleDescription(summary);
|
|
1031
|
+
expect(optionsButton).toHaveAttribute("title", summary);
|
|
1032
|
+
|
|
1033
|
+
// An untouched feed: the funnel, no count, and nothing remembered.
|
|
1034
|
+
expect(getTriggerIcon()).toBe(IconProp.Filter);
|
|
1035
|
+
expect(screen.queryByTestId("feed-options-count")).not.toBeInTheDocument();
|
|
1036
|
+
expect(getStoredSortOrderKeys()).toEqual([]);
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
test("filters on the feed's own event type column and says so when nothing matches", async () => {
|
|
1040
|
+
renderFeed();
|
|
1041
|
+
|
|
1042
|
+
await waitFor(() => {
|
|
1043
|
+
expect(screen.getByText(NO_ITEMS_MESSAGE)).toBeInTheDocument();
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
const panel: HTMLElement = openFilterAndSort();
|
|
1047
|
+
|
|
1048
|
+
fireEvent.click(
|
|
1049
|
+
within(panel).getByTestId(
|
|
1050
|
+
`feed-options-event-type-${KubernetesClusterFeedEventType.OwnerUserAdded}`,
|
|
1051
|
+
),
|
|
1052
|
+
);
|
|
1053
|
+
|
|
1054
|
+
await waitFor(() => {
|
|
1055
|
+
expect(mockGetListCalls).toHaveLength(2);
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
const request: GetListRequest = mockGetListCalls[1]!;
|
|
1059
|
+
const query: Record<string, unknown> = request["query"] as Record<
|
|
1060
|
+
string,
|
|
1061
|
+
unknown
|
|
1062
|
+
>;
|
|
1063
|
+
const eventTypeFilter: unknown = query["kubernetesClusterFeedEventType"];
|
|
1064
|
+
|
|
1065
|
+
expect(query["kubernetesClusterId"]).toBe(CLUSTER_ID);
|
|
1066
|
+
expect(eventTypeFilter).toBeInstanceOf(Includes);
|
|
1067
|
+
expect((eventTypeFilter as Includes).values).toEqual([
|
|
1068
|
+
KubernetesClusterFeedEventType.OwnerUserAdded,
|
|
1069
|
+
]);
|
|
1070
|
+
expect(request["skip"]).toBe(0);
|
|
1071
|
+
expect(request["limit"]).toBe(DEFAULT_LIMIT);
|
|
1072
|
+
|
|
1073
|
+
/*
|
|
1074
|
+
* An empty filtered feed must not claim the cluster has no history at
|
|
1075
|
+
* all.
|
|
1076
|
+
*/
|
|
1077
|
+
expect(
|
|
1078
|
+
await screen.findByText(FILTERED_FEED_NO_ITEMS_MESSAGE),
|
|
1079
|
+
).toBeInTheDocument();
|
|
1080
|
+
expect(screen.queryByText(NO_ITEMS_MESSAGE)).not.toBeInTheDocument();
|
|
1081
|
+
|
|
1082
|
+
/*
|
|
1083
|
+
* The trigger keeps its name with a count on it, and its description now
|
|
1084
|
+
* says the feed is narrowed - to one of this feed's event types.
|
|
1085
|
+
*/
|
|
1086
|
+
const optionsButton: HTMLElement = getFilterAndSortButton();
|
|
1087
|
+
|
|
1088
|
+
expect(screen.getByTestId("feed-options-count")).toHaveTextContent(/^1$/);
|
|
1089
|
+
expect(optionsButton).toHaveAccessibleDescription(
|
|
1090
|
+
getExpectedSummary({
|
|
1091
|
+
sortOrder: SortOrder.Descending,
|
|
1092
|
+
eventTypes: [KubernetesClusterFeedEventType.OwnerUserAdded],
|
|
1093
|
+
}),
|
|
1094
|
+
);
|
|
1095
|
+
expect(optionsButton).not.toHaveAccessibleDescription(
|
|
1096
|
+
getExpectedSummary(DEFAULT_FEED_OPTIONS),
|
|
1097
|
+
);
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
test("Oldest first re-reads the first window in ascending order, draws it exactly as the API returned it, and remembers it under this feed's key only", async () => {
|
|
1101
|
+
// sort-entry-0 is the newest event, sort-entry-11 the oldest.
|
|
1102
|
+
const newestFirst: Array<KubernetesClusterFeed> = Array.from(
|
|
1103
|
+
{ length: 12 },
|
|
1104
|
+
(_value: unknown, index: number): KubernetesClusterFeed => {
|
|
1105
|
+
return feedItem(
|
|
1106
|
+
KubernetesClusterFeedEventType.KubernetesClusterUpdated,
|
|
1107
|
+
`sort-entry-${index}`,
|
|
1108
|
+
new Date(Date.UTC(2024, 0, 12 - index)),
|
|
1109
|
+
);
|
|
1110
|
+
},
|
|
1111
|
+
);
|
|
1112
|
+
const oldestFirst: Array<KubernetesClusterFeed> = [
|
|
1113
|
+
...newestFirst,
|
|
1114
|
+
].reverse();
|
|
1115
|
+
|
|
1116
|
+
/*
|
|
1117
|
+
* The stub answers each order with its own rows, as the API would. The
|
|
1118
|
+
* ascending window (entry-11 down to entry-2) is neither the newest window
|
|
1119
|
+
* reversed nor anything a client-side sort by time would draw, so the
|
|
1120
|
+
* page can only show it by showing the API's rows as they came.
|
|
1121
|
+
*/
|
|
1122
|
+
mockGetListResponse = (request: GetListRequest): Promise<unknown> => {
|
|
1123
|
+
const sort: Record<string, unknown> = request["sort"] as Record<
|
|
1124
|
+
string,
|
|
1125
|
+
unknown
|
|
1126
|
+
>;
|
|
1127
|
+
const rows: Array<KubernetesClusterFeed> =
|
|
1128
|
+
sort["postedAt"] === SortOrder.Ascending ? oldestFirst : newestFirst;
|
|
1129
|
+
|
|
1130
|
+
return Promise.resolve({
|
|
1131
|
+
data: rows.slice(0, request["limit"] as number),
|
|
1132
|
+
count: rows.length,
|
|
1133
|
+
});
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
type GetDrawnEntries = () => Array<string>;
|
|
1137
|
+
|
|
1138
|
+
const getDrawnEntries: GetDrawnEntries = (): Array<string> => {
|
|
1139
|
+
return screen
|
|
1140
|
+
.getAllByText(/sort-entry-/)
|
|
1141
|
+
.map((element: HTMLElement): string => {
|
|
1142
|
+
return element.textContent || "";
|
|
1143
|
+
});
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
type GetMarkdown = (items: Array<KubernetesClusterFeed>) => Array<string>;
|
|
1147
|
+
|
|
1148
|
+
const getMarkdown: GetMarkdown = (
|
|
1149
|
+
items: Array<KubernetesClusterFeed>,
|
|
1150
|
+
): Array<string> => {
|
|
1151
|
+
return items.map((item: KubernetesClusterFeed): string => {
|
|
1152
|
+
return item.feedInfoInMarkdown || "";
|
|
1153
|
+
});
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
renderFeed();
|
|
1157
|
+
await waitFor(() => {
|
|
1158
|
+
expect(screen.getAllByText(/sort-entry-/)).toHaveLength(DEFAULT_LIMIT);
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
fireEvent.click(screen.getByRole("button", { name: "More" }));
|
|
1162
|
+
await waitFor(() => {
|
|
1163
|
+
expect(screen.getAllByText(/sort-entry-/)).toHaveLength(12);
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
// Nothing is remembered until the reader changes something.
|
|
1167
|
+
expect(getStoredSortOrderKeys()).toEqual([]);
|
|
1168
|
+
|
|
1169
|
+
const panel: HTMLElement = openFilterAndSort();
|
|
1170
|
+
|
|
1171
|
+
fireEvent.click(
|
|
1172
|
+
within(panel).getByTestId(`feed-options-sort-${SortOrder.Ascending}`),
|
|
1173
|
+
);
|
|
1174
|
+
|
|
1175
|
+
await waitForFeed(3);
|
|
1176
|
+
|
|
1177
|
+
// Reversing the loaded rows would show the newest twelve upside down.
|
|
1178
|
+
const request: GetListRequest = mockGetListCalls[2]!;
|
|
1179
|
+
|
|
1180
|
+
expect(request["sort"]).toEqual({
|
|
1181
|
+
postedAt: SortOrder.Ascending,
|
|
1182
|
+
});
|
|
1183
|
+
expect(request["skip"]).toBe(0);
|
|
1184
|
+
expect(request["limit"]).toBe(DEFAULT_LIMIT);
|
|
1185
|
+
expect(Object.keys(getQuery(request))).toEqual([RESOURCE_ID_COLUMN]);
|
|
1186
|
+
|
|
1187
|
+
await waitFor(() => {
|
|
1188
|
+
expect(getDrawnEntries()).toEqual(
|
|
1189
|
+
getMarkdown(oldestFirst.slice(0, DEFAULT_LIMIT)),
|
|
1190
|
+
);
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
/*
|
|
1194
|
+
* The trigger says so without being opened: the sort glyph instead of the
|
|
1195
|
+
* funnel, and a description that leads with the new order.
|
|
1196
|
+
*/
|
|
1197
|
+
expect(getTriggerIcon()).toBe(IconProp.BarsArrowUp);
|
|
1198
|
+
expect(getFilterAndSortButton()).toHaveAccessibleDescription(
|
|
1199
|
+
getExpectedSummary({ sortOrder: SortOrder.Ascending, eventTypes: [] }),
|
|
1200
|
+
);
|
|
1201
|
+
|
|
1202
|
+
/*
|
|
1203
|
+
* Remembered under this feed's own key, and only there: another
|
|
1204
|
+
* product's feed must not open oldest first because of this one.
|
|
1205
|
+
*/
|
|
1206
|
+
const storageKey: string = getSortOrderStorageKey(EVENT_TYPE_COLUMN);
|
|
1207
|
+
|
|
1208
|
+
expect(window.localStorage.getItem(storageKey)).toBe(SortOrder.Ascending);
|
|
1209
|
+
expect(
|
|
1210
|
+
window.localStorage.getItem(
|
|
1211
|
+
getSortOrderStorageKey("dockerHostFeedEventType"),
|
|
1212
|
+
),
|
|
1213
|
+
).toBeNull();
|
|
1214
|
+
expect(getStoredSortOrderKeys()).toEqual([storageKey]);
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
test("drops the event type filter when the feed moves to another cluster, and keeps the sort order", async () => {
|
|
1218
|
+
/*
|
|
1219
|
+
* The dashboard moves between clusters without remounting the feed. The
|
|
1220
|
+
* filter was chosen for one cluster's investigation, so the next cluster
|
|
1221
|
+
* must start unfiltered - from its very first request, or the API is
|
|
1222
|
+
* asked for the old cluster's filter and the reader briefly sees a feed
|
|
1223
|
+
* with events missing. The sort order is a preference and stays.
|
|
1224
|
+
*/
|
|
1225
|
+
const clusterA: ObjectID = CLUSTER_ID;
|
|
1226
|
+
const clusterB: ObjectID = ObjectID.generate();
|
|
1227
|
+
const tickedEventType: string =
|
|
1228
|
+
KubernetesClusterFeedEventType.OwnerUserAdded;
|
|
1229
|
+
|
|
1230
|
+
type ExpectUnfilteredOldestFirst = (
|
|
1231
|
+
request: GetListRequest,
|
|
1232
|
+
resourceId: ObjectID,
|
|
1233
|
+
) => void;
|
|
1234
|
+
|
|
1235
|
+
const expectUnfilteredOldestFirst: ExpectUnfilteredOldestFirst = (
|
|
1236
|
+
request: GetListRequest,
|
|
1237
|
+
resourceId: ObjectID,
|
|
1238
|
+
): void => {
|
|
1239
|
+
const query: Record<string, unknown> = getQuery(request);
|
|
1240
|
+
|
|
1241
|
+
expect(Object.keys(query)).toEqual([RESOURCE_ID_COLUMN]);
|
|
1242
|
+
expect(String(query[RESOURCE_ID_COLUMN])).toBe(resourceId.toString());
|
|
1243
|
+
expect(request["sort"]).toEqual({ postedAt: SortOrder.Ascending });
|
|
1244
|
+
expect(request["skip"]).toBe(0);
|
|
1245
|
+
expect(request["limit"]).toBe(DEFAULT_LIMIT);
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
type ExpectNothingTicked = () => void;
|
|
1249
|
+
|
|
1250
|
+
// Reopens the panel on the feed as it is now, and closes it again.
|
|
1251
|
+
const expectNothingTicked: ExpectNothingTicked = (): void => {
|
|
1252
|
+
const reopenedPanel: HTMLElement = openFilterAndSort();
|
|
1253
|
+
const checkboxes: Array<HTMLElement> =
|
|
1254
|
+
within(reopenedPanel).getAllByRole("checkbox");
|
|
1255
|
+
|
|
1256
|
+
expect(checkboxes).toHaveLength(ALL_EVENT_TYPES.length);
|
|
1257
|
+
|
|
1258
|
+
for (const checkbox of checkboxes) {
|
|
1259
|
+
expect(checkbox).not.toBeChecked();
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
expect(
|
|
1263
|
+
within(reopenedPanel).getByTestId(
|
|
1264
|
+
`feed-options-sort-${SortOrder.Ascending}`,
|
|
1265
|
+
),
|
|
1266
|
+
).toHaveAttribute("aria-checked", "true");
|
|
1267
|
+
|
|
1268
|
+
closeFilterAndSort();
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
const view: ReturnType<typeof render> = renderFeed(clusterA);
|
|
1272
|
+
await waitForFeed(1);
|
|
1273
|
+
|
|
1274
|
+
// On cluster A: oldest first, then only owners being added.
|
|
1275
|
+
const panel: HTMLElement = openFilterAndSort();
|
|
1276
|
+
|
|
1277
|
+
fireEvent.click(
|
|
1278
|
+
within(panel).getByTestId(`feed-options-sort-${SortOrder.Ascending}`),
|
|
1279
|
+
);
|
|
1280
|
+
await waitForFeed(2);
|
|
1281
|
+
|
|
1282
|
+
fireEvent.click(
|
|
1283
|
+
within(
|
|
1284
|
+
screen.getByRole("dialog", { name: FEED_OPTIONS_TEXT.panelLabel }),
|
|
1285
|
+
).getByTestId(`feed-options-event-type-${tickedEventType}`),
|
|
1286
|
+
);
|
|
1287
|
+
await waitForFeed(3);
|
|
1288
|
+
|
|
1289
|
+
const filteredRequest: GetListRequest = mockGetListCalls[2]!;
|
|
1290
|
+
const eventTypeFilter: unknown =
|
|
1291
|
+
getQuery(filteredRequest)[EVENT_TYPE_COLUMN];
|
|
1292
|
+
|
|
1293
|
+
expect(eventTypeFilter).toBeInstanceOf(Includes);
|
|
1294
|
+
expect((eventTypeFilter as Includes).values).toEqual([tickedEventType]);
|
|
1295
|
+
expect(filteredRequest["sort"]).toEqual({ postedAt: SortOrder.Ascending });
|
|
1296
|
+
expect(screen.getByTestId("feed-options-count")).toHaveTextContent(/^1$/);
|
|
1297
|
+
|
|
1298
|
+
closeFilterAndSort();
|
|
1299
|
+
|
|
1300
|
+
// To cluster B.
|
|
1301
|
+
view.rerender(getFeedElement(clusterB));
|
|
1302
|
+
await waitForFeed(4);
|
|
1303
|
+
|
|
1304
|
+
const requestsForB: Array<GetListRequest> = getRequestsFor(clusterB);
|
|
1305
|
+
|
|
1306
|
+
/*
|
|
1307
|
+
* One request for B, and it was already unfiltered - not a filtered one
|
|
1308
|
+
* that a later reset then replaced.
|
|
1309
|
+
*/
|
|
1310
|
+
expect(requestsForB).toHaveLength(1);
|
|
1311
|
+
expectUnfilteredOldestFirst(requestsForB[0]!, clusterB);
|
|
1312
|
+
|
|
1313
|
+
// An empty unfiltered feed says the cluster has no history, not "no match".
|
|
1314
|
+
expect(await screen.findByText(NO_ITEMS_MESSAGE)).toBeInTheDocument();
|
|
1315
|
+
expect(
|
|
1316
|
+
screen.queryByText(FILTERED_FEED_NO_ITEMS_MESSAGE),
|
|
1317
|
+
).not.toBeInTheDocument();
|
|
1318
|
+
|
|
1319
|
+
expect(screen.queryByTestId("feed-options-count")).not.toBeInTheDocument();
|
|
1320
|
+
expect(getTriggerIcon()).toBe(IconProp.BarsArrowUp);
|
|
1321
|
+
expect(getFilterAndSortButton()).toHaveAccessibleDescription(
|
|
1322
|
+
getExpectedSummary({ sortOrder: SortOrder.Ascending, eventTypes: [] }),
|
|
1323
|
+
);
|
|
1324
|
+
expectNothingTicked();
|
|
1325
|
+
|
|
1326
|
+
// Nothing asked B for the old filter afterwards either.
|
|
1327
|
+
expect(getRequestsFor(clusterB)).toHaveLength(1);
|
|
1328
|
+
|
|
1329
|
+
// The sort order is still remembered, for this feed only.
|
|
1330
|
+
expect(getStoredSortOrderKeys()).toEqual([
|
|
1331
|
+
getSortOrderStorageKey(EVENT_TYPE_COLUMN),
|
|
1332
|
+
]);
|
|
1333
|
+
expect(
|
|
1334
|
+
window.localStorage.getItem(getSortOrderStorageKey(EVENT_TYPE_COLUMN)),
|
|
1335
|
+
).toBe(SortOrder.Ascending);
|
|
1336
|
+
|
|
1337
|
+
// Back to cluster A: its old filter does not come back with it.
|
|
1338
|
+
const requestCountBeforeReturn: number = mockGetListCalls.length;
|
|
1339
|
+
|
|
1340
|
+
view.rerender(getFeedElement(clusterA));
|
|
1341
|
+
await waitForFeed(requestCountBeforeReturn + 1);
|
|
1342
|
+
|
|
1343
|
+
const requestsAfterReturn: Array<GetListRequest> = mockGetListCalls.slice(
|
|
1344
|
+
requestCountBeforeReturn,
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
expect(requestsAfterReturn).toHaveLength(1);
|
|
1348
|
+
expectUnfilteredOldestFirst(requestsAfterReturn[0]!, clusterA);
|
|
1349
|
+
|
|
1350
|
+
expect(await screen.findByText(NO_ITEMS_MESSAGE)).toBeInTheDocument();
|
|
1351
|
+
expect(screen.queryByTestId("feed-options-count")).not.toBeInTheDocument();
|
|
1352
|
+
expect(getFilterAndSortButton()).toHaveAccessibleDescription(
|
|
1353
|
+
getExpectedSummary({ sortOrder: SortOrder.Ascending, eventTypes: [] }),
|
|
1354
|
+
);
|
|
1355
|
+
expectNothingTicked();
|
|
1356
|
+
});
|
|
747
1357
|
});
|
|
@@ -227,4 +227,34 @@ describe("Resource feed pages", () => {
|
|
|
227
227
|
expect(page).toContain("ResourceFeed");
|
|
228
228
|
},
|
|
229
229
|
);
|
|
230
|
+
|
|
231
|
+
test.each(FEED_PAGES)(
|
|
232
|
+
"$product's feed page offers its own model's event types to Filter & Sort",
|
|
233
|
+
(spec: FeedPageSpec) => {
|
|
234
|
+
const page: string = read(
|
|
235
|
+
"Pages",
|
|
236
|
+
spec.pagesDirectory,
|
|
237
|
+
"View",
|
|
238
|
+
"Feed.tsx",
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
/*
|
|
242
|
+
* The pages are near copies, so the realistic slip is pasting another
|
|
243
|
+
* product's enum: it compiles, and the checklist then offers event
|
|
244
|
+
* types this feed never records. The enum must be the one named after
|
|
245
|
+
* the page's own feed model. The SLO page takes its props from
|
|
246
|
+
* getSloResourceFeedProps, so its list lives there.
|
|
247
|
+
*/
|
|
248
|
+
const modelMatch: RegExpMatchArray | null =
|
|
249
|
+
page.match(/<ResourceFeed<(\w+)>/);
|
|
250
|
+
|
|
251
|
+
expect(modelMatch).toBeTruthy();
|
|
252
|
+
|
|
253
|
+
const source: string = page.includes("getSloResourceFeedProps")
|
|
254
|
+
? read("Components", "Slo", "SloFeed.tsx")
|
|
255
|
+
: page;
|
|
256
|
+
|
|
257
|
+
expect(source).toContain(`Object.values(${modelMatch![1]}EventType)`);
|
|
258
|
+
},
|
|
259
|
+
);
|
|
230
260
|
});
|