@oneuptime/common 12.0.6 → 12.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AIInsight.ts +1 -1
- package/Models/DatabaseModels/DatabaseBaseModel/FileModel.ts +2 -3
- package/Models/DatabaseModels/DeletedProject.ts +595 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/Monitor.ts +111 -0
- package/Models/DatabaseModels/Project.ts +3 -3
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -1
- package/Server/API/FileAPI.ts +15 -2
- package/Server/API/MicrosoftTeamsAPI.ts +6 -1
- package/Server/API/ProjectAPI.ts +214 -0
- package/Server/API/StatusPageAPI.ts +39 -6
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.ts +50 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/AIAgentService.ts +20 -0
- package/Server/Services/DashboardDomainService.ts +73 -28
- package/Server/Services/DatabaseService.ts +1 -0
- package/Server/Services/DeletedProjectService.ts +138 -0
- package/Server/Services/FileService.ts +61 -0
- package/Server/Services/IncidentEpisodePublicNoteService.ts +18 -0
- package/Server/Services/IncidentPublicNoteService.ts +18 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +311 -0
- package/Server/Services/ProbeService.ts +20 -0
- package/Server/Services/ProjectService.ts +297 -1
- package/Server/Services/PushNotificationService.ts +71 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +18 -0
- package/Server/Services/StatusPageAnnouncementService.ts +53 -0
- package/Server/Services/StatusPageDomainService.ts +73 -28
- package/Server/Services/TeamMemberService.ts +117 -12
- package/Server/Services/TelemetryIngestionKeyService.ts +1 -0
- package/Server/Services/UserWebhookService.ts +34 -109
- package/Server/Services/WorkspaceNotificationRuleService.ts +69 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +37 -1
- package/Server/Types/Database/DeleteById.ts +2 -0
- package/Server/Types/Database/DeleteOneBy.ts +6 -0
- package/Server/Types/Domain.ts +41 -0
- package/Server/Types/Workflow/Components/API/Delete.ts +2 -1
- package/Server/Types/Workflow/Components/API/Get.ts +2 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +2 -1
- package/Server/Types/Workflow/Components/API/Post.ts +2 -1
- package/Server/Types/Workflow/Components/API/Put.ts +2 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +41 -2
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +25 -19
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +6 -5
- package/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.ts +32 -14
- package/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.ts +31 -13
- package/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.ts +29 -20
- package/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.ts +28 -21
- package/Server/Types/Workflow/Components/BaseModel/LogComponentError.ts +89 -0
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +208 -0
- package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +11 -0
- package/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.ts +41 -18
- package/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.ts +45 -19
- package/Server/Types/Workflow/Components/Discord/SendMessageToChannel.ts +16 -9
- package/Server/Types/Workflow/Components/IncomingWebhookUtils.ts +66 -0
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +15 -9
- package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +17 -0
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +171 -0
- package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +3 -3
- package/Server/Utils/AI/SRE/Insights/FixRouting.ts +3 -2
- package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +1 -1
- package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +6 -6
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +43 -5
- package/Server/Utils/DataSource/EgressGuard.ts +176 -16
- package/Server/Utils/DataSource/HttpFetch.ts +6 -45
- package/Server/Utils/Execute.ts +8 -0
- package/Server/Utils/InlineImageAccessTokenSync.ts +23 -0
- package/Server/Utils/LLM/LLMService.ts +57 -12
- package/Server/Utils/Monitor/MonitorAlert.ts +40 -0
- package/Server/Utils/Monitor/MonitorDependencySuppression.ts +192 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +40 -0
- package/Server/Utils/Monitor/MonitorResource.ts +36 -0
- package/Server/Utils/Response.ts +142 -6
- package/Server/Utils/SSRFProtection.ts +273 -60
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/VM/VMRunner.ts +88 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +555 -49
- package/Server/Utils/Workspace/Slack/Slack.ts +25 -2
- package/Server/Utils/Workspace/WorkspaceBase.ts +6 -0
- package/Tests/App/Dashboard/DangerZone.test.tsx +334 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +539 -0
- package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +678 -0
- package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +706 -0
- package/Tests/App/Dashboard/DashboardVariablesModal.test.tsx +508 -0
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +470 -0
- package/Tests/App/StatusPage/StatusPageDetailPagesNotFound.test.tsx +308 -0
- package/Tests/Models/DatabaseModels/DeletedProjectModel.test.ts +251 -0
- package/Tests/Models/DatabaseModels/SavedViewJSONColumnTuples.test.ts +115 -0
- package/Tests/Server/API/FileAPIAccessControl.test.ts +316 -0
- package/Tests/Server/API/ProjectAPI.test.ts +299 -1
- package/Tests/Server/API/ProjectAPIAdjustBalance.test.ts +459 -0
- package/Tests/Server/API/ProjectAPIAdjustBalanceBillingDisabled.test.ts +177 -0
- package/Tests/Server/Services/DatabaseServiceDeletionReason.test.ts +123 -0
- package/Tests/Server/Services/DeletedProjectService.test.ts +415 -0
- package/Tests/Server/Services/DeletedProjectServiceSelfHosted.test.ts +70 -0
- package/Tests/Server/Services/DomainSubdomainHooks.test.ts +94 -0
- package/Tests/Server/Services/FileIsPublicBooleanMigration.test.ts +244 -0
- package/Tests/Server/Services/FileServiceMimeType.test.ts +89 -0
- package/Tests/Server/Services/MonitorServiceDependencyValidation.test.ts +702 -0
- package/Tests/Server/Services/ProjectServiceAdjustBalance.test.ts +615 -0
- package/Tests/Server/Services/ProjectServiceDeleteAudit.test.ts +406 -0
- package/Tests/Server/Services/PublishTimeImageVisibility.test.ts +450 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +669 -0
- package/Tests/Server/Services/UserWebhookSSRF.test.ts +174 -0
- package/Tests/Server/Services/WebPushEndpointAllowlist.test.ts +133 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenRepoint.test.ts +618 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +26 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +215 -0
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +742 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ModelArguments.test.ts +371 -0
- package/Tests/Server/Utils/AI/CodeAgentCommandGuard.test.ts +204 -0
- package/Tests/Server/Utils/AI/CodeAgentWriteLimits.test.ts +213 -0
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +52 -5
- package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +3 -0
- package/Tests/Server/Utils/AI/LLMServiceEgressGuard.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +3 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +17 -5
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +14 -1
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +35 -0
- package/Tests/Server/Utils/AI/StubLLMEgressGuard.ts +25 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +62 -7
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +69 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +330 -0
- package/Tests/Server/Utils/GitHubCreatePullRequestWithToken.test.ts +393 -0
- package/Tests/Server/Utils/InlineImageAccessTokenSync.test.ts +202 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppression.test.ts +811 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +604 -0
- package/Tests/Server/Utils/ResponseSendFileResponse.test.ts +192 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +406 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +1169 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +2 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +1605 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTenantResolution.test.ts +1218 -0
- package/Tests/Types/AI/AIChatMessageStatus.test.ts +79 -0
- package/Tests/Types/AI/AIInsightStatus.test.ts +40 -0
- package/Tests/Types/API/HostnameAuthorityParsing.test.ts +157 -0
- package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +27 -0
- package/Tests/Types/DataSource/DataSourceLimits.test.ts +71 -0
- package/Tests/Types/DataSource/DataSourceType.test.ts +181 -0
- package/Tests/Types/DomainSubdomainValidation.test.ts +90 -0
- package/Tests/Types/JSONFunctions.test.ts +252 -1
- package/Tests/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.test.ts +61 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.test.ts +62 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.test.ts +68 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpVersion.test.ts +51 -0
- package/Tests/UI/Components/404.test.tsx +1 -1
- package/Tests/UI/Components/Alert.test.tsx +1 -1
- package/Tests/UI/Components/Badge.test.tsx +1 -1
- package/Tests/UI/Components/BasicForm.test.tsx +39 -10
- package/Tests/UI/Components/Button.test.tsx +1 -1
- package/Tests/UI/Components/Card.test.tsx +1 -1
- package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +1 -1
- package/Tests/UI/Components/ColorViewer.test.tsx +1 -1
- package/Tests/UI/Components/ComponentsModal.test.tsx +7 -1
- package/Tests/UI/Components/DictionaryOfStrings.test.tsx +1 -1
- package/Tests/UI/Components/Dropdown.test.tsx +1 -1
- package/Tests/UI/Components/FilePicker.test.tsx +1 -1
- package/Tests/UI/Components/HiddenText.test.tsx +1 -1
- package/Tests/UI/Components/Input.test.tsx +1 -1
- package/Tests/UI/Components/Loader.test.tsx +1 -1
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +1 -1
- package/Tests/UI/Components/MasterPage.test.tsx +1 -1
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +489 -0
- package/Tests/UI/Components/ModelDelete.test.tsx +214 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1 -1
- package/Tests/UI/Components/NavBar.test.tsx +1 -1
- package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +1 -1
- package/Tests/UI/Components/Pagination.test.tsx +1 -1
- package/Tests/UI/Components/Pill.test.tsx +1 -1
- package/Tests/UI/Components/Probe.test.tsx +1 -1
- package/Tests/UI/Components/ProgressBar.test.tsx +1 -1
- package/Tests/UI/Components/RadioButtons.test.tsx +1 -1
- package/Tests/UI/Components/SideMenuItem.test.tsx +1 -1
- package/Tests/UI/Components/SideOver.test.tsx +500 -6
- package/Tests/UI/Components/Tabs.test.tsx +1 -1
- package/Tests/UI/Components/TelemetrySearchBar.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +1 -1
- package/Tests/UI/Components/TextArea.test.tsx +1 -1
- package/Tests/UI/Components/Toast.test.tsx +1 -1
- package/Tests/UI/Components/Toggle.test.tsx +1 -1
- package/Tests/UI/Components/TopSection.test.tsx +1 -1
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +1 -1
- package/Tests/UI/Rum/ReplayScrubber.test.tsx +1 -1
- package/Tests/UI/Rum/ReplayStage.test.tsx +1 -1
- package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +1 -1
- package/Tests/UI/Utils/LocalStorageArrayRoundTrip.test.ts +72 -0
- package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +19 -2
- package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +44 -0
- package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +288 -5
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +42 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +27 -0
- package/Tests/Utils/LegacySerializedArray.test.ts +139 -0
- package/Tests/Utils/Monitor/MonitorDependencyRule.test.ts +190 -0
- package/Tests/Utils/Realtime.test.ts +78 -0
- package/Tests/Utils/Telemetry/SavedViewFilters.test.ts +246 -0
- package/Tests/__mocks__/i18next-browser-languagedetector.js +21 -0
- package/Types/AI/FixPullRequestCiStatus.ts +1 -1
- package/Types/AI/FixVerificationStatus.ts +2 -2
- package/Types/API/Hostname.ts +92 -4
- package/Types/API/URL.ts +43 -25
- package/Types/Billing/BalanceAdjustmentType.ts +14 -0
- package/Types/Billing/ProjectBalanceType.ts +13 -0
- package/Types/Domain.ts +26 -0
- package/Types/File/MimeType.ts +11 -0
- package/Types/JSONFunctions.ts +15 -0
- package/Types/Telemetry/TelemetrySavedViewState.ts +9 -1
- package/Types/Workflow/Components/BaseModel.ts +92 -28
- package/Typings/Index.d.ts +12 -0
- package/UI/Components/GanttChart/ChartContainer.tsx +4 -3
- package/UI/Components/Modal/ConfirmModal.tsx +10 -5
- package/UI/Components/Modal/Modal.tsx +27 -47
- package/UI/Components/ModelDelete/ModelDelete.tsx +23 -6
- package/UI/Components/SideOver/SideOver.tsx +195 -68
- package/UI/Utils/PageScrollLock.ts +66 -0
- package/Utils/Dashboard/HtmlWidgetDocument.ts +4 -2
- package/Utils/Dashboard/VariableInterpolation.ts +19 -1
- package/Utils/LegacySerializedArray.ts +60 -0
- package/Utils/Monitor/MonitorDependencyRule.ts +88 -0
- package/Utils/Telemetry/SavedViewFilters.ts +88 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js +1 -1
- package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js +2 -3
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DeletedProject.js +640 -0
- package/build/dist/Models/DatabaseModels/DeletedProject.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +106 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +3 -3
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/FileAPI.js +14 -2
- package/build/dist/Server/API/FileAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +6 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +157 -4
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +27 -3
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIAgentService.js +17 -0
- package/build/dist/Server/Services/AIAgentService.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +55 -18
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +1 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DeletedProjectService.js +124 -0
- package/build/dist/Server/Services/DeletedProjectService.js.map +1 -0
- package/build/dist/Server/Services/FileService.js +52 -0
- package/build/dist/Server/Services/FileService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +10 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +232 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +17 -0
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +228 -6
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +56 -0
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +10 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageAnnouncementService.js +54 -0
- package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +55 -18
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +97 -6
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js +1 -0
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +31 -87
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +65 -0
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +23 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +37 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +35 -2
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +15 -11
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +2 -3
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js +21 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js +21 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js +18 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js +18 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js +43 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +116 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +7 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js +24 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js +27 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js +14 -6
- package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js +42 -0
- package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +15 -6
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +10 -2
- package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +141 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +6 -6
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +39 -5
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +94 -10
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +1 -19
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
- package/build/dist/Server/Utils/Execute.js +2 -2
- package/build/dist/Server/Utils/Execute.js.map +1 -1
- package/build/dist/Server/Utils/InlineImageAccessTokenSync.js +13 -0
- package/build/dist/Server/Utils/InlineImageAccessTokenSync.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +43 -12
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +44 -24
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js +158 -0
- package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +50 -30
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +32 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +112 -5
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +219 -53
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +73 -1
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +445 -43
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +18 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
- package/build/dist/Types/AI/FixPullRequestCiStatus.js +1 -1
- package/build/dist/Types/AI/FixVerificationStatus.js +2 -2
- package/build/dist/Types/API/Hostname.js +73 -4
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/API/URL.js +42 -24
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/BalanceAdjustmentType.js +15 -0
- package/build/dist/Types/Billing/BalanceAdjustmentType.js.map +1 -0
- package/build/dist/Types/Billing/ProjectBalanceType.js +14 -0
- package/build/dist/Types/Billing/ProjectBalanceType.js.map +1 -0
- package/build/dist/Types/Domain.js +22 -0
- package/build/dist/Types/Domain.js.map +1 -1
- package/build/dist/Types/File/MimeType.js +11 -0
- package/build/dist/Types/File/MimeType.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +14 -0
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +83 -28
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/ChartContainer.js +4 -3
- package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +3 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +24 -34
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js +11 -6
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
- package/build/dist/UI/Components/SideOver/SideOver.js +105 -30
- package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
- package/build/dist/UI/Utils/PageScrollLock.js +52 -0
- package/build/dist/UI/Utils/PageScrollLock.js.map +1 -0
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +4 -2
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -1
- package/build/dist/Utils/Dashboard/VariableInterpolation.js +19 -1
- package/build/dist/Utils/Dashboard/VariableInterpolation.js.map +1 -1
- package/build/dist/Utils/LegacySerializedArray.js +51 -0
- package/build/dist/Utils/LegacySerializedArray.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorDependencyRule.js +47 -0
- package/build/dist/Utils/Monitor/MonitorDependencyRule.js.map +1 -0
- package/build/dist/Utils/Telemetry/SavedViewFilters.js +52 -0
- package/build/dist/Utils/Telemetry/SavedViewFilters.js.map +1 -0
- package/jest.config.json +3 -0
- package/package.json +1 -1
- package/tsconfig.json +3 -1
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
import TeamMember from "../../../Models/DatabaseModels/TeamMember";
|
|
2
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
3
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
4
|
+
import DatabaseConfig from "../../../Server/DatabaseConfig";
|
|
5
|
+
import MailService from "../../../Server/Services/MailService";
|
|
6
|
+
import ProjectSCIMService from "../../../Server/Services/ProjectSCIMService";
|
|
7
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
8
|
+
import TeamMemberService from "../../../Server/Services/TeamMemberService";
|
|
9
|
+
import UserNotificationRuleService from "../../../Server/Services/UserNotificationRuleService";
|
|
10
|
+
import UserNotificationSettingService from "../../../Server/Services/UserNotificationSettingService";
|
|
11
|
+
import UserService from "../../../Server/Services/UserService";
|
|
12
|
+
import ProductAnalytics from "../../../Server/Utils/ProductAnalytics";
|
|
13
|
+
import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
14
|
+
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
15
|
+
import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
|
|
16
|
+
import Hostname from "../../../Types/API/Hostname";
|
|
17
|
+
import Protocol from "../../../Types/API/Protocol";
|
|
18
|
+
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
19
|
+
import Email from "../../../Types/Email";
|
|
20
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
21
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
22
|
+
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
23
|
+
import EmailMessage from "../../../Types/Email/EmailMessage";
|
|
24
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* "Accept the invitation automatically" - the checkbox a master admin gets on
|
|
28
|
+
* the Admin Dashboard invite forms.
|
|
29
|
+
*
|
|
30
|
+
* The rule the service has to hold is narrow and load-bearing: a membership may
|
|
31
|
+
* only be created already accepted by an internal write or by a master admin.
|
|
32
|
+
* A project owner or admin who could do it would be able to pull somebody's
|
|
33
|
+
* account into their project - and into whatever that team's permissions grant
|
|
34
|
+
* - without that person ever agreeing. Acceptance is also what the `Owned`
|
|
35
|
+
* permission scope keys off (TeamMemberService.getTeamIdsForUser filters on
|
|
36
|
+
* hasAcceptedInvitation), so a forged acceptance is a permission grant.
|
|
37
|
+
*
|
|
38
|
+
* The other half is that an auto-accepted member has to be indistinguishable
|
|
39
|
+
* from one who accepted the invitation themselves: same timestamp column, same
|
|
40
|
+
* per-project notification defaults, and an email that does not tell them to go
|
|
41
|
+
* and accept something that is already accepted.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
45
|
+
"11111111-1111-4111-8111-111111111111",
|
|
46
|
+
);
|
|
47
|
+
const TEAM_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
|
|
48
|
+
const USER_ID: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
|
|
49
|
+
const MEMBER_ID: ObjectID = new ObjectID(
|
|
50
|
+
"44444444-4444-4444-8444-444444444444",
|
|
51
|
+
);
|
|
52
|
+
const INVITER_ID: ObjectID = new ObjectID(
|
|
53
|
+
"55555555-5555-4555-8555-555555555555",
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const MEMBER_EMAIL: Email = new Email("member@company.com");
|
|
57
|
+
|
|
58
|
+
// A date the server could never have produced for "now".
|
|
59
|
+
const FORGED_ACCEPTED_AT: Date = new Date("2001-01-01T00:00:00.000Z");
|
|
60
|
+
|
|
61
|
+
// Calls a protected/private hook without widening the service's public surface.
|
|
62
|
+
function callHook(
|
|
63
|
+
service: unknown,
|
|
64
|
+
name: string,
|
|
65
|
+
...args: Array<unknown>
|
|
66
|
+
): Promise<unknown> {
|
|
67
|
+
const hooks: Record<
|
|
68
|
+
string,
|
|
69
|
+
(...hookArgs: Array<unknown>) => Promise<unknown>
|
|
70
|
+
> = service as Record<
|
|
71
|
+
string,
|
|
72
|
+
(...hookArgs: Array<unknown>) => Promise<unknown>
|
|
73
|
+
>;
|
|
74
|
+
|
|
75
|
+
return hooks[name]!.apply(service, args);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* The props a master admin's Admin Dashboard request arrives with. */
|
|
79
|
+
function masterAdminProps(): DatabaseCommonInteractionProps {
|
|
80
|
+
return {
|
|
81
|
+
isMasterAdmin: true,
|
|
82
|
+
userId: INVITER_ID,
|
|
83
|
+
} as DatabaseCommonInteractionProps;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* The props a project owner inviting from project settings arrives with. */
|
|
87
|
+
function projectUserProps(): DatabaseCommonInteractionProps {
|
|
88
|
+
return {
|
|
89
|
+
userId: INVITER_ID,
|
|
90
|
+
tenantId: PROJECT_ID,
|
|
91
|
+
} as DatabaseCommonInteractionProps;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function rootProps(): DatabaseCommonInteractionProps {
|
|
95
|
+
return { isRoot: true } as DatabaseCommonInteractionProps;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function memberToCreate(data: {
|
|
99
|
+
hasAcceptedInvitation?: boolean | undefined;
|
|
100
|
+
invitationAcceptedAt?: Date | undefined;
|
|
101
|
+
}): TeamMember {
|
|
102
|
+
const member: TeamMember = new TeamMember();
|
|
103
|
+
member.projectId = PROJECT_ID;
|
|
104
|
+
member.teamId = TEAM_ID;
|
|
105
|
+
member.userId = USER_ID;
|
|
106
|
+
|
|
107
|
+
if (data.hasAcceptedInvitation !== undefined) {
|
|
108
|
+
member.hasAcceptedInvitation = data.hasAcceptedInvitation;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (data.invitationAcceptedAt !== undefined) {
|
|
112
|
+
member.invitationAcceptedAt = data.invitationAcceptedAt;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return member;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function createBy(data: {
|
|
119
|
+
props: DatabaseCommonInteractionProps;
|
|
120
|
+
hasAcceptedInvitation?: boolean | undefined;
|
|
121
|
+
invitationAcceptedAt?: Date | undefined;
|
|
122
|
+
email?: string | undefined;
|
|
123
|
+
}): CreateBy<TeamMember> {
|
|
124
|
+
const built: CreateBy<TeamMember> = {
|
|
125
|
+
data: memberToCreate({
|
|
126
|
+
hasAcceptedInvitation: data.hasAcceptedInvitation,
|
|
127
|
+
invitationAcceptedAt: data.invitationAcceptedAt,
|
|
128
|
+
}),
|
|
129
|
+
props: data.props,
|
|
130
|
+
} as CreateBy<TeamMember>;
|
|
131
|
+
|
|
132
|
+
if (data.email) {
|
|
133
|
+
built.miscDataProps = { email: data.email };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return built;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function createdMember(hasAcceptedInvitation: boolean): TeamMember {
|
|
140
|
+
const member: TeamMember = new TeamMember(MEMBER_ID);
|
|
141
|
+
member._id = MEMBER_ID.toString();
|
|
142
|
+
member.projectId = PROJECT_ID;
|
|
143
|
+
member.teamId = TEAM_ID;
|
|
144
|
+
member.userId = USER_ID;
|
|
145
|
+
member.hasAcceptedInvitation = hasAcceptedInvitation;
|
|
146
|
+
|
|
147
|
+
return member;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function verifiedUser(): User {
|
|
151
|
+
const user: User = new User(USER_ID);
|
|
152
|
+
user._id = USER_ID.toString();
|
|
153
|
+
user.email = MEMBER_EMAIL;
|
|
154
|
+
user.isEmailVerified = true;
|
|
155
|
+
|
|
156
|
+
return user;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let sendMailSpy: jest.SpyInstance;
|
|
160
|
+
let addSettingsSpy: jest.SpyInstance;
|
|
161
|
+
let addRuleSpy: jest.SpyInstance;
|
|
162
|
+
let findUserByIdSpy: jest.SpyInstance;
|
|
163
|
+
|
|
164
|
+
beforeEach(() => {
|
|
165
|
+
/*
|
|
166
|
+
* The SCIM push-groups guard runs for every non-root create, so it has to
|
|
167
|
+
* answer before anything else in onBeforeCreate is reachable.
|
|
168
|
+
*/
|
|
169
|
+
jest
|
|
170
|
+
.spyOn(ProjectSCIMService, "countBy")
|
|
171
|
+
.mockResolvedValue(new PositiveNumber(0));
|
|
172
|
+
|
|
173
|
+
// No existing membership - the duplicate-invite guard at the end of the hook.
|
|
174
|
+
jest.spyOn(TeamMemberService, "findOneBy").mockResolvedValue(null);
|
|
175
|
+
|
|
176
|
+
jest
|
|
177
|
+
.spyOn(ProjectService, "findOneById")
|
|
178
|
+
.mockResolvedValue({ name: "Acme Production" } as Project);
|
|
179
|
+
|
|
180
|
+
jest
|
|
181
|
+
.spyOn(DatabaseConfig, "getHost")
|
|
182
|
+
.mockResolvedValue(new Hostname("oneuptime.test"));
|
|
183
|
+
jest
|
|
184
|
+
.spyOn(DatabaseConfig, "getHttpProtocol")
|
|
185
|
+
.mockResolvedValue(Protocol.HTTPS);
|
|
186
|
+
|
|
187
|
+
sendMailSpy = jest
|
|
188
|
+
.spyOn(MailService, "sendMail")
|
|
189
|
+
.mockResolvedValue(undefined as never);
|
|
190
|
+
|
|
191
|
+
findUserByIdSpy = jest
|
|
192
|
+
.spyOn(UserService, "findOneById")
|
|
193
|
+
.mockResolvedValue(verifiedUser());
|
|
194
|
+
jest.spyOn(UserService, "findByEmail").mockResolvedValue(verifiedUser());
|
|
195
|
+
|
|
196
|
+
addSettingsSpy = jest
|
|
197
|
+
.spyOn(
|
|
198
|
+
UserNotificationSettingService,
|
|
199
|
+
"addDefaultNotificationSettingsForUser",
|
|
200
|
+
)
|
|
201
|
+
.mockResolvedValue(undefined as never);
|
|
202
|
+
addRuleSpy = jest
|
|
203
|
+
.spyOn(UserNotificationRuleService, "addDefaultNotificationRuleForUser")
|
|
204
|
+
.mockResolvedValue(undefined as never);
|
|
205
|
+
|
|
206
|
+
jest
|
|
207
|
+
.spyOn(TeamMemberService, "refreshTokens")
|
|
208
|
+
.mockResolvedValue(undefined as never);
|
|
209
|
+
jest
|
|
210
|
+
.spyOn(
|
|
211
|
+
TeamMemberService,
|
|
212
|
+
"updateSubscriptionSeatsByUniqueTeamMembersInProject",
|
|
213
|
+
)
|
|
214
|
+
.mockResolvedValue(undefined as never);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
afterEach(() => {
|
|
218
|
+
jest.restoreAllMocks();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("TeamMemberService.onBeforeCreate - who may create an accepted membership", () => {
|
|
222
|
+
test("a master admin's tick of the checkbox is honoured", async () => {
|
|
223
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
224
|
+
props: masterAdminProps(),
|
|
225
|
+
hasAcceptedInvitation: true,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
229
|
+
|
|
230
|
+
expect(create.data.hasAcceptedInvitation).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("an internal (isRoot) write may still create an accepted membership", async () => {
|
|
234
|
+
/*
|
|
235
|
+
* SSO, OIDC, SCIM and project creation all create memberships that are
|
|
236
|
+
* accepted on arrival. They must keep working.
|
|
237
|
+
*/
|
|
238
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
239
|
+
props: rootProps(),
|
|
240
|
+
hasAcceptedInvitation: true,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
244
|
+
|
|
245
|
+
expect(create.data.hasAcceptedInvitation).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("a project user asking for one is forced back to invited", async () => {
|
|
249
|
+
/*
|
|
250
|
+
* The whole point of the guard. A project owner inviting from project
|
|
251
|
+
* settings can set this field - the column's create access control allows
|
|
252
|
+
* it - so the service, not the permission layer, is what refuses.
|
|
253
|
+
*/
|
|
254
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
255
|
+
props: projectUserProps(),
|
|
256
|
+
hasAcceptedInvitation: true,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
260
|
+
|
|
261
|
+
expect(create.data.hasAcceptedInvitation).toBe(false);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("a project user's forged acceptance timestamp is dropped too", async () => {
|
|
265
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
266
|
+
props: projectUserProps(),
|
|
267
|
+
hasAcceptedInvitation: true,
|
|
268
|
+
invitationAcceptedAt: FORGED_ACCEPTED_AT,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
272
|
+
|
|
273
|
+
expect(create.data.hasAcceptedInvitation).toBe(false);
|
|
274
|
+
expect(create.data.invitationAcceptedAt).toBeUndefined();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("a master admin who leaves the checkbox alone still only invites", async () => {
|
|
278
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
279
|
+
props: masterAdminProps(),
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
283
|
+
|
|
284
|
+
expect(create.data.hasAcceptedInvitation).toBeFalsy();
|
|
285
|
+
expect(create.data.invitationAcceptedAt).toBeUndefined();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("a master admin who unticks it invites rather than accepts", async () => {
|
|
289
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
290
|
+
props: masterAdminProps(),
|
|
291
|
+
hasAcceptedInvitation: false,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
295
|
+
|
|
296
|
+
expect(create.data.hasAcceptedInvitation).toBe(false);
|
|
297
|
+
expect(create.data.invitationAcceptedAt).toBeUndefined();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("the duplicate-invite guard still runs for an auto-accepted invite", async () => {
|
|
301
|
+
/*
|
|
302
|
+
* Auto-accept must not become a way to create a second membership row for
|
|
303
|
+
* the same person on the same team.
|
|
304
|
+
*/
|
|
305
|
+
jest
|
|
306
|
+
.spyOn(TeamMemberService, "findOneBy")
|
|
307
|
+
.mockResolvedValue(createdMember(true));
|
|
308
|
+
|
|
309
|
+
await expect(
|
|
310
|
+
callHook(
|
|
311
|
+
TeamMemberService,
|
|
312
|
+
"onBeforeCreate",
|
|
313
|
+
createBy({
|
|
314
|
+
props: masterAdminProps(),
|
|
315
|
+
hasAcceptedInvitation: true,
|
|
316
|
+
}),
|
|
317
|
+
),
|
|
318
|
+
).rejects.toBeInstanceOf(BadDataException);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe("TeamMemberService.onBeforeCreate - the acceptance timestamp", () => {
|
|
323
|
+
test("an accepted membership is stamped, so no row says Member with no accepted-at date", async () => {
|
|
324
|
+
const before: Date = new Date();
|
|
325
|
+
|
|
326
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
327
|
+
props: masterAdminProps(),
|
|
328
|
+
hasAcceptedInvitation: true,
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
332
|
+
|
|
333
|
+
const after: Date = new Date();
|
|
334
|
+
const stamped: Date = create.data.invitationAcceptedAt!;
|
|
335
|
+
|
|
336
|
+
expect(stamped).toBeInstanceOf(Date);
|
|
337
|
+
expect(stamped.getTime()).toBeGreaterThanOrEqual(before.getTime());
|
|
338
|
+
expect(stamped.getTime()).toBeLessThanOrEqual(after.getTime());
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("the stamp is the server's clock, not a date supplied by the request", async () => {
|
|
342
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
343
|
+
props: masterAdminProps(),
|
|
344
|
+
hasAcceptedInvitation: true,
|
|
345
|
+
invitationAcceptedAt: FORGED_ACCEPTED_AT,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
349
|
+
|
|
350
|
+
expect(create.data.invitationAcceptedAt).not.toEqual(FORGED_ACCEPTED_AT);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("an accepted-at date on a membership that is only invited is removed", async () => {
|
|
354
|
+
/*
|
|
355
|
+
* The inverse disagreement: a pending row carrying an acceptance date would
|
|
356
|
+
* read as accepted to anything that looks at the date rather than the flag.
|
|
357
|
+
*/
|
|
358
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
359
|
+
props: masterAdminProps(),
|
|
360
|
+
hasAcceptedInvitation: false,
|
|
361
|
+
invitationAcceptedAt: FORGED_ACCEPTED_AT,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
365
|
+
|
|
366
|
+
expect(create.data.invitationAcceptedAt).toBeUndefined();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("an internal write's own timestamp is normalised to the server clock", async () => {
|
|
370
|
+
const create: CreateBy<TeamMember> = createBy({
|
|
371
|
+
props: rootProps(),
|
|
372
|
+
hasAcceptedInvitation: true,
|
|
373
|
+
invitationAcceptedAt: FORGED_ACCEPTED_AT,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
await callHook(TeamMemberService, "onBeforeCreate", create);
|
|
377
|
+
|
|
378
|
+
expect(create.data.hasAcceptedInvitation).toBe(true);
|
|
379
|
+
expect(create.data.invitationAcceptedAt).not.toEqual(FORGED_ACCEPTED_AT);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe("TeamMemberService.onBeforeCreate - the email the invitee receives", () => {
|
|
384
|
+
function sentMail(): EmailMessage {
|
|
385
|
+
expect(sendMailSpy).toHaveBeenCalledTimes(1);
|
|
386
|
+
|
|
387
|
+
return sendMailSpy.mock.calls[0]![0] as EmailMessage;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
test("an auto-accepted invite says the person has been added, not invited", async () => {
|
|
391
|
+
await callHook(
|
|
392
|
+
TeamMemberService,
|
|
393
|
+
"onBeforeCreate",
|
|
394
|
+
createBy({
|
|
395
|
+
props: masterAdminProps(),
|
|
396
|
+
hasAcceptedInvitation: true,
|
|
397
|
+
email: MEMBER_EMAIL.toString(),
|
|
398
|
+
}),
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const mail: EmailMessage = sentMail();
|
|
402
|
+
|
|
403
|
+
expect(mail.vars["isInvitationAccepted"]).toBe("true");
|
|
404
|
+
expect(mail.subject).toBe("You have been added to Acme Production");
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test("an ordinary invite keeps the invitation wording", async () => {
|
|
408
|
+
await callHook(
|
|
409
|
+
TeamMemberService,
|
|
410
|
+
"onBeforeCreate",
|
|
411
|
+
createBy({
|
|
412
|
+
props: masterAdminProps(),
|
|
413
|
+
email: MEMBER_EMAIL.toString(),
|
|
414
|
+
}),
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
const mail: EmailMessage = sentMail();
|
|
418
|
+
|
|
419
|
+
expect(mail.vars["isInvitationAccepted"]).toBe("false");
|
|
420
|
+
expect(mail.subject).toBe("You have been invited to Acme Production");
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test("a project user's rejected auto-accept does not leak into the email either", async () => {
|
|
424
|
+
/*
|
|
425
|
+
* The email is built after the flag has been forced back to false, so it
|
|
426
|
+
* has to describe what actually happened rather than what was asked for.
|
|
427
|
+
*/
|
|
428
|
+
await callHook(
|
|
429
|
+
TeamMemberService,
|
|
430
|
+
"onBeforeCreate",
|
|
431
|
+
createBy({
|
|
432
|
+
props: projectUserProps(),
|
|
433
|
+
hasAcceptedInvitation: true,
|
|
434
|
+
email: MEMBER_EMAIL.toString(),
|
|
435
|
+
}),
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
const mail: EmailMessage = sentMail();
|
|
439
|
+
|
|
440
|
+
expect(mail.vars["isInvitationAccepted"]).toBe("false");
|
|
441
|
+
expect(mail.subject).toBe("You have been invited to Acme Production");
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test("the template variable is always set, so neither branch of the template is skipped", async () => {
|
|
445
|
+
await callHook(
|
|
446
|
+
TeamMemberService,
|
|
447
|
+
"onBeforeCreate",
|
|
448
|
+
createBy({
|
|
449
|
+
props: rootProps(),
|
|
450
|
+
email: MEMBER_EMAIL.toString(),
|
|
451
|
+
}),
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
expect(Object.keys(sentMail().vars)).toContain("isInvitationAccepted");
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe("TeamMemberService.onCreateSuccess - an auto-accepted member is a real member", () => {
|
|
459
|
+
function onCreate(hasAcceptedInvitation: boolean): OnCreate<TeamMember> {
|
|
460
|
+
return {
|
|
461
|
+
createBy: createBy({
|
|
462
|
+
props: masterAdminProps(),
|
|
463
|
+
hasAcceptedInvitation: hasAcceptedInvitation,
|
|
464
|
+
}),
|
|
465
|
+
carryForward: null,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
test("the per-project notification defaults are added", async () => {
|
|
470
|
+
/*
|
|
471
|
+
* These are normally added by the accept-invitation update. A membership
|
|
472
|
+
* created already accepted never goes through that update, so without this
|
|
473
|
+
* the member is one who is never notified about anything.
|
|
474
|
+
*/
|
|
475
|
+
await callHook(
|
|
476
|
+
TeamMemberService,
|
|
477
|
+
"onCreateSuccess",
|
|
478
|
+
onCreate(true),
|
|
479
|
+
createdMember(true),
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
expect(addSettingsSpy).toHaveBeenCalledWith(USER_ID, PROJECT_ID);
|
|
483
|
+
expect(addRuleSpy).toHaveBeenCalledWith(PROJECT_ID, USER_ID, MEMBER_EMAIL);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test("a pending invitation does not get them - the person has not joined yet", async () => {
|
|
487
|
+
await callHook(
|
|
488
|
+
TeamMemberService,
|
|
489
|
+
"onCreateSuccess",
|
|
490
|
+
onCreate(false),
|
|
491
|
+
createdMember(false),
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
expect(addSettingsSpy).not.toHaveBeenCalled();
|
|
495
|
+
expect(addRuleSpy).not.toHaveBeenCalled();
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test("an unverified email is skipped, exactly as the accept path skips it", async () => {
|
|
499
|
+
const unverified: User = verifiedUser();
|
|
500
|
+
unverified.isEmailVerified = false;
|
|
501
|
+
findUserByIdSpy.mockResolvedValue(unverified);
|
|
502
|
+
|
|
503
|
+
await callHook(
|
|
504
|
+
TeamMemberService,
|
|
505
|
+
"onCreateSuccess",
|
|
506
|
+
onCreate(true),
|
|
507
|
+
createdMember(true),
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
expect(addSettingsSpy).not.toHaveBeenCalled();
|
|
511
|
+
expect(addRuleSpy).not.toHaveBeenCalled();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test("a user record that has gone missing does not throw", async () => {
|
|
515
|
+
findUserByIdSpy.mockResolvedValue(null);
|
|
516
|
+
|
|
517
|
+
await expect(
|
|
518
|
+
callHook(
|
|
519
|
+
TeamMemberService,
|
|
520
|
+
"onCreateSuccess",
|
|
521
|
+
onCreate(true),
|
|
522
|
+
createdMember(true),
|
|
523
|
+
),
|
|
524
|
+
).resolves.toBeDefined();
|
|
525
|
+
|
|
526
|
+
expect(addSettingsSpy).not.toHaveBeenCalled();
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
test("a failure adding the defaults does not fail the invite", async () => {
|
|
530
|
+
/*
|
|
531
|
+
* The membership row is already committed by the time this runs. Throwing
|
|
532
|
+
* would report "invite failed" for a member who exists.
|
|
533
|
+
*/
|
|
534
|
+
addSettingsSpy.mockRejectedValue(new Error("postgres is down") as never);
|
|
535
|
+
|
|
536
|
+
await expect(
|
|
537
|
+
callHook(
|
|
538
|
+
TeamMemberService,
|
|
539
|
+
"onCreateSuccess",
|
|
540
|
+
onCreate(true),
|
|
541
|
+
createdMember(true),
|
|
542
|
+
),
|
|
543
|
+
).resolves.toBeDefined();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
test("the created member is returned unchanged", async () => {
|
|
547
|
+
const created: TeamMember = createdMember(true);
|
|
548
|
+
|
|
549
|
+
const returned: unknown = await callHook(
|
|
550
|
+
TeamMemberService,
|
|
551
|
+
"onCreateSuccess",
|
|
552
|
+
onCreate(true),
|
|
553
|
+
created,
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
expect(returned).toBe(created);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
test("the activation event reports that the invitation was auto-accepted", async () => {
|
|
560
|
+
const captureSpy: jest.SpyInstance = jest
|
|
561
|
+
.spyOn(ProductAnalytics, "captureForUser")
|
|
562
|
+
.mockReturnValue(undefined);
|
|
563
|
+
|
|
564
|
+
await callHook(
|
|
565
|
+
TeamMemberService,
|
|
566
|
+
"onCreateSuccess",
|
|
567
|
+
onCreate(true),
|
|
568
|
+
createdMember(true),
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
572
|
+
expect.objectContaining({
|
|
573
|
+
event: "server/team_member_invited",
|
|
574
|
+
properties: expect.objectContaining({
|
|
575
|
+
has_accepted_invitation: true,
|
|
576
|
+
}),
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
describe("TeamMemberService.onUpdateSuccess - accepting an invitation by hand still works", () => {
|
|
583
|
+
function onUpdate(data: Record<string, unknown>): OnUpdate<TeamMember> {
|
|
584
|
+
return {
|
|
585
|
+
updateBy: {
|
|
586
|
+
query: { _id: MEMBER_ID.toString() },
|
|
587
|
+
data: data,
|
|
588
|
+
props: { isRoot: true },
|
|
589
|
+
limit: 1,
|
|
590
|
+
skip: 0,
|
|
591
|
+
} as unknown as UpdateBy<TeamMember>,
|
|
592
|
+
carryForward: null,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
beforeEach(() => {
|
|
597
|
+
const row: TeamMember = createdMember(true);
|
|
598
|
+
row.user = verifiedUser();
|
|
599
|
+
|
|
600
|
+
jest.spyOn(TeamMemberService, "findBy").mockResolvedValue([row]);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("accepting adds the same defaults the auto-accept path adds", async () => {
|
|
604
|
+
await callHook(
|
|
605
|
+
TeamMemberService,
|
|
606
|
+
"onUpdateSuccess",
|
|
607
|
+
onUpdate({ hasAcceptedInvitation: true }),
|
|
608
|
+
[MEMBER_ID],
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
expect(addSettingsSpy).toHaveBeenCalledWith(USER_ID, PROJECT_ID);
|
|
612
|
+
expect(addRuleSpy).toHaveBeenCalledWith(PROJECT_ID, USER_ID, MEMBER_EMAIL);
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
test("an update that is not an acceptance adds nothing", async () => {
|
|
616
|
+
await callHook(
|
|
617
|
+
TeamMemberService,
|
|
618
|
+
"onUpdateSuccess",
|
|
619
|
+
onUpdate({ teamId: TEAM_ID }),
|
|
620
|
+
[MEMBER_ID],
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
expect(addSettingsSpy).not.toHaveBeenCalled();
|
|
624
|
+
expect(addRuleSpy).not.toHaveBeenCalled();
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
test("the row's own user is used, so accepting costs no extra lookup", async () => {
|
|
628
|
+
await callHook(
|
|
629
|
+
TeamMemberService,
|
|
630
|
+
"onUpdateSuccess",
|
|
631
|
+
onUpdate({ hasAcceptedInvitation: true }),
|
|
632
|
+
[MEMBER_ID],
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
expect(findUserByIdSpy).not.toHaveBeenCalled();
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
test("an unverified email is still skipped on the accept path", async () => {
|
|
639
|
+
const row: TeamMember = createdMember(true);
|
|
640
|
+
const unverified: User = verifiedUser();
|
|
641
|
+
unverified.isEmailVerified = false;
|
|
642
|
+
row.user = unverified;
|
|
643
|
+
|
|
644
|
+
jest.spyOn(TeamMemberService, "findBy").mockResolvedValue([row]);
|
|
645
|
+
|
|
646
|
+
await callHook(
|
|
647
|
+
TeamMemberService,
|
|
648
|
+
"onUpdateSuccess",
|
|
649
|
+
onUpdate({ hasAcceptedInvitation: true }),
|
|
650
|
+
[MEMBER_ID],
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
expect(addSettingsSpy).not.toHaveBeenCalled();
|
|
654
|
+
expect(addRuleSpy).not.toHaveBeenCalled();
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
test("a failure adding the defaults does not fail the acceptance", async () => {
|
|
658
|
+
addRuleSpy.mockRejectedValue(new Error("postgres is down") as never);
|
|
659
|
+
|
|
660
|
+
await expect(
|
|
661
|
+
callHook(
|
|
662
|
+
TeamMemberService,
|
|
663
|
+
"onUpdateSuccess",
|
|
664
|
+
onUpdate({ hasAcceptedInvitation: true }),
|
|
665
|
+
[MEMBER_ID],
|
|
666
|
+
),
|
|
667
|
+
).resolves.toBeDefined();
|
|
668
|
+
});
|
|
669
|
+
});
|