@memberjunction/server 6.2.0-edge.6 → 6.2.0-edge.8
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/README.md +69 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
- package/dist/agentSessions/BridgeHostInstance.js +14 -4
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
- package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
- package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
- package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
- package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +31 -8
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +85 -41
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +14 -4
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +28 -4
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
- package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
- package/dist/agentSessions/conversationBrowsers.js +118 -0
- package/dist/agentSessions/conversationBrowsers.js.map +1 -0
- package/dist/agentSessions/sessionDeadline.d.ts +43 -0
- package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
- package/dist/agentSessions/sessionDeadline.js +68 -0
- package/dist/agentSessions/sessionDeadline.js.map +1 -0
- package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
- package/dist/auth/APIKeyScopeAuth.js +5 -4
- package/dist/auth/APIKeyScopeAuth.js.map +1 -1
- package/dist/auth/loginEmailResolution.d.ts +41 -0
- package/dist/auth/loginEmailResolution.d.ts.map +1 -0
- package/dist/auth/loginEmailResolution.js +47 -0
- package/dist/auth/loginEmailResolution.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +221 -9
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +1 -1
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +1 -1
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
- package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
- package/dist/auth/magicLink/redeemLanding.js +7 -4
- package/dist/auth/magicLink/redeemLanding.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +27 -3
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/magicLink/types.js +0 -4
- package/dist/auth/magicLink/types.js.map +1 -1
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +1 -0
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/backgroundSweepConfigUnits.d.ts +231 -0
- package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
- package/dist/backgroundSweepConfigUnits.js +180 -0
- package/dist/backgroundSweepConfigUnits.js.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +2 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -1
- package/dist/config.d.ts +906 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +41 -12
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +3 -1
- package/dist/coreRoutes.d.ts.map +1 -1
- package/dist/coreRoutes.js +6 -4
- package/dist/coreRoutes.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +1072 -5
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +10174 -4618
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +50 -5
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +329 -132
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +47 -7
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/globalCors.d.ts.map +1 -1
- package/dist/globalCors.js +1 -0
- package/dist/globalCors.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +201 -43
- package/dist/index.js.map +1 -1
- package/dist/integration/ConnectionPause.d.ts +121 -0
- package/dist/integration/ConnectionPause.d.ts.map +1 -0
- package/dist/integration/ConnectionPause.js +183 -0
- package/dist/integration/ConnectionPause.js.map +1 -0
- package/dist/integration/CustomColumnPromoter.d.ts +72 -6
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +87 -46
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/integration/EntityMapLifecycle.d.ts +78 -4
- package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
- package/dist/integration/EntityMapLifecycle.js +199 -5
- package/dist/integration/EntityMapLifecycle.js.map +1 -1
- package/dist/integration/InactiveRowWarnings.d.ts +31 -0
- package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
- package/dist/integration/InactiveRowWarnings.js +29 -0
- package/dist/integration/InactiveRowWarnings.js.map +1 -1
- package/dist/integration/ObjectNamesByID.d.ts +28 -0
- package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
- package/dist/integration/ObjectNamesByID.js +87 -0
- package/dist/integration/ObjectNamesByID.js.map +1 -0
- package/dist/integration/PostRestartIntrospect.d.ts +26 -0
- package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
- package/dist/integration/PostRestartIntrospect.js +27 -0
- package/dist/integration/PostRestartIntrospect.js.map +1 -0
- package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
- package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
- package/dist/integration/RSUPostRestartProgress.js +323 -0
- package/dist/integration/RSUPostRestartProgress.js.map +1 -0
- package/dist/integration/RSUProgressBridge.d.ts +41 -2
- package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
- package/dist/integration/RSUProgressBridge.js +84 -1
- package/dist/integration/RSUProgressBridge.js.map +1 -1
- package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
- package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
- package/dist/integration/RunArtifactAuthorization.js +45 -0
- package/dist/integration/RunArtifactAuthorization.js.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
- package/dist/integration/SchemaRefreshLaunch.js +3 -2
- package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
- package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
- package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
- package/dist/integration/ScopedFieldsForBuild.js +91 -0
- package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
- package/dist/integration/TargetConfigGuard.d.ts +40 -0
- package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
- package/dist/integration/TargetConfigGuard.js +40 -0
- package/dist/integration/TargetConfigGuard.js.map +1 -0
- package/dist/logging/StartupLogger.d.ts +50 -0
- package/dist/logging/StartupLogger.d.ts.map +1 -1
- package/dist/logging/StartupLogger.js +15 -22
- package/dist/logging/StartupLogger.js.map +1 -1
- package/dist/middleware/BaseServerMiddleware.d.ts +21 -3
- package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
- package/dist/middleware/BaseServerMiddleware.js +4 -2
- package/dist/middleware/BaseServerMiddleware.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
- package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +22 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +2 -0
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.js +1 -0
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +4 -7
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/requestOrigin.d.ts +22 -0
- package/dist/requestOrigin.d.ts.map +1 -0
- package/dist/requestOrigin.js +60 -0
- package/dist/requestOrigin.js.map +1 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +10 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +18 -2
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +6 -2
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.js +61 -16
- package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
- package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +34 -13
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/InfoResolver.js +1 -1
- package/dist/resolvers/InfoResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +13 -2
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +38 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +181 -88
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +185 -67
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
- package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +19 -18
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +3 -1
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +7 -8
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/TagGovernanceResolver.d.ts +12 -0
- package/dist/resolvers/TagGovernanceResolver.d.ts.map +1 -1
- package/dist/resolvers/TagGovernanceResolver.js +84 -0
- package/dist/resolvers/TagGovernanceResolver.js.map +1 -1
- package/dist/resolvers/TelemetryResolver.d.ts +13 -8
- package/dist/resolvers/TelemetryResolver.d.ts.map +1 -1
- package/dist/resolvers/TelemetryResolver.js +40 -9
- package/dist/resolvers/TelemetryResolver.js.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +2 -0
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +34 -4
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
- package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
- package/dist/rest/MediaAccessKeys.js +6 -2
- package/dist/rest/MediaAccessKeys.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +11 -5
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.js +2 -4
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +2 -1
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +3 -1
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +11 -2
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +2 -26
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +38 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/services/WorkQueueHostService.d.ts +2 -1
- package/dist/services/WorkQueueHostService.d.ts.map +1 -1
- package/dist/services/WorkQueueHostService.js +19 -4
- package/dist/services/WorkQueueHostService.js.map +1 -1
- package/dist/services/workQueueConfig.d.ts +2 -2
- package/dist/services/workQueueConfig.js +5 -5
- package/dist/services/workQueueConfig.js.map +1 -1
- package/dist/sharedCache.js +1 -1
- package/dist/sharedCache.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +55 -2
- package/dist/util.js.map +1 -1
- package/package.json +107 -100
- package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
- package/src/__tests__/ActiveOperations.test.ts +68 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
- package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
- package/src/__tests__/BatchApplyParity.test.ts +109 -0
- package/src/__tests__/BridgeHostInstance.test.ts +42 -0
- package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
- package/src/__tests__/ConnectionPause.test.ts +192 -0
- package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
- package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
- package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
- package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
- package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
- package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
- package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
- package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
- package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
- package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
- package/src/__tests__/MagicLinkKeys.test.ts +102 -0
- package/src/__tests__/MediaAccessKeys.test.ts +57 -10
- package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
- package/src/__tests__/ObjectNamesByID.test.ts +176 -0
- package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
- package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
- package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
- package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
- package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
- package/src/__tests__/RSUProgressBridge.test.ts +188 -5
- package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
- package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
- package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
- package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
- package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +327 -26
- package/src/__tests__/ResolverBase.diagnosticSenderRegistrations.test.ts +26 -0
- package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
- package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
- package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
- package/src/__tests__/RunAIAgentResolver.streaming.test.ts +28 -4
- package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
- package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
- package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
- package/src/__tests__/SessionJanitor.test.ts +195 -4
- package/src/__tests__/SessionManager.test.ts +69 -0
- package/src/__tests__/TagGovernanceAndAutotagResolvers.test.ts +261 -0
- package/src/__tests__/TagGovernanceResolver.test.ts +60 -2
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
- package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
- package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
- package/src/__tests__/WorkQueueHostService.test.ts +25 -7
- package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
- package/src/__tests__/contextRequestOrigin.test.ts +105 -0
- package/src/__tests__/conversationBrowsers.test.ts +62 -0
- package/src/__tests__/globalCors.test.ts +12 -0
- package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
- package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
- package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
- package/src/__tests__/requestOrigin.test.ts +104 -0
- package/src/__tests__/review-capture-valid-token-log.test.ts +98 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +5 -5
- package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
- package/src/__tests__/sessionDeadline.test.ts +51 -0
- package/src/__tests__/sharedCache.test.ts +11 -0
- package/src/__tests__/unifiedAuth.test.ts +73 -1
- package/src/agentSessions/BridgeHostInstance.ts +20 -5
- package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
- package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
- package/src/agentSessions/SessionJanitor.ts +92 -42
- package/src/agentSessions/SessionManager.ts +38 -8
- package/src/agentSessions/conversationBrowsers.ts +143 -0
- package/src/agentSessions/sessionDeadline.ts +81 -0
- package/src/auth/APIKeyScopeAuth.ts +5 -4
- package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
- package/src/auth/loginEmailResolution.ts +67 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
- package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
- package/src/auth/magicLink/MagicLinkService.ts +250 -10
- package/src/auth/magicLink/index.ts +1 -1
- package/src/auth/magicLink/redeemLanding.ts +7 -4
- package/src/auth/magicLink/types.ts +28 -3
- package/src/auth/newUsers.ts +1 -0
- package/src/backgroundSweepConfigUnits.ts +213 -0
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +2 -1
- package/src/config.ts +69 -0
- package/src/context.ts +46 -15
- package/src/coreRoutes.ts +14 -7
- package/src/generated/graphql-schemas/__mj.ts +10360 -6479
- package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
- package/src/generic/ResolverBase.ts +422 -143
- package/src/generic/RunViewResolver.ts +96 -49
- package/src/globalCors.ts +1 -0
- package/src/index.ts +246 -44
- package/src/integration/ConnectionPause.ts +222 -0
- package/src/integration/CustomColumnPromoter.ts +123 -53
- package/src/integration/EntityMapLifecycle.ts +229 -6
- package/src/integration/InactiveRowWarnings.ts +50 -0
- package/src/integration/ObjectNamesByID.ts +97 -0
- package/src/integration/PostRestartIntrospect.ts +49 -0
- package/src/integration/RSUPostRestartProgress.ts +359 -0
- package/src/integration/RSUProgressBridge.ts +85 -2
- package/src/integration/RunArtifactAuthorization.ts +65 -0
- package/src/integration/SchemaRefreshLaunch.ts +12 -2
- package/src/integration/ScopedFieldsForBuild.ts +113 -0
- package/src/integration/TargetConfigGuard.ts +71 -0
- package/src/logging/StartupLogger.ts +24 -0
- package/src/middleware/BaseServerMiddleware.ts +20 -3
- package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
- package/src/realtimeSessions/sessionAccess.ts +30 -0
- package/src/realtimeWidget/WidgetSessionService.ts +2 -0
- package/src/realtimeWidget/visitorIdentity.ts +1 -0
- package/src/realtimeWidget/widgetCore.ts +4 -8
- package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
- package/src/requestOrigin.ts +64 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +27 -2
- package/src/resolvers/AgentSessionResolver.ts +6 -2
- package/src/resolvers/AutotagPipelineResolver.ts +59 -14
- package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
- package/src/resolvers/GetDataResolver.ts +35 -13
- package/src/resolvers/InfoResolver.ts +1 -1
- package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
- package/src/resolvers/MCPResolver.ts +15 -2
- package/src/resolvers/QuerySystemUserResolver.ts +211 -100
- package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
- package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
- package/src/resolvers/RunAIAgentResolver.ts +265 -86
- package/src/resolvers/RunAIPromptResolver.ts +18 -18
- package/src/resolvers/RunTemplateResolver.ts +3 -1
- package/src/resolvers/SqlLoggingConfigResolver.ts +7 -9
- package/src/resolvers/TagGovernanceResolver.ts +65 -0
- package/src/resolvers/TelemetryResolver.ts +44 -9
- package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
- package/src/resolvers/meetingRecordingRegistration.ts +2 -0
- package/src/resolvers/meetings/meetingMutations.ts +37 -7
- package/src/rest/MediaAccessKeys.ts +6 -2
- package/src/rest/OAuthCallbackHandler.ts +13 -5
- package/src/rest/RealtimeRecordingHandler.ts +5 -9
- package/src/rest/ViewOperationsHandler.ts +2 -1
- package/src/rest/setupRESTEndpoints.ts +11 -2
- package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
- package/src/services/TaskGraphActionRunner.ts +2 -23
- package/src/services/TaskGraphAgentRunner.ts +43 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
- package/src/services/WorkQueueHostService.ts +26 -4
- package/src/services/workQueueConfig.ts +5 -5
- package/src/sharedCache.ts +1 -1
- package/src/types.ts +18 -0
- package/src/util.ts +55 -3
- package/src/services/TaskOrchestration-Integration.md +0 -188
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* everything.txt C6 — "deactivate" has to stop the connection, not just flag it.
|
|
3
|
+
*
|
|
4
|
+
* Before this, IsActive=false stopped syncs (the engine checks it) and nothing else. The cron
|
|
5
|
+
* jobs kept firing, DISCOVERY was never gated at all — so a paused connection went on rescanning
|
|
6
|
+
* the vendor on a timer — and an in-flight sync ran to the end. Each test below pins one of the
|
|
7
|
+
* three things that now have to happen, plus the two things that must NOT.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect } from 'vitest';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
|
|
13
|
+
const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
14
|
+
|
|
15
|
+
function methodBody(name: string): string {
|
|
16
|
+
const i = SRC.indexOf(`async ${name}(`);
|
|
17
|
+
expect(i, `${name} not found`).toBeGreaterThan(-1);
|
|
18
|
+
const rest = SRC.slice(i);
|
|
19
|
+
const end = rest.search(/\n @(Query|Mutation)\(/);
|
|
20
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('IntegrationDeactivateConnection — pause means pause', () => {
|
|
24
|
+
const body = () => methodBody('IntegrationDeactivateConnection');
|
|
25
|
+
|
|
26
|
+
it('writes IsActive=false before anything else can fail', () => {
|
|
27
|
+
// The flag is what every other actor reads. If a later step throws, the connection must
|
|
28
|
+
// still be paused — a half-done pause that left IsActive true would be the worst outcome.
|
|
29
|
+
const b = body();
|
|
30
|
+
const flag = b.indexOf('ci.IsActive = false');
|
|
31
|
+
const schedules = b.indexOf('findScheduledJobsForConnection');
|
|
32
|
+
expect(flag).toBeGreaterThan(-1);
|
|
33
|
+
expect(schedules).toBeGreaterThan(flag);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('pauses the scheduled jobs', () => {
|
|
37
|
+
expect(body()).toMatch(/DecideSchedulesToPause/);
|
|
38
|
+
expect(body()).toMatch(/setScheduledJobStatus\([^)]*'Paused'/s);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('records what it paused through the guard that survives a second pause', () => {
|
|
42
|
+
// DecidePauseWrite is the only thing standing between a re-pause and a lost record.
|
|
43
|
+
expect(body()).toMatch(/DecidePauseWrite/);
|
|
44
|
+
expect(body()).toMatch(/WritePausedSchedules/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('asks an in-flight sync to stop', () => {
|
|
48
|
+
expect(body()).toMatch(/CancelSyncAsync/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('reports how far the cancel actually reached instead of assuming', () => {
|
|
52
|
+
const b = body();
|
|
53
|
+
expect(b).toMatch(/DescribeCancelScope/);
|
|
54
|
+
expect(b).toMatch(/IsSyncRunningInThisProcess/);
|
|
55
|
+
expect(b).toMatch(/CancelScope/);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('NEVER touches entity maps — those are the user\'s table selection, not pause state', () => {
|
|
59
|
+
const b = body();
|
|
60
|
+
expect(b).not.toMatch(/DisableUnselectedEntityMaps/);
|
|
61
|
+
expect(b).not.toMatch(/SetEntityMapEnabled/);
|
|
62
|
+
expect(b).not.toMatch(/Company Integration Entity Maps/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns the richer type, so a caller can tell a real stop from a hope', () => {
|
|
66
|
+
expect(SRC).toMatch(/@Mutation\(\(\) => DeactivateConnectionOutput\)\s*\n\s*async IntegrationDeactivateConnection/);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('IntegrationReactivateConnection — restore exactly what pause took', () => {
|
|
71
|
+
const body = () => methodBody('IntegrationReactivateConnection');
|
|
72
|
+
|
|
73
|
+
it('restores through the record, not by activating everything', () => {
|
|
74
|
+
const b = body();
|
|
75
|
+
expect(b).toMatch(/DecideSchedulesToResume/);
|
|
76
|
+
// The tell for the bug this avoids: no unconditional sweep to Active.
|
|
77
|
+
expect(b).not.toMatch(/jobs\.map\([^)]*'Active'/s);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('consumes the record so the NEXT pause is not treated as a re-pause', () => {
|
|
81
|
+
// DecidePauseWrite refuses to write when a record is stored. Leaving a consumed record
|
|
82
|
+
// behind would make every future pause a silent no-op.
|
|
83
|
+
expect(body()).toMatch(/WritePausedSchedules\(ci\.Configuration, \[\]\)/);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('still does not rescan the source by default', () => {
|
|
87
|
+
// Resuming a connection and rescanning its schema stay separate decisions.
|
|
88
|
+
expect(body()).toMatch(/@Arg\("runSchemaRefresh"[^\n]*defaultValue: false/);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('a Save() that returns false is never mistaken for one that worked', () => {
|
|
93
|
+
// Save() reports a logical failure by returning false, not by throwing. An unchecked save of the
|
|
94
|
+
// pause record strands the schedules it paused; an unchecked save of the cleared record makes the
|
|
95
|
+
// next pause record nothing.
|
|
96
|
+
for (const name of ['IntegrationDeactivateConnection', 'IntegrationReactivateConnection']) {
|
|
97
|
+
it(`${name} checks every ci.Save()`, () => {
|
|
98
|
+
const b = methodBody(name);
|
|
99
|
+
const all = b.match(/ci\.Save\(\)/g) ?? [];
|
|
100
|
+
const checked = b.match(/if \(!await ci\.Save\(\)\)/g) ?? [];
|
|
101
|
+
expect(all.length).toBeGreaterThan(1);
|
|
102
|
+
expect(checked.length).toBe(all.length);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
it('pause saves its record BEFORE it moves any job, and moves nothing if that save fails', () => {
|
|
107
|
+
const b = methodBody('IntegrationDeactivateConnection');
|
|
108
|
+
const record = b.indexOf('ci.Configuration = WritePausedSchedules(');
|
|
109
|
+
const recordSave = b.indexOf('if (!await ci.Save())', record);
|
|
110
|
+
const firstMove = b.indexOf("setScheduledJobStatus(rec.ID, 'Paused'");
|
|
111
|
+
expect(record).toBeGreaterThan(-1);
|
|
112
|
+
expect(recordSave).toBeGreaterThan(record);
|
|
113
|
+
expect(firstMove).toBeGreaterThan(recordSave);
|
|
114
|
+
expect(b.slice(recordSave, firstMove)).toMatch(/throw new Error/);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('resume says so on every reply when the record will not clear', () => {
|
|
118
|
+
const b = methodBody('IntegrationReactivateConnection');
|
|
119
|
+
expect(b).toMatch(/scheduleNote = ' The record of paused schedules could not be cleared/);
|
|
120
|
+
const carried = (b.match(/\+ scheduleNote/g) ?? []).length + (b.match(/\$\{scheduleNote\}/g) ?? []).length;
|
|
121
|
+
expect(carried).toBe(4);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('the connection job finder escapes through the shared helpers', () => {
|
|
126
|
+
it('validates the connection id with sqlUuid and quotes job type ids with EscapeSQLString', () => {
|
|
127
|
+
const i = SRC.indexOf('private async findScheduledJobsForConnection');
|
|
128
|
+
const b = SRC.slice(i, SRC.indexOf('private async describeIfPaused'));
|
|
129
|
+
expect(i).toBeGreaterThan(-1);
|
|
130
|
+
expect(b).not.toMatch(/\.replace\(\/'\/g/);
|
|
131
|
+
expect(b).toMatch(/sqlUuid\(companyIntegrationID, 'companyIntegrationID'\)/);
|
|
132
|
+
expect(b).toMatch(/EscapeSQLString\(id\)/);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('a paused connection is not scanned', () => {
|
|
137
|
+
it('the detached discovery start refuses', () => {
|
|
138
|
+
expect(methodBody('IntegrationStartSchemaRefresh')).toMatch(/describeIfPaused/);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('the synchronous refresh refuses', () => {
|
|
142
|
+
expect(methodBody('IntegrationRefreshConnectorSchema')).toMatch(/describeIfPaused/);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('schema evolution refuses', () => {
|
|
146
|
+
expect(methodBody('IntegrationSchemaEvolution')).toMatch(/describeIfPaused/);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('both lock-taking paths check BEFORE acquiring the maintenance lock, so a refusal leaks no lock', () => {
|
|
150
|
+
for (const name of ['IntegrationRefreshConnectorSchema', 'IntegrationSchemaEvolution']) {
|
|
151
|
+
const b = methodBody(name);
|
|
152
|
+
const gate = b.indexOf('describeIfPaused');
|
|
153
|
+
const lock = b.indexOf('AcquireMaintenanceLock');
|
|
154
|
+
expect(gate, `${name}: gate missing`).toBeGreaterThan(-1);
|
|
155
|
+
expect(lock, `${name}: lock missing`).toBeGreaterThan(-1);
|
|
156
|
+
expect(gate, `${name}: gate must precede the lock`).toBeLessThan(lock);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('the gate fails OPEN — an unreadable connection is not declared paused', () => {
|
|
161
|
+
const i = SRC.indexOf('private async describeIfPaused');
|
|
162
|
+
expect(i).toBeGreaterThan(-1);
|
|
163
|
+
const b = SRC.slice(i, i + 1400);
|
|
164
|
+
expect(b).toMatch(/catch[\s\S]*return null/);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pause and resume, driven through the resolver with a Save() that returns false.
|
|
3
|
+
*
|
|
4
|
+
* Save() reports a logical failure by returning false rather than throwing, so the only way to
|
|
5
|
+
* prove a failed save is handled is to run the mutation against one. ConnectionPauseResolver.test.ts
|
|
6
|
+
* pins the shape of the code; these pin what happens.
|
|
7
|
+
*/
|
|
8
|
+
// type-graphql decorators call `Reflect.getMetadata`, which only exists once this polyfill is
|
|
9
|
+
// loaded. MUST precede any import that pulls in the resolver file.
|
|
10
|
+
import 'reflect-metadata';
|
|
11
|
+
|
|
12
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
13
|
+
|
|
14
|
+
vi.mock('type-graphql', () => {
|
|
15
|
+
const noopDecorator = () => () => undefined;
|
|
16
|
+
return {
|
|
17
|
+
Resolver: noopDecorator, Mutation: noopDecorator, Query: noopDecorator, Subscription: noopDecorator,
|
|
18
|
+
ObjectType: noopDecorator, InputType: noopDecorator, Field: noopDecorator, Arg: noopDecorator,
|
|
19
|
+
Args: noopDecorator, Ctx: noopDecorator, PubSub: noopDecorator, Root: noopDecorator,
|
|
20
|
+
Directive: noopDecorator, Authorized: noopDecorator, UseMiddleware: noopDecorator,
|
|
21
|
+
createUnionType: () => class {}, registerEnumType: () => undefined,
|
|
22
|
+
AuthorizationError: class AuthorizationError extends Error {},
|
|
23
|
+
Float: class {}, Int: class {}, ID: class {},
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
vi.mock('../config.js', () => ({ configInfo: { databaseSettings: { requestTimeout: 120_000 } } }));
|
|
28
|
+
|
|
29
|
+
const state = vi.hoisted(() => ({
|
|
30
|
+
/** Every Save() result in order; anything past the end saves. */
|
|
31
|
+
saves: [] as boolean[],
|
|
32
|
+
/** The order things happened in, across the connection row and the jobs. */
|
|
33
|
+
events: [] as string[],
|
|
34
|
+
ci: null as null | Record<string, unknown>,
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
vi.mock('../util.js', async importOriginal => ({
|
|
38
|
+
...(await importOriginal<typeof import('../util.js')>()),
|
|
39
|
+
GetReadWriteProvider: () => ({
|
|
40
|
+
GetEntityObject: async () => state.ci,
|
|
41
|
+
}),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const { IntegrationDiscoveryResolver } = await import('../resolvers/IntegrationDiscoveryResolver.js');
|
|
45
|
+
const { IntegrationEngine } = await import('@memberjunction/integration-engine');
|
|
46
|
+
const { ReadPausedSchedules, WritePausedSchedules } = await import('../integration/ConnectionPause.js');
|
|
47
|
+
|
|
48
|
+
type Job = { ID: string; Status: string; Kind: 'sync' | 'discovery' };
|
|
49
|
+
|
|
50
|
+
function connection(configuration: string | null): Record<string, unknown> {
|
|
51
|
+
const ci: Record<string, unknown> = {
|
|
52
|
+
IsActive: true,
|
|
53
|
+
ScheduleEnabled: true,
|
|
54
|
+
Configuration: configuration,
|
|
55
|
+
LatestResult: { Message: 'the database said no' },
|
|
56
|
+
InnerLoad: async () => true,
|
|
57
|
+
Save: async () => {
|
|
58
|
+
const ok = state.saves.length > 0 ? state.saves.shift()! : true;
|
|
59
|
+
state.events.push(`save:${ok ? 'ok' : 'failed'}:${ReadPausedSchedules(ci.Configuration as string).map(r => r.ID).join(',')}`);
|
|
60
|
+
return ok;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
return ci;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolverWith(jobs: Job[]) {
|
|
67
|
+
return {
|
|
68
|
+
getAuthenticatedUser: () => ({ ID: 'user-1' }),
|
|
69
|
+
findScheduledJobsForConnection: async () => jobs,
|
|
70
|
+
setScheduledJobStatus: async (id: string, status: string) => {
|
|
71
|
+
state.events.push(`job:${id}:${status}`);
|
|
72
|
+
return true;
|
|
73
|
+
},
|
|
74
|
+
formatError: (e: unknown) => String(e),
|
|
75
|
+
startSchemaRefreshPipelineDetached: () => undefined,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const ctx = { providers: [] } as never;
|
|
80
|
+
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
state.saves = [];
|
|
83
|
+
state.events = [];
|
|
84
|
+
vi.spyOn(IntegrationEngine, 'GetSyncProgressAsync').mockResolvedValue(null as never);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('IntegrationDeactivateConnection with a failing Save()', () => {
|
|
88
|
+
const jobs: Job[] = [
|
|
89
|
+
{ ID: 'job-sync', Status: 'Active', Kind: 'sync' },
|
|
90
|
+
{ ID: 'job-disc', Status: 'Active', Kind: 'discovery' },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
it('saves the record of what it will pause before it pauses anything', async () => {
|
|
94
|
+
state.ci = connection(null);
|
|
95
|
+
const out = await IntegrationDiscoveryResolver.prototype.IntegrationDeactivateConnection.call(resolverWith(jobs), 'ci-1', ctx);
|
|
96
|
+
expect(out.Success).toBe(true);
|
|
97
|
+
expect(out.SchedulesPaused).toBe(2);
|
|
98
|
+
expect(state.events.slice(0, 4)).toEqual([
|
|
99
|
+
'save:ok:', // IsActive = false
|
|
100
|
+
'save:ok:job-sync,job-disc', // the record, before any job moves
|
|
101
|
+
'job:job-sync:Paused',
|
|
102
|
+
'job:job-disc:Paused',
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('pauses nothing, and says so, when the record will not save', async () => {
|
|
107
|
+
state.ci = connection(null);
|
|
108
|
+
state.saves = [true, false];
|
|
109
|
+
const out = await IntegrationDiscoveryResolver.prototype.IntegrationDeactivateConnection.call(resolverWith(jobs), 'ci-1', ctx);
|
|
110
|
+
expect(out.Success).toBe(true);
|
|
111
|
+
expect(out.SchedulesPaused).toBe(0);
|
|
112
|
+
expect(state.events.filter(e => e.startsWith('job:'))).toEqual([]);
|
|
113
|
+
expect(out.Message).toContain('Could not pause the schedules');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('stops at the flag when IsActive will not save', async () => {
|
|
117
|
+
state.ci = connection(null);
|
|
118
|
+
state.saves = [false];
|
|
119
|
+
const out = await IntegrationDiscoveryResolver.prototype.IntegrationDeactivateConnection.call(resolverWith(jobs), 'ci-1', ctx);
|
|
120
|
+
expect(out.Success).toBe(false);
|
|
121
|
+
expect(state.events).toEqual(['save:failed:']);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('IntegrationReactivateConnection with a failing Save()', () => {
|
|
126
|
+
const paused: Job[] = [{ ID: 'job-sync', Status: 'Paused', Kind: 'sync' }];
|
|
127
|
+
const record = WritePausedSchedules(null, [{ ID: 'job-sync', Kind: 'sync' }]);
|
|
128
|
+
|
|
129
|
+
it('restores the recorded jobs and clears the record', async () => {
|
|
130
|
+
state.ci = connection(record);
|
|
131
|
+
const out = await IntegrationDiscoveryResolver.prototype.IntegrationReactivateConnection.call(resolverWith(paused), 'ci-1', false, false, ctx);
|
|
132
|
+
expect(out).toEqual({ Success: true, Message: 'Reactivated' });
|
|
133
|
+
expect(state.events).toEqual(['save:ok:job-sync', 'job:job-sync:Active', 'save:ok:']);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('says so when the record will not clear, instead of reporting a clean resume', async () => {
|
|
137
|
+
state.ci = connection(record);
|
|
138
|
+
state.saves = [true, false];
|
|
139
|
+
const out = await IntegrationDiscoveryResolver.prototype.IntegrationReactivateConnection.call(resolverWith(paused), 'ci-1', false, false, ctx);
|
|
140
|
+
expect(out.Success).toBe(true);
|
|
141
|
+
expect(out.Message).toContain('could not be cleared');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completing a promotion after the restart.
|
|
3
|
+
*
|
|
4
|
+
* The post-restart consumer calls CompletePromotion with no catalog scope. Unscoped, a connection
|
|
5
|
+
* with per-connection rows reads as having none, so building its catalog writer threw
|
|
6
|
+
* PER_CONNECTION_CATALOG_MISSING. CompletePromotion caught that per entity and returned normally,
|
|
7
|
+
* and the consumer marked the pending work Completed: the columns existed in the database and were
|
|
8
|
+
* never mapped, with nothing failed anywhere.
|
|
9
|
+
*
|
|
10
|
+
* So: the catalog writes run in the connection's own scope, entered per entry; and a failed entry
|
|
11
|
+
* no longer returns normally — the others still run, then the failure is thrown, so the consumer
|
|
12
|
+
* fails the pending row instead of completing it.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
|
|
16
|
+
const h = vi.hoisted(() => ({
|
|
17
|
+
scopeAtBuild: [] as Array<string | undefined>,
|
|
18
|
+
savedFields: [] as string[],
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock('@memberjunction/schema-engine', () => ({
|
|
22
|
+
RuntimeSchemaManager: { get Instance() { return {}; } },
|
|
23
|
+
}));
|
|
24
|
+
vi.mock('@memberjunction/integration-schema-builder', () => ({ DDLGenerator: class {} }));
|
|
25
|
+
|
|
26
|
+
// The real scope; only the writer is replaced. It behaves as the real one does for a connection
|
|
27
|
+
// with per-connection rows: outside that connection's scope it cannot tell, and refuses.
|
|
28
|
+
vi.mock('@memberjunction/integration-engine', async (orig) => {
|
|
29
|
+
const actual = await orig<typeof import('@memberjunction/integration-engine')>();
|
|
30
|
+
return {
|
|
31
|
+
...actual,
|
|
32
|
+
BuildCatalogWriter: () => {
|
|
33
|
+
const ci = actual.CurrentCatalogCI();
|
|
34
|
+
h.scopeAtBuild.push(ci);
|
|
35
|
+
if (!ci) throw new Error('PER_CONNECTION_CATALOG_MISSING: built outside the connection scope');
|
|
36
|
+
return {
|
|
37
|
+
ObjectsInScope: async () => [{ Name: 'Members', ID: 'obj-members' }],
|
|
38
|
+
FieldsForObject: async () => [],
|
|
39
|
+
LoadField: async () => null,
|
|
40
|
+
NewFieldRow: async () => {
|
|
41
|
+
const row: Record<string, unknown> = {};
|
|
42
|
+
return Object.assign(row, { Save: async () => { h.savedFields.push(String(row.Name)); return true; } });
|
|
43
|
+
},
|
|
44
|
+
StampNewField: () => undefined,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
import type { UserInfo, IMetadataProvider, EntityInfo } from '@memberjunction/core';
|
|
51
|
+
import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
|
|
52
|
+
import { WithCatalogScope } from '@memberjunction/integration-engine';
|
|
53
|
+
import { IntegrationCustomColumnPromoter } from '../integration/CustomColumnPromoter.js';
|
|
54
|
+
|
|
55
|
+
const USER = { ID: 'u-1' } as UserInfo;
|
|
56
|
+
const CONN = 'CCCCCCCC-0000-0000-0000-000000000001';
|
|
57
|
+
const OTHER_CONN = 'CCCCCCCC-0000-0000-0000-000000000002';
|
|
58
|
+
|
|
59
|
+
type Seams = {
|
|
60
|
+
createIntegrationObjectFields: (integrationID: string, entityMapID: string, objectName: string, named: unknown[]) => Promise<void>;
|
|
61
|
+
activeFieldMaps: unknown; createFieldMaps: unknown; spreadAndRebaseline: unknown;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function provider(known: string[]): IMetadataProvider {
|
|
65
|
+
return {
|
|
66
|
+
EntityByName: (n: string) => (known.includes(n) ? ({ Name: n } as unknown as EntityInfo) : undefined),
|
|
67
|
+
PlatformKey: 'sqlserver',
|
|
68
|
+
} as unknown as IMetadataProvider;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const region = { sourceKey: 'region', columnName: 'region', candidate: { Coverage: 0.5, Inferred: { SchemaFieldType: 'nvarchar', MaxLength: 50 } } };
|
|
72
|
+
|
|
73
|
+
function entry(entityName: string) {
|
|
74
|
+
return {
|
|
75
|
+
EntityName: entityName, EntityMapID: `em-${entityName}`, ExternalObjectName: 'Members', IntegrationID: 'int-1',
|
|
76
|
+
Columns: [{ SourceKey: 'region', ColumnName: 'region', SchemaFieldType: 'nvarchar', MaxLength: 50, Coverage: 0.5 }],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
h.scopeAtBuild.length = 0;
|
|
82
|
+
h.savedFields.length = 0;
|
|
83
|
+
// No catalog entities registered: entering a scope loads nothing and reads nothing.
|
|
84
|
+
vi.spyOn(IntegrationEngineBase.Instance, 'ProviderToUse', 'get').mockImplementation(() => ({ EntityByName: () => undefined }) as never);
|
|
85
|
+
vi.spyOn(IntegrationEngineBase.Instance, 'EntityMaps', 'get')
|
|
86
|
+
.mockReturnValue([{ ID: 'em-Members', CompanyIntegrationID: CONN }] as never);
|
|
87
|
+
vi.spyOn(IntegrationEngineBase.Instance, 'GetCompanyIntegrationByID')
|
|
88
|
+
.mockImplementation((id: string) => ({ ID: id }) as never);
|
|
89
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
90
|
+
});
|
|
91
|
+
afterEach(() => { vi.restoreAllMocks(); });
|
|
92
|
+
|
|
93
|
+
describe('the catalog writes of a promotion run in the connection\'s own scope', () => {
|
|
94
|
+
it('called with no scope (the post-restart consumer), the writer is built in the connection\'s scope', async () => {
|
|
95
|
+
const promoter = new IntegrationCustomColumnPromoter(USER, provider([])) as unknown as Seams;
|
|
96
|
+
await promoter.createIntegrationObjectFields('int-1', 'em-Members', 'Members', [region]);
|
|
97
|
+
expect(h.scopeAtBuild).toEqual([CONN]);
|
|
98
|
+
expect(h.savedFields).toEqual(['region']);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('called inside another connection\'s scope, the entry\'s own connection wins', async () => {
|
|
102
|
+
const promoter = new IntegrationCustomColumnPromoter(USER, provider([])) as unknown as Seams;
|
|
103
|
+
await WithCatalogScope(OTHER_CONN, () => promoter.createIntegrationObjectFields('int-1', 'em-Members', 'Members', [region]), USER);
|
|
104
|
+
expect(h.scopeAtBuild).toEqual([CONN]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('CompletePromotion does not complete over a failed entity', () => {
|
|
109
|
+
function stub(known: string[], failing: string[] = []) {
|
|
110
|
+
const promoter = new IntegrationCustomColumnPromoter(USER, provider(known));
|
|
111
|
+
const p = promoter as unknown as Seams;
|
|
112
|
+
p.activeFieldMaps = vi.fn().mockResolvedValue(new Map<string, string>());
|
|
113
|
+
p.createIntegrationObjectFields = vi.fn(async (_i: string, emID: string) => {
|
|
114
|
+
if (failing.some(f => emID === `em-${f}`)) throw new Error('PER_CONNECTION_CATALOG_MISSING');
|
|
115
|
+
});
|
|
116
|
+
const maps = vi.fn().mockResolvedValue(undefined);
|
|
117
|
+
p.createFieldMaps = maps;
|
|
118
|
+
p.spreadAndRebaseline = vi.fn().mockResolvedValue(undefined);
|
|
119
|
+
return { promoter, maps };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
it('every entry completes: returns the columns and throws nothing', async () => {
|
|
123
|
+
const { promoter } = stub(['A', 'B']);
|
|
124
|
+
await expect(promoter.CompletePromotion([entry('A'), entry('B')])).resolves.toEqual([
|
|
125
|
+
{ EntityName: 'A', ColumnName: 'region' },
|
|
126
|
+
{ EntityName: 'B', ColumnName: 'region' },
|
|
127
|
+
]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('one entry fails: the others still complete, then it throws naming the failed one', async () => {
|
|
131
|
+
const { promoter, maps } = stub(['A', 'B'], ['A']);
|
|
132
|
+
await expect(promoter.CompletePromotion([entry('A'), entry('B')]))
|
|
133
|
+
.rejects.toThrow(/PROMOTION_INCOMPLETE: 1 of 2 .*A: PER_CONNECTION_CATALOG_MISSING/);
|
|
134
|
+
expect(maps).toHaveBeenCalledTimes(1); // B's field maps were still created
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('an entity that is not found after the restart is a failure, not a skip', async () => {
|
|
138
|
+
const { promoter } = stub(['B']);
|
|
139
|
+
await expect(promoter.CompletePromotion([entry('A'), entry('B')]))
|
|
140
|
+
.rejects.toThrow(/PROMOTION_INCOMPLETE: 1 of 2 .*A: entity not found/);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-connection catalog rows carry a plain FK to the connection and nothing cascades, so a
|
|
3
|
+
* connection that ever ran a discovery on it could not be deleted at all: the CompanyIntegration
|
|
4
|
+
* delete violated the constraint and the whole cascade rolled back (MJ-CAT-18). The fix deletes
|
|
5
|
+
* the field rows, then the object rows, inside the same transaction group, before the connection.
|
|
6
|
+
*
|
|
7
|
+
* Pinned from source, like PostRestartMapsInCatalogScope: the delete is a transaction over a live
|
|
8
|
+
* provider, and the fact being pinned is ORDER inside one method body.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect } from 'vitest';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
|
|
14
|
+
const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
15
|
+
const start = SRC.indexOf('async IntegrationDeleteConnection(');
|
|
16
|
+
const end = SRC.indexOf('// ── SCHEMA EVOLUTION', start);
|
|
17
|
+
const BODY = SRC.slice(start, end);
|
|
18
|
+
|
|
19
|
+
describe('IntegrationDeleteConnection removes the per-connection catalog before the connection', () => {
|
|
20
|
+
it('is scoped to the method body', () => {
|
|
21
|
+
expect(start).toBeGreaterThan(0);
|
|
22
|
+
expect(end).toBeGreaterThan(start);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('deletes per-connection fields, then objects, then the CompanyIntegration — in that order', () => {
|
|
26
|
+
const fields = BODY.indexOf("EntityName: 'MJ: Company Integration Object Fields'");
|
|
27
|
+
const objects = BODY.indexOf("EntityName: 'MJ: Company Integration Objects'");
|
|
28
|
+
const fieldDelete = BODY.indexOf('field.TransactionGroup = tg;');
|
|
29
|
+
const objectDelete = BODY.indexOf('object.TransactionGroup = tg;');
|
|
30
|
+
const ciDelete = BODY.indexOf('ci.TransactionGroup = tg;');
|
|
31
|
+
expect(objects).toBeGreaterThan(0);
|
|
32
|
+
expect(fields).toBeGreaterThan(objects);
|
|
33
|
+
expect(fieldDelete).toBeGreaterThan(fields);
|
|
34
|
+
expect(objectDelete).toBeGreaterThan(fieldDelete);
|
|
35
|
+
expect(ciDelete).toBeGreaterThan(objectDelete);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('queues every catalog delete on the same transaction group as the cascade', () => {
|
|
39
|
+
expect(BODY).toMatch(/field\.TransactionGroup = tg;\s*await field\.Delete\(\);/);
|
|
40
|
+
expect(BODY).toMatch(/object\.TransactionGroup = tg;\s*await object\.Delete\(\);/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('is guarded on the catalog entities being registered, so an unmigrated workspace is untouched', () => {
|
|
44
|
+
expect(BODY).toMatch(/md\.Entities\.some\(e => e\.Name === 'MJ: Company Integration Objects'\)/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('selects the fields by the objects of THIS connection only', () => {
|
|
48
|
+
expect(BODY).toMatch(/ExtraFilter: `CompanyIntegrationID='\$\{companyIntegrationID\}'`,\s*ResultType: 'entity_object',\s*\}, sysUser\);\s*const perConnectionObjects/);
|
|
49
|
+
expect(BODY).toMatch(/ExtraFilter: `CompanyIntegrationObjectID IN \(\$\{objectIDs\}\)`/);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan.md: "going to a page should NEVER, NEVER trigger such things automatically, it must in a
|
|
3
|
+
* very clean way tell the user to click a button to start discovery of tables".
|
|
4
|
+
*
|
|
5
|
+
* A button needs a call that returns at once. The only discovery entry point that existed was
|
|
6
|
+
* synchronous - it runs the whole pipeline inline and times out at the gateway on a large catalog -
|
|
7
|
+
* so both wizards auto-triggered on page entry and adopted whatever run they happened to find.
|
|
8
|
+
* IntegrationStartSchemaRefresh is the explicit start: it returns a tailable RunID immediately.
|
|
9
|
+
*
|
|
10
|
+
* Source-level, for the same reason the sibling default tests are: these are decorator and
|
|
11
|
+
* argument shapes, and invoking the resolver needs a provider, a connector driver and the RSU
|
|
12
|
+
* pipeline stood up.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect } from 'vitest';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
|
|
18
|
+
const SRC = readFileSync(
|
|
19
|
+
join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
20
|
+
|
|
21
|
+
function methodBody(name: string): string {
|
|
22
|
+
const start = SRC.indexOf(`async ${name}(`);
|
|
23
|
+
expect(start, `${name} not found`).toBeGreaterThan(-1);
|
|
24
|
+
const rest = SRC.slice(start);
|
|
25
|
+
const end = rest.search(/\n @(Query|Mutation)\(/);
|
|
26
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('IntegrationStartSchemaRefresh', () => {
|
|
30
|
+
it('exists as a mutation', () => {
|
|
31
|
+
expect(SRC).toMatch(/@Mutation\(\(\) => StartSchemaRefreshOutput\)/);
|
|
32
|
+
expect(SRC).toMatch(/async IntegrationStartSchemaRefresh\(/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('launches DETACHED rather than awaiting the pipeline', () => {
|
|
36
|
+
const body = methodBody('IntegrationStartSchemaRefresh');
|
|
37
|
+
expect(body).toMatch(/startSchemaRefreshPipelineDetached\(/);
|
|
38
|
+
// an await on the pipeline would reintroduce the gateway timeout this exists to avoid
|
|
39
|
+
expect(body).not.toMatch(/await this\.runSchemaRefreshPipeline\(/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('refuses a second start with the reason, instead of handing back a doomed RunID', () => {
|
|
43
|
+
const body = methodBody('IntegrationStartSchemaRefresh');
|
|
44
|
+
expect(body).toMatch(/GetMaintenanceLock\(/);
|
|
45
|
+
expect(body).toMatch(/RunID: 'not-started'/);
|
|
46
|
+
expect(body).toMatch(/BlockedBy/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('probes the lock BEFORE launching, or the refusal is useless', () => {
|
|
50
|
+
const body = methodBody('IntegrationStartSchemaRefresh');
|
|
51
|
+
expect(body.indexOf('GetMaintenanceLock('))
|
|
52
|
+
.toBeLessThan(body.indexOf('startSchemaRefreshPipelineDetached('));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('uses the read-write provider, since the pipeline writes the catalog', () => {
|
|
56
|
+
expect(methodBody('IntegrationStartSchemaRefresh')).toMatch(/GetReadWriteProvider\(/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('is a separate mutation, so the synchronous one keeps its real-counts contract', () => {
|
|
60
|
+
// the synchronous reply promises counts because it waited; a detached launch has only zeros
|
|
61
|
+
expect(SRC).toMatch(/async IntegrationRefreshConnectorSchema\(/);
|
|
62
|
+
const detached = methodBody('IntegrationStartSchemaRefresh');
|
|
63
|
+
expect(detached).not.toMatch(/ObjectsCreated/);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
3
|
+
|
|
4
|
+
const { submitMock } = vi.hoisted(() => ({
|
|
5
|
+
submitMock: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
vi.mock('@memberjunction/task-graph', () => ({
|
|
9
|
+
TaskGraphService: class {
|
|
10
|
+
public Submit = submitMock;
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
import { DurableEntityActionTaskSubmitter } from '../services/DurableEntityActionTaskSubmitter.js';
|
|
15
|
+
|
|
16
|
+
describe('DurableEntityActionTaskSubmitter', () => {
|
|
17
|
+
const contextUser = { ID: 'user-1', Email: 'test@example.com' } as UserInfo;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
submitMock.mockReset();
|
|
21
|
+
submitMock.mockResolvedValue({ Success: true, ParentTaskID: 'parent-task-1' });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('enlists in request.Provider when supplied instead of minting an isolated provider connection', async () => {
|
|
25
|
+
const savingProvider = { Name: 'saving-tx-provider', GetEntityObject: vi.fn() } as object as IMetadataProvider;
|
|
26
|
+
const createProvider = vi.fn();
|
|
27
|
+
const submitter = new DurableEntityActionTaskSubmitter({ CreateProvider: createProvider }, 'env-1');
|
|
28
|
+
|
|
29
|
+
const result = await submitter.Submit({
|
|
30
|
+
EntityActionID: 'ea-1',
|
|
31
|
+
ActionID: 'act-1',
|
|
32
|
+
ActionName: 'Send Order Webhook',
|
|
33
|
+
EntityID: 'ent-1',
|
|
34
|
+
EntityName: 'Orders',
|
|
35
|
+
RecordID: 'ID|ord-1',
|
|
36
|
+
InvocationType: 'AfterCreate',
|
|
37
|
+
RedactedParams: { OrderID: 'ord-1' },
|
|
38
|
+
ContextUser: contextUser,
|
|
39
|
+
Provider: savingProvider,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(result).toEqual({ Success: true, ParentTaskID: 'parent-task-1' });
|
|
43
|
+
expect(createProvider).not.toHaveBeenCalled();
|
|
44
|
+
expect(submitMock).toHaveBeenCalledTimes(1);
|
|
45
|
+
expect(submitMock.mock.calls[0][1]).toMatchObject({
|
|
46
|
+
EnvironmentID: 'env-1',
|
|
47
|
+
ContextUser: contextUser,
|
|
48
|
+
Provider: savingProvider,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('falls back to providerFactory.CreateProvider() when request.Provider is omitted', async () => {
|
|
53
|
+
const factoryProvider = { Name: 'factory-provider' } as IMetadataProvider;
|
|
54
|
+
const createProvider = vi.fn().mockResolvedValue(factoryProvider);
|
|
55
|
+
const submitter = new DurableEntityActionTaskSubmitter({ CreateProvider: createProvider }, 'env-1');
|
|
56
|
+
|
|
57
|
+
const result = await submitter.Submit({
|
|
58
|
+
EntityActionID: 'ea-1',
|
|
59
|
+
ActionID: 'act-1',
|
|
60
|
+
ActionName: 'Send Order Webhook',
|
|
61
|
+
EntityID: 'ent-1',
|
|
62
|
+
EntityName: 'Orders',
|
|
63
|
+
RecordID: 'ID|ord-1',
|
|
64
|
+
InvocationType: 'AfterCreate',
|
|
65
|
+
RedactedParams: {},
|
|
66
|
+
ContextUser: contextUser,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual({ Success: true, ParentTaskID: 'parent-task-1' });
|
|
70
|
+
expect(createProvider).toHaveBeenCalledTimes(1);
|
|
71
|
+
expect(submitMock.mock.calls[0][1]).toMatchObject({ Provider: factoryProvider });
|
|
72
|
+
});
|
|
73
|
+
});
|