@oneuptime/common 12.0.3 → 12.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AIRun.ts +38 -1
- package/Models/DatabaseModels/AlertFeed.ts +30 -0
- package/Models/DatabaseModels/CephCluster.ts +14 -0
- package/Models/DatabaseModels/CodeRepository.ts +14 -6
- package/Models/DatabaseModels/Dashboard.ts +28 -0
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/Incident.ts +2 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentFeed.ts +30 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/LogSavedView.ts +33 -0
- package/Models/DatabaseModels/NetworkDevice.ts +14 -0
- package/Models/DatabaseModels/NetworkInterface.ts +18 -0
- package/Models/DatabaseModels/Project.ts +247 -11
- package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
- package/Models/DatabaseModels/Service.ts +30 -0
- package/Models/DatabaseModels/StatusPage.ts +28 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +75 -55
- package/Server/API/AIInvestigationAPI.ts +175 -17
- package/Server/API/DashboardAPI.ts +429 -22
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/StatusPageAPI.ts +38 -19
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +23 -3
- package/Server/Infrastructure/ClickhouseConfig.ts +15 -1
- package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
- package/Server/Infrastructure/GlobalCache.ts +37 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
- package/Server/Services/AIRunService.ts +78 -10
- package/Server/Services/AIService.ts +94 -22
- package/Server/Services/AlertFeedService.ts +5 -0
- package/Server/Services/AnalyticsDatabaseService.ts +101 -1
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +227 -3
- package/Server/Services/IncidentFeedService.ts +5 -0
- package/Server/Services/LlmLogService.ts +46 -2
- package/Server/Services/OpenTelemetryIngestService.ts +230 -26
- package/Server/Services/UserService.ts +184 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
- package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +223 -22
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +25 -24
- package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
- package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +25 -24
- package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
- package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
- package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
- package/Server/Utils/AI/SRE/README.md +1 -1
- package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
- package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
- package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
- package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
- package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
- package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +46 -0
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
- package/Server/Utils/PasswordHash.ts +330 -0
- package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
- package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1184 -0
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
- package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
- package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
- package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
- package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
- package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +594 -0
- package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
- package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
- package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
- package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
- package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
- package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
- package/Tests/Server/API/Helpers.ts +6 -1
- package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +128 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
- package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
- package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +396 -20
- package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
- package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +30 -1
- package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
- package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +880 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
- package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
- package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
- package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
- package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
- package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
- package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
- package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
- package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
- package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
- package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
- package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
- package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
- package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +313 -20
- package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
- package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
- package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
- package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +585 -0
- package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
- package/Tests/Types/Database/ColumnLength.test.ts +7 -1
- package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
- package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
- package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
- package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
- package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
- package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
- package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
- package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
- package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
- package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
- package/Tests/Types/Permission.test.ts +137 -0
- package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +158 -0
- package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
- package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
- package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
- package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
- package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
- package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
- package/Tests/UI/Utils/Project.test.ts +31 -0
- package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
- package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
- package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
- package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
- package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
- package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
- package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
- package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
- package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
- package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
- package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
- package/Types/AI/CodeFixTaskContext.ts +63 -3
- package/Types/AI/CodeFixTaskType.ts +3 -3
- package/Types/Database/ColumnLength.ts +9 -1
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/HashedString.ts +140 -4
- package/Types/Monitor/CriteriaFilter.ts +4 -0
- package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
- package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
- package/Types/Monitor/MonitorMetricType.ts +8 -0
- package/Types/Monitor/MonitorStep.ts +10 -1
- package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
- package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
- package/Types/Permission.ts +23 -175
- package/Types/Probe/ProbeMonitorResponse.ts +6 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/ErrorBoundary.tsx +138 -11
- package/UI/Components/Feed/FeedItem.tsx +9 -2
- package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
- package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
- package/UI/Components/MoreMenu/MoreMenu.tsx +285 -97
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +8 -5
- package/UI/Components/Navbar/NavBar.tsx +12 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
- package/UI/Utils/DownloadFile.ts +16 -8
- package/UI/Utils/ErrorSupportBundle.ts +805 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +19 -8
- package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
- package/UI/Utils/Project.ts +4 -0
- package/UI/Utils/StatusPage.ts +8 -2
- package/UI/Utils/UseDashboardGridDnd.ts +917 -0
- package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
- package/Utils/Dashboard/GridLayout.ts +523 -0
- package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
- package/Utils/Monitor/MonitorMetricType.ts +44 -2
- package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
- package/Utils/StatusPage/GroupTree.ts +157 -66
- package/Utils/StatusPage/ResourceUptime.ts +37 -2
- package/Utils/TeamMembersByProject.ts +237 -0
- package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
- package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +39 -1
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
- package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +2 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
- package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +257 -13
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
- package/build/dist/Models/DatabaseModels/Service.js +28 -1
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +32 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +49 -48
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +111 -11
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +312 -15
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +94 -167
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +29 -11
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +187 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +21 -3
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +12 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +58 -12
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +74 -21
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertFeedService.js +3 -0
- package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +86 -2
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +180 -3
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFeedService.js +3 -0
- package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
- package/build/dist/Server/Services/LlmLogService.js +22 -1
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +165 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +156 -28
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +21 -19
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +21 -19
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
- package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
- package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/PasswordHash.js +258 -0
- package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
- package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
- package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
- package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
- package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
- package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
- package/build/dist/Types/Database/ColumnLength.js +9 -1
- package/build/dist/Types/Database/ColumnLength.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
- package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
- package/build/dist/Types/HashedString.js +96 -5
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
- package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
- package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +6 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
- package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
- package/build/dist/Types/Permission.js +23 -155
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +67 -8
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
- package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +193 -65
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Utils/DownloadFile.js +15 -8
- package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
- package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
- package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
- package/build/dist/UI/Utils/Project.js +3 -0
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/UI/Utils/StatusPage.js +4 -4
- package/build/dist/UI/Utils/StatusPage.js.map +1 -1
- package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
- package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
- package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
- package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
- package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
- package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
- package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
- package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
- package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
- package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterEach,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
it,
|
|
7
|
+
jest,
|
|
8
|
+
} from "@jest/globals";
|
|
9
|
+
import ModelAPI, { ListResult } from "../../../UI/Utils/ModelAPI/ModelAPI";
|
|
10
|
+
import UserProjectsModelAPI, {
|
|
11
|
+
UserProjectRow,
|
|
12
|
+
} from "../../../UI/Utils/ModelAPI/UserProjectsModelAPI";
|
|
13
|
+
import API from "../../../UI/Utils/API/API";
|
|
14
|
+
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
15
|
+
import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
|
|
16
|
+
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
17
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
18
|
+
import Team from "../../../Models/DatabaseModels/Team";
|
|
19
|
+
import TeamMember from "../../../Models/DatabaseModels/TeamMember";
|
|
20
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
21
|
+
import Query from "../../../Types/BaseDatabase/Query";
|
|
22
|
+
import Select from "../../../Types/BaseDatabase/Select";
|
|
23
|
+
import { JSONArray, JSONObject } from "../../../Types/JSON";
|
|
24
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* This API is what makes the Admin Dashboard's User > Projects table page over
|
|
28
|
+
* PROJECTS instead of over memberships. The table itself is unchanged, so every
|
|
29
|
+
* behaviour below is invisible from the component: if the response is not
|
|
30
|
+
* mapped back into rows the Teams column renders empty, if `count` is not
|
|
31
|
+
* carried through the pager offers pages that render empty, and if deleting a
|
|
32
|
+
* row deletes one membership the row stays on screen and the delete looks like
|
|
33
|
+
* it silently failed.
|
|
34
|
+
*
|
|
35
|
+
* The payloads here are built the way the server builds them - the model
|
|
36
|
+
* serialized with BaseModel.toJSON, aggregates attached alongside - so these
|
|
37
|
+
* tests also pin the wire contract between the two halves rather than a
|
|
38
|
+
* hand-written idea of it.
|
|
39
|
+
*
|
|
40
|
+
* ModelAPI's statics are spied rather than the module being mocked, because
|
|
41
|
+
* UserProjectsModelAPI extends ModelAPI and delegates to it.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const USER_ID: ObjectID = new ObjectID("00000000-0000-4000-8000-000000000001");
|
|
45
|
+
const PROJECT_ONE_ID: string = "00000000-0000-4000-8000-0000000000a1";
|
|
46
|
+
const PROJECT_TWO_ID: string = "00000000-0000-4000-8000-0000000000a2";
|
|
47
|
+
|
|
48
|
+
type RowSpec = {
|
|
49
|
+
id?: string | undefined;
|
|
50
|
+
projectId: string;
|
|
51
|
+
projectName: string;
|
|
52
|
+
teams?: Array<{ id: string; name: string }> | undefined;
|
|
53
|
+
hasAcceptedInvitation?: boolean | undefined;
|
|
54
|
+
pendingTeamCount?: number | undefined;
|
|
55
|
+
teamMemberIds?: Array<string> | undefined;
|
|
56
|
+
joinedAt?: Date | undefined;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* The JSON one row of POST /user/:userId/projects is made of. Mirrors
|
|
61
|
+
* UserAPI.serializeUserProjectMembership.
|
|
62
|
+
*/
|
|
63
|
+
const buildRowJson: (spec: RowSpec) => JSONObject = (
|
|
64
|
+
spec: RowSpec,
|
|
65
|
+
): JSONObject => {
|
|
66
|
+
const teamMember: TeamMember = new TeamMember();
|
|
67
|
+
|
|
68
|
+
if (spec.id) {
|
|
69
|
+
teamMember._id = spec.id;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
teamMember.userId = USER_ID;
|
|
73
|
+
teamMember.projectId = new ObjectID(spec.projectId);
|
|
74
|
+
teamMember.hasAcceptedInvitation = spec.hasAcceptedInvitation ?? true;
|
|
75
|
+
|
|
76
|
+
const project: Project = new Project();
|
|
77
|
+
project._id = spec.projectId;
|
|
78
|
+
project.name = spec.projectName;
|
|
79
|
+
teamMember.project = project;
|
|
80
|
+
|
|
81
|
+
if (spec.joinedAt) {
|
|
82
|
+
teamMember.createdAt = spec.joinedAt;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const teams: Array<Team> = (spec.teams || []).map(
|
|
86
|
+
(team: { id: string; name: string }) => {
|
|
87
|
+
const teamModel: Team = new Team();
|
|
88
|
+
teamModel._id = team.id;
|
|
89
|
+
teamModel.name = team.name;
|
|
90
|
+
return teamModel;
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const json: JSONObject = BaseModel.toJSON(teamMember, TeamMember);
|
|
95
|
+
|
|
96
|
+
json["teams"] = BaseModel.toJSONArray(teams, Team) as JSONArray;
|
|
97
|
+
json["teamCount"] = teams.length;
|
|
98
|
+
json["pendingTeamCount"] = spec.pendingTeamCount ?? 0;
|
|
99
|
+
json["teamMemberIds"] = spec.teamMemberIds || (spec.id ? [spec.id] : []);
|
|
100
|
+
|
|
101
|
+
return json;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const buildListResponse: (data: {
|
|
105
|
+
rows: Array<JSONObject>;
|
|
106
|
+
count?: number | undefined;
|
|
107
|
+
skip?: number | undefined;
|
|
108
|
+
limit?: number | undefined;
|
|
109
|
+
}) => HTTPResponse<JSONArray> = (data: {
|
|
110
|
+
rows: Array<JSONObject>;
|
|
111
|
+
count?: number | undefined;
|
|
112
|
+
skip?: number | undefined;
|
|
113
|
+
limit?: number | undefined;
|
|
114
|
+
}): HTTPResponse<JSONArray> => {
|
|
115
|
+
return new HTTPResponse<JSONArray>(
|
|
116
|
+
200,
|
|
117
|
+
{
|
|
118
|
+
data: data.rows,
|
|
119
|
+
count: data.count ?? data.rows.length,
|
|
120
|
+
skip: data.skip ?? 0,
|
|
121
|
+
limit: data.limit ?? 10,
|
|
122
|
+
},
|
|
123
|
+
{},
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* Hand-rolled rather than jest.SpiedFunction<typeof API.fetch>: the statics are
|
|
129
|
+
* generic, and the installed jest typings cannot describe a spy on a generic
|
|
130
|
+
* method without collapsing it to one instantiation.
|
|
131
|
+
*/
|
|
132
|
+
interface Spy {
|
|
133
|
+
mockResolvedValue: (value: unknown) => Spy;
|
|
134
|
+
mockResolvedValueOnce: (value: unknown) => Spy;
|
|
135
|
+
mockImplementation: (fn: (...args: Array<unknown>) => unknown) => Spy;
|
|
136
|
+
mock: { calls: Array<Array<unknown>> };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let fetchSpy: Spy;
|
|
140
|
+
let postSpy: Spy;
|
|
141
|
+
let getItemSpy: Spy;
|
|
142
|
+
let getListSpy: Spy;
|
|
143
|
+
let deleteItemSpy: Spy;
|
|
144
|
+
|
|
145
|
+
type FetchArgs = {
|
|
146
|
+
method: unknown;
|
|
147
|
+
url: { toString: () => string };
|
|
148
|
+
data: Record<string, unknown>;
|
|
149
|
+
headers?: Record<string, string> | undefined;
|
|
150
|
+
params?: Record<string, string> | undefined;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const fetchCallArgs: (callIndex: number) => FetchArgs = (
|
|
154
|
+
callIndex: number,
|
|
155
|
+
): FetchArgs => {
|
|
156
|
+
return fetchSpy.mock.calls[callIndex]![0] as FetchArgs;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const postCallArgs: (callIndex: number) => {
|
|
160
|
+
url: { toString: () => string };
|
|
161
|
+
data: Record<string, unknown>;
|
|
162
|
+
headers?: Record<string, string> | undefined;
|
|
163
|
+
} = (
|
|
164
|
+
callIndex: number,
|
|
165
|
+
): {
|
|
166
|
+
url: { toString: () => string };
|
|
167
|
+
data: Record<string, unknown>;
|
|
168
|
+
headers?: Record<string, string> | undefined;
|
|
169
|
+
} => {
|
|
170
|
+
return postSpy.mock.calls[callIndex]![0] as {
|
|
171
|
+
url: { toString: () => string };
|
|
172
|
+
data: Record<string, unknown>;
|
|
173
|
+
headers?: Record<string, string> | undefined;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const listProjects: (data?: {
|
|
178
|
+
skip?: number | undefined;
|
|
179
|
+
limit?: number | undefined;
|
|
180
|
+
query?: Query<TeamMember> | undefined;
|
|
181
|
+
}) => Promise<ListResult<TeamMember>> = async (data?: {
|
|
182
|
+
skip?: number | undefined;
|
|
183
|
+
limit?: number | undefined;
|
|
184
|
+
query?: Query<TeamMember> | undefined;
|
|
185
|
+
}): Promise<ListResult<TeamMember>> => {
|
|
186
|
+
return UserProjectsModelAPI.getList<TeamMember>({
|
|
187
|
+
modelType: TeamMember,
|
|
188
|
+
query: data?.query || ({ userId: USER_ID } as Query<TeamMember>),
|
|
189
|
+
limit: data?.limit ?? 10,
|
|
190
|
+
skip: data?.skip ?? 0,
|
|
191
|
+
select: {} as Select<TeamMember>,
|
|
192
|
+
sort: {} as never,
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
beforeEach(() => {
|
|
197
|
+
fetchSpy = (jest.spyOn(API, "fetch") as unknown as Spy).mockImplementation(
|
|
198
|
+
async () => {
|
|
199
|
+
return buildListResponse({ rows: [] });
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
postSpy = (jest.spyOn(API, "post") as unknown as Spy).mockImplementation(
|
|
204
|
+
async () => {
|
|
205
|
+
return new HTTPResponse<JSONObject>(
|
|
206
|
+
200,
|
|
207
|
+
{ numberOfMembershipsDeleted: 2 },
|
|
208
|
+
{},
|
|
209
|
+
);
|
|
210
|
+
},
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
getItemSpy = jest.spyOn(ModelAPI, "getItem") as unknown as Spy;
|
|
214
|
+
getListSpy = jest.spyOn(ModelAPI, "getList") as unknown as Spy;
|
|
215
|
+
deleteItemSpy = (
|
|
216
|
+
jest.spyOn(ModelAPI, "deleteItem") as unknown as Spy
|
|
217
|
+
).mockImplementation(async () => {
|
|
218
|
+
return undefined;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
jest.spyOn(ModelAPI, "getCommonHeaders").mockReturnValue({});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
afterEach(() => {
|
|
225
|
+
jest.restoreAllMocks();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("UserProjectsModelAPI.getList", () => {
|
|
229
|
+
it("asks the projects-of-a-user endpoint for the user in the query", async () => {
|
|
230
|
+
await listProjects();
|
|
231
|
+
|
|
232
|
+
expect(fetchSpy.mock.calls).toHaveLength(1);
|
|
233
|
+
expect(fetchCallArgs(0).url.toString()).toContain(
|
|
234
|
+
`/user/${USER_ID.toString()}/projects`,
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("accepts the user id as a plain string too", async () => {
|
|
239
|
+
await listProjects({
|
|
240
|
+
query: { userId: USER_ID.toString() } as unknown as Query<TeamMember>,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
expect(fetchCallArgs(0).url.toString()).toContain(
|
|
244
|
+
`/user/${USER_ID.toString()}/projects`,
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("refuses to list without a user rather than reporting an empty list", async () => {
|
|
249
|
+
/*
|
|
250
|
+
* An empty list here renders as "this user is in no projects", which is a
|
|
251
|
+
* wrong answer rather than a missing one.
|
|
252
|
+
*/
|
|
253
|
+
await expect(
|
|
254
|
+
listProjects({ query: {} as Query<TeamMember> }),
|
|
255
|
+
).rejects.toThrow();
|
|
256
|
+
|
|
257
|
+
expect(fetchSpy.mock.calls).toHaveLength(0);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("asks the server for the page the table wants", async () => {
|
|
261
|
+
await listProjects({ skip: 20, limit: 10 });
|
|
262
|
+
|
|
263
|
+
expect(fetchCallArgs(0).params).toEqual({ skip: "20", limit: "10" });
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("sends the caller's headers, so the Admin Dashboard's tenant-less ones win", async () => {
|
|
267
|
+
/*
|
|
268
|
+
* A subclass overrides getCommonHeaders to send no tenant (see
|
|
269
|
+
* AdminUserProjectsModelAPI), which only works if the header set is read
|
|
270
|
+
* through `this` rather than off ModelAPI directly.
|
|
271
|
+
*/
|
|
272
|
+
jest
|
|
273
|
+
.spyOn(ModelAPI, "getCommonHeaders")
|
|
274
|
+
.mockReturnValue({ "x-test": "from-this" });
|
|
275
|
+
|
|
276
|
+
await listProjects();
|
|
277
|
+
|
|
278
|
+
expect(fetchCallArgs(0).headers).toEqual({ "x-test": "from-this" });
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("hydrates each row into a TeamMember carrying its project", async () => {
|
|
282
|
+
fetchSpy.mockResolvedValue(
|
|
283
|
+
buildListResponse({
|
|
284
|
+
rows: [
|
|
285
|
+
buildRowJson({
|
|
286
|
+
id: "m1",
|
|
287
|
+
projectId: PROJECT_ONE_ID,
|
|
288
|
+
projectName: "Acme Production",
|
|
289
|
+
teams: [{ id: "t1", name: "Owners" }],
|
|
290
|
+
}),
|
|
291
|
+
],
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
296
|
+
|
|
297
|
+
const row: TeamMember = result.data[0]!;
|
|
298
|
+
|
|
299
|
+
expect(row).toBeInstanceOf(TeamMember);
|
|
300
|
+
expect(row.project).toBeInstanceOf(Project);
|
|
301
|
+
expect(row.project?.name?.toString()).toBe("Acme Production");
|
|
302
|
+
expect(row.projectId?.toString()).toBe(PROJECT_ONE_ID);
|
|
303
|
+
expect(row._id?.toString()).toBe("m1");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("carries every team of the project onto the row", async () => {
|
|
307
|
+
fetchSpy.mockResolvedValue(
|
|
308
|
+
buildListResponse({
|
|
309
|
+
rows: [
|
|
310
|
+
buildRowJson({
|
|
311
|
+
id: "m1",
|
|
312
|
+
projectId: PROJECT_ONE_ID,
|
|
313
|
+
projectName: "Acme Production",
|
|
314
|
+
teams: [
|
|
315
|
+
{ id: "t1", name: "Engineering" },
|
|
316
|
+
{ id: "t2", name: "Owners" },
|
|
317
|
+
],
|
|
318
|
+
}),
|
|
319
|
+
],
|
|
320
|
+
}),
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
324
|
+
const row: UserProjectRow = result.data[0]! as UserProjectRow;
|
|
325
|
+
|
|
326
|
+
expect(row.teamsForProject).toHaveLength(2);
|
|
327
|
+
expect(row.teamsForProject[0]).toBeInstanceOf(Team);
|
|
328
|
+
expect(
|
|
329
|
+
row.teamsForProject.map((team: Team) => {
|
|
330
|
+
return team.name?.toString();
|
|
331
|
+
}),
|
|
332
|
+
).toEqual(["Engineering", "Owners"]);
|
|
333
|
+
expect(row.teamCountForProject).toBe(2);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("leaves a project with no teams as an empty list rather than undefined", async () => {
|
|
337
|
+
/*
|
|
338
|
+
* The Teams cell maps over this. Undefined would throw in the middle of
|
|
339
|
+
* rendering the table instead of showing "no teams".
|
|
340
|
+
*/
|
|
341
|
+
fetchSpy.mockResolvedValue(
|
|
342
|
+
buildListResponse({
|
|
343
|
+
rows: [
|
|
344
|
+
buildRowJson({
|
|
345
|
+
id: "m1",
|
|
346
|
+
projectId: PROJECT_ONE_ID,
|
|
347
|
+
projectName: "Acme Production",
|
|
348
|
+
}),
|
|
349
|
+
],
|
|
350
|
+
}),
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
354
|
+
const row: UserProjectRow = result.data[0]! as UserProjectRow;
|
|
355
|
+
|
|
356
|
+
expect(row.teamsForProject).toEqual([]);
|
|
357
|
+
expect(row.teamCountForProject).toBe(0);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("carries the pending-invitation count the Status cell reads", async () => {
|
|
361
|
+
fetchSpy.mockResolvedValue(
|
|
362
|
+
buildListResponse({
|
|
363
|
+
rows: [
|
|
364
|
+
buildRowJson({
|
|
365
|
+
id: "m1",
|
|
366
|
+
projectId: PROJECT_ONE_ID,
|
|
367
|
+
projectName: "Acme Production",
|
|
368
|
+
teams: [
|
|
369
|
+
{ id: "t1", name: "Owners" },
|
|
370
|
+
{ id: "t2", name: "Engineering" },
|
|
371
|
+
],
|
|
372
|
+
hasAcceptedInvitation: true,
|
|
373
|
+
pendingTeamCount: 1,
|
|
374
|
+
}),
|
|
375
|
+
],
|
|
376
|
+
}),
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
380
|
+
const row: UserProjectRow = result.data[0]! as UserProjectRow;
|
|
381
|
+
|
|
382
|
+
expect(row.hasAcceptedInvitation).toBe(true);
|
|
383
|
+
expect(row.pendingTeamCountForProject).toBe(1);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("reads a missing pending count as none rather than NaN", async () => {
|
|
387
|
+
const rowJson: JSONObject = buildRowJson({
|
|
388
|
+
id: "m1",
|
|
389
|
+
projectId: PROJECT_ONE_ID,
|
|
390
|
+
projectName: "Acme Production",
|
|
391
|
+
});
|
|
392
|
+
delete rowJson["pendingTeamCount"];
|
|
393
|
+
|
|
394
|
+
fetchSpy.mockResolvedValue(buildListResponse({ rows: [rowJson] }));
|
|
395
|
+
|
|
396
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
397
|
+
|
|
398
|
+
expect((result.data[0]! as UserProjectRow).pendingTeamCountForProject).toBe(
|
|
399
|
+
0,
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("carries the memberships the row was folded from", async () => {
|
|
404
|
+
fetchSpy.mockResolvedValue(
|
|
405
|
+
buildListResponse({
|
|
406
|
+
rows: [
|
|
407
|
+
buildRowJson({
|
|
408
|
+
id: "m1",
|
|
409
|
+
projectId: PROJECT_ONE_ID,
|
|
410
|
+
projectName: "Acme Production",
|
|
411
|
+
teamMemberIds: ["m1", "m2", "m3"],
|
|
412
|
+
}),
|
|
413
|
+
],
|
|
414
|
+
}),
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
418
|
+
|
|
419
|
+
expect((result.data[0]! as UserProjectRow).teamMemberIdsForProject).toEqual(
|
|
420
|
+
["m1", "m2", "m3"],
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("reports the server's count of projects, not the number of rows on the page", async () => {
|
|
425
|
+
fetchSpy.mockResolvedValue(
|
|
426
|
+
buildListResponse({
|
|
427
|
+
rows: [
|
|
428
|
+
buildRowJson({
|
|
429
|
+
id: "m1",
|
|
430
|
+
projectId: PROJECT_ONE_ID,
|
|
431
|
+
projectName: "Acme Production",
|
|
432
|
+
}),
|
|
433
|
+
],
|
|
434
|
+
count: 42,
|
|
435
|
+
skip: 10,
|
|
436
|
+
limit: 1,
|
|
437
|
+
}),
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
441
|
+
|
|
442
|
+
expect(result.count).toBe(42);
|
|
443
|
+
expect(result.skip).toBe(10);
|
|
444
|
+
expect(result.limit).toBe(1);
|
|
445
|
+
expect(result.data).toHaveLength(1);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it("keeps the order the server sorted the projects into", async () => {
|
|
449
|
+
fetchSpy.mockResolvedValue(
|
|
450
|
+
buildListResponse({
|
|
451
|
+
rows: [
|
|
452
|
+
buildRowJson({
|
|
453
|
+
id: "m1",
|
|
454
|
+
projectId: PROJECT_ONE_ID,
|
|
455
|
+
projectName: "Acme Production",
|
|
456
|
+
}),
|
|
457
|
+
buildRowJson({
|
|
458
|
+
id: "m2",
|
|
459
|
+
projectId: PROJECT_TWO_ID,
|
|
460
|
+
projectName: "Acme Staging",
|
|
461
|
+
}),
|
|
462
|
+
],
|
|
463
|
+
}),
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
467
|
+
|
|
468
|
+
expect(
|
|
469
|
+
result.data.map((row: TeamMember) => {
|
|
470
|
+
return row.project?.name?.toString();
|
|
471
|
+
}),
|
|
472
|
+
).toEqual(["Acme Production", "Acme Staging"]);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it("returns nothing for a user in no project", async () => {
|
|
476
|
+
const result: ListResult<TeamMember> = await listProjects();
|
|
477
|
+
|
|
478
|
+
expect(result.data).toEqual([]);
|
|
479
|
+
expect(result.count).toBe(0);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it("throws the server's error rather than an empty list", async () => {
|
|
483
|
+
fetchSpy.mockResolvedValue(
|
|
484
|
+
new HTTPErrorResponse(500, { message: "boom" }, {}),
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
await expect(listProjects()).rejects.toBeInstanceOf(HTTPErrorResponse);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("behaves like the plain API for a model it was not written for", async () => {
|
|
491
|
+
const projects: ListResult<Project> = {
|
|
492
|
+
data: [],
|
|
493
|
+
count: 0,
|
|
494
|
+
skip: 0,
|
|
495
|
+
limit: 10,
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
getListSpy.mockResolvedValue(projects as never);
|
|
499
|
+
|
|
500
|
+
const result: ListResult<Project> =
|
|
501
|
+
await UserProjectsModelAPI.getList<Project>({
|
|
502
|
+
modelType: Project,
|
|
503
|
+
query: {} as Query<Project>,
|
|
504
|
+
limit: 10,
|
|
505
|
+
skip: 0,
|
|
506
|
+
select: {} as Select<Project>,
|
|
507
|
+
sort: {} as never,
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
expect(result).toBe(projects);
|
|
511
|
+
expect(fetchSpy.mock.calls).toHaveLength(0);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
describe("UserProjectsModelAPI.deleteItem", () => {
|
|
516
|
+
const buildRowMembership: (data: {
|
|
517
|
+
id: string;
|
|
518
|
+
projectId?: string | undefined;
|
|
519
|
+
}) => TeamMember = (data: {
|
|
520
|
+
id: string;
|
|
521
|
+
projectId?: string | undefined;
|
|
522
|
+
}): TeamMember => {
|
|
523
|
+
const teamMember: TeamMember = new TeamMember();
|
|
524
|
+
teamMember._id = data.id;
|
|
525
|
+
teamMember.userId = USER_ID;
|
|
526
|
+
|
|
527
|
+
if (data.projectId) {
|
|
528
|
+
teamMember.projectId = new ObjectID(data.projectId);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return teamMember;
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
/*
|
|
535
|
+
* Routing this through one endpoint is about atomicity, not tidiness. The
|
|
536
|
+
* server refuses to remove the last accepted member of the Owners team, and
|
|
537
|
+
* it refuses per request - so a DELETE-per-membership loop destroys every
|
|
538
|
+
* other team first and only then reports the failure, leaving the person
|
|
539
|
+
* stripped of teams the admin was just told they had not lost.
|
|
540
|
+
*/
|
|
541
|
+
it("removes the user from the project in a single request", async () => {
|
|
542
|
+
getItemSpy.mockResolvedValue(
|
|
543
|
+
buildRowMembership({ id: "m1", projectId: PROJECT_ONE_ID }) as never,
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
await UserProjectsModelAPI.deleteItem<TeamMember>({
|
|
547
|
+
modelType: TeamMember,
|
|
548
|
+
id: new ObjectID("m1"),
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
expect(postSpy.mock.calls).toHaveLength(1);
|
|
552
|
+
expect(postCallArgs(0).url.toString()).toContain(
|
|
553
|
+
`/user/${USER_ID.toString()}/remove-from-project`,
|
|
554
|
+
);
|
|
555
|
+
expect(postCallArgs(0).data).toEqual({ projectId: PROJECT_ONE_ID });
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it("never issues a delete per membership", async () => {
|
|
559
|
+
getItemSpy.mockResolvedValue(
|
|
560
|
+
buildRowMembership({ id: "m1", projectId: PROJECT_ONE_ID }) as never,
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
await UserProjectsModelAPI.deleteItem<TeamMember>({
|
|
564
|
+
modelType: TeamMember,
|
|
565
|
+
id: new ObjectID("m1"),
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
expect(deleteItemSpy.mock.calls).toHaveLength(0);
|
|
569
|
+
// No sibling enumeration either - the server resolves the set itself.
|
|
570
|
+
expect(getListSpy.mock.calls).toHaveLength(0);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it("resolves the user and project from the row it was handed", async () => {
|
|
574
|
+
getItemSpy.mockResolvedValue(
|
|
575
|
+
buildRowMembership({ id: "m1", projectId: PROJECT_ONE_ID }) as never,
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
await UserProjectsModelAPI.deleteItem<TeamMember>({
|
|
579
|
+
modelType: TeamMember,
|
|
580
|
+
id: new ObjectID("m1"),
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
const getItemArgs: { id: ObjectID; select: Record<string, boolean> } =
|
|
584
|
+
getItemSpy.mock.calls[0]![0] as {
|
|
585
|
+
id: ObjectID;
|
|
586
|
+
select: Record<string, boolean>;
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
expect(getItemArgs.id.toString()).toBe("m1");
|
|
590
|
+
expect(getItemArgs.select["userId"]).toBe(true);
|
|
591
|
+
expect(getItemArgs.select["projectId"]).toBe(true);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it("throws the server's error rather than reporting a removal that did not happen", async () => {
|
|
595
|
+
getItemSpy.mockResolvedValue(
|
|
596
|
+
buildRowMembership({ id: "m1", projectId: PROJECT_ONE_ID }) as never,
|
|
597
|
+
);
|
|
598
|
+
postSpy.mockResolvedValue(
|
|
599
|
+
new HTTPErrorResponse(
|
|
600
|
+
400,
|
|
601
|
+
{
|
|
602
|
+
message:
|
|
603
|
+
"This team should have at least 1 member who has accepted the invitation.",
|
|
604
|
+
},
|
|
605
|
+
{},
|
|
606
|
+
),
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
await expect(
|
|
610
|
+
UserProjectsModelAPI.deleteItem<TeamMember>({
|
|
611
|
+
modelType: TeamMember,
|
|
612
|
+
id: new ObjectID("m1"),
|
|
613
|
+
}),
|
|
614
|
+
).rejects.toBeInstanceOf(HTTPErrorResponse);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it("falls back to the single delete when the row is already gone", async () => {
|
|
618
|
+
getItemSpy.mockResolvedValue(null as never);
|
|
619
|
+
|
|
620
|
+
await UserProjectsModelAPI.deleteItem<TeamMember>({
|
|
621
|
+
modelType: TeamMember,
|
|
622
|
+
id: new ObjectID("m1"),
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
expect(postSpy.mock.calls).toHaveLength(0);
|
|
626
|
+
expect(deleteItemSpy.mock.calls).toHaveLength(1);
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it("falls back to the single delete rather than guessing which project to empty", async () => {
|
|
630
|
+
// The membership came back without the project the row stands for.
|
|
631
|
+
getItemSpy.mockResolvedValue(buildRowMembership({ id: "m1" }) as never);
|
|
632
|
+
|
|
633
|
+
await UserProjectsModelAPI.deleteItem<TeamMember>({
|
|
634
|
+
modelType: TeamMember,
|
|
635
|
+
id: new ObjectID("m1"),
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
expect(postSpy.mock.calls).toHaveLength(0);
|
|
639
|
+
expect(deleteItemSpy.mock.calls).toHaveLength(1);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("behaves like the plain API for a model it was not written for", async () => {
|
|
643
|
+
await UserProjectsModelAPI.deleteItem<Project>({
|
|
644
|
+
modelType: Project,
|
|
645
|
+
id: new ObjectID(PROJECT_ONE_ID),
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
expect(deleteItemSpy.mock.calls).toHaveLength(1);
|
|
649
|
+
expect(postSpy.mock.calls).toHaveLength(0);
|
|
650
|
+
expect(getItemSpy.mock.calls).toHaveLength(0);
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
/*
|
|
655
|
+
* AdminUserProjectsModelAPI subclasses this to send no `tenantid` header - a
|
|
656
|
+
* master admin is not working inside a project. That only holds if every
|
|
657
|
+
* request this class makes reads its headers through `this`; a `ModelAPI.x(...)`
|
|
658
|
+
* anywhere in here silently reverts to the base headers, which is invisible
|
|
659
|
+
* until a request is rejected for carrying a tenant the caller is not in.
|
|
660
|
+
*/
|
|
661
|
+
describe("UserProjectsModelAPI in a subclass that overrides its headers", () => {
|
|
662
|
+
class SubclassedUserProjectsModelAPI extends UserProjectsModelAPI {
|
|
663
|
+
public static override getCommonHeaders(): Dictionary<string> {
|
|
664
|
+
return { "x-subclass": "yes" };
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
it("uses the subclass's headers to list projects", async () => {
|
|
669
|
+
await SubclassedUserProjectsModelAPI.getList<TeamMember>({
|
|
670
|
+
modelType: TeamMember,
|
|
671
|
+
query: { userId: USER_ID } as Query<TeamMember>,
|
|
672
|
+
limit: 10,
|
|
673
|
+
skip: 0,
|
|
674
|
+
select: {} as Select<TeamMember>,
|
|
675
|
+
sort: {} as never,
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
expect(fetchCallArgs(0).headers).toEqual({ "x-subclass": "yes" });
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it("uses the subclass's headers when it falls through to the plain list", async () => {
|
|
682
|
+
/*
|
|
683
|
+
* The fall-through is a real code path: a table's entity filter fetches
|
|
684
|
+
* its dropdown options through the table's own modelAPI.
|
|
685
|
+
*/
|
|
686
|
+
await SubclassedUserProjectsModelAPI.getList<Project>({
|
|
687
|
+
modelType: Project,
|
|
688
|
+
query: {} as Query<Project>,
|
|
689
|
+
limit: 10,
|
|
690
|
+
skip: 0,
|
|
691
|
+
select: {} as Select<Project>,
|
|
692
|
+
sort: {} as never,
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
expect(getListSpy.mock.calls).toHaveLength(1);
|
|
696
|
+
expect(fetchCallArgs(0).headers).toEqual({ "x-subclass": "yes" });
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("uses the subclass's headers to remove a user from a project", async () => {
|
|
700
|
+
const membership: TeamMember = new TeamMember();
|
|
701
|
+
membership._id = "m1";
|
|
702
|
+
membership.userId = USER_ID;
|
|
703
|
+
membership.projectId = new ObjectID(PROJECT_ONE_ID);
|
|
704
|
+
|
|
705
|
+
getItemSpy.mockResolvedValue(membership as never);
|
|
706
|
+
|
|
707
|
+
await SubclassedUserProjectsModelAPI.deleteItem<TeamMember>({
|
|
708
|
+
modelType: TeamMember,
|
|
709
|
+
id: new ObjectID("m1"),
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
expect(postCallArgs(0).headers).toEqual({ "x-subclass": "yes" });
|
|
713
|
+
});
|
|
714
|
+
});
|