@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
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
import { afterAll, describe, expect, test } from "@jest/globals";
|
|
2
|
+
import childProcess from "child_process";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { URL } from "url";
|
|
7
|
+
import vm from "vm";
|
|
8
|
+
import { FRONTEND_ENVIRONMENT_CACHE_CONTROL } from "../../Server/Utils/FrontendEnvironment";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Common/Scripts/generate-service-worker.js turns the Dashboard's
|
|
12
|
+
* sw.js.template into public/sw.js at build time, and the Enterprise split
|
|
13
|
+
* gives it two new jobs:
|
|
14
|
+
*
|
|
15
|
+
* 1. The cache version carries the EDITION. The Community and Enterprise
|
|
16
|
+
* images of one release share APP_VERSION and GIT_SHA but bundle a
|
|
17
|
+
* different Dashboard, and the worker serves /dist/ assets cache-first, so
|
|
18
|
+
* without the edition a browser keeps the other edition's bundle after a
|
|
19
|
+
* deployment switches images. The edition is decided exactly as the
|
|
20
|
+
* frontend build decides it (Common/UI/esbuild-enterprise.js):
|
|
21
|
+
* ONEUPTIME_EDITION, else "is ee/<Frontend>/Index.tsx on disk".
|
|
22
|
+
*
|
|
23
|
+
* 2. env.js is network-only: never written to a cache, never answered
|
|
24
|
+
* from one. The server renders it per deployment (the edition among it)
|
|
25
|
+
* and sends it Cache-Control: no-store; as a .js path it used to fall
|
|
26
|
+
* into the cache-first static-asset strategy, and then into a
|
|
27
|
+
* network-first one that still kept a copy.
|
|
28
|
+
*
|
|
29
|
+
* 3. More generally, no strategy writes a response the server marked
|
|
30
|
+
* Cache-Control: no-store to a cache.
|
|
31
|
+
*
|
|
32
|
+
* The generator runs in a node subprocess, the way the build runs it. The
|
|
33
|
+
* generated worker then runs for real in a vm context against fake caches and
|
|
34
|
+
* a fake network, so the caching assertions are about behaviour, not text.
|
|
35
|
+
* Every ee/ here is a fixture (ONEUPTIME_EE_DIR, or a fake repository or image
|
|
36
|
+
* layout): the Common Test job runs with ee/ removed.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
const GENERATOR: string = path.resolve(
|
|
40
|
+
__dirname,
|
|
41
|
+
"..",
|
|
42
|
+
"..",
|
|
43
|
+
"Scripts",
|
|
44
|
+
"generate-service-worker.js",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const DASHBOARD_TEMPLATE: string = path.resolve(
|
|
48
|
+
__dirname,
|
|
49
|
+
"..",
|
|
50
|
+
"..",
|
|
51
|
+
"..",
|
|
52
|
+
"App",
|
|
53
|
+
"FeatureSet",
|
|
54
|
+
"Dashboard",
|
|
55
|
+
"sw.js.template",
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const CACHE_VERSION_LINE: RegExp = /const CACHE_VERSION = '([^']+)';/;
|
|
59
|
+
const UNREPLACED_PLACEHOLDER: RegExp = /\{\{[A-Z_]+\}\}/;
|
|
60
|
+
|
|
61
|
+
const temporaryRoots: Array<string> = [];
|
|
62
|
+
|
|
63
|
+
function makeTempDir(prefix: string): string {
|
|
64
|
+
const root: string = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
65
|
+
temporaryRoots.push(root);
|
|
66
|
+
return root;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type ChildEnvironment = Record<string, string>;
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
* The child's environment: this process's, minus anything that would decide
|
|
73
|
+
* the edition or the version for it, plus the overrides.
|
|
74
|
+
*/
|
|
75
|
+
function childEnvironment(overrides: ChildEnvironment): ChildEnvironment {
|
|
76
|
+
const environment: ChildEnvironment = {};
|
|
77
|
+
|
|
78
|
+
for (const key of Object.keys(process.env)) {
|
|
79
|
+
const value: string | undefined = process.env[key];
|
|
80
|
+
|
|
81
|
+
if (typeof value === "string") {
|
|
82
|
+
environment[key] = value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
delete environment["ONEUPTIME_EDITION"];
|
|
87
|
+
delete environment["ONEUPTIME_EE_DIR"];
|
|
88
|
+
delete environment["APP_VERSION"];
|
|
89
|
+
delete environment["GIT_SHA"];
|
|
90
|
+
|
|
91
|
+
return { ...environment, ...overrides };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// An ee/ fixture holding the plugin entry of the given frontends.
|
|
95
|
+
function makeEnterpriseDir(frontends: Array<string>): string {
|
|
96
|
+
const eeDir: string = makeTempDir("sw-ee-");
|
|
97
|
+
|
|
98
|
+
for (const frontend of frontends) {
|
|
99
|
+
fs.mkdirSync(path.join(eeDir, frontend), { recursive: true });
|
|
100
|
+
fs.writeFileSync(
|
|
101
|
+
path.join(eeDir, frontend, "Index.tsx"),
|
|
102
|
+
"export default {};\n",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return eeDir;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface EditionResult {
|
|
110
|
+
edition: string | null;
|
|
111
|
+
error: string | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// getEdition(frontendDir, env), run in a subprocess.
|
|
115
|
+
function readEdition(
|
|
116
|
+
frontendDir: string,
|
|
117
|
+
env: ChildEnvironment,
|
|
118
|
+
): EditionResult {
|
|
119
|
+
const script: string = `
|
|
120
|
+
const generator = require(${JSON.stringify(GENERATOR)});
|
|
121
|
+
try {
|
|
122
|
+
console.log(JSON.stringify({ edition: generator.getEdition(${JSON.stringify(frontendDir)}, ${JSON.stringify(env)}), error: null }));
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.log(JSON.stringify({ edition: null, error: error.message }));
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
return JSON.parse(
|
|
129
|
+
childProcess
|
|
130
|
+
.execFileSync(process.execPath, ["-e", script], {
|
|
131
|
+
encoding: "utf8",
|
|
132
|
+
env: childEnvironment({}),
|
|
133
|
+
})
|
|
134
|
+
.trim(),
|
|
135
|
+
) as EditionResult;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// A frontend directory holding a copy of the real Dashboard template.
|
|
139
|
+
function makeFrontendDir(parent: string, frontendName: string): string {
|
|
140
|
+
const frontendDir: string = path.join(parent, frontendName);
|
|
141
|
+
fs.mkdirSync(frontendDir, { recursive: true });
|
|
142
|
+
fs.copyFileSync(DASHBOARD_TEMPLATE, path.join(frontendDir, "sw.js.template"));
|
|
143
|
+
return frontendDir;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface GenerateResult {
|
|
147
|
+
status: number;
|
|
148
|
+
output: string;
|
|
149
|
+
worker: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Runs the generator's CLI the way the Dashboard build does.
|
|
153
|
+
function generate(
|
|
154
|
+
env: ChildEnvironment,
|
|
155
|
+
frontendName: string = "Dashboard",
|
|
156
|
+
): GenerateResult {
|
|
157
|
+
const frontendDir: string = makeFrontendDir(
|
|
158
|
+
makeTempDir("sw-frontend-"),
|
|
159
|
+
frontendName,
|
|
160
|
+
);
|
|
161
|
+
const outputPath: string = path.join(frontendDir, "public", "sw.js");
|
|
162
|
+
|
|
163
|
+
const result: childProcess.SpawnSyncReturns<string> = childProcess.spawnSync(
|
|
164
|
+
process.execPath,
|
|
165
|
+
[
|
|
166
|
+
GENERATOR,
|
|
167
|
+
path.join(frontendDir, "sw.js.template"),
|
|
168
|
+
outputPath,
|
|
169
|
+
"OneUptime Dashboard",
|
|
170
|
+
],
|
|
171
|
+
{
|
|
172
|
+
encoding: "utf8",
|
|
173
|
+
env: childEnvironment({
|
|
174
|
+
APP_VERSION: "13.1.0",
|
|
175
|
+
GIT_SHA: "0123abcd99887766",
|
|
176
|
+
...env,
|
|
177
|
+
}),
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
status: result.status ?? -1,
|
|
183
|
+
output: `${result.stdout}${result.stderr}`,
|
|
184
|
+
worker: fs.existsSync(outputPath)
|
|
185
|
+
? fs.readFileSync(outputPath, "utf8")
|
|
186
|
+
: "",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function cacheVersionOf(worker: string): string {
|
|
191
|
+
const match: RegExpMatchArray | null = worker.match(CACHE_VERSION_LINE);
|
|
192
|
+
return match?.[1] ?? "";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/*
|
|
196
|
+
* ---------------------------------------------------------------------------
|
|
197
|
+
* A minimal service-worker runtime: fake caches, a fake network, and the
|
|
198
|
+
* event plumbing the worker uses.
|
|
199
|
+
* ---------------------------------------------------------------------------
|
|
200
|
+
*/
|
|
201
|
+
|
|
202
|
+
interface FakeResponse {
|
|
203
|
+
body: string;
|
|
204
|
+
ok: boolean;
|
|
205
|
+
status: number;
|
|
206
|
+
headers: { get: (name: string) => string | null };
|
|
207
|
+
clone: () => FakeResponse;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Response headers by lower-case name, as the fake server sends them.
|
|
211
|
+
type FakeHeaders = Record<string, string>;
|
|
212
|
+
|
|
213
|
+
function makeResponse(
|
|
214
|
+
body: string,
|
|
215
|
+
date: Date = new Date(),
|
|
216
|
+
headers: FakeHeaders = {},
|
|
217
|
+
status: number = 200,
|
|
218
|
+
): FakeResponse {
|
|
219
|
+
const response: FakeResponse = {
|
|
220
|
+
body,
|
|
221
|
+
ok: status >= 200 && status < 300,
|
|
222
|
+
status,
|
|
223
|
+
headers: {
|
|
224
|
+
get: (name: string): string | null => {
|
|
225
|
+
const key: string = name.toLowerCase();
|
|
226
|
+
|
|
227
|
+
if (key in headers) {
|
|
228
|
+
return headers[key] as string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return key === "date" ? date.toUTCString() : null;
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
clone: (): FakeResponse => {
|
|
235
|
+
return response;
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
return response;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
interface FakeRequest {
|
|
242
|
+
url: string;
|
|
243
|
+
method: string;
|
|
244
|
+
mode: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
type RequestKey = FakeRequest | string;
|
|
248
|
+
|
|
249
|
+
function keyOf(request: RequestKey): string {
|
|
250
|
+
return typeof request === "string" ? request : request.url;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
class FakeCaches {
|
|
254
|
+
public readonly stores: Map<string, Map<string, FakeResponse>> = new Map();
|
|
255
|
+
|
|
256
|
+
public open(name: string): Promise<{
|
|
257
|
+
put: (request: RequestKey, response: FakeResponse) => Promise<void>;
|
|
258
|
+
addAll: (urls: Array<string>) => Promise<void>;
|
|
259
|
+
}> {
|
|
260
|
+
let store: Map<string, FakeResponse> | undefined = this.stores.get(name);
|
|
261
|
+
|
|
262
|
+
if (!store) {
|
|
263
|
+
store = new Map();
|
|
264
|
+
this.stores.set(name, store);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const target: Map<string, FakeResponse> = store;
|
|
268
|
+
|
|
269
|
+
return Promise.resolve({
|
|
270
|
+
put: (request: RequestKey, response: FakeResponse): Promise<void> => {
|
|
271
|
+
target.set(keyOf(request), response);
|
|
272
|
+
return Promise.resolve();
|
|
273
|
+
},
|
|
274
|
+
addAll: (): Promise<void> => {
|
|
275
|
+
return Promise.resolve();
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
public match(request: RequestKey): Promise<FakeResponse | undefined> {
|
|
281
|
+
for (const store of this.stores.values()) {
|
|
282
|
+
const hit: FakeResponse | undefined = store.get(keyOf(request));
|
|
283
|
+
|
|
284
|
+
if (hit) {
|
|
285
|
+
return Promise.resolve(hit);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return Promise.resolve(undefined);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
public keys(): Promise<Array<string>> {
|
|
293
|
+
return Promise.resolve(Array.from(this.stores.keys()));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
public delete(name: string): Promise<boolean> {
|
|
297
|
+
return Promise.resolve(this.stores.delete(name));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
class FakeNetwork {
|
|
302
|
+
public online: boolean = true;
|
|
303
|
+
public readonly requests: Array<string> = [];
|
|
304
|
+
// What the fake server sends for a URL beyond a plain 200.
|
|
305
|
+
public readonly headersByUrl: Map<string, FakeHeaders> = new Map();
|
|
306
|
+
public readonly statusByUrl: Map<string, number> = new Map();
|
|
307
|
+
|
|
308
|
+
public fetch(request: RequestKey): Promise<FakeResponse> {
|
|
309
|
+
const url: string = keyOf(request);
|
|
310
|
+
this.requests.push(url);
|
|
311
|
+
|
|
312
|
+
if (!this.online) {
|
|
313
|
+
return Promise.reject(new Error("Failed to fetch"));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return Promise.resolve(
|
|
317
|
+
makeResponse(
|
|
318
|
+
`network:${url}`,
|
|
319
|
+
new Date(),
|
|
320
|
+
this.headersByUrl.get(url) || {},
|
|
321
|
+
this.statusByUrl.get(url) || 200,
|
|
322
|
+
),
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
type Listener = (event: Record<string, unknown>) => void;
|
|
328
|
+
|
|
329
|
+
interface RunningWorker {
|
|
330
|
+
listeners: Record<string, Listener>;
|
|
331
|
+
caches: FakeCaches;
|
|
332
|
+
network: FakeNetwork;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function startWorker(source: string): RunningWorker {
|
|
336
|
+
const listeners: Record<string, Listener> = {};
|
|
337
|
+
const caches: FakeCaches = new FakeCaches();
|
|
338
|
+
const network: FakeNetwork = new FakeNetwork();
|
|
339
|
+
const noop: () => void = (): void => {};
|
|
340
|
+
|
|
341
|
+
class SandboxResponse {
|
|
342
|
+
public readonly body: string;
|
|
343
|
+
public readonly ok: boolean = false;
|
|
344
|
+
public readonly status: number;
|
|
345
|
+
|
|
346
|
+
public constructor(body: string, init: { status: number }) {
|
|
347
|
+
this.body = body;
|
|
348
|
+
this.status = init.status;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
vm.runInNewContext(source, {
|
|
353
|
+
self: {
|
|
354
|
+
addEventListener: (type: string, listener: Listener): void => {
|
|
355
|
+
listeners[type] = listener;
|
|
356
|
+
},
|
|
357
|
+
skipWaiting: (): Promise<void> => {
|
|
358
|
+
return Promise.resolve();
|
|
359
|
+
},
|
|
360
|
+
clients: {
|
|
361
|
+
claim: (): Promise<void> => {
|
|
362
|
+
return Promise.resolve();
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
location: { origin: "https://oneuptime.example" },
|
|
366
|
+
},
|
|
367
|
+
caches,
|
|
368
|
+
fetch: (request: RequestKey): Promise<FakeResponse> => {
|
|
369
|
+
return network.fetch(request);
|
|
370
|
+
},
|
|
371
|
+
clients: {},
|
|
372
|
+
Response: SandboxResponse,
|
|
373
|
+
URL,
|
|
374
|
+
console: { log: noop, error: noop, warn: noop },
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
return { listeners, caches, network };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function fetchThroughWorker(
|
|
381
|
+
worker: RunningWorker,
|
|
382
|
+
url: string,
|
|
383
|
+
): Promise<FakeResponse> {
|
|
384
|
+
const holder: { response: Promise<FakeResponse> | null } = { response: null };
|
|
385
|
+
|
|
386
|
+
worker.listeners["fetch"]?.({
|
|
387
|
+
request: { url, method: "GET", mode: "cors" },
|
|
388
|
+
respondWith: (response: Promise<FakeResponse>): void => {
|
|
389
|
+
holder.response = response;
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
if (holder.response === null) {
|
|
394
|
+
throw new Error(`the worker did not respond to ${url}`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return await holder.response;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function seedCache(
|
|
401
|
+
worker: RunningWorker,
|
|
402
|
+
cacheName: string,
|
|
403
|
+
url: string,
|
|
404
|
+
body: string,
|
|
405
|
+
date: Date = new Date(),
|
|
406
|
+
): Promise<void> {
|
|
407
|
+
const cache: {
|
|
408
|
+
put: (request: RequestKey, response: FakeResponse) => Promise<void>;
|
|
409
|
+
} = await worker.caches.open(cacheName);
|
|
410
|
+
await cache.put(url, makeResponse(body, date));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// The names of the caches holding a copy of `url`.
|
|
414
|
+
function cachesHolding(worker: RunningWorker, url: string): Array<string> {
|
|
415
|
+
return Array.from(worker.caches.stores.entries())
|
|
416
|
+
.filter((entry: [string, Map<string, FakeResponse>]) => {
|
|
417
|
+
return entry[1].has(url);
|
|
418
|
+
})
|
|
419
|
+
.map((entry: [string, Map<string, FakeResponse>]) => {
|
|
420
|
+
return entry[0];
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Lets the worker's un-awaited background cache refresh finish.
|
|
425
|
+
function settle(): Promise<void> {
|
|
426
|
+
return new Promise<void>((resolve: () => void) => {
|
|
427
|
+
setTimeout(resolve, 20);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const ORIGIN: string = "https://oneuptime.example";
|
|
432
|
+
// Older than the worker's 7-day static cache duration.
|
|
433
|
+
const EIGHT_DAYS_AGO: Date = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000);
|
|
434
|
+
|
|
435
|
+
describe("generate-service-worker.js", () => {
|
|
436
|
+
afterAll(() => {
|
|
437
|
+
while (temporaryRoots.length > 0) {
|
|
438
|
+
fs.rmSync(temporaryRoots.pop() as string, {
|
|
439
|
+
recursive: true,
|
|
440
|
+
force: true,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
describe("the edition, decided like the frontend build decides it", () => {
|
|
446
|
+
test("auto: enterprise when this frontend's ee plugin exists", () => {
|
|
447
|
+
const eeDir: string = makeEnterpriseDir(["Dashboard"]);
|
|
448
|
+
const frontendDir: string = makeFrontendDir(
|
|
449
|
+
makeTempDir("sw-edition-"),
|
|
450
|
+
"Dashboard",
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
expect(readEdition(frontendDir, { ONEUPTIME_EE_DIR: eeDir })).toEqual({
|
|
454
|
+
edition: "enterprise",
|
|
455
|
+
error: null,
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test("auto: community when there is no ee plugin", () => {
|
|
460
|
+
const eeDir: string = makeEnterpriseDir([]);
|
|
461
|
+
const frontendDir: string = makeFrontendDir(
|
|
462
|
+
makeTempDir("sw-edition-"),
|
|
463
|
+
"Dashboard",
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
expect(
|
|
467
|
+
readEdition(frontendDir, { ONEUPTIME_EE_DIR: eeDir }).edition,
|
|
468
|
+
).toBe("community");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("auto: the plugin must be THIS frontend's (ee/<Frontend>/Index.tsx)", () => {
|
|
472
|
+
const eeDir: string = makeEnterpriseDir(["AdminDashboard"]);
|
|
473
|
+
const parent: string = makeTempDir("sw-edition-");
|
|
474
|
+
|
|
475
|
+
expect(
|
|
476
|
+
readEdition(makeFrontendDir(parent, "Dashboard"), {
|
|
477
|
+
ONEUPTIME_EE_DIR: eeDir,
|
|
478
|
+
}).edition,
|
|
479
|
+
).toBe("community");
|
|
480
|
+
expect(
|
|
481
|
+
readEdition(makeFrontendDir(parent, "AdminDashboard"), {
|
|
482
|
+
ONEUPTIME_EE_DIR: eeDir,
|
|
483
|
+
}).edition,
|
|
484
|
+
).toBe("enterprise");
|
|
485
|
+
expect(
|
|
486
|
+
readEdition(makeFrontendDir(parent, "Accounts"), {
|
|
487
|
+
ONEUPTIME_EE_DIR: makeEnterpriseDir(["Dashboard", "AdminDashboard"]),
|
|
488
|
+
}).edition,
|
|
489
|
+
).toBe("community");
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test("auto: finds ee/ in the repository layout (packages/App/FeatureSet/<Frontend> -> <repo>/ee)", () => {
|
|
493
|
+
const repository: string = makeTempDir("sw-repo-");
|
|
494
|
+
const frontendDir: string = makeFrontendDir(
|
|
495
|
+
path.join(repository, "packages", "App", "FeatureSet"),
|
|
496
|
+
"Dashboard",
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
expect(readEdition(frontendDir, {}).edition).toBe("community");
|
|
500
|
+
|
|
501
|
+
fs.mkdirSync(path.join(repository, "ee", "Dashboard"), {
|
|
502
|
+
recursive: true,
|
|
503
|
+
});
|
|
504
|
+
fs.writeFileSync(
|
|
505
|
+
path.join(repository, "ee", "Dashboard", "Index.tsx"),
|
|
506
|
+
"export default {};\n",
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
expect(readEdition(frontendDir, {}).edition).toBe("enterprise");
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
test("auto: finds ee/ in the image layout (/usr/src/app/FeatureSet/<Frontend> -> /usr/src/ee)", () => {
|
|
513
|
+
const usrSrc: string = makeTempDir("sw-image-");
|
|
514
|
+
const frontendDir: string = makeFrontendDir(
|
|
515
|
+
path.join(usrSrc, "app", "FeatureSet"),
|
|
516
|
+
"Dashboard",
|
|
517
|
+
);
|
|
518
|
+
fs.mkdirSync(path.join(usrSrc, "ee", "Dashboard"), { recursive: true });
|
|
519
|
+
fs.writeFileSync(
|
|
520
|
+
path.join(usrSrc, "ee", "Dashboard", "Index.tsx"),
|
|
521
|
+
"export default {};\n",
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
expect(readEdition(frontendDir, {}).edition).toBe("enterprise");
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test("auto: a directory named Index.tsx is not a plugin", () => {
|
|
528
|
+
const eeDir: string = makeTempDir("sw-ee-dir-");
|
|
529
|
+
fs.mkdirSync(path.join(eeDir, "Dashboard", "Index.tsx"), {
|
|
530
|
+
recursive: true,
|
|
531
|
+
});
|
|
532
|
+
const frontendDir: string = makeFrontendDir(
|
|
533
|
+
makeTempDir("sw-edition-"),
|
|
534
|
+
"Dashboard",
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
expect(
|
|
538
|
+
readEdition(frontendDir, { ONEUPTIME_EE_DIR: eeDir }).edition,
|
|
539
|
+
).toBe("community");
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
test("ONEUPTIME_EDITION=community wins even with the plugin on disk", () => {
|
|
543
|
+
const eeDir: string = makeEnterpriseDir(["Dashboard"]);
|
|
544
|
+
const frontendDir: string = makeFrontendDir(
|
|
545
|
+
makeTempDir("sw-edition-"),
|
|
546
|
+
"Dashboard",
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
expect(
|
|
550
|
+
readEdition(frontendDir, {
|
|
551
|
+
ONEUPTIME_EE_DIR: eeDir,
|
|
552
|
+
ONEUPTIME_EDITION: "community",
|
|
553
|
+
}).edition,
|
|
554
|
+
).toBe("community");
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
test("ONEUPTIME_EDITION=enterprise is enterprise (the bundle step fails if ee/ is missing)", () => {
|
|
558
|
+
const frontendDir: string = makeFrontendDir(
|
|
559
|
+
makeTempDir("sw-edition-"),
|
|
560
|
+
"Dashboard",
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
expect(
|
|
564
|
+
readEdition(frontendDir, {
|
|
565
|
+
ONEUPTIME_EE_DIR: makeEnterpriseDir([]),
|
|
566
|
+
ONEUPTIME_EDITION: " Enterprise ",
|
|
567
|
+
}).edition,
|
|
568
|
+
).toBe("enterprise");
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
test("an unknown ONEUPTIME_EDITION is an error, not a guess", () => {
|
|
572
|
+
const frontendDir: string = makeFrontendDir(
|
|
573
|
+
makeTempDir("sw-edition-"),
|
|
574
|
+
"Dashboard",
|
|
575
|
+
);
|
|
576
|
+
const result: EditionResult = readEdition(frontendDir, {
|
|
577
|
+
ONEUPTIME_EDITION: "premium",
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
expect(result.edition).toBeNull();
|
|
581
|
+
expect(result.error).toContain("ONEUPTIME_EDITION must be one of");
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
describe("the generated worker", () => {
|
|
586
|
+
test("puts the edition into the cache version: enterprise", () => {
|
|
587
|
+
const result: GenerateResult = generate({
|
|
588
|
+
ONEUPTIME_EE_DIR: makeEnterpriseDir(["Dashboard"]),
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
expect(result.status).toBe(0);
|
|
592
|
+
expect(cacheVersionOf(result.worker)).toBe(
|
|
593
|
+
"oneuptime-v13.1.0-enterprise-0123abcd",
|
|
594
|
+
);
|
|
595
|
+
expect(result.worker).toContain(" * Edition: enterprise");
|
|
596
|
+
expect(result.output).toContain(
|
|
597
|
+
"Cache version: oneuptime-v13.1.0-enterprise-0123abcd",
|
|
598
|
+
);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
test("puts the edition into the cache version: community", () => {
|
|
602
|
+
const result: GenerateResult = generate({
|
|
603
|
+
ONEUPTIME_EE_DIR: makeEnterpriseDir([]),
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
expect(result.status).toBe(0);
|
|
607
|
+
expect(cacheVersionOf(result.worker)).toBe(
|
|
608
|
+
"oneuptime-v13.1.0-community-0123abcd",
|
|
609
|
+
);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
test("the two editions of one release never share a cache version", () => {
|
|
613
|
+
const community: string = cacheVersionOf(
|
|
614
|
+
generate({ ONEUPTIME_EDITION: "community" }).worker,
|
|
615
|
+
);
|
|
616
|
+
const enterprise: string = cacheVersionOf(
|
|
617
|
+
generate({
|
|
618
|
+
ONEUPTIME_EDITION: "enterprise",
|
|
619
|
+
ONEUPTIME_EE_DIR: makeEnterpriseDir(["Dashboard"]),
|
|
620
|
+
}).worker,
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
expect(community).not.toBe("");
|
|
624
|
+
expect(enterprise).not.toBe("");
|
|
625
|
+
expect(community).not.toBe(enterprise);
|
|
626
|
+
// And neither is a prefix of the other, which the activate clean-up relies on.
|
|
627
|
+
expect(enterprise.startsWith(community)).toBe(false);
|
|
628
|
+
expect(community.startsWith(enterprise)).toBe(false);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test("leaves no placeholder unreplaced", () => {
|
|
632
|
+
const result: GenerateResult = generate({
|
|
633
|
+
ONEUPTIME_EDITION: "community",
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
expect(result.worker).not.toMatch(UNREPLACED_PLACEHOLDER);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
test("fails the build on an unknown ONEUPTIME_EDITION", () => {
|
|
640
|
+
const result: GenerateResult = generate({ ONEUPTIME_EDITION: "premium" });
|
|
641
|
+
|
|
642
|
+
expect(result.status).toBe(1);
|
|
643
|
+
expect(result.output).toContain("ONEUPTIME_EDITION must be one of");
|
|
644
|
+
expect(result.worker).toBe("");
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
describe("the generated worker at runtime", () => {
|
|
649
|
+
interface StartedWorker {
|
|
650
|
+
worker: RunningWorker;
|
|
651
|
+
cacheVersion: string;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function startEnterpriseWorker(): StartedWorker {
|
|
655
|
+
const source: string = generate({
|
|
656
|
+
ONEUPTIME_EE_DIR: makeEnterpriseDir(["Dashboard"]),
|
|
657
|
+
}).worker;
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
worker: startWorker(source),
|
|
661
|
+
cacheVersion: cacheVersionOf(source),
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
test("serves env.js from the network even when a cached copy exists", async () => {
|
|
666
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
667
|
+
const worker: RunningWorker = started.worker;
|
|
668
|
+
const cacheVersion: string = started.cacheVersion;
|
|
669
|
+
const url: string = `${ORIGIN}/dashboard/env.js`;
|
|
670
|
+
await seedCache(worker, `${cacheVersion}-static`, url, "stale env.js");
|
|
671
|
+
|
|
672
|
+
const response: FakeResponse = await fetchThroughWorker(worker, url);
|
|
673
|
+
|
|
674
|
+
expect(response.body).toBe(`network:${url}`);
|
|
675
|
+
expect(worker.network.requests).toEqual([url]);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test.each<[string, FakeHeaders]>([
|
|
679
|
+
[
|
|
680
|
+
"with the server's Cache-Control",
|
|
681
|
+
{ "cache-control": FRONTEND_ENVIRONMENT_CACHE_CONTROL },
|
|
682
|
+
],
|
|
683
|
+
// A proxy that strips the header must not make env.js cacheable.
|
|
684
|
+
["with no Cache-Control at all", {}],
|
|
685
|
+
])(
|
|
686
|
+
"never writes env.js to a cache (%s)",
|
|
687
|
+
async (_label: string, headers: FakeHeaders) => {
|
|
688
|
+
const worker: RunningWorker = startEnterpriseWorker().worker;
|
|
689
|
+
const url: string = `${ORIGIN}/dashboard/env.js`;
|
|
690
|
+
worker.network.headersByUrl.set(url, headers);
|
|
691
|
+
|
|
692
|
+
const first: FakeResponse = await fetchThroughWorker(worker, url);
|
|
693
|
+
const second: FakeResponse = await fetchThroughWorker(worker, url);
|
|
694
|
+
await settle();
|
|
695
|
+
|
|
696
|
+
expect(first.body).toBe(`network:${url}`);
|
|
697
|
+
expect(second.body).toBe(`network:${url}`);
|
|
698
|
+
expect(worker.network.requests).toEqual([url, url]);
|
|
699
|
+
expect(cachesHolding(worker, url)).toEqual([]);
|
|
700
|
+
},
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
test("offline, env.js is not answered from a copy an older worker cached", async () => {
|
|
704
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
705
|
+
const worker: RunningWorker = started.worker;
|
|
706
|
+
const cacheVersion: string = started.cacheVersion;
|
|
707
|
+
const url: string = `${ORIGIN}/dashboard/env.js`;
|
|
708
|
+
await seedCache(
|
|
709
|
+
worker,
|
|
710
|
+
`${cacheVersion}-dynamic`,
|
|
711
|
+
url,
|
|
712
|
+
"last known env.js",
|
|
713
|
+
);
|
|
714
|
+
worker.network.online = false;
|
|
715
|
+
|
|
716
|
+
const response: FakeResponse = await fetchThroughWorker(worker, url);
|
|
717
|
+
|
|
718
|
+
expect(response.body).not.toBe("last known env.js");
|
|
719
|
+
expect(response.status).toBe(503);
|
|
720
|
+
expect(worker.network.requests).toEqual([url]);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
test("treats every frontend's env.js the same way", async () => {
|
|
724
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
725
|
+
const worker: RunningWorker = started.worker;
|
|
726
|
+
const cacheVersion: string = started.cacheVersion;
|
|
727
|
+
const url: string = `${ORIGIN}/admin/env.js`;
|
|
728
|
+
await seedCache(worker, `${cacheVersion}-static`, url, "stale env.js");
|
|
729
|
+
|
|
730
|
+
expect((await fetchThroughWorker(worker, url)).body).toBe(
|
|
731
|
+
`network:${url}`,
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
const fresh: string = `${ORIGIN}/status-page/env.js`;
|
|
735
|
+
await fetchThroughWorker(worker, fresh);
|
|
736
|
+
await settle();
|
|
737
|
+
|
|
738
|
+
expect(cachesHolding(worker, fresh)).toEqual([]);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
test("a page the server marks no-store is served but not cached (network first)", async () => {
|
|
742
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
743
|
+
const worker: RunningWorker = started.worker;
|
|
744
|
+
const cacheVersion: string = started.cacheVersion;
|
|
745
|
+
const noStore: string = `${ORIGIN}/dashboard/settings`;
|
|
746
|
+
const cacheable: string = `${ORIGIN}/dashboard/home`;
|
|
747
|
+
worker.network.headersByUrl.set(noStore, {
|
|
748
|
+
"cache-control": FRONTEND_ENVIRONMENT_CACHE_CONTROL,
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
expect((await fetchThroughWorker(worker, noStore)).body).toBe(
|
|
752
|
+
`network:${noStore}`,
|
|
753
|
+
);
|
|
754
|
+
await fetchThroughWorker(worker, cacheable);
|
|
755
|
+
await settle();
|
|
756
|
+
|
|
757
|
+
expect(cachesHolding(worker, noStore)).toEqual([]);
|
|
758
|
+
// Negative control: the same strategy still caches a normal page.
|
|
759
|
+
expect(cachesHolding(worker, cacheable)).toEqual([
|
|
760
|
+
`${cacheVersion}-dynamic`,
|
|
761
|
+
]);
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
test("an asset the server marks no-store is served but not cached (cache first)", async () => {
|
|
765
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
766
|
+
const worker: RunningWorker = started.worker;
|
|
767
|
+
const cacheVersion: string = started.cacheVersion;
|
|
768
|
+
const noStore: string = `${ORIGIN}/dashboard/dist/Private.js`;
|
|
769
|
+
const cacheable: string = `${ORIGIN}/dashboard/dist/Index.js`;
|
|
770
|
+
worker.network.headersByUrl.set(noStore, { "cache-control": "no-store" });
|
|
771
|
+
|
|
772
|
+
expect((await fetchThroughWorker(worker, noStore)).body).toBe(
|
|
773
|
+
`network:${noStore}`,
|
|
774
|
+
);
|
|
775
|
+
await fetchThroughWorker(worker, cacheable);
|
|
776
|
+
await settle();
|
|
777
|
+
|
|
778
|
+
expect(cachesHolding(worker, noStore)).toEqual([]);
|
|
779
|
+
expect(cachesHolding(worker, cacheable)).toEqual([
|
|
780
|
+
`${cacheVersion}-static`,
|
|
781
|
+
]);
|
|
782
|
+
|
|
783
|
+
// Not cached, so the next request goes to the network again.
|
|
784
|
+
await fetchThroughWorker(worker, noStore);
|
|
785
|
+
expect(
|
|
786
|
+
worker.network.requests.filter((url: string) => {
|
|
787
|
+
return url === noStore;
|
|
788
|
+
}),
|
|
789
|
+
).toHaveLength(2);
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
test.each<[string, boolean]>([
|
|
793
|
+
["no-store", false],
|
|
794
|
+
["private, no-store", false],
|
|
795
|
+
["private, max-age=0", true],
|
|
796
|
+
])(
|
|
797
|
+
"the background refresh of a stale asset (Cache-Control %p) replaces the cached copy: %p",
|
|
798
|
+
async (cacheControl: string, replaced: boolean) => {
|
|
799
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
800
|
+
const worker: RunningWorker = started.worker;
|
|
801
|
+
const cacheVersion: string = started.cacheVersion;
|
|
802
|
+
const url: string = `${ORIGIN}/dashboard/dist/Index.js`;
|
|
803
|
+
await seedCache(
|
|
804
|
+
worker,
|
|
805
|
+
`${cacheVersion}-static`,
|
|
806
|
+
url,
|
|
807
|
+
"stale bundle",
|
|
808
|
+
EIGHT_DAYS_AGO,
|
|
809
|
+
);
|
|
810
|
+
worker.network.headersByUrl.set(url, {
|
|
811
|
+
"cache-control": cacheControl,
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
// Served from the cache at once; the refresh runs behind it.
|
|
815
|
+
expect((await fetchThroughWorker(worker, url)).body).toBe(
|
|
816
|
+
"stale bundle",
|
|
817
|
+
);
|
|
818
|
+
await settle();
|
|
819
|
+
|
|
820
|
+
expect(worker.network.requests).toEqual([url]);
|
|
821
|
+
expect(
|
|
822
|
+
worker.caches.stores.get(`${cacheVersion}-static`)?.get(url)?.body,
|
|
823
|
+
).toBe(replaced ? `network:${url}` : "stale bundle");
|
|
824
|
+
},
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
test.each<[string, boolean]>([
|
|
828
|
+
[FRONTEND_ENVIRONMENT_CACHE_CONTROL, false],
|
|
829
|
+
["no-store", false],
|
|
830
|
+
["No-Store", false],
|
|
831
|
+
["public, max-age=0, NO-STORE ", false],
|
|
832
|
+
["no-cache", true],
|
|
833
|
+
["private, max-age=600", true],
|
|
834
|
+
["max-age=60, must-revalidate", true],
|
|
835
|
+
])(
|
|
836
|
+
"reads no-store as a Cache-Control directive: %p -> cached: %p",
|
|
837
|
+
async (cacheControl: string, cached: boolean) => {
|
|
838
|
+
const worker: RunningWorker = startEnterpriseWorker().worker;
|
|
839
|
+
const url: string = `${ORIGIN}/dashboard/incidents`;
|
|
840
|
+
worker.network.headersByUrl.set(url, {
|
|
841
|
+
"cache-control": cacheControl,
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
await fetchThroughWorker(worker, url);
|
|
845
|
+
await settle();
|
|
846
|
+
|
|
847
|
+
expect(cachesHolding(worker, url).length > 0).toBe(cached);
|
|
848
|
+
},
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
test("never caches an error response", async () => {
|
|
852
|
+
const worker: RunningWorker = startEnterpriseWorker().worker;
|
|
853
|
+
const url: string = `${ORIGIN}/dashboard/missing`;
|
|
854
|
+
worker.network.statusByUrl.set(url, 404);
|
|
855
|
+
|
|
856
|
+
expect((await fetchThroughWorker(worker, url)).status).toBe(404);
|
|
857
|
+
await settle();
|
|
858
|
+
|
|
859
|
+
expect(cachesHolding(worker, url)).toEqual([]);
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
test("still serves bundles cache-first (the cache version is what invalidates them)", async () => {
|
|
863
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
864
|
+
const worker: RunningWorker = started.worker;
|
|
865
|
+
const cacheVersion: string = started.cacheVersion;
|
|
866
|
+
const url: string = `${ORIGIN}/dashboard/dist/Index.js`;
|
|
867
|
+
await seedCache(worker, `${cacheVersion}-static`, url, "cached bundle");
|
|
868
|
+
|
|
869
|
+
const response: FakeResponse = await fetchThroughWorker(worker, url);
|
|
870
|
+
|
|
871
|
+
expect(response.body).toBe("cached bundle");
|
|
872
|
+
expect(worker.network.requests).toEqual([]);
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
test("drops the other edition's caches on activate", async () => {
|
|
876
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
877
|
+
const worker: RunningWorker = started.worker;
|
|
878
|
+
const cacheVersion: string = started.cacheVersion;
|
|
879
|
+
const communityVersion: string = cacheVersion.replace(
|
|
880
|
+
"-enterprise-",
|
|
881
|
+
"-community-",
|
|
882
|
+
);
|
|
883
|
+
await seedCache(
|
|
884
|
+
worker,
|
|
885
|
+
`${communityVersion}-static`,
|
|
886
|
+
`${ORIGIN}/dashboard/dist/Index.js`,
|
|
887
|
+
"community bundle",
|
|
888
|
+
);
|
|
889
|
+
await seedCache(
|
|
890
|
+
worker,
|
|
891
|
+
`${cacheVersion}-static`,
|
|
892
|
+
`${ORIGIN}/dashboard/dist/Index.js`,
|
|
893
|
+
"enterprise bundle",
|
|
894
|
+
);
|
|
895
|
+
await seedCache(worker, "someone-elses-cache", `${ORIGIN}/x`, "x");
|
|
896
|
+
|
|
897
|
+
const holder: { done: Promise<unknown> | null } = { done: null };
|
|
898
|
+
worker.listeners["activate"]?.({
|
|
899
|
+
waitUntil: (done: Promise<unknown>): void => {
|
|
900
|
+
holder.done = done;
|
|
901
|
+
},
|
|
902
|
+
});
|
|
903
|
+
await holder.done;
|
|
904
|
+
|
|
905
|
+
expect(Array.from(worker.caches.stores.keys()).sort()).toEqual(
|
|
906
|
+
[`${cacheVersion}-static`, "someone-elses-cache"].sort(),
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
test("reports its edition-qualified cache version to the page", () => {
|
|
911
|
+
const started: StartedWorker = startEnterpriseWorker();
|
|
912
|
+
const worker: RunningWorker = started.worker;
|
|
913
|
+
const cacheVersion: string = started.cacheVersion;
|
|
914
|
+
const posted: Array<unknown> = [];
|
|
915
|
+
|
|
916
|
+
worker.listeners["message"]?.({
|
|
917
|
+
data: { type: "GET_VERSION" },
|
|
918
|
+
ports: [
|
|
919
|
+
{
|
|
920
|
+
postMessage: (message: unknown): void => {
|
|
921
|
+
posted.push(message);
|
|
922
|
+
},
|
|
923
|
+
},
|
|
924
|
+
],
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
expect(posted).toEqual([{ version: cacheVersion }]);
|
|
928
|
+
expect(cacheVersion).toContain("-enterprise-");
|
|
929
|
+
});
|
|
930
|
+
});
|
|
931
|
+
});
|