@oneuptime/common 12.0.29 → 12.0.31
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/SecurityEvent.ts +48 -16
- package/Models/AnalyticsModels/ThreatIntelIndicator.ts +10 -5
- package/Models/DatabaseModels/AlertSeverity.ts +44 -0
- package/Models/DatabaseModels/DetectionRule.ts +16 -5
- package/Models/DatabaseModels/GoogleSecOpsConnection.ts +14 -5
- package/Models/DatabaseModels/IncidentSeverity.ts +44 -0
- package/Models/DatabaseModels/Index.ts +13 -0
- package/Models/DatabaseModels/NetworkDevice.ts +113 -3
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +88 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +22 -0
- package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +499 -0
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +43 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +662 -0
- package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +584 -0
- package/Models/DatabaseModels/ThreatIntelFeed.ts +16 -5
- package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +546 -0
- package/Models/DatabaseModels/UserOnCallShiftReminder.ts +231 -0
- package/Models/DatabaseModels/UserOnCallShiftReminderLog.ts +331 -0
- package/Server/API/GlobalConfigAPI.ts +362 -183
- package/Server/API/OnCallCalendarAPI.ts +2194 -0
- package/Server/API/TelemetryAPI.ts +42 -4
- package/Server/EnvironmentConfig.ts +250 -0
- package/Server/Infrastructure/OnCallCalendarFeedCache.ts +1050 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.ts +253 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/MultipartFormData.ts +118 -32
- package/Server/Middleware/OnCallCalendarFeedRateLimit.ts +487 -0
- package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +9 -1
- package/Server/Services/AlertEpisodeOnCallRuleEngineService.ts +9 -1
- package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +9 -1
- package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +9 -1
- package/Server/Services/AlertGroupingEngineService.ts +9 -1
- package/Server/Services/AlertLabelRuleEngineService.ts +9 -1
- package/Server/Services/AlertOnCallRuleEngineService.ts +9 -1
- package/Server/Services/AlertOwnerRuleEngineService.ts +9 -1
- package/Server/Services/AlertPrivacyRuleEngineService.ts +9 -1
- package/Server/Services/AlertReminderRuleService.ts +9 -1
- package/Server/Services/AutoRemediationRuleEngineService.ts +9 -1
- package/Server/Services/CephClusterLabelRuleEngineService.ts +9 -1
- package/Server/Services/CephClusterOwnerRuleEngineService.ts +9 -1
- package/Server/Services/CloudResourceLabelRuleEngineService.ts +9 -1
- package/Server/Services/CloudResourceOwnerRuleEngineService.ts +9 -1
- package/Server/Services/DashboardLabelRuleEngineService.ts +9 -1
- package/Server/Services/DashboardOwnerRuleEngineService.ts +9 -1
- package/Server/Services/DockerHostLabelRuleEngineService.ts +9 -1
- package/Server/Services/DockerHostOwnerRuleEngineService.ts +9 -1
- package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +9 -1
- package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +9 -1
- package/Server/Services/HostLabelRuleEngineService.ts +9 -1
- package/Server/Services/HostOwnerRuleEngineService.ts +9 -1
- package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +9 -1
- package/Server/Services/IncidentEpisodeOnCallRuleEngineService.ts +9 -1
- package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +9 -1
- package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +9 -1
- package/Server/Services/IncidentGroupingEngineService.ts +9 -1
- package/Server/Services/IncidentLabelRuleEngineService.ts +9 -1
- package/Server/Services/IncidentOnCallRuleEngineService.ts +9 -1
- package/Server/Services/IncidentOwnerRuleEngineService.ts +9 -1
- package/Server/Services/IncidentPrivacyRuleEngineService.ts +9 -1
- package/Server/Services/IncidentReminderRuleService.ts +9 -1
- package/Server/Services/IncidentSlaRuleService.ts +9 -1
- package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +9 -1
- package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +9 -1
- package/Server/Services/Index.ts +14 -0
- package/Server/Services/IoTFleetLabelRuleEngineService.ts +9 -1
- package/Server/Services/IoTFleetOwnerRuleEngineService.ts +9 -1
- package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +9 -1
- package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +9 -1
- package/Server/Services/KubernetesResourceService.ts +5 -1
- package/Server/Services/MonitorLabelRuleEngineService.ts +9 -1
- package/Server/Services/MonitorOwnerRuleEngineService.ts +9 -1
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +35 -0
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +96 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +9 -1
- package/Server/Services/NetworkDeviceOidTemplateService.ts +201 -0
- package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +9 -1
- package/Server/Services/NetworkDeviceService.ts +209 -1
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +32 -0
- package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +9 -1
- package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +9 -1
- package/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.ts +260 -0
- package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +9 -1
- package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +70 -3
- package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +138 -3
- package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +9 -1
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +1465 -134
- package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +167 -31
- package/Server/Services/PodmanHostLabelRuleEngineService.ts +9 -1
- package/Server/Services/PodmanHostOwnerRuleEngineService.ts +9 -1
- package/Server/Services/ProjectOnCallCalendarFeedService.ts +192 -0
- package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +9 -1
- package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +9 -1
- package/Server/Services/RumApplicationLabelRuleEngineService.ts +9 -1
- package/Server/Services/RumApplicationOwnerRuleEngineService.ts +9 -1
- package/Server/Services/RunbookLabelRuleEngineService.ts +9 -1
- package/Server/Services/RunbookOwnerRuleEngineService.ts +9 -1
- package/Server/Services/RunbookRuleEngineService.ts +9 -1
- package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +9 -1
- package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +9 -1
- package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +9 -1
- package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +9 -1
- package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +9 -1
- package/Server/Services/ServiceLabelRuleEngineService.ts +9 -1
- package/Server/Services/ServiceOwnerRuleEngineService.ts +9 -1
- package/Server/Services/StatusPageLabelRuleEngineService.ts +9 -1
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +7 -0
- package/Server/Services/StatusPageOwnerRuleEngineService.ts +9 -1
- package/Server/Services/TeamMemberService.ts +489 -1
- package/Server/Services/TeamService.ts +50 -0
- package/Server/Services/TelemetrySourceMapService.ts +266 -23
- package/Server/Services/UserNotificationSettingService.ts +44 -1
- package/Server/Services/UserOnCallCalendarFeedService.ts +159 -0
- package/Server/Services/UserOnCallShiftReminderLogService.ts +151 -0
- package/Server/Services/UserOnCallShiftReminderService.ts +112 -0
- package/Server/Services/UserService.ts +27 -0
- package/Server/Services/WorkflowLabelRuleEngineService.ts +9 -1
- package/Server/Services/WorkflowOwnerRuleEngineService.ts +9 -1
- package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +186 -0
- package/Server/Utils/Monitor/CapturedMetricAttributeUtil.ts +245 -0
- package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +21 -2
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +55 -21
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +32 -0
- package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +15 -2
- package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +18 -1
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +10 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +34 -0
- package/Server/Utils/OnCall/CalendarFeedToken.ts +241 -0
- package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1488 -0
- package/Server/Utils/OnCall/OnCallCalendarFeedUrls.ts +230 -0
- package/Server/Utils/OnCall/OnCallShiftChangeListeners.ts +227 -0
- package/Server/Utils/OnCall/OnCallShiftMaterializer.ts +1127 -0
- package/Server/Utils/OnCall/OnCallShiftReminderListener.ts +174 -0
- package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +2584 -0
- package/Server/Utils/Response.ts +134 -0
- package/Server/Utils/Rules/RuleEngineRuleRead.ts +48 -0
- package/Server/Utils/StartServer.ts +10 -0
- package/Server/Utils/Telemetry/AppMetrics.ts +51 -0
- package/Server/Utils/Telemetry/ExceptionSanitizer.ts +14 -1
- package/Server/Utils/Telemetry/LogExceptionExtractor.ts +14 -2
- package/Server/Utils/Telemetry/SourceMapResolver.ts +16 -5
- package/Server/Utils/Telemetry/StackTraceParser.ts +399 -0
- package/Tests/App/Dashboard/EventOverlayHook.test.tsx +125 -0
- package/Tests/App/Dashboard/HeaderImageAssets.test.tsx +93 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedLinks.test.tsx +229 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +489 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +149 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedUtil.test.ts +855 -0
- package/Tests/App/Dashboard/OnCallPersonalCalendarFeed.test.tsx +1209 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3 -3
- package/Tests/App/Dashboard/OnCallSharedCalendarFeedCard.test.tsx +1019 -0
- package/Tests/App/Dashboard/OnCallShiftRemindersAndUpcomingShifts.test.tsx +655 -0
- package/Tests/App/Dashboard/UserElementEmail.test.tsx +12 -4
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +1 -1
- package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +73 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +14 -0
- package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +469 -0
- package/Tests/Models/OnCallCalendarFeedModels.test.ts +642 -0
- package/Tests/Models/OnCallDutyPolicyScheduleCalendarFeed.test.ts +288 -0
- package/Tests/Models/OnCallDutyPolicyScheduleShiftConfigVersion.test.ts +88 -0
- package/Tests/Models/SecurityDomainAccessControl.test.ts +429 -0
- package/Tests/Models/UserOnCallShiftReminderModels.test.ts +368 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +657 -0
- package/Tests/Server/API/Helpers.ts +8 -0
- package/Tests/Server/API/OnCallCalendarAPI.test.ts +4461 -0
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +84 -6
- package/Tests/Server/API/TelemetryResolveStackTraceAPI.test.ts +805 -0
- package/Tests/Server/EnvironmentConfigOnCallCalendarFeed.test.ts +357 -0
- package/Tests/Server/Infrastructure/OnCallCalendarFeedCache.test.ts +1589 -0
- package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +394 -0
- package/Tests/Server/Middleware/IdentityRateLimit.test.ts +1144 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +618 -3
- package/Tests/Server/Middleware/OnCallCalendarFeedRateLimit.test.ts +1365 -0
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +70 -0
- package/Tests/Server/Services/MonitorLabelRuleEngineScale.test.ts +520 -0
- package/Tests/Server/Services/NetworkDeviceOidTemplateService.test.ts +332 -0
- package/Tests/Server/Services/NetworkDeviceOidTemplateWriteGuards.test.ts +409 -0
- package/Tests/Server/Services/OnCallCalendarFeedServices.test.ts +1103 -0
- package/Tests/Server/Services/OnCallDutyPolicyScheduleResolver.test.ts +1037 -0
- package/Tests/Server/Services/OnCallShiftConfigPropagation.test.ts +1270 -0
- package/Tests/Server/Services/RuleEngineRuleFetchLimit.test.ts +239 -0
- package/Tests/Server/Services/TeamDeleteMemberCleanup.test.ts +156 -0
- package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +755 -0
- package/Tests/Server/Services/TelemetrySourceMapService.test.ts +1210 -2
- package/Tests/Server/Services/UserNotificationSettingShiftReminderDefaults.test.ts +267 -0
- package/Tests/Server/Services/UserOnCallShiftReminderServices.test.ts +583 -0
- package/Tests/Server/Services/UserServiceSeatLimit.test.ts +182 -0
- package/Tests/Server/Types/AnalyticsDatabase/SecurityEventModelPermission.test.ts +255 -0
- package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +254 -0
- package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +212 -0
- package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +413 -0
- package/Tests/Server/Utils/CodeRepository.test.ts +689 -0
- package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +495 -0
- package/Tests/Server/Utils/ExceptionSanitizer.test.ts +77 -0
- package/Tests/Server/Utils/Monitor/CapturedMetricAttributeUtil.test.ts +284 -0
- package/Tests/Server/Utils/Monitor/MonitorCustomMetricAttributes.test.ts +503 -0
- package/Tests/Server/Utils/Monitor/MonitorTemplatePrototypePollution.test.ts +106 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceWalkLogWritePath.test.ts +33 -4
- package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +14 -4
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +44 -0
- package/Tests/Server/Utils/OnCall/CalendarFeedToken.test.ts +467 -0
- package/Tests/Server/Utils/OnCall/OnCallCalendarFeedRenderer.test.ts +2142 -0
- package/Tests/Server/Utils/OnCall/OnCallCalendarFeedUrls.test.ts +278 -0
- package/Tests/Server/Utils/OnCall/OnCallResolverTestHarness.ts +440 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftChangeListeners.test.ts +314 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftMaterializer.test.ts +1502 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftReminderListener.test.ts +491 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftReminderRunner.test.ts +3088 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftReminderTestHarness.ts +910 -0
- package/Tests/Server/Utils/OpenAPISpec.test.ts +536 -0
- package/Tests/Server/Utils/ResponseSendCalendarResponse.test.ts +561 -0
- package/Tests/Server/Utils/Rules/RuleEngineRuleRead.test.ts +115 -0
- package/Tests/Server/Utils/StackTraceParser.test.ts +610 -0
- package/Tests/Server/Utils/StartServerEncryptionSecretWarning.test.ts +64 -0
- package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +29 -0
- package/Tests/Server/Utils/Telemetry/LogExceptionExtractor.test.ts +0 -0
- package/Tests/Server/Utils/Telemetry/SourceMapLimits.test.ts +656 -0
- package/Tests/Server/Utils/Telemetry/SourceMapResolver.test.ts +100 -0
- package/Tests/Types/Calendar/ICalendar.test.ts +488 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpOidListUtil.test.ts +454 -0
- package/Tests/Types/NotificationSetting/NotificationSettingEventType.test.ts +53 -0
- package/Tests/Types/OnCallDutyPolicy/CalendarFeedTestFixtures.ts +310 -0
- package/Tests/Types/OnCallDutyPolicy/CalendarFeedWindow.test.ts +205 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilLayerMetaAndIterationCap.test.ts +458 -0
- package/Tests/Types/OnCallDutyPolicy/MaterializedShift.test.ts +197 -0
- package/Tests/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.test.ts +1986 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtilGroupKey.test.ts +678 -0
- package/Tests/Types/OnCallDutyPolicy/ShiftSeamUtil.test.ts +417 -0
- package/Tests/Types/SecurityRolePermissions.test.ts +193 -0
- package/Tests/UI/Components/Charts/AnnotationLayout.test.ts +773 -0
- package/Tests/UI/Components/Charts/ChartAnnotationRail.test.tsx +789 -0
- package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +254 -0
- package/Tests/UI/Components/Charts/TimeAnnotation.test.ts +471 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +405 -0
- package/Tests/UI/Components/ErrorMessageRefreshControl.test.tsx +83 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +686 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableDeleteAndEmptyStates.test.tsx +341 -0
- package/Tests/UI/Components/OrderedStatesList.test.tsx +5 -5
- package/Tests/UI/Components/PendingProjectInvitations.test.tsx +1 -1
- package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +195 -0
- package/Tests/UI/Components/XAxis.test.ts +270 -5
- package/Tests/UI/Components/XAxisDaylightSaving.test.ts +113 -0
- package/Tests/UI/Styles/ChartFocusRing.test.ts +153 -0
- package/Tests/UI/Styles/ChartFocusRingCoverage.test.tsx +377 -0
- package/Tests/UI/Styles/ThemeStylesheet.ts +162 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/Utils/EnterpriseLicenseSeats.test.ts +507 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +951 -1
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +238 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +1056 -0
- package/Tests/Utils/NetworkDiscovery/ReverseDnsNameUtil.test.ts +882 -0
- package/Types/Calendar/ICalendar.ts +442 -0
- package/Types/Date.ts +39 -0
- package/Types/Email/EmailTemplateType.ts +2 -0
- package/Types/Monitor/SnmpMonitor/SnmpOidListUtil.ts +333 -0
- package/Types/NotificationSetting/NotificationSettingEventType.ts +4 -0
- package/Types/OnCallDutyPolicy/CalendarFeedWindow.ts +140 -0
- package/Types/OnCallDutyPolicy/Layer.ts +190 -35
- package/Types/OnCallDutyPolicy/MaterializedShift.ts +274 -0
- package/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.ts +1415 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +159 -4
- package/Types/OnCallDutyPolicy/ShiftSeamUtil.ts +106 -0
- package/Types/Permission.ts +96 -0
- package/Types/Rum/SessionReplay.ts +21 -0
- package/Types/Telemetry/SourceMap.ts +8 -0
- package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.tsx +176 -0
- package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.ts +745 -0
- package/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.tsx +576 -0
- package/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.tsx +204 -0
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +491 -544
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +324 -372
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +487 -540
- package/UI/Components/Charts/ChartLibrary/Types/FormattedReferenceRegion.ts +7 -3
- package/UI/Components/Charts/ChartLibrary/Types/FormattedTimeReferenceLine.ts +14 -3
- package/UI/Components/Charts/Types/ChartEventKind.ts +13 -0
- package/UI/Components/Charts/Types/ReferenceRegionProps.ts +6 -0
- package/UI/Components/Charts/Types/TimeReferenceLineProps.ts +20 -3
- package/UI/Components/Charts/Utils/TimeAnnotation.ts +69 -35
- package/UI/Components/Charts/Utils/XAxis.ts +125 -18
- package/UI/Components/EditionLabel/EditionLabel.tsx +280 -26
- package/UI/Components/ErrorMessage/ErrorMessage.tsx +21 -10
- package/UI/Components/Feed/FeedItem.tsx +4 -5
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -2
- package/UI/Components/Header/UserProfile/UserProfile.tsx +1 -2
- package/UI/Components/Image/Image.tsx +20 -3
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +1 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +150 -24
- package/UI/Components/Table/TableHeader.tsx +64 -48
- package/UI/Components/Table/TableRow.tsx +13 -3
- package/UI/Styles/Theme.css +38 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +271 -0
- package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +6 -0
- package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +141 -10
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +55 -3
- package/Utils/NetworkDiscovery/ReverseDnsNameUtil.ts +151 -0
- package/Utils/Rules/RuleEngineLimits.ts +32 -0
- package/build/dist/Models/AnalyticsModels/SecurityEvent.js +48 -16
- package/build/dist/Models/AnalyticsModels/SecurityEvent.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js +10 -5
- package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertSeverity.js +46 -1
- package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DetectionRule.js +16 -5
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +14 -5
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentSeverity.js +46 -1
- package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +13 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +115 -3
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +88 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +519 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +44 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +699 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +620 -0
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js +16 -5
- package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +585 -0
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js +247 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js +355 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js.map +1 -0
- package/build/dist/Server/API/GlobalConfigAPI.js +293 -176
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallCalendarAPI.js +1371 -0
- package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -0
- package/build/dist/Server/API/TelemetryAPI.js +31 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +185 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js +663 -0
- package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js +103 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.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/MultipartFormData.js +93 -28
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js +342 -0
- package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertGroupingEngineService.js +8 -1
- package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertOnCallRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js +8 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +8 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/HostLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/HostOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentReminderRuleService.js +8 -1
- package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSlaRuleService.js +8 -1
- package/build/dist/Server/Services/IncidentSlaRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +14 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +5 -1
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +30 -21
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +64 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js +180 -0
- package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +154 -1
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +20 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js +219 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +47 -3
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +112 -3
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +955 -9
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +93 -6
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js +157 -0
- package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookRuleEngineService.js +8 -1
- package/build/dist/Server/Services/RunbookRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +8 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +6 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +396 -1
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamService.js +43 -0
- package/build/dist/Server/Services/TeamService.js.map +1 -1
- package/build/dist/Server/Services/TelemetrySourceMapService.js +213 -19
- package/build/dist/Server/Services/TelemetrySourceMapService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +26 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallCalendarFeedService.js +140 -0
- package/build/dist/Server/Services/UserOnCallCalendarFeedService.js.map +1 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js +116 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js.map +1 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderService.js +92 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderService.js.map +1 -0
- package/build/dist/Server/Services/UserService.js +25 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +8 -1
- package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +8 -1
- package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +145 -0
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js +201 -0
- package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +19 -2
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +42 -23
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +29 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +11 -2
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +16 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +10 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +33 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js +161 -0
- package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js +983 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js +155 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js +145 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js +752 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js +92 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1786 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -0
- package/build/dist/Server/Utils/Response.js +97 -0
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js +37 -0
- package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +9 -1
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js +41 -0
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +12 -1
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +14 -2
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js +15 -5
- package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/StackTraceParser.js +307 -0
- package/build/dist/Server/Utils/Telemetry/StackTraceParser.js.map +1 -1
- package/build/dist/Types/Calendar/ICalendar.js +290 -0
- package/build/dist/Types/Calendar/ICalendar.js.map +1 -0
- package/build/dist/Types/Date.js +27 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +2 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js +253 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js.map +1 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js +97 -0
- package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +113 -19
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js +134 -0
- package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js +800 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +97 -3
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js +77 -0
- package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js.map +1 -0
- package/build/dist/Types/Permission.js +86 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +19 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js +83 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js +531 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js +264 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js +122 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +213 -233
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +139 -152
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +212 -232
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Types/ChartEventKind.js +14 -0
- package/build/dist/UI/Components/Charts/Types/ChartEventKind.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +57 -28
- package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +103 -17
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +198 -23
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +15 -5
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js +4 -5
- package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -2
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Header/UserProfile/UserProfile.js.map +1 -1
- package/build/dist/UI/Components/Image/Image.js +6 -1
- package/build/dist/UI/Components/Image/Image.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/MemberRoleAssignment/MemberRoleAssignment.js +1 -2
- package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +104 -23
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +20 -22
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +13 -4
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +153 -0
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +127 -7
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +50 -3
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js +132 -0
- package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js.map +1 -0
- package/build/dist/Utils/Rules/RuleEngineLimits.js +31 -0
- package/build/dist/Utils/Rules/RuleEngineLimits.js.map +1 -0
- package/jest.config.json +1 -0
- package/package.json +2 -2
|
@@ -1,3 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The per-release ceiling used to be a hardcoded 100 that also bounded what
|
|
3
|
+
* resolveFramesForService would READ. This suite pins the two apart:
|
|
4
|
+
*
|
|
5
|
+
* - the ceiling is an operator knob (SOURCE_MAP_MAX_MAPS_PER_RELEASE) that
|
|
6
|
+
* gates WRITES, enforced on the create path itself;
|
|
7
|
+
* - the read path bounds itself by BYTES loaded
|
|
8
|
+
* (SOURCE_MAP_MAX_BYTES_PER_RESOLVE), so a release that fits the write
|
|
9
|
+
* gate always resolves in full.
|
|
10
|
+
*
|
|
11
|
+
* SourceMapMaxBytesPerResolve is read at call time, so a getter over the real
|
|
12
|
+
* config lets a case pick a budget measured in tens of bytes while every
|
|
13
|
+
* other value stays production-real. bytesPerResolve === null means "use the
|
|
14
|
+
* configured value", which is what every non-budget case runs with.
|
|
15
|
+
*/
|
|
16
|
+
const mockResolveBudget: { bytesPerResolve: number | null } = {
|
|
17
|
+
bytesPerResolve: null,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
jest.mock("../../../Server/EnvironmentConfig", () => {
|
|
21
|
+
const actualConfig: Record<string, unknown> = jest.requireActual(
|
|
22
|
+
"../../../Server/EnvironmentConfig",
|
|
23
|
+
) as Record<string, unknown>;
|
|
24
|
+
|
|
25
|
+
const mockedConfig: Record<string, unknown> = { ...actualConfig };
|
|
26
|
+
|
|
27
|
+
Object.defineProperty(mockedConfig, "SourceMapMaxBytesPerResolve", {
|
|
28
|
+
configurable: true,
|
|
29
|
+
enumerable: true,
|
|
30
|
+
get: (): number => {
|
|
31
|
+
return (
|
|
32
|
+
mockResolveBudget.bytesPerResolve ??
|
|
33
|
+
(actualConfig["SourceMapMaxBytesPerResolve"] as number)
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return mockedConfig;
|
|
39
|
+
});
|
|
40
|
+
|
|
1
41
|
import TelemetrySourceMapService, {
|
|
2
42
|
MAX_SOURCE_MAP_SIZE_IN_BYTES,
|
|
3
43
|
MAX_SOURCE_MAPS_PER_RELEASE,
|
|
@@ -7,7 +47,18 @@ import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
|
7
47
|
import TelemetrySourceMap from "../../../Models/DatabaseModels/TelemetrySourceMap";
|
|
8
48
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
9
49
|
import ObjectID from "../../../Types/ObjectID";
|
|
10
|
-
import
|
|
50
|
+
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
51
|
+
import LIMIT_MAX from "../../../Types/Database/LimitMax";
|
|
52
|
+
import logger from "../../../Server/Utils/Logger";
|
|
53
|
+
import { MAX_FRAMES_TO_RESOLVE } from "../../../Server/Utils/Telemetry/SourceMapResolver";
|
|
54
|
+
import {
|
|
55
|
+
SourceMapMaxMapsPerRelease,
|
|
56
|
+
SourceMapRetentionInDays,
|
|
57
|
+
} from "../../../Server/EnvironmentConfig";
|
|
58
|
+
import {
|
|
59
|
+
MinifiedStackFrame,
|
|
60
|
+
ResolveStackTraceResult,
|
|
61
|
+
} from "../../../Types/Telemetry/SourceMap";
|
|
11
62
|
import { afterEach, describe, expect, it, jest } from "@jest/globals";
|
|
12
63
|
|
|
13
64
|
/*
|
|
@@ -76,6 +127,25 @@ const invokeOnBeforeCreate: InvokeOnBeforeCreateFunction = async (
|
|
|
76
127
|
).onBeforeCreate(createBy);
|
|
77
128
|
};
|
|
78
129
|
|
|
130
|
+
type CaptureOnBeforeCreateRejectionFunction = (
|
|
131
|
+
createBy: CreateBy<TelemetrySourceMap>,
|
|
132
|
+
) => Promise<Error>;
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* The rejection itself, not just its type: the per-release message has to
|
|
136
|
+
* name the knob an operator would raise, so the message is asserted on.
|
|
137
|
+
*/
|
|
138
|
+
const captureOnBeforeCreateRejection: CaptureOnBeforeCreateRejectionFunction =
|
|
139
|
+
async (createBy: CreateBy<TelemetrySourceMap>): Promise<Error> => {
|
|
140
|
+
try {
|
|
141
|
+
await invokeOnBeforeCreate(createBy);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
return err as Error;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error("Expected onBeforeCreate to reject, but it resolved.");
|
|
147
|
+
};
|
|
148
|
+
|
|
79
149
|
type MakeRowFunction = (
|
|
80
150
|
bundlePath: string,
|
|
81
151
|
content?: string,
|
|
@@ -94,8 +164,144 @@ const makeRow: MakeRowFunction = (
|
|
|
94
164
|
return row;
|
|
95
165
|
};
|
|
96
166
|
|
|
167
|
+
type MakeListingRowFunction = (data: {
|
|
168
|
+
bundlePath: string;
|
|
169
|
+
sizeInBytes?: number | null | undefined;
|
|
170
|
+
createdAt?: Date | undefined;
|
|
171
|
+
}) => TelemetrySourceMap;
|
|
172
|
+
|
|
173
|
+
/*
|
|
174
|
+
* A row as the resolver's FIRST findBy returns it: no content, but the two
|
|
175
|
+
* columns the byte budget reads — the stored size, and the timestamp that
|
|
176
|
+
* breaks ties between maps that match a frame equally well.
|
|
177
|
+
*/
|
|
178
|
+
const makeListingRow: MakeListingRowFunction = (data: {
|
|
179
|
+
bundlePath: string;
|
|
180
|
+
sizeInBytes?: number | null | undefined;
|
|
181
|
+
createdAt?: Date | undefined;
|
|
182
|
+
}): TelemetrySourceMap => {
|
|
183
|
+
const row: TelemetrySourceMap = makeRow(data.bundlePath);
|
|
184
|
+
|
|
185
|
+
if (data.sizeInBytes !== undefined) {
|
|
186
|
+
/*
|
|
187
|
+
* Widened deliberately: the column is typed number, but a row written
|
|
188
|
+
* outside the create hook can hold null or a nonsense count, and the
|
|
189
|
+
* budget has to charge those something.
|
|
190
|
+
*/
|
|
191
|
+
(row as unknown as { sizeInBytes: number | null | undefined }).sizeInBytes =
|
|
192
|
+
data.sizeInBytes;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (data.createdAt) {
|
|
196
|
+
row.createdAt = data.createdAt;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return row;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
type MakeFrameFunction = (fileName: string) => MinifiedStackFrame;
|
|
203
|
+
|
|
204
|
+
/*
|
|
205
|
+
* A frame that the fixture map can actually resolve (line 1, column 46 maps
|
|
206
|
+
* to onSelect), so "did this map get loaded" and "did this frame resolve" are
|
|
207
|
+
* the same question.
|
|
208
|
+
*/
|
|
209
|
+
const makeFrame: MakeFrameFunction = (fileName: string): MinifiedStackFrame => {
|
|
210
|
+
return {
|
|
211
|
+
functionName: "e.onSelect",
|
|
212
|
+
fileName: fileName,
|
|
213
|
+
lineNumber: 1,
|
|
214
|
+
columnNumber: 46,
|
|
215
|
+
inApp: true,
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
type FindBySpy = ReturnType<typeof jest.spyOn>;
|
|
220
|
+
|
|
221
|
+
type ContentFetchArgsFunction = (findBySpy: FindBySpy) => {
|
|
222
|
+
query: Record<string, unknown>;
|
|
223
|
+
limit: number;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/** Arguments of the SECOND findBy — the content fetch. */
|
|
227
|
+
const contentFetchArgs: ContentFetchArgsFunction = (
|
|
228
|
+
findBySpy: FindBySpy,
|
|
229
|
+
): { query: Record<string, unknown>; limit: number } => {
|
|
230
|
+
return findBySpy.mock.calls[1]![0] as never;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
type RequestedContentIdsFunction = (findBySpy: FindBySpy) => Array<string>;
|
|
234
|
+
|
|
235
|
+
/*
|
|
236
|
+
* The ids the content fetch actually asked for. QueryHelper.any produces a
|
|
237
|
+
* TypeORM Raw operator whose parameters object holds the id list under a
|
|
238
|
+
* random key, so it is dug out rather than compared structurally.
|
|
239
|
+
*/
|
|
240
|
+
const requestedContentIds: RequestedContentIdsFunction = (
|
|
241
|
+
findBySpy: FindBySpy,
|
|
242
|
+
): Array<string> => {
|
|
243
|
+
const idOperator: { objectLiteralParameters?: Record<string, unknown> } =
|
|
244
|
+
contentFetchArgs(findBySpy).query["_id"] as never;
|
|
245
|
+
|
|
246
|
+
const idList: Array<unknown> = Object.values(
|
|
247
|
+
idOperator.objectLiteralParameters || {},
|
|
248
|
+
)[0] as Array<unknown>;
|
|
249
|
+
|
|
250
|
+
return idList.map((id: unknown) => {
|
|
251
|
+
return String(id);
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
type MockReleaseRowCountFunction = (count: number) => FindBySpy;
|
|
256
|
+
|
|
257
|
+
const mockReleaseRowCount: MockReleaseRowCountFunction = (
|
|
258
|
+
count: number,
|
|
259
|
+
): FindBySpy => {
|
|
260
|
+
return jest
|
|
261
|
+
.spyOn(TelemetrySourceMapService, "countBy")
|
|
262
|
+
.mockResolvedValue(new PositiveNumber(count) as never);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
type MockStoredBundlePathsFunction = (bundlePaths: Array<string>) => FindBySpy;
|
|
266
|
+
|
|
267
|
+
const mockStoredBundlePaths: MockStoredBundlePathsFunction = (
|
|
268
|
+
bundlePaths: Array<string>,
|
|
269
|
+
): FindBySpy => {
|
|
270
|
+
return jest
|
|
271
|
+
.spyOn(TelemetrySourceMapService, "getStoredBundlePathsForRelease")
|
|
272
|
+
.mockResolvedValue(bundlePaths as never);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
type MakeBundlePathsFunction = (count: number) => Array<string>;
|
|
276
|
+
|
|
277
|
+
const makeBundlePaths: MakeBundlePathsFunction = (
|
|
278
|
+
count: number,
|
|
279
|
+
): Array<string> => {
|
|
280
|
+
const bundlePaths: Array<string> = [];
|
|
281
|
+
|
|
282
|
+
for (let index: number = 0; index < count; index++) {
|
|
283
|
+
bundlePaths.push(`chunk-${index}.js`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return bundlePaths;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/*
|
|
290
|
+
* One frame, three stored bundles that match it at three different strengths.
|
|
291
|
+
* The score is the number of trailing path segments the frame and the bundle
|
|
292
|
+
* share, so "x/y/main.abc123.js" (3) beats "y/main.abc123.js" (2) beats
|
|
293
|
+
* "main.abc123.js" (1). That ordering is what the byte budget spends: when
|
|
294
|
+
* not everything fits, the weakest match is what goes.
|
|
295
|
+
*/
|
|
296
|
+
const SCORED_FRAME_FILE_NAME: string =
|
|
297
|
+
"https://cdn.example.com/x/y/main.abc123.js";
|
|
298
|
+
const STRONG_MATCH_PATH: string = "x/y/main.abc123.js";
|
|
299
|
+
const MEDIUM_MATCH_PATH: string = "y/main.abc123.js";
|
|
300
|
+
const WEAK_MATCH_PATH: string = "main.abc123.js";
|
|
301
|
+
|
|
97
302
|
afterEach(() => {
|
|
98
303
|
jest.restoreAllMocks();
|
|
304
|
+
mockResolveBudget.bytesPerResolve = null;
|
|
99
305
|
});
|
|
100
306
|
|
|
101
307
|
describe("retention configuration", () => {
|
|
@@ -107,10 +313,91 @@ describe("retention configuration", () => {
|
|
|
107
313
|
SOURCE_MAP_RETENTION_DAYS,
|
|
108
314
|
);
|
|
109
315
|
});
|
|
316
|
+
|
|
317
|
+
it("drives retention from the SOURCE_MAP_RETENTION_DAYS knob rather than a literal", () => {
|
|
318
|
+
/*
|
|
319
|
+
* The service must not re-declare the number. If the constant ever drifts
|
|
320
|
+
* from the config, an operator who sets SOURCE_MAP_RETENTION_DAYS gets a
|
|
321
|
+
* sweep interval they did not ask for — silently, since nothing else
|
|
322
|
+
* reports the effective value.
|
|
323
|
+
*/
|
|
324
|
+
expect(SOURCE_MAP_RETENTION_DAYS).toBe(SourceMapRetentionInDays);
|
|
325
|
+
expect(TelemetrySourceMapService.hardDeleteItemsOlderThanDays).toBe(
|
|
326
|
+
SourceMapRetentionInDays,
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("drives the per-release ceiling from the SOURCE_MAP_MAX_MAPS_PER_RELEASE knob", () => {
|
|
331
|
+
expect(MAX_SOURCE_MAPS_PER_RELEASE).toBe(SourceMapMaxMapsPerRelease);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
/*
|
|
335
|
+
* The two assertions above are only as strong as the environment they run
|
|
336
|
+
* in: with nothing configured, SourceMapRetentionInDays IS 90 and
|
|
337
|
+
* SourceMapMaxMapsPerRelease IS 1000, so a service that went back to
|
|
338
|
+
* hardcoding those two literals would satisfy both of them and every other
|
|
339
|
+
* case in this file.
|
|
340
|
+
*
|
|
341
|
+
* The only way to tell "reads the knob" from "happens to equal the knob's
|
|
342
|
+
* default" is to configure something the defaults are not, and re-enter the
|
|
343
|
+
* module — both constants are evaluated once, at import, and
|
|
344
|
+
* hardDeleteItemsOlderThanDays is stamped from one of them in the
|
|
345
|
+
* constructor, so a fresh registry is what it takes to observe them at all.
|
|
346
|
+
*/
|
|
347
|
+
it("picks up configured values rather than the defaults it happens to match", async () => {
|
|
348
|
+
const originalEnv: NodeJS.ProcessEnv = { ...process.env };
|
|
349
|
+
|
|
350
|
+
// Two numbers no default in this system is, so neither can coincide.
|
|
351
|
+
process.env["SOURCE_MAP_MAX_MAPS_PER_RELEASE"] = "4242";
|
|
352
|
+
process.env["SOURCE_MAP_RETENTION_DAYS"] = "17";
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
/*
|
|
356
|
+
* resetModules rather than isolateModules: the mock factory reaches for
|
|
357
|
+
* the real EnvironmentConfig with requireActual, and an isolated
|
|
358
|
+
* registry still serves that from the copy loaded when this file was
|
|
359
|
+
* imported — so the module would come back holding the defaults and the
|
|
360
|
+
* case would prove nothing. This is the same reload the config suite
|
|
361
|
+
* uses.
|
|
362
|
+
*/
|
|
363
|
+
jest.resetModules();
|
|
364
|
+
|
|
365
|
+
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
|
|
366
|
+
const reloaded: {
|
|
367
|
+
default: { hardDeleteItemsOlderThanDays: number };
|
|
368
|
+
MAX_SOURCE_MAPS_PER_RELEASE: number;
|
|
369
|
+
SOURCE_MAP_RETENTION_DAYS: number;
|
|
370
|
+
} = require("../../../Server/Services/TelemetrySourceMapService");
|
|
371
|
+
/* eslint-enable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
|
|
372
|
+
|
|
373
|
+
expect(reloaded.MAX_SOURCE_MAPS_PER_RELEASE).toBe(4242);
|
|
374
|
+
expect(reloaded.SOURCE_MAP_RETENTION_DAYS).toBe(17);
|
|
375
|
+
/*
|
|
376
|
+
* And the configured retention reaches the sweep the worker actually
|
|
377
|
+
* reads, not just the exported constant.
|
|
378
|
+
*/
|
|
379
|
+
expect(reloaded.default.hardDeleteItemsOlderThanDays).toBe(17);
|
|
380
|
+
} finally {
|
|
381
|
+
process.env = originalEnv;
|
|
382
|
+
/*
|
|
383
|
+
* Leave the registry as it was found. Every other case in this file
|
|
384
|
+
* works against the singleton captured by the imports at the top, which
|
|
385
|
+
* this does not disturb.
|
|
386
|
+
*/
|
|
387
|
+
jest.resetModules();
|
|
388
|
+
}
|
|
389
|
+
});
|
|
110
390
|
});
|
|
111
391
|
|
|
112
392
|
describe("onBeforeCreate", () => {
|
|
113
393
|
it("stamps sizeInBytes from the content", async () => {
|
|
394
|
+
/*
|
|
395
|
+
* Every create that survives validation now counts the release's rows, so
|
|
396
|
+
* the count is mocked here rather than reaching a database that no unit
|
|
397
|
+
* test connects to.
|
|
398
|
+
*/
|
|
399
|
+
mockReleaseRowCount(0);
|
|
400
|
+
|
|
114
401
|
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy();
|
|
115
402
|
|
|
116
403
|
await invokeOnBeforeCreate(createBy);
|
|
@@ -179,6 +466,232 @@ describe("onBeforeCreate", () => {
|
|
|
179
466
|
});
|
|
180
467
|
});
|
|
181
468
|
|
|
469
|
+
/*
|
|
470
|
+
* The per-release ceiling on the create path.
|
|
471
|
+
*
|
|
472
|
+
* The upload endpoint checks a whole batch up front, but TelemetrySourceMap
|
|
473
|
+
* is a full CRUD resource: POST /api/telemetry-source-map reaches create()
|
|
474
|
+
* without ever passing that check, so without this hook the configured
|
|
475
|
+
* ceiling was advisory on that path.
|
|
476
|
+
*/
|
|
477
|
+
describe("onBeforeCreate per-release ceiling", () => {
|
|
478
|
+
it("counts the release's rows scoped to the tenant, service and version, as root", async () => {
|
|
479
|
+
const countBySpy: FindBySpy = mockReleaseRowCount(0);
|
|
480
|
+
|
|
481
|
+
await invokeOnBeforeCreate(makeCreateBy());
|
|
482
|
+
|
|
483
|
+
const countArgs: {
|
|
484
|
+
query: Record<string, unknown>;
|
|
485
|
+
props: Record<string, unknown>;
|
|
486
|
+
skip: number;
|
|
487
|
+
limit: number;
|
|
488
|
+
} = countBySpy.mock.calls[0]![0] as never;
|
|
489
|
+
|
|
490
|
+
expect(countArgs.query["projectId"]).toBe(PROJECT_ID);
|
|
491
|
+
expect(countArgs.query["serviceId"]).toBe(SERVICE_ID);
|
|
492
|
+
expect(countArgs.query["serviceVersion"]).toBe("1.4.2");
|
|
493
|
+
expect(countArgs.skip).toBe(0);
|
|
494
|
+
expect(countArgs.limit).toBe(LIMIT_MAX);
|
|
495
|
+
// The hook runs inside create, before permissions are re-derived.
|
|
496
|
+
expect(countArgs.props["isRoot"]).toBe(true);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it("admits a create one map under the ceiling without paying for the distinct-bundle listing", async () => {
|
|
500
|
+
mockReleaseRowCount(MAX_SOURCE_MAPS_PER_RELEASE - 1);
|
|
501
|
+
const bundlePathsSpy: FindBySpy = mockStoredBundlePaths([]);
|
|
502
|
+
|
|
503
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy();
|
|
504
|
+
|
|
505
|
+
await invokeOnBeforeCreate(createBy);
|
|
506
|
+
|
|
507
|
+
/*
|
|
508
|
+
* Below the ceiling there is nothing to decide, whatever the rows hold —
|
|
509
|
+
* so the second, more expensive query must not run. This is what keeps a
|
|
510
|
+
* bulk upload paying one indexed COUNT per insert and no more.
|
|
511
|
+
*/
|
|
512
|
+
expect(bundlePathsSpy).not.toHaveBeenCalled();
|
|
513
|
+
expect(createBy.data.sizeInBytes).toBe(
|
|
514
|
+
Buffer.byteLength(FIXTURE_MAP, "utf8"),
|
|
515
|
+
);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("rejects a new bundle when the release already holds the ceiling in distinct bundles", async () => {
|
|
519
|
+
mockReleaseRowCount(MAX_SOURCE_MAPS_PER_RELEASE);
|
|
520
|
+
mockStoredBundlePaths(makeBundlePaths(MAX_SOURCE_MAPS_PER_RELEASE));
|
|
521
|
+
|
|
522
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy({
|
|
523
|
+
bundlePath: "brand-new-chunk.js",
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const error: Error = await captureOnBeforeCreateRejection(createBy);
|
|
527
|
+
|
|
528
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
529
|
+
expect(error.message).toContain(
|
|
530
|
+
`limit of ${MAX_SOURCE_MAPS_PER_RELEASE} per release`,
|
|
531
|
+
);
|
|
532
|
+
/*
|
|
533
|
+
* The message has to name the knob: the only fix a self-hosted operator
|
|
534
|
+
* has for a full release is raising it (or deleting maps), and nothing
|
|
535
|
+
* else in the response says what it is called.
|
|
536
|
+
*/
|
|
537
|
+
expect(error.message).toContain("SOURCE_MAP_MAX_MAPS_PER_RELEASE");
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it("rejects a new bundle when the release is already above the ceiling", async () => {
|
|
541
|
+
const overCeiling: number = MAX_SOURCE_MAPS_PER_RELEASE + 7;
|
|
542
|
+
|
|
543
|
+
mockReleaseRowCount(overCeiling);
|
|
544
|
+
mockStoredBundlePaths(makeBundlePaths(overCeiling));
|
|
545
|
+
|
|
546
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy({
|
|
547
|
+
bundlePath: "brand-new-chunk.js",
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
const error: Error = await captureOnBeforeCreateRejection(createBy);
|
|
551
|
+
|
|
552
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
553
|
+
expect(error.message).toContain("SOURCE_MAP_MAX_MAPS_PER_RELEASE");
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("refuses the full release before stamping sizeInBytes, so nothing half-processed is handed on", async () => {
|
|
557
|
+
mockReleaseRowCount(MAX_SOURCE_MAPS_PER_RELEASE);
|
|
558
|
+
mockStoredBundlePaths(makeBundlePaths(MAX_SOURCE_MAPS_PER_RELEASE));
|
|
559
|
+
|
|
560
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy({
|
|
561
|
+
bundlePath: "brand-new-chunk.js",
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
await captureOnBeforeCreateRejection(createBy);
|
|
565
|
+
|
|
566
|
+
expect(createBy.data.sizeInBytes).toBeUndefined();
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
it("still admits a re-upload of a bundle the release already holds when it is at the ceiling", async () => {
|
|
570
|
+
/*
|
|
571
|
+
* A CI re-run replaces every bundle of a release. Rejecting a path the
|
|
572
|
+
* release already contains would fail that re-run outright, even though
|
|
573
|
+
* it does not grow the release by a single bundle.
|
|
574
|
+
*/
|
|
575
|
+
mockReleaseRowCount(MAX_SOURCE_MAPS_PER_RELEASE);
|
|
576
|
+
mockStoredBundlePaths(makeBundlePaths(MAX_SOURCE_MAPS_PER_RELEASE));
|
|
577
|
+
|
|
578
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy({
|
|
579
|
+
bundlePath: "chunk-0.js",
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
await invokeOnBeforeCreate(createBy);
|
|
583
|
+
|
|
584
|
+
expect(createBy.data.sizeInBytes).toBe(
|
|
585
|
+
Buffer.byteLength(FIXTURE_MAP, "utf8"),
|
|
586
|
+
);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it("compares the incoming bundle against stored paths after normalization", async () => {
|
|
590
|
+
/*
|
|
591
|
+
* "/main.abc123.js" and "main.abc123.js" are the same bundle to the
|
|
592
|
+
* resolver, so they must be the same bundle to the gate too — otherwise a
|
|
593
|
+
* release at the ceiling rejects a re-upload purely over a leading slash.
|
|
594
|
+
*/
|
|
595
|
+
mockReleaseRowCount(MAX_SOURCE_MAPS_PER_RELEASE);
|
|
596
|
+
mockStoredBundlePaths([
|
|
597
|
+
"/main.abc123.js",
|
|
598
|
+
...makeBundlePaths(MAX_SOURCE_MAPS_PER_RELEASE - 1),
|
|
599
|
+
]);
|
|
600
|
+
|
|
601
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy({
|
|
602
|
+
bundlePath: "main.abc123.js",
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
await invokeOnBeforeCreate(createBy);
|
|
606
|
+
|
|
607
|
+
expect(createBy.data.sizeInBytes).toBe(
|
|
608
|
+
Buffer.byteLength(FIXTURE_MAP, "utf8"),
|
|
609
|
+
);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it("admits a new bundle when duplicate rows, not distinct bundles, reached the row count", async () => {
|
|
613
|
+
/*
|
|
614
|
+
* The model deliberately allows more than one row per bundle (a racing
|
|
615
|
+
* double upload must not fail CI). Rejecting on the ROW count alone would
|
|
616
|
+
* therefore refuse a legitimate new bundle purely because a duplicate row
|
|
617
|
+
* exists somewhere in the release — the ceiling counts distinct bundles.
|
|
618
|
+
*/
|
|
619
|
+
mockReleaseRowCount(MAX_SOURCE_MAPS_PER_RELEASE);
|
|
620
|
+
mockStoredBundlePaths([
|
|
621
|
+
...makeBundlePaths(MAX_SOURCE_MAPS_PER_RELEASE - 1),
|
|
622
|
+
"chunk-0.js",
|
|
623
|
+
]);
|
|
624
|
+
|
|
625
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy({
|
|
626
|
+
bundlePath: "brand-new-chunk.js",
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
await invokeOnBeforeCreate(createBy);
|
|
630
|
+
|
|
631
|
+
expect(createBy.data.sizeInBytes).toBe(
|
|
632
|
+
Buffer.byteLength(FIXTURE_MAP, "utf8"),
|
|
633
|
+
);
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it("falls back to the tenant on props when the row itself carries no projectId", async () => {
|
|
637
|
+
/*
|
|
638
|
+
* A tenant-scoped create leaves projectId to the tenant column, so the
|
|
639
|
+
* gate has to read props.tenantId — otherwise the whole check silently
|
|
640
|
+
* no-ops on exactly the path most creates take.
|
|
641
|
+
*/
|
|
642
|
+
const countBySpy: FindBySpy = mockReleaseRowCount(0);
|
|
643
|
+
|
|
644
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy();
|
|
645
|
+
delete createBy.data.projectId;
|
|
646
|
+
createBy.props.tenantId = PROJECT_ID;
|
|
647
|
+
|
|
648
|
+
await invokeOnBeforeCreate(createBy);
|
|
649
|
+
|
|
650
|
+
const countArgs: { query: Record<string, unknown> } = countBySpy.mock
|
|
651
|
+
.calls[0]![0] as never;
|
|
652
|
+
expect(countArgs.query["projectId"]).toBe(PROJECT_ID);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it("does not count when the create can be scoped to no project at all", async () => {
|
|
656
|
+
const countBySpy: FindBySpy = mockReleaseRowCount(0);
|
|
657
|
+
|
|
658
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy();
|
|
659
|
+
delete createBy.data.projectId;
|
|
660
|
+
|
|
661
|
+
await invokeOnBeforeCreate(createBy);
|
|
662
|
+
|
|
663
|
+
/*
|
|
664
|
+
* Not a silent pass: the tenant column check rejects this create on its
|
|
665
|
+
* own, with a better message than a count could give.
|
|
666
|
+
*/
|
|
667
|
+
expect(countBySpy).not.toHaveBeenCalled();
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
it("does not count when the create carries no serviceId", async () => {
|
|
671
|
+
const countBySpy: FindBySpy = mockReleaseRowCount(0);
|
|
672
|
+
|
|
673
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy();
|
|
674
|
+
delete createBy.data.serviceId;
|
|
675
|
+
|
|
676
|
+
await invokeOnBeforeCreate(createBy);
|
|
677
|
+
|
|
678
|
+
expect(countBySpy).not.toHaveBeenCalled();
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it("does not count when the release version is missing, because validation rejects first", async () => {
|
|
682
|
+
const countBySpy: FindBySpy = mockReleaseRowCount(0);
|
|
683
|
+
|
|
684
|
+
const createBy: CreateBy<TelemetrySourceMap> = makeCreateBy({
|
|
685
|
+
serviceVersion: "",
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
await expect(invokeOnBeforeCreate(createBy)).rejects.toThrow(
|
|
689
|
+
BadDataException,
|
|
690
|
+
);
|
|
691
|
+
expect(countBySpy).not.toHaveBeenCalled();
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
182
695
|
describe("replaceSourceMap", () => {
|
|
183
696
|
it("hard deletes existing rows for the same bundle before creating the new one", async () => {
|
|
184
697
|
const callOrder: Array<string> = [];
|
|
@@ -305,9 +818,23 @@ describe("resolveFramesForService", () => {
|
|
|
305
818
|
expect(listArgs.query["projectId"]).toBe(PROJECT_ID);
|
|
306
819
|
expect(listArgs.query["serviceId"]).toBe(SERVICE_ID);
|
|
307
820
|
expect(listArgs.query["serviceVersion"]).toBe("1.4.2");
|
|
308
|
-
|
|
821
|
+
/*
|
|
822
|
+
* LIMIT_MAX, deliberately NOT the per-release ceiling. The ceiling counts
|
|
823
|
+
* distinct bundle PATHS while a read limit counts ROWS; tying the read to
|
|
824
|
+
* it made duplicate rows push real bundles out of resolution (see the
|
|
825
|
+
* duplicate-row case below). Memory is bounded by the byte budget now.
|
|
826
|
+
*/
|
|
827
|
+
expect(listArgs.limit).toBe(LIMIT_MAX);
|
|
309
828
|
// The listing must not pull content.
|
|
310
829
|
expect(listArgs.select["content"]).toBeUndefined();
|
|
830
|
+
/*
|
|
831
|
+
* It must pull the size, though: the byte budget charges each candidate
|
|
832
|
+
* its stored size, and a listing that did not select the column would
|
|
833
|
+
* charge every row the per-map ceiling instead.
|
|
834
|
+
*/
|
|
835
|
+
expect(listArgs.select["sizeInBytes"]).toBe(true);
|
|
836
|
+
expect(listArgs.select["createdAt"]).toBe(true);
|
|
837
|
+
expect(listArgs.select["bundlePath"]).toBe(true);
|
|
311
838
|
|
|
312
839
|
// The content fetch stays tenant-scoped too.
|
|
313
840
|
const contentArgs: {
|
|
@@ -362,6 +889,37 @@ describe("resolveFramesForService", () => {
|
|
|
362
889
|
expect(String(idList[0])).toBe(matchedRow.id!.toString());
|
|
363
890
|
});
|
|
364
891
|
|
|
892
|
+
it("asks the content fetch for exactly as many rows as it selected", async () => {
|
|
893
|
+
/*
|
|
894
|
+
* The content limit used to be the per-release ceiling, which asked the
|
|
895
|
+
* database for room for a thousand rows to fetch two. It is the size of
|
|
896
|
+
* the selected set now, so the query cannot return more than was chosen.
|
|
897
|
+
*/
|
|
898
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
899
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
900
|
+
.mockResolvedValueOnce([
|
|
901
|
+
makeListingRow({ bundlePath: STRONG_MATCH_PATH, sizeInBytes: 100 }),
|
|
902
|
+
makeListingRow({ bundlePath: WEAK_MATCH_PATH, sizeInBytes: 100 }),
|
|
903
|
+
makeListingRow({ bundlePath: "admin.f00baa.js", sizeInBytes: 100 }),
|
|
904
|
+
] as never)
|
|
905
|
+
.mockResolvedValueOnce([
|
|
906
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
907
|
+
makeRow(WEAK_MATCH_PATH, FIXTURE_MAP),
|
|
908
|
+
] as never);
|
|
909
|
+
|
|
910
|
+
const result: ResolveStackTraceResult =
|
|
911
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
912
|
+
projectId: PROJECT_ID,
|
|
913
|
+
serviceId: SERVICE_ID,
|
|
914
|
+
serviceVersion: "1.4.2",
|
|
915
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
expect(result.sourceMapCount).toBe(3);
|
|
919
|
+
expect(requestedContentIds(findBySpy)).toHaveLength(2);
|
|
920
|
+
expect(contentFetchArgs(findBySpy).limit).toBe(2);
|
|
921
|
+
});
|
|
922
|
+
|
|
365
923
|
it("skips the content fetch entirely when no stored bundle matches any frame", async () => {
|
|
366
924
|
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
367
925
|
.spyOn(TelemetrySourceMapService, "findBy")
|
|
@@ -426,6 +984,87 @@ describe("resolveFramesForService", () => {
|
|
|
426
984
|
expect(result.frames[0]!.originalFunctionName).toBe("newest");
|
|
427
985
|
});
|
|
428
986
|
|
|
987
|
+
it("still resolves the last distinct bundle of a full release when a duplicate row exists", async () => {
|
|
988
|
+
/*
|
|
989
|
+
* THE REGRESSION THIS FIXES.
|
|
990
|
+
*
|
|
991
|
+
* A release holding the maximum number of distinct bundles, plus one
|
|
992
|
+
* duplicate row of an early bundle — which the model allows, because a
|
|
993
|
+
* racing double upload must not fail CI. That is one row MORE than the
|
|
994
|
+
* per-release ceiling.
|
|
995
|
+
*
|
|
996
|
+
* The listing used to read with limit = MAX_SOURCE_MAPS_PER_RELEASE, so
|
|
997
|
+
* the duplicate consumed the slot of the last distinct bundle: the map
|
|
998
|
+
* uploaded fine, showed in the dashboard, and then silently never
|
|
999
|
+
* resolved, with nothing anywhere saying why. The listing reads LIMIT_MAX
|
|
1000
|
+
* and dedupes afterwards now, so a release that fits the write gate is
|
|
1001
|
+
* fully reachable however many duplicate rows it accumulated.
|
|
1002
|
+
*
|
|
1003
|
+
* The findBy mock honours the limit it is handed, exactly as the database
|
|
1004
|
+
* would — otherwise this case would pass under the old code too.
|
|
1005
|
+
*/
|
|
1006
|
+
const listingRows: Array<TelemetrySourceMap> = [];
|
|
1007
|
+
|
|
1008
|
+
for (let index: number = 0; index < MAX_SOURCE_MAPS_PER_RELEASE; index++) {
|
|
1009
|
+
listingRows.push(
|
|
1010
|
+
makeListingRow({ bundlePath: `chunk-${index}.js`, sizeInBytes: 1024 }),
|
|
1011
|
+
);
|
|
1012
|
+
|
|
1013
|
+
if (index === 0) {
|
|
1014
|
+
// The duplicate row, newest-first next to the bundle it duplicates.
|
|
1015
|
+
listingRows.push(
|
|
1016
|
+
makeListingRow({ bundlePath: "chunk-0.js", sizeInBytes: 1024 }),
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const lastDistinctBundlePath: string = `chunk-${
|
|
1022
|
+
MAX_SOURCE_MAPS_PER_RELEASE - 1
|
|
1023
|
+
}.js`;
|
|
1024
|
+
const lastDistinctRow: TelemetrySourceMap =
|
|
1025
|
+
listingRows[listingRows.length - 1]!;
|
|
1026
|
+
|
|
1027
|
+
expect(listingRows).toHaveLength(MAX_SOURCE_MAPS_PER_RELEASE + 1);
|
|
1028
|
+
expect(lastDistinctRow.bundlePath).toBe(lastDistinctBundlePath);
|
|
1029
|
+
|
|
1030
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
1031
|
+
TelemetrySourceMapService,
|
|
1032
|
+
"findBy",
|
|
1033
|
+
);
|
|
1034
|
+
|
|
1035
|
+
findBySpy.mockImplementationOnce((async (listQuery: {
|
|
1036
|
+
limit: number;
|
|
1037
|
+
skip: number;
|
|
1038
|
+
}): Promise<Array<TelemetrySourceMap>> => {
|
|
1039
|
+
return listingRows.slice(
|
|
1040
|
+
listQuery.skip,
|
|
1041
|
+
listQuery.skip + listQuery.limit,
|
|
1042
|
+
);
|
|
1043
|
+
}) as never);
|
|
1044
|
+
|
|
1045
|
+
findBySpy.mockResolvedValueOnce([
|
|
1046
|
+
makeRow(lastDistinctBundlePath, FIXTURE_MAP),
|
|
1047
|
+
] as never);
|
|
1048
|
+
|
|
1049
|
+
const result: ResolveStackTraceResult =
|
|
1050
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1051
|
+
projectId: PROJECT_ID,
|
|
1052
|
+
serviceId: SERVICE_ID,
|
|
1053
|
+
serviceVersion: "1.4.2",
|
|
1054
|
+
frames: [makeFrame(lastDistinctBundlePath)],
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
// The duplicate collapsed away, leaving every distinct bundle listed.
|
|
1058
|
+
expect(result.sourceMapCount).toBe(MAX_SOURCE_MAPS_PER_RELEASE);
|
|
1059
|
+
expect(requestedContentIds(findBySpy)).toEqual([
|
|
1060
|
+
lastDistinctRow.id!.toString(),
|
|
1061
|
+
]);
|
|
1062
|
+
expect(result.resolvedCount).toBe(1);
|
|
1063
|
+
expect(result.frames[0]!.resolved).toBe(true);
|
|
1064
|
+
expect(result.frames[0]!.originalFunctionName).toBe("onSelect");
|
|
1065
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
1066
|
+
});
|
|
1067
|
+
|
|
429
1068
|
it("returns unresolved frames without querying when the release is empty", async () => {
|
|
430
1069
|
const findBySpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
431
1070
|
TelemetrySourceMapService,
|
|
@@ -453,6 +1092,32 @@ describe("resolveFramesForService", () => {
|
|
|
453
1092
|
expect(result.resolvedCount).toBe(0);
|
|
454
1093
|
expect(result.frames).toHaveLength(1);
|
|
455
1094
|
expect(result.frames[0]!.resolved).toBe(false);
|
|
1095
|
+
/*
|
|
1096
|
+
* Zero, never undefined: the field is required on the response, and the
|
|
1097
|
+
* dashboard reads it to decide whether to tell the user that symbols are
|
|
1098
|
+
* missing for a reason an operator can fix.
|
|
1099
|
+
*/
|
|
1100
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
it("returns nothing skipped when there are no frames to resolve", async () => {
|
|
1104
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
1105
|
+
TelemetrySourceMapService,
|
|
1106
|
+
"findBy",
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1109
|
+
const result: ResolveStackTraceResult =
|
|
1110
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1111
|
+
projectId: PROJECT_ID,
|
|
1112
|
+
serviceId: SERVICE_ID,
|
|
1113
|
+
serviceVersion: "1.4.2",
|
|
1114
|
+
frames: [],
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
1118
|
+
expect(result.frames).toHaveLength(0);
|
|
1119
|
+
expect(result.sourceMapCount).toBe(0);
|
|
1120
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
456
1121
|
});
|
|
457
1122
|
|
|
458
1123
|
it("returns unresolved frames when no maps exist for the release", async () => {
|
|
@@ -481,3 +1146,546 @@ describe("resolveFramesForService", () => {
|
|
|
481
1146
|
expect(result.frames[0]!.resolved).toBe(false);
|
|
482
1147
|
});
|
|
483
1148
|
});
|
|
1149
|
+
|
|
1150
|
+
/*
|
|
1151
|
+
* The byte budget: the bound that used to be implied by the per-release
|
|
1152
|
+
* count, and a far tighter one, because it measures the thing that actually
|
|
1153
|
+
* consumes memory. Resolution materialises whole maps (up to 50 MB each) and
|
|
1154
|
+
* the set it loads is chosen by a caller-supplied frames array.
|
|
1155
|
+
*/
|
|
1156
|
+
describe("resolveFramesForService byte budget", () => {
|
|
1157
|
+
it("loads every matching map when the whole matched set fits", async () => {
|
|
1158
|
+
const warnSpy: ReturnType<typeof jest.spyOn> = jest
|
|
1159
|
+
.spyOn(logger, "warn")
|
|
1160
|
+
.mockImplementation((): void => {});
|
|
1161
|
+
|
|
1162
|
+
mockResolveBudget.bytesPerResolve = 1024 * 1024;
|
|
1163
|
+
|
|
1164
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1165
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1166
|
+
sizeInBytes: 100,
|
|
1167
|
+
});
|
|
1168
|
+
const mediumRow: TelemetrySourceMap = makeListingRow({
|
|
1169
|
+
bundlePath: MEDIUM_MATCH_PATH,
|
|
1170
|
+
sizeInBytes: 100,
|
|
1171
|
+
});
|
|
1172
|
+
const weakRow: TelemetrySourceMap = makeListingRow({
|
|
1173
|
+
bundlePath: WEAK_MATCH_PATH,
|
|
1174
|
+
sizeInBytes: 100,
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1178
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1179
|
+
.mockResolvedValueOnce([strongRow, mediumRow, weakRow] as never)
|
|
1180
|
+
.mockResolvedValueOnce([
|
|
1181
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1182
|
+
] as never);
|
|
1183
|
+
|
|
1184
|
+
const result: ResolveStackTraceResult =
|
|
1185
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1186
|
+
projectId: PROJECT_ID,
|
|
1187
|
+
serviceId: SERVICE_ID,
|
|
1188
|
+
serviceVersion: "1.4.2",
|
|
1189
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
expect(requestedContentIds(findBySpy).sort()).toEqual(
|
|
1193
|
+
[
|
|
1194
|
+
strongRow.id!.toString(),
|
|
1195
|
+
mediumRow.id!.toString(),
|
|
1196
|
+
weakRow.id!.toString(),
|
|
1197
|
+
].sort(),
|
|
1198
|
+
);
|
|
1199
|
+
expect(contentFetchArgs(findBySpy).limit).toBe(3);
|
|
1200
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
1201
|
+
// Nothing was dropped, so there is nothing for an operator to hear about.
|
|
1202
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
it("skips the maps that do not fit and reports them on the response", async () => {
|
|
1206
|
+
const warnSpy: ReturnType<typeof jest.spyOn> = jest
|
|
1207
|
+
.spyOn(logger, "warn")
|
|
1208
|
+
.mockImplementation((): void => {});
|
|
1209
|
+
|
|
1210
|
+
// Room for two of the three 100-byte maps.
|
|
1211
|
+
mockResolveBudget.bytesPerResolve = 250;
|
|
1212
|
+
|
|
1213
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1214
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1215
|
+
sizeInBytes: 100,
|
|
1216
|
+
});
|
|
1217
|
+
const mediumRow: TelemetrySourceMap = makeListingRow({
|
|
1218
|
+
bundlePath: MEDIUM_MATCH_PATH,
|
|
1219
|
+
sizeInBytes: 100,
|
|
1220
|
+
});
|
|
1221
|
+
const weakRow: TelemetrySourceMap = makeListingRow({
|
|
1222
|
+
bundlePath: WEAK_MATCH_PATH,
|
|
1223
|
+
sizeInBytes: 100,
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1227
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1228
|
+
.mockResolvedValueOnce([strongRow, mediumRow, weakRow] as never)
|
|
1229
|
+
.mockResolvedValueOnce([
|
|
1230
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1231
|
+
] as never);
|
|
1232
|
+
|
|
1233
|
+
const result: ResolveStackTraceResult =
|
|
1234
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1235
|
+
projectId: PROJECT_ID,
|
|
1236
|
+
serviceId: SERVICE_ID,
|
|
1237
|
+
serviceVersion: "1.4.2",
|
|
1238
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
expect(requestedContentIds(findBySpy).sort()).toEqual(
|
|
1242
|
+
[strongRow.id!.toString(), mediumRow.id!.toString()].sort(),
|
|
1243
|
+
);
|
|
1244
|
+
expect(contentFetchArgs(findBySpy).limit).toBe(2);
|
|
1245
|
+
/*
|
|
1246
|
+
* The count is the whole point of the field: a half-resolved stack trace
|
|
1247
|
+
* looks identical to "the maps were never uploaded" without it.
|
|
1248
|
+
*/
|
|
1249
|
+
expect(result.sourceMapsSkippedForSize).toBe(1);
|
|
1250
|
+
|
|
1251
|
+
// One warning for the whole request, naming the knob that would fix it.
|
|
1252
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
1253
|
+
expect(String(warnSpy.mock.calls[0]![0])).toContain(
|
|
1254
|
+
"SOURCE_MAP_MAX_BYTES_PER_RESOLVE",
|
|
1255
|
+
);
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
it("keeps the best-matching map and drops the weakest when only one fits", async () => {
|
|
1259
|
+
/*
|
|
1260
|
+
* What gets dropped must be the weakest match, not an arbitrary row —
|
|
1261
|
+
* that is what makes a budget defensible at all. The frame shares three
|
|
1262
|
+
* trailing segments with the strong bundle and one with the weak one.
|
|
1263
|
+
*/
|
|
1264
|
+
mockResolveBudget.bytesPerResolve = 100;
|
|
1265
|
+
|
|
1266
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1267
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1268
|
+
sizeInBytes: 100,
|
|
1269
|
+
});
|
|
1270
|
+
const mediumRow: TelemetrySourceMap = makeListingRow({
|
|
1271
|
+
bundlePath: MEDIUM_MATCH_PATH,
|
|
1272
|
+
sizeInBytes: 100,
|
|
1273
|
+
});
|
|
1274
|
+
const weakRow: TelemetrySourceMap = makeListingRow({
|
|
1275
|
+
bundlePath: WEAK_MATCH_PATH,
|
|
1276
|
+
sizeInBytes: 100,
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1280
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1281
|
+
// Listed weakest-first, so only the score can explain the choice.
|
|
1282
|
+
.mockResolvedValueOnce([weakRow, mediumRow, strongRow] as never)
|
|
1283
|
+
.mockResolvedValueOnce([
|
|
1284
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1285
|
+
] as never);
|
|
1286
|
+
|
|
1287
|
+
const result: ResolveStackTraceResult =
|
|
1288
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1289
|
+
projectId: PROJECT_ID,
|
|
1290
|
+
serviceId: SERVICE_ID,
|
|
1291
|
+
serviceVersion: "1.4.2",
|
|
1292
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
expect(requestedContentIds(findBySpy)).toEqual([strongRow.id!.toString()]);
|
|
1296
|
+
expect(result.sourceMapsSkippedForSize).toBe(2);
|
|
1297
|
+
// The one map that was worth loading still resolved the frame.
|
|
1298
|
+
expect(result.resolvedCount).toBe(1);
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
it("still fits a smaller lower-scoring map after skipping a big one", async () => {
|
|
1302
|
+
/*
|
|
1303
|
+
* The budget loop continues rather than breaking. One oversized map in
|
|
1304
|
+
* the middle of the ranking must not cost the rest of the stack trace its
|
|
1305
|
+
* symbols — a break here would drop the small map that still fits.
|
|
1306
|
+
*/
|
|
1307
|
+
mockResolveBudget.bytesPerResolve = 1000;
|
|
1308
|
+
|
|
1309
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1310
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1311
|
+
sizeInBytes: 100,
|
|
1312
|
+
});
|
|
1313
|
+
const oversizedMediumRow: TelemetrySourceMap = makeListingRow({
|
|
1314
|
+
bundlePath: MEDIUM_MATCH_PATH,
|
|
1315
|
+
sizeInBytes: 5000,
|
|
1316
|
+
});
|
|
1317
|
+
const smallWeakRow: TelemetrySourceMap = makeListingRow({
|
|
1318
|
+
bundlePath: WEAK_MATCH_PATH,
|
|
1319
|
+
sizeInBytes: 50,
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1323
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1324
|
+
.mockResolvedValueOnce([
|
|
1325
|
+
strongRow,
|
|
1326
|
+
oversizedMediumRow,
|
|
1327
|
+
smallWeakRow,
|
|
1328
|
+
] as never)
|
|
1329
|
+
.mockResolvedValueOnce([
|
|
1330
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1331
|
+
makeRow(WEAK_MATCH_PATH, FIXTURE_MAP),
|
|
1332
|
+
] as never);
|
|
1333
|
+
|
|
1334
|
+
const result: ResolveStackTraceResult =
|
|
1335
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1336
|
+
projectId: PROJECT_ID,
|
|
1337
|
+
serviceId: SERVICE_ID,
|
|
1338
|
+
serviceVersion: "1.4.2",
|
|
1339
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
expect(requestedContentIds(findBySpy).sort()).toEqual(
|
|
1343
|
+
[strongRow.id!.toString(), smallWeakRow.id!.toString()].sort(),
|
|
1344
|
+
);
|
|
1345
|
+
expect(contentFetchArgs(findBySpy).limit).toBe(2);
|
|
1346
|
+
expect(result.sourceMapsSkippedForSize).toBe(1);
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
it("attempts the single best map even when it alone exceeds the budget", async () => {
|
|
1350
|
+
/*
|
|
1351
|
+
* A budget configured below the per-map ceiling should degrade to "one
|
|
1352
|
+
* map at a time", not to "nothing ever resolves". Peak memory is
|
|
1353
|
+
* therefore max(budget, one map) — bounded either way.
|
|
1354
|
+
*/
|
|
1355
|
+
mockResolveBudget.bytesPerResolve = 10;
|
|
1356
|
+
|
|
1357
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1358
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1359
|
+
sizeInBytes: 5000,
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1363
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1364
|
+
.mockResolvedValueOnce([strongRow] as never)
|
|
1365
|
+
.mockResolvedValueOnce([
|
|
1366
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1367
|
+
] as never);
|
|
1368
|
+
|
|
1369
|
+
const result: ResolveStackTraceResult =
|
|
1370
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1371
|
+
projectId: PROJECT_ID,
|
|
1372
|
+
serviceId: SERVICE_ID,
|
|
1373
|
+
serviceVersion: "1.4.2",
|
|
1374
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
expect(requestedContentIds(findBySpy)).toEqual([strongRow.id!.toString()]);
|
|
1378
|
+
// It was loaded, not skipped: nothing to report and nothing to warn about.
|
|
1379
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
1380
|
+
expect(result.resolvedCount).toBe(1);
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
it("skips everything after the over-budget best map, having already spent the budget", async () => {
|
|
1384
|
+
mockResolveBudget.bytesPerResolve = 10;
|
|
1385
|
+
|
|
1386
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1387
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1388
|
+
sizeInBytes: 5000,
|
|
1389
|
+
});
|
|
1390
|
+
const tinyWeakRow: TelemetrySourceMap = makeListingRow({
|
|
1391
|
+
bundlePath: WEAK_MATCH_PATH,
|
|
1392
|
+
sizeInBytes: 5,
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1396
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1397
|
+
.mockResolvedValueOnce([strongRow, tinyWeakRow] as never)
|
|
1398
|
+
.mockResolvedValueOnce([
|
|
1399
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1400
|
+
] as never);
|
|
1401
|
+
|
|
1402
|
+
const result: ResolveStackTraceResult =
|
|
1403
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1404
|
+
projectId: PROJECT_ID,
|
|
1405
|
+
serviceId: SERVICE_ID,
|
|
1406
|
+
serviceVersion: "1.4.2",
|
|
1407
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1410
|
+
expect(requestedContentIds(findBySpy)).toEqual([strongRow.id!.toString()]);
|
|
1411
|
+
expect(result.sourceMapsSkippedForSize).toBe(1);
|
|
1412
|
+
});
|
|
1413
|
+
|
|
1414
|
+
it("loads a map that lands exactly on the budget", async () => {
|
|
1415
|
+
// Boundary: the check is "over budget", not "at budget".
|
|
1416
|
+
mockResolveBudget.bytesPerResolve = 200;
|
|
1417
|
+
|
|
1418
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1419
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1420
|
+
sizeInBytes: 100,
|
|
1421
|
+
});
|
|
1422
|
+
const weakRow: TelemetrySourceMap = makeListingRow({
|
|
1423
|
+
bundlePath: WEAK_MATCH_PATH,
|
|
1424
|
+
sizeInBytes: 100,
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1428
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1429
|
+
.mockResolvedValueOnce([strongRow, weakRow] as never)
|
|
1430
|
+
.mockResolvedValueOnce([
|
|
1431
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1432
|
+
makeRow(WEAK_MATCH_PATH, FIXTURE_MAP),
|
|
1433
|
+
] as never);
|
|
1434
|
+
|
|
1435
|
+
const result: ResolveStackTraceResult =
|
|
1436
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1437
|
+
projectId: PROJECT_ID,
|
|
1438
|
+
serviceId: SERVICE_ID,
|
|
1439
|
+
serviceVersion: "1.4.2",
|
|
1440
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
expect(requestedContentIds(findBySpy)).toHaveLength(2);
|
|
1444
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
it("skips a map that lands one byte over the budget", async () => {
|
|
1448
|
+
mockResolveBudget.bytesPerResolve = 199;
|
|
1449
|
+
|
|
1450
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1451
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1452
|
+
sizeInBytes: 100,
|
|
1453
|
+
});
|
|
1454
|
+
const weakRow: TelemetrySourceMap = makeListingRow({
|
|
1455
|
+
bundlePath: WEAK_MATCH_PATH,
|
|
1456
|
+
sizeInBytes: 100,
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1460
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1461
|
+
.mockResolvedValueOnce([strongRow, weakRow] as never)
|
|
1462
|
+
.mockResolvedValueOnce([
|
|
1463
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1464
|
+
] as never);
|
|
1465
|
+
|
|
1466
|
+
const result: ResolveStackTraceResult =
|
|
1467
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1468
|
+
projectId: PROJECT_ID,
|
|
1469
|
+
serviceId: SERVICE_ID,
|
|
1470
|
+
serviceVersion: "1.4.2",
|
|
1471
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
expect(requestedContentIds(findBySpy)).toEqual([strongRow.id!.toString()]);
|
|
1475
|
+
expect(result.sourceMapsSkippedForSize).toBe(1);
|
|
1476
|
+
});
|
|
1477
|
+
|
|
1478
|
+
it("prefers the newer upload when two maps match equally well and only one fits", async () => {
|
|
1479
|
+
/*
|
|
1480
|
+
* Same match quality, so the tie-break decides — and it is the same
|
|
1481
|
+
* newest-wins rule the dedupe uses, rather than whatever order the rows
|
|
1482
|
+
* happened to arrive in.
|
|
1483
|
+
*/
|
|
1484
|
+
mockResolveBudget.bytesPerResolve = 100;
|
|
1485
|
+
|
|
1486
|
+
const olderRow: TelemetrySourceMap = makeListingRow({
|
|
1487
|
+
bundlePath: "a.abc123.js",
|
|
1488
|
+
sizeInBytes: 100,
|
|
1489
|
+
createdAt: new Date("2024-01-01T00:00:00.000Z"),
|
|
1490
|
+
});
|
|
1491
|
+
const newerRow: TelemetrySourceMap = makeListingRow({
|
|
1492
|
+
bundlePath: "b.abc123.js",
|
|
1493
|
+
sizeInBytes: 100,
|
|
1494
|
+
createdAt: new Date("2024-06-01T00:00:00.000Z"),
|
|
1495
|
+
});
|
|
1496
|
+
|
|
1497
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1498
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1499
|
+
.mockResolvedValueOnce([olderRow, newerRow] as never)
|
|
1500
|
+
.mockResolvedValueOnce([makeRow("b.abc123.js", FIXTURE_MAP)] as never);
|
|
1501
|
+
|
|
1502
|
+
const result: ResolveStackTraceResult =
|
|
1503
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1504
|
+
projectId: PROJECT_ID,
|
|
1505
|
+
serviceId: SERVICE_ID,
|
|
1506
|
+
serviceVersion: "1.4.2",
|
|
1507
|
+
frames: [makeFrame("a.abc123.js"), makeFrame("b.abc123.js")],
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
expect(requestedContentIds(findBySpy)).toEqual([newerRow.id!.toString()]);
|
|
1511
|
+
expect(result.sourceMapsSkippedForSize).toBe(1);
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
/*
|
|
1515
|
+
* Every shape of "no usable size" a row can carry. onBeforeCreate stamps
|
|
1516
|
+
* sizeInBytes on every insert, so these belong to rows written straight to
|
|
1517
|
+
* the database — and an unknown size must not become the way to slip past a
|
|
1518
|
+
* budget that exists to bound memory. Each is charged the per-map ceiling.
|
|
1519
|
+
*/
|
|
1520
|
+
const UNKNOWN_SIZE_CASES: Array<{
|
|
1521
|
+
label: string;
|
|
1522
|
+
sizeInBytes: number | null | undefined;
|
|
1523
|
+
}> = [
|
|
1524
|
+
{ label: "undefined", sizeInBytes: undefined },
|
|
1525
|
+
{ label: "null", sizeInBytes: null },
|
|
1526
|
+
{ label: "zero", sizeInBytes: 0 },
|
|
1527
|
+
{ label: "NaN", sizeInBytes: Number.NaN },
|
|
1528
|
+
{ label: "negative", sizeInBytes: -1 },
|
|
1529
|
+
];
|
|
1530
|
+
|
|
1531
|
+
for (const unknownSizeCase of UNKNOWN_SIZE_CASES) {
|
|
1532
|
+
it(`charges a row whose sizeInBytes is ${unknownSizeCase.label} the per-map ceiling, not zero`, async () => {
|
|
1533
|
+
mockResolveBudget.bytesPerResolve = 1000;
|
|
1534
|
+
|
|
1535
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1536
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1537
|
+
sizeInBytes: 100,
|
|
1538
|
+
});
|
|
1539
|
+
const unknownSizeRow: TelemetrySourceMap = makeListingRow({
|
|
1540
|
+
bundlePath: MEDIUM_MATCH_PATH,
|
|
1541
|
+
sizeInBytes: unknownSizeCase.sizeInBytes,
|
|
1542
|
+
});
|
|
1543
|
+
|
|
1544
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1545
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1546
|
+
.mockResolvedValueOnce([strongRow, unknownSizeRow] as never)
|
|
1547
|
+
.mockResolvedValueOnce([
|
|
1548
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1549
|
+
] as never);
|
|
1550
|
+
|
|
1551
|
+
const result: ResolveStackTraceResult =
|
|
1552
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1553
|
+
projectId: PROJECT_ID,
|
|
1554
|
+
serviceId: SERVICE_ID,
|
|
1555
|
+
serviceVersion: "1.4.2",
|
|
1556
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1557
|
+
});
|
|
1558
|
+
|
|
1559
|
+
/*
|
|
1560
|
+
* Charged 50 MB against a 1000-byte budget, so it cannot fit — where a
|
|
1561
|
+
* zero charge would have let it through unmetered.
|
|
1562
|
+
*/
|
|
1563
|
+
expect(requestedContentIds(findBySpy)).toEqual([
|
|
1564
|
+
strongRow.id!.toString(),
|
|
1565
|
+
]);
|
|
1566
|
+
expect(result.sourceMapsSkippedForSize).toBe(1);
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
it("still loads an unknown-size map when the budget can absorb the per-map ceiling", async () => {
|
|
1571
|
+
/*
|
|
1572
|
+
* The ceiling is a charge, not a ban: a budget with room for a
|
|
1573
|
+
* worst-case map still loads a row whose size was never stamped.
|
|
1574
|
+
*/
|
|
1575
|
+
mockResolveBudget.bytesPerResolve = MAX_SOURCE_MAP_SIZE_IN_BYTES + 100;
|
|
1576
|
+
|
|
1577
|
+
const strongRow: TelemetrySourceMap = makeListingRow({
|
|
1578
|
+
bundlePath: STRONG_MATCH_PATH,
|
|
1579
|
+
sizeInBytes: 100,
|
|
1580
|
+
});
|
|
1581
|
+
const unknownSizeRow: TelemetrySourceMap = makeListingRow({
|
|
1582
|
+
bundlePath: MEDIUM_MATCH_PATH,
|
|
1583
|
+
sizeInBytes: undefined,
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1587
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1588
|
+
.mockResolvedValueOnce([strongRow, unknownSizeRow] as never)
|
|
1589
|
+
.mockResolvedValueOnce([
|
|
1590
|
+
makeRow(STRONG_MATCH_PATH, FIXTURE_MAP),
|
|
1591
|
+
makeRow(MEDIUM_MATCH_PATH, FIXTURE_MAP),
|
|
1592
|
+
] as never);
|
|
1593
|
+
|
|
1594
|
+
const result: ResolveStackTraceResult =
|
|
1595
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1596
|
+
projectId: PROJECT_ID,
|
|
1597
|
+
serviceId: SERVICE_ID,
|
|
1598
|
+
serviceVersion: "1.4.2",
|
|
1599
|
+
frames: [makeFrame(SCORED_FRAME_FILE_NAME)],
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1602
|
+
expect(requestedContentIds(findBySpy).sort()).toEqual(
|
|
1603
|
+
[strongRow.id!.toString(), unknownSizeRow.id!.toString()].sort(),
|
|
1604
|
+
);
|
|
1605
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1608
|
+
|
|
1609
|
+
/*
|
|
1610
|
+
* Only the frames resolveFrames will actually attempt can pull a map in. The
|
|
1611
|
+
* frames array is parsed from client-supplied stack trace text, so matching
|
|
1612
|
+
* against all of it would let a caller drive an O(rows x frames) scan and
|
|
1613
|
+
* select maps that could never be used anyway.
|
|
1614
|
+
*/
|
|
1615
|
+
describe("resolveFramesForService frame budget", () => {
|
|
1616
|
+
it("loads a map for a bundle referenced by the last resolvable frame", async () => {
|
|
1617
|
+
const frames: Array<MinifiedStackFrame> = [];
|
|
1618
|
+
|
|
1619
|
+
for (let index: number = 0; index < MAX_FRAMES_TO_RESOLVE - 1; index++) {
|
|
1620
|
+
frames.push(makeFrame("unrelated-chunk.js"));
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// The 500th frame — the last one resolveFrames will attempt.
|
|
1624
|
+
frames.push(makeFrame("main.abc123.js"));
|
|
1625
|
+
expect(frames).toHaveLength(MAX_FRAMES_TO_RESOLVE);
|
|
1626
|
+
|
|
1627
|
+
const listRow: TelemetrySourceMap = makeListingRow({
|
|
1628
|
+
bundlePath: "main.abc123.js",
|
|
1629
|
+
sizeInBytes: 100,
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1632
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1633
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1634
|
+
.mockResolvedValueOnce([listRow] as never)
|
|
1635
|
+
.mockResolvedValueOnce([makeRow("main.abc123.js", FIXTURE_MAP)] as never);
|
|
1636
|
+
|
|
1637
|
+
const result: ResolveStackTraceResult =
|
|
1638
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1639
|
+
projectId: PROJECT_ID,
|
|
1640
|
+
serviceId: SERVICE_ID,
|
|
1641
|
+
serviceVersion: "1.4.2",
|
|
1642
|
+
frames: frames,
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1645
|
+
expect(requestedContentIds(findBySpy)).toEqual([listRow.id!.toString()]);
|
|
1646
|
+
expect(result.resolvedCount).toBe(1);
|
|
1647
|
+
expect(result.frames[MAX_FRAMES_TO_RESOLVE - 1]!.resolved).toBe(true);
|
|
1648
|
+
});
|
|
1649
|
+
|
|
1650
|
+
it("does not load a map for a bundle referenced only past the frame budget", async () => {
|
|
1651
|
+
const frames: Array<MinifiedStackFrame> = [];
|
|
1652
|
+
|
|
1653
|
+
for (let index: number = 0; index < MAX_FRAMES_TO_RESOLVE; index++) {
|
|
1654
|
+
frames.push(makeFrame("unrelated-chunk.js"));
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/*
|
|
1658
|
+
* The 501st frame. Its bundle IS stored — but the frame is past what
|
|
1659
|
+
* resolveFrames will attempt, so loading its map would spend the budget
|
|
1660
|
+
* on symbols that can never be handed back.
|
|
1661
|
+
*/
|
|
1662
|
+
frames.push(makeFrame("main.abc123.js"));
|
|
1663
|
+
|
|
1664
|
+
const findBySpy: ReturnType<typeof jest.spyOn> = jest
|
|
1665
|
+
.spyOn(TelemetrySourceMapService, "findBy")
|
|
1666
|
+
.mockResolvedValueOnce([
|
|
1667
|
+
makeListingRow({ bundlePath: "main.abc123.js", sizeInBytes: 100 }),
|
|
1668
|
+
] as never);
|
|
1669
|
+
|
|
1670
|
+
const result: ResolveStackTraceResult =
|
|
1671
|
+
await TelemetrySourceMapService.resolveFramesForService({
|
|
1672
|
+
projectId: PROJECT_ID,
|
|
1673
|
+
serviceId: SERVICE_ID,
|
|
1674
|
+
serviceVersion: "1.4.2",
|
|
1675
|
+
frames: frames,
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
// No content fetch at all: nothing matched a frame that counts.
|
|
1679
|
+
expect(findBySpy).toHaveBeenCalledTimes(1);
|
|
1680
|
+
expect(result.sourceMapCount).toBe(1);
|
|
1681
|
+
expect(result.resolvedCount).toBe(0);
|
|
1682
|
+
expect(result.sourceMapsSkippedForSize).toBe(0);
|
|
1683
|
+
|
|
1684
|
+
/*
|
|
1685
|
+
* One output frame per input frame regardless — the dashboard overlay
|
|
1686
|
+
* discards length-mismatched responses wholesale.
|
|
1687
|
+
*/
|
|
1688
|
+
expect(result.frames).toHaveLength(MAX_FRAMES_TO_RESOLVE + 1);
|
|
1689
|
+
expect(result.frames[MAX_FRAMES_TO_RESOLVE]!.resolved).toBe(false);
|
|
1690
|
+
});
|
|
1691
|
+
});
|