@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
|
@@ -24,16 +24,22 @@ vi.mock('type-graphql', () => {
|
|
|
24
24
|
return Object.fromEntries(exportNames.map((name) => [name, decoratorFactory]));
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
+
const hoisted = vi.hoisted(() => ({
|
|
28
|
+
runAgentInConversation: vi.fn().mockResolvedValue({ agentResult: { success: true } }),
|
|
29
|
+
}));
|
|
30
|
+
|
|
27
31
|
vi.mock('@memberjunction/ai-agents', () => ({
|
|
28
32
|
AgentRunner: class {
|
|
29
|
-
RunAgentInConversation() {
|
|
30
|
-
return
|
|
33
|
+
RunAgentInConversation(...args: unknown[]) {
|
|
34
|
+
return hoisted.runAgentInConversation(...args);
|
|
31
35
|
}
|
|
32
36
|
CreateAgentReplyDetail() {
|
|
33
37
|
return Promise.resolve({ ID: 'reply-1', __mj_CreatedAt: new Date() }); // the server creates the reply row (A19)
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
ArtifactToolManager: class {},
|
|
41
|
+
CanAgentOwnSubAgentConsults: vi.fn(() => false),
|
|
42
|
+
LoadOpenSubAgentConsults: vi.fn(async () => []),
|
|
37
43
|
}));
|
|
38
44
|
|
|
39
45
|
vi.mock('../realtimeWidget/widgetGuestElevation.js', () => ({
|
|
@@ -46,6 +52,7 @@ import type { AppContext, UserPayload } from '../types.js';
|
|
|
46
52
|
|
|
47
53
|
const USER = { ID: 'user-1', Email: 'user@example.com' };
|
|
48
54
|
const STOPPED_RUN = '16787b44-29f6-44a5-8bd4-fc1538e79275';
|
|
55
|
+
const CONSULT_RUN = 'c0ffee00-29f6-44a5-8bd4-fc1538e79275';
|
|
49
56
|
|
|
50
57
|
type Fn = ReturnType<typeof vi.fn>;
|
|
51
58
|
|
|
@@ -55,7 +62,9 @@ interface ResolverSeams {
|
|
|
55
62
|
loadConversationHistoryWithAttachments: Fn;
|
|
56
63
|
executeAIAgent: Fn;
|
|
57
64
|
executeAgentInBackground: Fn;
|
|
58
|
-
|
|
65
|
+
findChainablePredecessor: Fn;
|
|
66
|
+
validateAgent: Fn;
|
|
67
|
+
syncFeedbackRequestFromConversation: Fn;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
/** Argument positions of lastRunId / autoPopulateLastRunPayload on the two dispatch seams. */
|
|
@@ -69,7 +78,7 @@ function makeResolver(): { resolver: RunAIAgentResolver; seams: ResolverSeams }
|
|
|
69
78
|
seams.loadConversationHistoryWithAttachments = vi.fn().mockResolvedValue([]);
|
|
70
79
|
seams.executeAIAgent = vi.fn().mockResolvedValue({ success: true, result: '{}' });
|
|
71
80
|
seams.executeAgentInBackground = vi.fn();
|
|
72
|
-
seams.
|
|
81
|
+
seams.findChainablePredecessor = vi.fn().mockResolvedValue(null);
|
|
73
82
|
return { resolver, seams };
|
|
74
83
|
}
|
|
75
84
|
|
|
@@ -102,7 +111,7 @@ afterEach(() => {
|
|
|
102
111
|
vi.restoreAllMocks();
|
|
103
112
|
});
|
|
104
113
|
|
|
105
|
-
describe('RunAIAgentFromConversationDetail — continuing from a
|
|
114
|
+
describe('RunAIAgentFromConversationDetail — continuing from a chainable predecessor run', () => {
|
|
106
115
|
let resolver: RunAIAgentResolver;
|
|
107
116
|
let seams: ResolverSeams;
|
|
108
117
|
|
|
@@ -111,19 +120,19 @@ describe('RunAIAgentFromConversationDetail — continuing from a user-stopped ru
|
|
|
111
120
|
});
|
|
112
121
|
|
|
113
122
|
it('chains the new run to the stopped predecessor and rolls its payload in when none was supplied', async () => {
|
|
114
|
-
seams.
|
|
123
|
+
seams.findChainablePredecessor.mockResolvedValue({ runId: STOPPED_RUN, reason: 'stopped' });
|
|
115
124
|
|
|
116
125
|
const result = await runMutation(resolver, {});
|
|
117
126
|
|
|
118
127
|
expect(result.success).toBe(true);
|
|
119
|
-
expect(seams.
|
|
128
|
+
expect(seams.findChainablePredecessor).toHaveBeenCalledWith('conv-1', 'agent-1', null, USER, expect.anything());
|
|
120
129
|
const args = seams.executeAIAgent.mock.calls[0];
|
|
121
130
|
expect(args[SYNC_LAST_RUN]).toBe(STOPPED_RUN);
|
|
122
131
|
expect(args[SYNC_AUTO]).toBe(true);
|
|
123
132
|
});
|
|
124
133
|
|
|
125
134
|
it('keeps the caller\'s payload as the start when one was supplied (chains, but does not roll the old one in)', async () => {
|
|
126
|
-
seams.
|
|
135
|
+
seams.findChainablePredecessor.mockResolvedValue({ runId: STOPPED_RUN, reason: 'stopped' });
|
|
127
136
|
|
|
128
137
|
await runMutation(resolver, { payload: '{"fromArtifact":true}' });
|
|
129
138
|
|
|
@@ -133,7 +142,7 @@ describe('RunAIAgentFromConversationDetail — continuing from a user-stopped ru
|
|
|
133
142
|
});
|
|
134
143
|
|
|
135
144
|
it('hands the chain to a fire-and-forget run too', async () => {
|
|
136
|
-
seams.
|
|
145
|
+
seams.findChainablePredecessor.mockResolvedValue({ runId: STOPPED_RUN, reason: 'stopped' });
|
|
137
146
|
|
|
138
147
|
await runMutation(resolver, { fireAndForget: true });
|
|
139
148
|
|
|
@@ -143,17 +152,83 @@ describe('RunAIAgentFromConversationDetail — continuing from a user-stopped ru
|
|
|
143
152
|
expect(args[BG_AUTO]).toBe(true);
|
|
144
153
|
});
|
|
145
154
|
|
|
155
|
+
it('chains an open-consult predecessor on both synchronous and fire-and-forget paths, setting autoPopulate only when no payload was supplied', async () => {
|
|
156
|
+
seams.findChainablePredecessor.mockResolvedValue({ runId: CONSULT_RUN, reason: 'open-consult' });
|
|
157
|
+
|
|
158
|
+
// 1. Synchronous without payload -> effectiveAutoPopulate = true
|
|
159
|
+
await runMutation(resolver, {});
|
|
160
|
+
const syncNoPayloadArgs = seams.executeAIAgent.mock.calls[0];
|
|
161
|
+
expect(syncNoPayloadArgs[SYNC_LAST_RUN]).toBe(CONSULT_RUN);
|
|
162
|
+
expect(syncNoPayloadArgs[SYNC_AUTO]).toBe(true);
|
|
163
|
+
|
|
164
|
+
// 2. Synchronous with payload -> effectiveAutoPopulate = undefined
|
|
165
|
+
await runMutation(resolver, { payload: '{"custom":1}' });
|
|
166
|
+
const syncWithPayloadArgs = seams.executeAIAgent.mock.calls[1];
|
|
167
|
+
expect(syncWithPayloadArgs[SYNC_LAST_RUN]).toBe(CONSULT_RUN);
|
|
168
|
+
expect(syncWithPayloadArgs[SYNC_AUTO]).toBeUndefined();
|
|
169
|
+
|
|
170
|
+
// 3. Fire-and-forget without payload -> effectiveAutoPopulate = true
|
|
171
|
+
await runMutation(resolver, { fireAndForget: true });
|
|
172
|
+
const bgNoPayloadArgs = seams.executeAgentInBackground.mock.calls[0];
|
|
173
|
+
expect(bgNoPayloadArgs[BG_LAST_RUN]).toBe(CONSULT_RUN);
|
|
174
|
+
expect(bgNoPayloadArgs[BG_AUTO]).toBe(true);
|
|
175
|
+
|
|
176
|
+
// 4. Fire-and-forget with payload -> effectiveAutoPopulate = undefined
|
|
177
|
+
await runMutation(resolver, { fireAndForget: true, payload: '{"custom":1}' });
|
|
178
|
+
const bgWithPayloadArgs = seams.executeAgentInBackground.mock.calls[1];
|
|
179
|
+
expect(bgWithPayloadArgs[BG_LAST_RUN]).toBe(CONSULT_RUN);
|
|
180
|
+
expect(bgWithPayloadArgs[BG_AUTO]).toBeUndefined();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('threads a lastRunId chained from findChainablePredecessor into syncFeedbackRequestFromConversation when executeAIAgent runs', async () => {
|
|
184
|
+
const realResolver = new RunAIAgentResolver();
|
|
185
|
+
const realSeams = realResolver as unknown as ResolverSeams;
|
|
186
|
+
realSeams.CheckAPIKeyScopeAuthorization = vi.fn().mockResolvedValue(undefined);
|
|
187
|
+
realSeams.GetUserFromPayload = vi.fn().mockReturnValue(USER);
|
|
188
|
+
realSeams.loadConversationHistoryWithAttachments = vi.fn().mockResolvedValue([
|
|
189
|
+
{ role: 'user', content: 'Proceed with option A' },
|
|
190
|
+
]);
|
|
191
|
+
realSeams.findChainablePredecessor = vi.fn().mockResolvedValue({
|
|
192
|
+
runId: CONSULT_RUN,
|
|
193
|
+
reason: 'open-consult',
|
|
194
|
+
});
|
|
195
|
+
realSeams.validateAgent = vi.fn().mockResolvedValue({
|
|
196
|
+
ID: 'agent-1',
|
|
197
|
+
Name: 'Director',
|
|
198
|
+
Status: 'Active',
|
|
199
|
+
});
|
|
200
|
+
realSeams.syncFeedbackRequestFromConversation = vi.fn().mockResolvedValue(undefined);
|
|
201
|
+
hoisted.runAgentInConversation.mockResolvedValueOnce({
|
|
202
|
+
agentResult: {
|
|
203
|
+
success: true,
|
|
204
|
+
agentRun: { ID: 'new-run-99', ConversationDetailID: 'detail-1' },
|
|
205
|
+
},
|
|
206
|
+
conversationId: 'conv-1',
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const res = await runMutation(realResolver, {});
|
|
210
|
+
|
|
211
|
+
expect(res.success).toBe(true);
|
|
212
|
+
expect(realSeams.syncFeedbackRequestFromConversation).toHaveBeenCalledWith(
|
|
213
|
+
CONSULT_RUN,
|
|
214
|
+
'new-run-99',
|
|
215
|
+
'Proceed with option A',
|
|
216
|
+
USER,
|
|
217
|
+
expect.anything()
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
146
221
|
it('looks up the predecessor on the branch the person\'s message was posted on', async () => {
|
|
147
222
|
await runMutation(resolver, { branchId: 'B' });
|
|
148
|
-
expect(seams.
|
|
223
|
+
expect(seams.findChainablePredecessor).toHaveBeenCalledWith('conv-1', 'agent-1', 'B', expect.anything(), expect.anything());
|
|
149
224
|
await runMutation(resolver, {});
|
|
150
|
-
expect(seams.
|
|
225
|
+
expect(seams.findChainablePredecessor).toHaveBeenLastCalledWith('conv-1', 'agent-1', null, expect.anything(), expect.anything());
|
|
151
226
|
});
|
|
152
227
|
|
|
153
228
|
it('a caller-supplied lastRunId wins and the lookup is skipped', async () => {
|
|
154
229
|
await runMutation(resolver, { lastRunId: 'caller-run', autoPopulate: false });
|
|
155
230
|
|
|
156
|
-
expect(seams.
|
|
231
|
+
expect(seams.findChainablePredecessor).not.toHaveBeenCalled();
|
|
157
232
|
const args = seams.executeAIAgent.mock.calls[0];
|
|
158
233
|
expect(args[SYNC_LAST_RUN]).toBe('caller-run');
|
|
159
234
|
expect(args[SYNC_AUTO]).toBe(false);
|
|
@@ -32,12 +32,22 @@ import type { AgentExecutionStreamingCallback } from '@memberjunction/ai-core-pl
|
|
|
32
32
|
import { AgentRunStatusPublisher } from '../resolvers/AgentRunStatusPublisher.js';
|
|
33
33
|
import type { UserPayload } from '../types.js';
|
|
34
34
|
|
|
35
|
-
const
|
|
35
|
+
const runFields = {
|
|
36
36
|
ID: 'run-1',
|
|
37
|
-
|
|
37
|
+
AgentID: 'agent-1',
|
|
38
|
+
Agent: 'Betty',
|
|
39
|
+
ParentRunID: null,
|
|
40
|
+
Status: 'Running',
|
|
41
|
+
ConversationID: 'conversation-1',
|
|
42
|
+
ConversationDetailID: 'detail-1',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const agentRun = {
|
|
46
|
+
...runFields,
|
|
47
|
+
GetAll: () => ({ ...runFields }),
|
|
38
48
|
};
|
|
39
49
|
|
|
40
|
-
function buildHarness() {
|
|
50
|
+
function buildHarness(run: object = agentRun) {
|
|
41
51
|
const publish = vi.fn().mockResolvedValue(undefined);
|
|
42
52
|
const pubSub = { publish } as unknown as PubSubEngine;
|
|
43
53
|
const userPayload = { sessionId: 'session-1', userRecord: { ID: 'user-1' } } as UserPayload;
|
|
@@ -46,7 +56,7 @@ function buildHarness() {
|
|
|
46
56
|
publisher.OnProgress({
|
|
47
57
|
step: 'initialization',
|
|
48
58
|
message: 'starting',
|
|
49
|
-
metadata: { agentRun },
|
|
59
|
+
metadata: { agentRun: run },
|
|
50
60
|
});
|
|
51
61
|
publish.mockClear();
|
|
52
62
|
const callback: AgentExecutionStreamingCallback = publisher.OnStreaming;
|
|
@@ -81,6 +91,20 @@ describe('AgentRunStatusPublisher streaming wire shape', () => {
|
|
|
81
91
|
});
|
|
82
92
|
});
|
|
83
93
|
|
|
94
|
+
it('sends the run summary on each chunk, not the steps and payload the run holds', () => {
|
|
95
|
+
const getAll = vi.fn(() => ({
|
|
96
|
+
...runFields,
|
|
97
|
+
FinalPayload: '{"rows":[1,2,3]}',
|
|
98
|
+
__runSteps: [{ ID: 'step-1', __promptRun: { Messages: 'the full prompt' } }],
|
|
99
|
+
}));
|
|
100
|
+
const { publish, callback } = buildHarness({ ...runFields, GetAll: getAll });
|
|
101
|
+
|
|
102
|
+
callback({ content: 'Hel', isComplete: false, stepType: 'prompt', kind: 'final-response' });
|
|
103
|
+
|
|
104
|
+
expect(publishedData(publish).agentRun).toEqual(runFields);
|
|
105
|
+
expect(getAll).not.toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
|
|
84
108
|
it('maps isComplete=true to isPartial=false on the final chunk', () => {
|
|
85
109
|
const { publish, callback } = buildHarness();
|
|
86
110
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* everything.txt C9: a custom column the source starts sending is CAPTURED automatically, but
|
|
3
|
+
* turning it into a real column "requires that the user accepts" it.
|
|
4
|
+
*
|
|
5
|
+
* A schema refresh is where an operator asks what changed on the source, so that is where the
|
|
6
|
+
* accumulated overflow columns should be offered. Offering is free; promoting is not. These pin
|
|
7
|
+
* the split, and the one thing that makes acceptance cheap enough to offer at all: the accepted
|
|
8
|
+
* columns ride the refresh's OWN migration instead of starting a second one, because two RSU
|
|
9
|
+
* restarts back-to-back for a single user action is its own incident class here.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
|
|
15
|
+
const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
16
|
+
const PROMOTER = readFileSync(join(__dirname, '..', 'integration', 'CustomColumnPromoter.ts'), 'utf-8');
|
|
17
|
+
|
|
18
|
+
function methodBody(name: string): string {
|
|
19
|
+
const i = SRC.indexOf(`async ${name}(`);
|
|
20
|
+
expect(i, `${name} not found`).toBeGreaterThan(-1);
|
|
21
|
+
const rest = SRC.slice(i);
|
|
22
|
+
const end = rest.search(/\n @(Query|Mutation)\(/);
|
|
23
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('the refresh offers the overflow columns', () => {
|
|
27
|
+
const body = () => methodBody('IntegrationSchemaEvolution');
|
|
28
|
+
|
|
29
|
+
it('reports candidates on the output', () => {
|
|
30
|
+
expect(SRC).toMatch(/CustomColumnCandidates\?: CustomColumnCandidate\[\]/);
|
|
31
|
+
expect(body()).toMatch(/ListCandidates\(/);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('reports them ALWAYS, not only when something was accepted', () => {
|
|
35
|
+
// The listing must not sit inside the acceptance branch — an operator cannot accept a column
|
|
36
|
+
// they were never shown.
|
|
37
|
+
const b = body();
|
|
38
|
+
const list = b.indexOf('ListCandidates(');
|
|
39
|
+
const acceptGate = b.indexOf('if (accepted.size > 0)');
|
|
40
|
+
expect(list).toBeGreaterThan(-1);
|
|
41
|
+
expect(acceptGate).toBeGreaterThan(-1);
|
|
42
|
+
expect(list, 'ListCandidates must run before the acceptance gate').toBeLessThan(acceptGate);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('promotes NOTHING by default', () => {
|
|
46
|
+
expect(SRC).toMatch(/@Arg\("acceptCustomColumns"[\s\S]{0,200}?defaultValue: \[\]/);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('acceptance', () => {
|
|
51
|
+
const body = () => methodBody('IntegrationSchemaEvolution');
|
|
52
|
+
|
|
53
|
+
it('plans only the entities that own an accepted column', () => {
|
|
54
|
+
// Planning a whole entity because ONE of its columns was accepted would promote its other
|
|
55
|
+
// candidates too — exactly the un-asked-for materialisation the gate exists to prevent.
|
|
56
|
+
const b = body();
|
|
57
|
+
// It is not enough that the narrowed list is COMPUTED — it has to be what gets planned.
|
|
58
|
+
expect(b).toMatch(/PlanPromotion\(companyIntegrationID, entitiesWithAccepted\)/);
|
|
59
|
+
expect(b).toMatch(/filter\(c => accepted\.has\(c\.ColumnName\.toLowerCase\(\)\)\)/);
|
|
60
|
+
expect(b).not.toMatch(/PlanPromotion\(companyIntegrationID, mapped\)/);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('folds the accepted migrations into the SAME batch as the refresh DDL', () => {
|
|
64
|
+
// The retrying variant, as every install path uses: it replays only while nothing was applied.
|
|
65
|
+
expect(body()).toMatch(/RunPipelineBatchWithRetry\(\[rsuInput, \.\.\.promotionInputs\]\)/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('still runs a batch when the refresh itself has no DDL', () => {
|
|
69
|
+
// Source shape unchanged but columns accepted: they still need their migration.
|
|
70
|
+
expect(body()).toMatch(/else if \(promotionInputs\.length > 0\)/);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('drops accepted columns from the outstanding offer, so they cannot be accepted twice', () => {
|
|
74
|
+
const b = body();
|
|
75
|
+
// The offer returned must be the FILTERED list, not the raw one.
|
|
76
|
+
expect(b).toMatch(/const acceptedNames = new Set\(promotedColumns\.map\(c => c\.ColumnName\.toLowerCase\(\)\)\)/);
|
|
77
|
+
expect(b).toMatch(/const outstandingCandidates = customColumnCandidates\.filter\(c => !acceptedNames\.has\(c\.ColumnName\.toLowerCase\(\)\)\)/);
|
|
78
|
+
expect(b).toMatch(/CustomColumnCandidates: outstandingCandidates\.length > 0 \? outstandingCandidates : undefined/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('a candidates failure costs the offer, never the refresh', () => {
|
|
82
|
+
const b = body();
|
|
83
|
+
const i = b.indexOf('catch (candErr)');
|
|
84
|
+
expect(i).toBeGreaterThan(-1);
|
|
85
|
+
const handler = b.slice(i, i + 400);
|
|
86
|
+
expect(handler).not.toMatch(/return \{[\s\S]{0,120}Success: false/);
|
|
87
|
+
expect(handler).toMatch(/customColumnCandidates = \[\]/);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('PlanPromotion — the split that makes one batch possible', () => {
|
|
92
|
+
it('is public and runs no pipeline itself', () => {
|
|
93
|
+
expect(PROMOTER).toMatch(/public async PlanPromotion\(/);
|
|
94
|
+
const i = PROMOTER.indexOf('public async PlanPromotion(');
|
|
95
|
+
const end = PROMOTER.indexOf('/** Entry point: promote custom columns');
|
|
96
|
+
const body = PROMOTER.slice(i, end);
|
|
97
|
+
expect(body).not.toMatch(/RunPipelineBatch\(/);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('still purges stale staged keys during planning, before any column is created', () => {
|
|
101
|
+
// This side effect belongs to planning: it is the only thing that ever cleans residue on rows
|
|
102
|
+
// a sync never rewrites, so moving it out of the plan would resurrect phantom candidates.
|
|
103
|
+
const i = PROMOTER.indexOf('public async PlanPromotion(');
|
|
104
|
+
const end = PROMOTER.indexOf('/** Entry point: promote custom columns');
|
|
105
|
+
expect(PROMOTER.slice(i, end)).toMatch(/purgeStaleOverflowKeys/);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('carries the post-restart PendingWork on its first input, so it survives being appended', () => {
|
|
109
|
+
const i = PROMOTER.indexOf('public async PlanPromotion(');
|
|
110
|
+
const end = PROMOTER.indexOf('/** Entry point: promote custom columns');
|
|
111
|
+
const body = PROMOTER.slice(i, end);
|
|
112
|
+
expect(body).toMatch(/batchInputs\[0\]\.PendingWork/);
|
|
113
|
+
expect(body).toMatch(/WorkType: 'promote-columns'/);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('PromoteForSync now goes through it, so the two paths cannot drift', () => {
|
|
117
|
+
expect(PROMOTER).toMatch(/const promotionPlan = await this\.PlanPromotion\(/);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -14,6 +14,8 @@ function summary(over: Partial<SchemaRefreshSummaryLike> = {}): SchemaRefreshSum
|
|
|
14
14
|
Succeeded: true,
|
|
15
15
|
ObjectsCreated: 12,
|
|
16
16
|
ObjectsUpdated: 3,
|
|
17
|
+
FieldsCreated: 69,
|
|
18
|
+
FieldsUpdated: 487,
|
|
17
19
|
UnresolvedObjects: ['Widgets'],
|
|
18
20
|
...over,
|
|
19
21
|
};
|
|
@@ -25,6 +27,8 @@ describe('BuildCreateConnectionMessage', () => {
|
|
|
25
27
|
InProgress: true,
|
|
26
28
|
ObjectsCreated: 0,
|
|
27
29
|
ObjectsUpdated: 0,
|
|
30
|
+
FieldsCreated: 0,
|
|
31
|
+
FieldsUpdated: 0,
|
|
28
32
|
UnresolvedObjects: [],
|
|
29
33
|
}));
|
|
30
34
|
|
|
@@ -33,13 +37,14 @@ describe('BuildCreateConnectionMessage', () => {
|
|
|
33
37
|
// The placeholder zeros must not surface as results.
|
|
34
38
|
expect(msg).not.toMatch(/\d+ created/);
|
|
35
39
|
expect(msg).not.toMatch(/\d+ updated/);
|
|
40
|
+
expect(msg).not.toMatch(/\d+ fields/);
|
|
36
41
|
expect(msg).not.toMatch(/PK-unresolved/);
|
|
37
42
|
});
|
|
38
43
|
|
|
39
44
|
it('reports real counts once the refresh has completed inline', () => {
|
|
40
45
|
const msg = BuildCreateConnectionMessage(true, summary());
|
|
41
46
|
|
|
42
|
-
expect(msg).toContain('12 created');
|
|
47
|
+
expect(msg).toContain('12 objects created');
|
|
43
48
|
expect(msg).toContain('3 updated');
|
|
44
49
|
expect(msg).toContain('1 PK-unresolved');
|
|
45
50
|
expect(msg).not.toMatch(/running/i);
|
|
@@ -74,6 +79,8 @@ describe('failed-refresh reporting', () => {
|
|
|
74
79
|
FailureMessage: 'ConnectionTest failed: No HubSpot credentials found',
|
|
75
80
|
ObjectsCreated: 0,
|
|
76
81
|
ObjectsUpdated: 0,
|
|
82
|
+
FieldsCreated: 0,
|
|
83
|
+
FieldsUpdated: 0,
|
|
77
84
|
UnresolvedObjects: [],
|
|
78
85
|
});
|
|
79
86
|
|
|
@@ -99,10 +106,25 @@ describe('failed-refresh reporting', () => {
|
|
|
99
106
|
|
|
100
107
|
it('still reports counts for a refresh that genuinely succeeded (update)', () => {
|
|
101
108
|
const msg = BuildUpdateConnectionMessage(summary());
|
|
102
|
-
expect(msg).toContain('12 created');
|
|
109
|
+
expect(msg).toContain('12 objects created');
|
|
103
110
|
expect(msg).toContain('3 updated');
|
|
104
111
|
expect(msg).not.toMatch(/FAILED/);
|
|
105
112
|
});
|
|
113
|
+
|
|
114
|
+
it('names the FIELD counts, so a refresh that only changed fields does not read as a no-op', () => {
|
|
115
|
+
// A PRESUPPOSED connector creates no objects on any run. A live PheedLoop refresh that created
|
|
116
|
+
// 69 fields and updated 487 reported "0 created, 0 updated" — literally true of objects, and
|
|
117
|
+
// read by every operator as "the refresh did nothing".
|
|
118
|
+
const fieldsOnly = summary({ ObjectsCreated: 0, ObjectsUpdated: 0, UnresolvedObjects: [] });
|
|
119
|
+
for (const msg of [
|
|
120
|
+
BuildCreateConnectionMessage(true, fieldsOnly),
|
|
121
|
+
BuildUpdateConnectionMessage(fieldsOnly),
|
|
122
|
+
]) {
|
|
123
|
+
expect(msg).toContain('69 fields created');
|
|
124
|
+
expect(msg).toContain('487 updated');
|
|
125
|
+
expect(msg).toContain('0 objects created');
|
|
126
|
+
}
|
|
127
|
+
});
|
|
106
128
|
});
|
|
107
129
|
|
|
108
130
|
describe('BuildDetachedRefreshMessage', () => {
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A schema build materializes the CONNECTION's field rows, read for the build, not whatever the
|
|
3
|
+
* engine happens to hold in memory.
|
|
4
|
+
*
|
|
5
|
+
* A connection's per-object field rows are warmed on demand, and the only site that warms them is
|
|
6
|
+
* the sync loop — so an apply reading them from the engine found `[]` for every object and built a
|
|
7
|
+
* migration with no tables: "Reusing 364 persisted IOs", then `[tables: ]` in 4 ms, no error.
|
|
8
|
+
*
|
|
9
|
+
* These pin the loader the five build paths now share: when it reads, what it reads, that a failed
|
|
10
|
+
* read is loud, and that with no per-connection catalog in scope nothing changes. The wiring into
|
|
11
|
+
* the resolver is pinned at the bottom against the source, because the resolver needs a provider,
|
|
12
|
+
* a connector and an RSU pipeline to invoke.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { RunView, type UserInfo } from '@memberjunction/core';
|
|
18
|
+
import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
|
|
19
|
+
import { WithCatalogScope } from '@memberjunction/integration-engine';
|
|
20
|
+
import { LoadScopedFieldsForBuild, FieldsForBuild, SCOPED_FIELDS_CHUNK } from '../integration/ScopedFieldsForBuild.js';
|
|
21
|
+
|
|
22
|
+
const USER = { ID: 'user-1' } as UserInfo;
|
|
23
|
+
type RunViewParams = Parameters<RunView['RunView']>[0];
|
|
24
|
+
type RunViewResult = Awaited<ReturnType<RunView['RunView']>>;
|
|
25
|
+
|
|
26
|
+
/** A per-connection field row as the per-connection field view returns it. */
|
|
27
|
+
const storedField = (objectID: string, Name: string, Status = 'Active', related: string | null = null) => ({
|
|
28
|
+
ID: `${objectID}-${Name}`, Name, DisplayName: Name, Description: null, Type: 'nvarchar', Length: 100,
|
|
29
|
+
Precision: null, Scale: null, AllowsNull: true, DefaultValue: null, IsPrimaryKey: Name === 'id',
|
|
30
|
+
IsUniqueKey: false, IsReadOnly: false, IsRequired: false, Status,
|
|
31
|
+
CompanyIntegrationObjectID: objectID, RelatedCompanyIntegrationObjectID: related,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** An engine whose per-connection FIELD cache is cold — the state an apply runs in. */
|
|
35
|
+
function installEngine(objectIDs: string[], hasCatalog = true) {
|
|
36
|
+
const getFields = vi.fn(() => []);
|
|
37
|
+
vi.spyOn(IntegrationEngineBase, 'Instance', 'get').mockReturnValue({
|
|
38
|
+
HasCompanyIntegrationCatalog: () => hasCatalog,
|
|
39
|
+
GetActiveIntegrationObjects: () => objectIDs.map(ID => ({ ID, Name: `obj-${ID}` })),
|
|
40
|
+
GetIntegrationObjectFields: getFields,
|
|
41
|
+
} as unknown as IntegrationEngineBase);
|
|
42
|
+
return getFields;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe('LoadScopedFieldsForBuild', () => {
|
|
46
|
+
let runView: ReturnType<typeof vi.spyOn>;
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
49
|
+
runView = vi.spyOn(RunView.prototype, 'RunView');
|
|
50
|
+
});
|
|
51
|
+
afterEach(() => { vi.restoreAllMocks(); });
|
|
52
|
+
|
|
53
|
+
it('reads nothing outside a catalog scope — the build reads the shared catalog as before', async () => {
|
|
54
|
+
installEngine(['CIO-1']);
|
|
55
|
+
expect(await LoadScopedFieldsForBuild('INT-1', USER)).toBeNull();
|
|
56
|
+
expect(runView).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('reads nothing for a connection in scope that has no catalog of its own', async () => {
|
|
60
|
+
installEngine(['CIO-1'], false);
|
|
61
|
+
const loaded = await WithCatalogScope('CI-1', () => LoadScopedFieldsForBuild('INT-1', USER));
|
|
62
|
+
expect(loaded).toBeNull();
|
|
63
|
+
expect(runView).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('reads the connection\'s field rows from the database, bypassing the cache and the row cap', async () => {
|
|
67
|
+
installEngine(['CIO-1', 'CIO-2']);
|
|
68
|
+
runView.mockResolvedValue({
|
|
69
|
+
Success: true,
|
|
70
|
+
Results: [storedField('CIO-1', 'id'), storedField('CIO-1', 'parent', 'Active', 'CIO-2'), storedField('CIO-2', 'id')],
|
|
71
|
+
} as unknown as RunViewResult);
|
|
72
|
+
|
|
73
|
+
const loaded = await WithCatalogScope('CI-1', () => LoadScopedFieldsForBuild('INT-1', USER));
|
|
74
|
+
|
|
75
|
+
const params = runView.mock.calls[0][0] as RunViewParams;
|
|
76
|
+
expect(params.EntityName).toBe('MJ: Company Integration Object Fields');
|
|
77
|
+
expect(params.ExtraFilter).toBe("CompanyIntegrationObjectID IN ('CIO-1','CIO-2')");
|
|
78
|
+
expect(params.ResultType).toBe('simple');
|
|
79
|
+
expect(params.BypassCache).toBe(true);
|
|
80
|
+
expect(params.IgnoreMaxRows).toBe(true);
|
|
81
|
+
// Keyed by lower-cased object id, and carrying the legacy names the build reads.
|
|
82
|
+
const rows = loaded?.get('cio-1') ?? [];
|
|
83
|
+
expect(rows.map(r => r.Name)).toEqual(['id', 'parent']);
|
|
84
|
+
expect(rows[1].IntegrationObjectID).toBe('CIO-1');
|
|
85
|
+
expect(rows[1].RelatedIntegrationObjectID).toBe('CIO-2');
|
|
86
|
+
expect(loaded?.get('cio-2')?.map(r => r.Name)).toEqual(['id']);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it(`reads ${SCOPED_FIELDS_CHUNK} objects per query`, async () => {
|
|
90
|
+
installEngine(Array.from({ length: 2 * SCOPED_FIELDS_CHUNK + 50 }, (_, i) => `CIO-${i}`));
|
|
91
|
+
runView.mockResolvedValue({ Success: true, Results: [] } as unknown as RunViewResult);
|
|
92
|
+
|
|
93
|
+
await WithCatalogScope('CI-1', () => LoadScopedFieldsForBuild('INT-1', USER));
|
|
94
|
+
|
|
95
|
+
expect(runView).toHaveBeenCalledTimes(3);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('THROWS on a failed read rather than building a schema from an empty field list', async () => {
|
|
99
|
+
installEngine(['CIO-1']);
|
|
100
|
+
runView.mockResolvedValue({ Success: false, ErrorMessage: 'timeout', Results: [] } as unknown as RunViewResult);
|
|
101
|
+
|
|
102
|
+
await expect(WithCatalogScope('CI-1', () => LoadScopedFieldsForBuild('INT-1', USER)))
|
|
103
|
+
.rejects.toThrow(/PER_CONNECTION_FIELDS_UNREADABLE/);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('FieldsForBuild', () => {
|
|
108
|
+
afterEach(() => { vi.restoreAllMocks(); });
|
|
109
|
+
|
|
110
|
+
it('with loaded rows, answers from them and never asks the (cold) engine', async () => {
|
|
111
|
+
const getFields = installEngine(['CIO-1']);
|
|
112
|
+
vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
113
|
+
vi.spyOn(RunView.prototype, 'RunView').mockResolvedValue(
|
|
114
|
+
{ Success: true, Results: [storedField('CIO-1', 'id'), storedField('CIO-1', 'name')] } as unknown as RunViewResult,
|
|
115
|
+
);
|
|
116
|
+
const loaded = await WithCatalogScope('CI-1', () => LoadScopedFieldsForBuild('INT-1', USER));
|
|
117
|
+
|
|
118
|
+
expect(FieldsForBuild('CIO-1', loaded, IntegrationEngineBase.Instance).map(f => f.Name)).toEqual(['id', 'name']);
|
|
119
|
+
expect(getFields).not.toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('without loaded rows, reads the engine exactly as before', () => {
|
|
123
|
+
const getFields = installEngine(['IO-1']);
|
|
124
|
+
FieldsForBuild('IO-1', null, IntegrationEngineBase.Instance);
|
|
125
|
+
expect(getFields).toHaveBeenCalledWith('IO-1');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('every schema build reads its fields through the loader', () => {
|
|
130
|
+
const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
131
|
+
|
|
132
|
+
it('all five build paths pass the loaded rows to the rebuild', () => {
|
|
133
|
+
const calls = SRC.match(/this\.buildSourceSchemaFromPersistedRows\([\s\S]*?\);/g) ?? [];
|
|
134
|
+
// ApplySchema, ApplyAll, buildSchemaForConnector, ApplyAllBatch, SchemaEvolution.
|
|
135
|
+
expect(calls.length).toBe(5);
|
|
136
|
+
const inline = /await LoadScopedFieldsForBuild\(companyIntegration\.IntegrationID, user\)/;
|
|
137
|
+
// SchemaEvolution and buildSchemaForConnector load once into `scopedFields`: evolution's
|
|
138
|
+
// field-map reconcile reads the same rows, and buildSchemaForConnector's prefetched (batch)
|
|
139
|
+
// branch needs them for its inactive-row warnings without running the rebuild at all.
|
|
140
|
+
const hoisted = /scopedFields,?\s*\)/;
|
|
141
|
+
for (const call of calls) {
|
|
142
|
+
expect(inline.test(call) || hoisted.test(call)).toBe(true);
|
|
143
|
+
}
|
|
144
|
+
const hoistedCalls = calls.filter(c => !inline.test(c));
|
|
145
|
+
expect(hoistedCalls).toHaveLength(2);
|
|
146
|
+
expect(SRC.match(/const scopedFields = await LoadScopedFieldsForBuild\(companyIntegration\.IntegrationID, user\);/g)).toHaveLength(2);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('the rebuild reads fields only through FieldsForBuild, never the engine directly', () => {
|
|
150
|
+
const start = SRC.indexOf('private buildSourceSchemaFromPersistedRows(');
|
|
151
|
+
expect(start).toBeGreaterThan(-1);
|
|
152
|
+
const body = SRC.slice(start, SRC.indexOf('private async runSchemaRefreshPipeline(', start));
|
|
153
|
+
expect(body).toMatch(/FieldsForBuild\(io\.ID, scopedFields, engine\)/);
|
|
154
|
+
expect(body).not.toMatch(/engine\.GetIntegrationObjectFields\(/);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* A refresh's field-map reconcile and the dependency map read the same cold per-connection fields.
|
|
160
|
+
* Reconcile is the destructive one: an empty field list there DISABLES every Active field map on the
|
|
161
|
+
* object, on a refresh that reports success. The dependency map only needs FK edges, which the scope
|
|
162
|
+
* carries without any field row being warm.
|
|
163
|
+
*/
|
|
164
|
+
describe('a refresh reads the connection\'s fields, never a cold engine cache', () => {
|
|
165
|
+
const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
166
|
+
const body = (start: string, end: string) => {
|
|
167
|
+
const i = SRC.indexOf(start);
|
|
168
|
+
expect(i).toBeGreaterThan(-1);
|
|
169
|
+
return SRC.slice(i, SRC.indexOf(end, i + start.length));
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
it('the field-map reconcile reads through FieldsForBuild with the loaded rows', () => {
|
|
173
|
+
const reconcile = body('private async reconcileFieldMapsForEntityMap(', '\n }\n');
|
|
174
|
+
expect(reconcile).toMatch(/FieldsForBuild\(integrationObjectID, scopedFields, IntegrationEngineBase\.Instance\)/);
|
|
175
|
+
expect(reconcile).not.toMatch(/\.GetIntegrationObjectFields\(/);
|
|
176
|
+
expect(SRC).toMatch(/this\.reconcileFieldMapsForEntityMap\(em, io\.ID, user, md, autoEnableNewColumns, scopedFields\)/);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('the re-key check reads the catalog key through FieldsForBuild with the loaded rows', () => {
|
|
180
|
+
expect(SRC).toMatch(/CatalogIdentityKey\(\s*FieldsForBuild\(io\.ID, scopedFields, IntegrationEngineBase\.Instance\)\s*\)/);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('nothing in the resolver reads field rows from the engine directly', () => {
|
|
184
|
+
expect(SRC).not.toMatch(/\.GetIntegrationObjectFields\(/);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('the dependency map reads FK parents from the edge set, not from field rows', () => {
|
|
188
|
+
const deps = body('private computeObjectDependencyNames(', '\n }\n');
|
|
189
|
+
expect(deps).toMatch(/engine\.GetRelatedIntegrationObjectIDs\(io\.ID\)/);
|
|
190
|
+
expect(deps).not.toMatch(/GetIntegrationObjectFields\(/);
|
|
191
|
+
});
|
|
192
|
+
});
|