@memberjunction/server 6.2.0-edge.4 → 6.2.0-edge.6
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 +47 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +91 -30
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +311 -81
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +17 -3
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +44 -10
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/apiRoot.d.ts +32 -0
- package/dist/apiRoot.d.ts.map +1 -0
- package/dist/apiRoot.js +67 -0
- package/dist/apiRoot.js.map +1 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +27 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/LoginScreenEngine.d.ts +22 -0
- package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
- package/dist/auth/LoginScreenEngine.js +57 -0
- package/dist/auth/LoginScreenEngine.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +52 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +10 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +37 -1
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/localDevAuth.d.ts +79 -0
- package/dist/auth/localDevAuth.d.ts.map +1 -0
- package/dist/auth/localDevAuth.js +439 -0
- package/dist/auth/localDevAuth.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +12 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.d.ts +1 -1
- package/dist/auth/sessionUserClone.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.js +3 -2
- package/dist/auth/sessionUserClone.js.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
- package/dist/broadcast/index.d.ts +6 -0
- package/dist/broadcast/index.d.ts.map +1 -0
- package/dist/broadcast/index.js +6 -0
- package/dist/broadcast/index.js.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
- package/dist/cache.d.ts +3 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +632 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +104 -4
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -8
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +29 -0
- package/dist/coreRoutes.d.ts.map +1 -0
- package/dist/coreRoutes.js +156 -0
- package/dist/coreRoutes.js.map +1 -0
- package/dist/generated/graphql-schemas/__mj.d.ts +1016 -29
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +5125 -434
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +85 -30
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +175 -60
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PubSubManager.d.ts +10 -0
- package/dist/generic/PubSubManager.d.ts.map +1 -1
- package/dist/generic/PubSubManager.js +44 -1
- package/dist/generic/PubSubManager.js.map +1 -1
- package/dist/generic/PushStatusResolver.d.ts +39 -6
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +94 -12
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +168 -69
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +464 -405
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +11 -3
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +91 -13
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
- package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
- package/dist/generic/actionOutputFieldSecurity.js +47 -0
- package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
- package/dist/generic/fieldSecurityTransport.d.ts +30 -0
- package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
- package/dist/generic/fieldSecurityTransport.js +30 -0
- package/dist/generic/fieldSecurityTransport.js.map +1 -0
- package/dist/globalCors.d.ts +33 -0
- package/dist/globalCors.d.ts.map +1 -0
- package/dist/globalCors.js +58 -0
- package/dist/globalCors.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +230 -222
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -1
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +50 -1
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +22 -2
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +2 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +2 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -1
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
- package/dist/realtimeSessions/index.d.ts +2 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -1
- package/dist/realtimeSessions/index.js +2 -0
- package/dist/realtimeSessions/index.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +32 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js +56 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +91 -8
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +127 -20
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.js +19 -3
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +201 -19
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/host-identity.d.ts +1 -1
- package/dist/realtimeWidget/host-identity.js +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts +20 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +17 -1
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +19 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +63 -12
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +1 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +40 -17
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.js +9 -7
- package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts +24 -3
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +57 -8
- package/dist/resolvers/DatasetResolver.js.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
- package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +3 -2
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +46 -3
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +211 -50
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +1 -0
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +87 -11
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +292 -105
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +117 -38
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +6 -3
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +49 -9
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +31 -3
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
- package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.js +5 -3
- package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +2 -0
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +33 -6
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -6
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RSUResolver.d.ts.map +1 -1
- package/dist/resolvers/RSUResolver.js +12 -0
- package/dist/resolvers/RSUResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +18 -10
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +92 -54
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +241 -67
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +4 -4
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTestResolver.js +3 -1
- package/dist/resolvers/RunTestResolver.js.map +1 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
- package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +4 -0
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +66 -13
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +34 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +150 -27
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts +1 -1
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js +2 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
- package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
- package/dist/resolvers/agent-run-data-guard.js +72 -0
- package/dist/resolvers/agent-run-data-guard.js.map +1 -0
- package/dist/rest/EntityCRUDHandler.d.ts +6 -0
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +19 -5
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +8 -5
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
- package/dist/rest/RealtimeRecordingHandler.js +383 -0
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts +9 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +43 -17
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +6 -0
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +6 -0
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/serverExtensionReservedRoots.d.ts +12 -10
- package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
- package/dist/serverExtensionReservedRoots.js +14 -17
- package/dist/serverExtensionReservedRoots.js.map +1 -1
- package/dist/serverRoleConfigUnits.d.ts +29 -0
- package/dist/serverRoleConfigUnits.d.ts.map +1 -0
- package/dist/serverRoleConfigUnits.js +33 -0
- package/dist/serverRoleConfigUnits.js.map +1 -0
- package/dist/services/MJStorageBlobStore.d.ts +8 -1
- package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
- package/dist/services/MJStorageBlobStore.js +25 -19
- package/dist/services/MJStorageBlobStore.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +2 -2
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +5 -3
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -5
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +157 -9
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +59 -2
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +626 -16
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts.map +1 -1
- package/dist/sharedCache.js +3 -0
- package/dist/sharedCache.js.map +1 -1
- package/dist/types.d.ts +23 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +99 -98
- package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
- package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
- package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
- package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
- package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +369 -18
- package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
- package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
- package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
- package/src/__tests__/LoginScreenEngine.test.ts +86 -0
- package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
- package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
- package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
- package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
- package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
- package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
- package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +243 -7
- package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
- package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
- package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +69 -13
- package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
- package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +20 -28
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +264 -0
- package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
- package/src/__tests__/RunTemplateResolver.test.ts +35 -2
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
- package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
- package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
- package/src/__tests__/SessionJanitor.test.ts +365 -10
- package/src/__tests__/SessionManager.test.ts +122 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +468 -23
- package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
- package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
- package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
- package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
- package/src/__tests__/WidgetSessionService.test.ts +583 -4
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -0
- package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
- package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +109 -0
- package/src/__tests__/conversationRunEvents.test.ts +56 -14
- package/src/__tests__/fieldSecurityFixtures.ts +91 -0
- package/src/__tests__/globalCors.test.ts +217 -0
- package/src/__tests__/localDevAuth.test.ts +196 -0
- package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
- package/src/__tests__/multiTenancy.security.test.ts +51 -0
- package/src/__tests__/multiTenancy.test.ts +1 -1
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
- package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +64 -1
- package/src/__tests__/realtimeRecordingHandler.test.ts +669 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
- package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
- package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
- package/src/__tests__/resolverBase.fls.test.ts +146 -97
- package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
- package/src/__tests__/runParamsTimeZone.test.ts +179 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
- package/src/__tests__/runViewProperties.test.ts +114 -0
- package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
- package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
- package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
- package/src/__tests__/sessionUserClone.test.ts +5 -1
- package/src/__tests__/sharedCache.test.ts +5 -3
- package/src/__tests__/startupSweepOrder.test.ts +40 -0
- package/src/__tests__/unifiedAuth.test.ts +28 -0
- package/src/__tests__/widgetGuestElevation.test.ts +2 -0
- package/src/agentSessions/SessionJanitor.ts +369 -93
- package/src/agentSessions/SessionManager.ts +45 -10
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +29 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/index.ts +57 -12
- package/src/auth/initializeProviders.ts +43 -1
- package/src/auth/localDevAuth.ts +528 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
- package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
- package/src/auth/magicLink/types.ts +10 -0
- package/src/auth/sessionUserClone.ts +3 -2
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
- package/src/broadcast/index.ts +5 -0
- package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
- package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
- package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
- package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
- package/src/cache.ts +2 -1
- package/src/config.ts +115 -5
- package/src/context.ts +32 -8
- package/src/coreRoutes.ts +181 -0
- package/src/generated/graphql-schemas/__mj.ts +3684 -284
- package/src/generic/CacheInvalidationResolver.ts +172 -60
- package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
- package/src/generic/PubSubManager.ts +54 -1
- package/src/generic/PushStatusResolver.ts +109 -12
- package/src/generic/ResolverBase.ts +540 -431
- package/src/generic/RunViewResolver.ts +97 -15
- package/src/generic/actionOutputFieldSecurity.ts +75 -0
- package/src/generic/fieldSecurityTransport.ts +62 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +239 -238
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/logging/variablesLoggingMiddleware.ts +59 -3
- package/src/multiTenancy/index.ts +24 -2
- package/src/realtimeSessions/README.md +7 -3
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
- package/src/realtimeSessions/index.ts +2 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +69 -0
- package/src/realtimeSessions/sessionAccess.ts +183 -23
- package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
- package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
- package/src/realtimeSessions/verificationWorkflow.ts +3 -1
- package/src/realtimeWidget/WidgetRouter.ts +22 -4
- package/src/realtimeWidget/WidgetSessionService.ts +252 -17
- package/src/realtimeWidget/host-identity.ts +1 -1
- package/src/realtimeWidget/widgetCore.ts +30 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
- package/src/resolvers/ActionResolver.ts +78 -15
- package/src/resolvers/AdhocQueryResolver.ts +1 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
- package/src/resolvers/AgentSessionResolver.ts +46 -18
- package/src/resolvers/ArtifactFileResolver.ts +9 -8
- package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
- package/src/resolvers/EntityRecordNameResolver.ts +3 -2
- package/src/resolvers/FetchEntityVectorsResolver.ts +248 -55
- package/src/resolvers/FileCategoryResolver.ts +1 -0
- package/src/resolvers/FileResolver.ts +334 -109
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/IntegrationDiscoveryResolver.ts +53 -9
- package/src/resolvers/MergeRecordsResolver.ts +23 -2
- package/src/resolvers/PipelineProgressResolver.ts +12 -3
- package/src/resolvers/QueryResolver.ts +31 -6
- package/src/resolvers/QuerySystemUserResolver.ts +15 -6
- package/src/resolvers/RSUResolver.ts +12 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +114 -66
- package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
- package/src/resolvers/RunAIAgentResolver.ts +296 -85
- package/src/resolvers/RunTemplateResolver.ts +6 -4
- package/src/resolvers/RunTestResolver.ts +3 -1
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/SyncDataResolver.ts +4 -0
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +69 -13
- package/src/resolvers/TransactionGroupResolver.ts +185 -30
- package/src/resolvers/UserViewResolver.ts +2 -1
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -0
- package/src/rest/EntityCRUDHandler.ts +22 -5
- package/src/rest/RESTEndpointHandler.ts +8 -5
- package/src/rest/RealtimeRecordingHandler.ts +467 -0
- package/src/rest/ViewOperationsHandler.ts +52 -18
- package/src/rest/setupRESTEndpoints.ts +6 -0
- package/src/serverExtensionReservedRoots.ts +14 -17
- package/src/serverRoleConfigUnits.ts +48 -0
- package/src/services/MJStorageBlobStore.ts +31 -30
- package/src/services/StartTaskGraphDispatcher.ts +2 -2
- package/src/services/TaskGraphActionRunner.ts +25 -5
- package/src/services/TaskGraphAgentRunner.ts +188 -11
- package/src/services/TaskGraphContinuationDeliverer.ts +738 -16
- package/src/sharedCache.ts +3 -0
- package/src/types.ts +23 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// type-graphql decorators call `Reflect.getMetadata`; the polyfill must load before the resolver.
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Field-level security and identity on `RunEntityAction`.
|
|
6
|
+
*
|
|
7
|
+
* - **Identity.** The record an entity action runs on is loaded for the CALLER. Loaded with no
|
|
8
|
+
* user, it fell back to the provider's current user (the system user on a server), which is
|
|
9
|
+
* full width and ignores the caller's row-level security as well.
|
|
10
|
+
* - **Output.** `ResultData` echoes the run's parameters back to the caller. The record bound as
|
|
11
|
+
* `'Entity Object Data'` (a plain copy of its field values) and any entity object among the
|
|
12
|
+
* parameters are projected for the caller; values of unknown shape are left alone.
|
|
13
|
+
*
|
|
14
|
+
* `RunAction` output gets the same backstop: entity objects are sent as their values projected for
|
|
15
|
+
* the caller, and plain objects shaped like a row of a field-secured entity lose the caller's
|
|
16
|
+
* denied keys.
|
|
17
|
+
*/
|
|
18
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
19
|
+
|
|
20
|
+
vi.mock('type-graphql', () => {
|
|
21
|
+
const noopDecorator = () => () => undefined;
|
|
22
|
+
return {
|
|
23
|
+
Resolver: noopDecorator, Mutation: noopDecorator, Query: noopDecorator, Subscription: noopDecorator,
|
|
24
|
+
ObjectType: noopDecorator, InputType: noopDecorator, Field: noopDecorator, Arg: noopDecorator,
|
|
25
|
+
Args: noopDecorator, Ctx: noopDecorator, PubSub: noopDecorator, Root: noopDecorator,
|
|
26
|
+
Directive: noopDecorator, Authorized: noopDecorator, UseMiddleware: noopDecorator,
|
|
27
|
+
createUnionType: () => class {},
|
|
28
|
+
AuthorizationError: class AuthorizationError extends Error {},
|
|
29
|
+
Float: class {}, Int: class {}, ID: class {},
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const hoisted = vi.hoisted(() => ({
|
|
34
|
+
runEntityAction: vi.fn(),
|
|
35
|
+
runAction: vi.fn(),
|
|
36
|
+
entityActions: [] as unknown[],
|
|
37
|
+
actionParams: [] as unknown[],
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
vi.mock('@memberjunction/actions', () => ({
|
|
41
|
+
ActionEngineServer: {
|
|
42
|
+
Instance: {
|
|
43
|
+
get ActionParams() { return hoisted.actionParams; },
|
|
44
|
+
Actions: [{ ID: 'AC000000-0000-4000-8000-000000000001', Name: 'Return Rows' }],
|
|
45
|
+
Config: vi.fn(async () => undefined),
|
|
46
|
+
RunAction: hoisted.runAction,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
EntityActionEngineServer: {
|
|
50
|
+
Instance: {
|
|
51
|
+
Config: vi.fn(async () => undefined),
|
|
52
|
+
get EntityActions() { return hoisted.entityActions; },
|
|
53
|
+
RunEntityAction: hoisted.runEntityAction,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
import { BaseEntity, EntityInfo, UserInfo } from '@memberjunction/core';
|
|
59
|
+
import { ActionResolver, type EntityActionInput, type RunActionInput } from '../resolvers/ActionResolver.js';
|
|
60
|
+
import type { AppContext } from '../types.js';
|
|
61
|
+
import { EMPLOYEES_ENTITY_ID, EmployeeEntity, FullWidthEmployeeRow, HRUser, INTERN_ROLE_ID, Intern, SECRET_SALARY } from './fieldSecurityFixtures.js';
|
|
62
|
+
|
|
63
|
+
const ENTITY_ACTION_ID = 'EA000000-0000-4000-8000-000000000001';
|
|
64
|
+
const RECORD_PARAM_ID = 'AP000000-0000-4000-8000-000000000001';
|
|
65
|
+
|
|
66
|
+
class EmployeeObject extends BaseEntity {}
|
|
67
|
+
|
|
68
|
+
/** An entity object holding every column: what a load with no user produces on a server. */
|
|
69
|
+
async function fullWidthEntityObject(entityInfo: EntityInfo): Promise<BaseEntity> {
|
|
70
|
+
const entity = new EmployeeObject(entityInfo);
|
|
71
|
+
await entity.LoadFromData(FullWidthEmployeeRow());
|
|
72
|
+
return entity;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function context(entityInfo: EntityInfo, getEntityObject: ReturnType<typeof vi.fn>): AppContext {
|
|
76
|
+
const provider = { Entities: [entityInfo], EntityByID: () => undefined, GetEntityObject: getEntityObject };
|
|
77
|
+
return {
|
|
78
|
+
providers: [{ type: 'Read-Only', provider }],
|
|
79
|
+
userPayload: { userRecord: Intern(), email: 'intern-1@test.com' },
|
|
80
|
+
} as unknown as AppContext;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const input = (): EntityActionInput => ({
|
|
84
|
+
EntityActionID: ENTITY_ACTION_ID,
|
|
85
|
+
InvocationType: 'SingleRecord',
|
|
86
|
+
EntityName: 'Employees',
|
|
87
|
+
PrimaryKey: { KeyValuePairs: [{ Key: 'ID', Value: 'emp-1' }] },
|
|
88
|
+
} as unknown as EntityActionInput);
|
|
89
|
+
|
|
90
|
+
let entityInfo: EntityInfo;
|
|
91
|
+
let getEntityObject: ReturnType<typeof vi.fn>;
|
|
92
|
+
|
|
93
|
+
beforeEach(async () => {
|
|
94
|
+
vi.clearAllMocks();
|
|
95
|
+
entityInfo = EmployeeEntity();
|
|
96
|
+
getEntityObject = vi.fn(async () => ({ InnerLoad: vi.fn(async () => true) }));
|
|
97
|
+
hoisted.entityActions.length = 0;
|
|
98
|
+
hoisted.entityActions.push({
|
|
99
|
+
ID: ENTITY_ACTION_ID,
|
|
100
|
+
EntityID: EMPLOYEES_ENTITY_ID,
|
|
101
|
+
Params: [{ ActionParamID: RECORD_PARAM_ID, ValueType: 'Entity Object Data' }],
|
|
102
|
+
});
|
|
103
|
+
hoisted.actionParams.length = 0;
|
|
104
|
+
hoisted.actionParams.push({ ID: RECORD_PARAM_ID, Name: 'Record' });
|
|
105
|
+
hoisted.runEntityAction.mockResolvedValue({ Success: true, Message: 'ok', Params: [] });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('RunEntityAction: the record is loaded for the caller', () => {
|
|
109
|
+
it('REGRESSION: loads the bound record with the caller as its user', async () => {
|
|
110
|
+
await new ActionResolver().RunEntityAction(input(), context(entityInfo, getEntityObject));
|
|
111
|
+
expect(getEntityObject).toHaveBeenCalledWith('Employees', expect.objectContaining({ ID: 'intern-1' }));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('refuses to run on a record the caller cannot load, with no hint whether it exists', async () => {
|
|
115
|
+
getEntityObject.mockResolvedValue({ InnerLoad: vi.fn(async () => false) });
|
|
116
|
+
const result = await new ActionResolver().RunEntityAction(input(), context(entityInfo, getEntityObject));
|
|
117
|
+
expect(result.Success).toBe(false);
|
|
118
|
+
expect(result.Message).toContain('Record not found or access denied');
|
|
119
|
+
expect(hoisted.runEntityAction).not.toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('RunEntityAction: echoed parameters are projected for the caller', () => {
|
|
124
|
+
it("REGRESSION: the 'Entity Object Data' parameter never carries a denied value", async () => {
|
|
125
|
+
const params = [{ Name: 'Record', Value: FullWidthEmployeeRow(), Type: 'Input' }];
|
|
126
|
+
hoisted.runEntityAction.mockResolvedValue({ Success: true, Message: 'ok', Params: params, RunParams: { Params: params } });
|
|
127
|
+
|
|
128
|
+
const result = await new ActionResolver().RunEntityAction(input(), context(entityInfo, getEntityObject));
|
|
129
|
+
|
|
130
|
+
expect(result.Success).toBe(true);
|
|
131
|
+
expect(result.ResultData).not.toContain(String(SECRET_SALARY));
|
|
132
|
+
expect(JSON.parse(result.ResultData!).Params[0].Value).toEqual({ ID: 'emp-1', Name: 'Ada', Notes: 'n' });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('REGRESSION: an entity object among the parameters is sent as its projected field values', async () => {
|
|
136
|
+
const params = [{ Name: 'Entity', Value: await fullWidthEntityObject(entityInfo), Type: 'Input' }];
|
|
137
|
+
hoisted.runEntityAction.mockResolvedValue({ Success: true, Message: 'ok', Params: params, RunParams: { Params: params } });
|
|
138
|
+
|
|
139
|
+
const result = await new ActionResolver().RunEntityAction(input(), context(entityInfo, getEntityObject));
|
|
140
|
+
|
|
141
|
+
expect(result.ResultData).not.toContain(String(SECRET_SALARY));
|
|
142
|
+
expect(JSON.parse(result.ResultData!).Params[0].Value).toMatchObject({ ID: 'emp-1', Name: 'Ada' });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('REGRESSION: an entity object of an entity the caller cannot read is sent as null, not as its other fields', async () => {
|
|
146
|
+
const unreadable = EmployeeEntity();
|
|
147
|
+
unreadable.Permissions.splice(0, unreadable.Permissions.length, ...unreadable.Permissions.filter(p => p.RoleID !== INTERN_ROLE_ID));
|
|
148
|
+
const object = await fullWidthEntityObject(unreadable);
|
|
149
|
+
const params = [
|
|
150
|
+
{ Name: 'One', Value: object, Type: 'Output' },
|
|
151
|
+
{ Name: 'Many', Value: [object], Type: 'Output' },
|
|
152
|
+
];
|
|
153
|
+
hoisted.runEntityAction.mockResolvedValue({ Success: true, Message: 'ok', Params: params, RunParams: { Params: params } });
|
|
154
|
+
|
|
155
|
+
const result = await new ActionResolver().RunEntityAction(input(), context(entityInfo, getEntityObject));
|
|
156
|
+
|
|
157
|
+
const sent = JSON.parse(result.ResultData!) as { Params: { Value: unknown }[]; RunParams: { Params: { Value: unknown }[] } };
|
|
158
|
+
expect(sent.Params[0].Value).toBeNull();
|
|
159
|
+
expect(sent.Params[1].Value).toEqual([null]);
|
|
160
|
+
expect(sent.RunParams.Params[0].Value).toBeNull();
|
|
161
|
+
expect(result.ResultData).not.toContain('Ada');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('leaves a parameter of unknown shape alone', async () => {
|
|
165
|
+
const params = [{ Name: 'Other', Value: { Salary: 1 }, Type: 'Output' }];
|
|
166
|
+
hoisted.runEntityAction.mockResolvedValue({ Success: true, Message: 'ok', Params: params });
|
|
167
|
+
|
|
168
|
+
const result = await new ActionResolver().RunEntityAction(input(), context(entityInfo, getEntityObject));
|
|
169
|
+
|
|
170
|
+
expect(JSON.parse(result.ResultData!).Params[0].Value).toEqual({ Salary: 1 });
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// ─── RunAction: general action output ────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
const ACTION_ID = 'AC000000-0000-4000-8000-000000000001';
|
|
177
|
+
|
|
178
|
+
/** An Owner may run any action, so these tests reach the output without the authorization lookup. */
|
|
179
|
+
function owner(user: UserInfo): UserInfo {
|
|
180
|
+
user.Type = 'Owner';
|
|
181
|
+
return user;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function runActionContext(entity: EntityInfo, user: UserInfo): AppContext {
|
|
185
|
+
const provider = { Entities: [entity], EntityByID: () => undefined };
|
|
186
|
+
return {
|
|
187
|
+
providers: [{ type: 'Read-Write', provider }],
|
|
188
|
+
userPayload: { userRecord: user, email: user.Email },
|
|
189
|
+
} as unknown as AppContext;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function runActionReturning(value: unknown, user: UserInfo): Promise<unknown> {
|
|
193
|
+
hoisted.runAction.mockResolvedValue({ Success: true, Message: 'ok', Params: [{ Name: 'Rows', Type: 'Output', Value: value }] });
|
|
194
|
+
const result = await new ActionResolver().RunAction({ ActionID: ACTION_ID } as RunActionInput, runActionContext(entityInfo, user));
|
|
195
|
+
expect(result.Success).toBe(true);
|
|
196
|
+
return JSON.parse(result.ResultData!)[0].Value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
describe('RunAction: output parameters are projected for the caller', () => {
|
|
200
|
+
it('REGRESSION: a system-loaded entity object is sent as its projected values to a restricted caller', async () => {
|
|
201
|
+
const value: BaseEntity = await fullWidthEntityObject(entityInfo);
|
|
202
|
+
expect(await runActionReturning(value, owner(Intern()))).toEqual({ ID: 'emp-1', Name: 'Ada', Notes: 'n' });
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('REGRESSION: the same entity object keeps the field for a caller allowed to read it', async () => {
|
|
206
|
+
const value: BaseEntity = await fullWidthEntityObject(entityInfo);
|
|
207
|
+
expect(await runActionReturning(value, owner(HRUser()))).toMatchObject({ ID: 'emp-1', Salary: SECRET_SALARY });
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('REGRESSION: a GetAll()-shaped row of a field-secured entity loses the denied field', async () => {
|
|
211
|
+
const value = await runActionReturning([FullWidthEmployeeRow()], owner(Intern()));
|
|
212
|
+
expect(value).toEqual([{ ID: 'emp-1', Name: 'Ada', Notes: 'n' }]);
|
|
213
|
+
expect(JSON.stringify(value)).not.toContain(String(SECRET_SALARY));
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('leaves a non-entity-shaped object untouched', async () => {
|
|
217
|
+
expect(await runActionReturning({ Salary: 1, Total: 2 }, owner(Intern()))).toEqual({ Salary: 1, Total: 2 });
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("REGRESSION: an entity object of an entity the caller cannot read is not exposed", async () => {
|
|
221
|
+
const unreadable = EmployeeEntity();
|
|
222
|
+
unreadable.Permissions.splice(0, unreadable.Permissions.length, ...unreadable.Permissions.filter(p => p.RoleID !== INTERN_ROLE_ID));
|
|
223
|
+
const object = await fullWidthEntityObject(unreadable);
|
|
224
|
+
hoisted.runAction.mockResolvedValue({ Success: true, Message: 'ok', Params: [
|
|
225
|
+
{ Name: 'One', Type: 'Output', Value: object },
|
|
226
|
+
{ Name: 'Many', Type: 'Output', Value: [object] },
|
|
227
|
+
] });
|
|
228
|
+
const result = await new ActionResolver().RunAction({ ActionID: ACTION_ID } as RunActionInput, runActionContext(unreadable, owner(Intern())));
|
|
229
|
+
// Exactly the shape this output had before entity objects were projected: the single object's
|
|
230
|
+
// Value dropped, the array's item nulled (and, as ever, the list serialized index-keyed).
|
|
231
|
+
expect(JSON.parse(result.ResultData!)).toEqual({ 0: { Name: 'One', Type: 'Output' }, 1: { Name: 'Many', Type: 'Output', Value: [null] } });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('sends the full row to a caller denied nothing', async () => {
|
|
235
|
+
expect(await runActionReturning([FullWidthEmployeeRow()], owner(HRUser()))).toEqual([FullWidthEmployeeRow()]);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression suite for the ad-hoc read-only execution path being SQL-Server-shaped.
|
|
3
|
+
*
|
|
4
|
+
* The bug — `ExecuteAdhocQuery` opened with
|
|
5
|
+
* `GetReadOnlyDataSource(context.dataSources, …)`, which looks for an **mssql
|
|
6
|
+
* `ConnectionPool`**. A PostgreSQL provider never populates `context.dataSources`
|
|
7
|
+
* with one, so every ad-hoc query on PostgreSQL returned
|
|
8
|
+
* "No read-only data source available for ad-hoc query execution".
|
|
9
|
+
*
|
|
10
|
+
* `next` fixed the execution path (R31): the resolver now hands the SQL to the
|
|
11
|
+
* read-only PROVIDER's `RunQuery`, and renders it for that provider's platform.
|
|
12
|
+
* This suite pins the dialect half of that contract — the platform comes from the
|
|
13
|
+
* provider the SQL runs on, never from a literal, and nothing reaches for mssql —
|
|
14
|
+
* and drives the resolver against a PostgreSQL provider with no mssql pool at all.
|
|
15
|
+
* Paging, the timeout clamp and the read authorizer have their own suites.
|
|
16
|
+
*/
|
|
17
|
+
import 'reflect-metadata';
|
|
18
|
+
|
|
19
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
20
|
+
import { readFileSync } from 'fs';
|
|
21
|
+
import { resolve } from 'path';
|
|
22
|
+
|
|
23
|
+
// `RunQueryResultType` is a type-graphql object type whose fields rely on
|
|
24
|
+
// `emitDecoratorMetadata`. esbuild (what vitest transforms with) cannot emit it,
|
|
25
|
+
// so importing QueryResolver for real fails at decoration time. The resolver only
|
|
26
|
+
// uses the class as the @Query return marker, so a bare stand-in is enough and
|
|
27
|
+
// keeps the REAL AdhocQueryResolver under test.
|
|
28
|
+
vi.mock('../resolvers/QueryResolver.js', () => ({
|
|
29
|
+
RunQueryResultType: class RunQueryResultType {},
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// The server config is read from mj.config at import; only the timeout cap is used here.
|
|
33
|
+
vi.mock('../config.js', () => ({ configInfo: { databaseSettings: { requestTimeout: 120_000 } } }));
|
|
34
|
+
|
|
35
|
+
import { AdhocQueryResolver } from '../resolvers/AdhocQueryResolver.js';
|
|
36
|
+
import type { AppContext } from '../types.js';
|
|
37
|
+
|
|
38
|
+
const ADHOC_RESOLVER_PATH = resolve(__dirname, '../resolvers/AdhocQueryResolver.ts');
|
|
39
|
+
|
|
40
|
+
function readResolverSource(): string {
|
|
41
|
+
return readFileSync(ADHOC_RESOLVER_PATH, 'utf8');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type RunQueryCall = { SQL: string; MaxRows?: number; StartRow?: number };
|
|
45
|
+
|
|
46
|
+
function postgresContext(calls: RunQueryCall[], rows: Record<string, unknown>[]): AppContext {
|
|
47
|
+
const provider = {
|
|
48
|
+
PlatformKey: 'postgresql',
|
|
49
|
+
RunQuery: async (params: RunQueryCall) => {
|
|
50
|
+
calls.push(params);
|
|
51
|
+
return { Success: true, Results: rows, RowCount: rows.length, TotalRowCount: rows.length };
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
// Deliberately EMPTY: a PostgreSQL tenant has no mssql ConnectionPool here.
|
|
56
|
+
// If the resolver still reaches for one, these tests fail.
|
|
57
|
+
dataSources: [],
|
|
58
|
+
providers: [{ type: 'Read-Only', provider }],
|
|
59
|
+
userPayload: { userRecord: { ID: 'u-1', Name: 'Tester' } },
|
|
60
|
+
} as unknown as AppContext;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
vi.restoreAllMocks();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('AdhocQueryResolver dialect source-shape contract', () => {
|
|
68
|
+
it('must NOT gate execution on a SQL-Server-only read-only data source', () => {
|
|
69
|
+
expect(readResolverSource()).not.toContain('GetReadOnlyDataSource');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('must NOT depend on the mssql driver', () => {
|
|
73
|
+
const src = readResolverSource();
|
|
74
|
+
expect(src).not.toMatch(/^import .* from 'mssql';$/m);
|
|
75
|
+
expect(src).not.toContain('new sql.Request(');
|
|
76
|
+
expect(src).not.toContain('sql.ConnectionPool');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('renders for the platform of the read-only provider the SQL runs on, not a literal', () => {
|
|
80
|
+
const src = readResolverSource();
|
|
81
|
+
expect(src).toContain('GetReadOnlyProvider(context.providers');
|
|
82
|
+
expect(src).toContain('Platform: provider.PlatformKey');
|
|
83
|
+
expect(src).not.toMatch(/platform\s*(:\s*DatabasePlatform\s*)?=\s*'sqlserver'/);
|
|
84
|
+
expect(src).not.toMatch(/Platform:\s*'sqlserver'/);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('ExecuteAdhocQuery on a PostgreSQL tenant', () => {
|
|
89
|
+
it('runs on the read-only provider instead of refusing for want of an mssql pool', async () => {
|
|
90
|
+
const resolver = new AdhocQueryResolver();
|
|
91
|
+
const authorize = vi.spyOn(resolver, 'AuthorizeRenderedSQL').mockReturnValue(null);
|
|
92
|
+
const calls: RunQueryCall[] = [];
|
|
93
|
+
|
|
94
|
+
const result = await resolver.ExecuteAdhocQuery(
|
|
95
|
+
{ SQL: 'SELECT ID FROM __mj.vwUsers' },
|
|
96
|
+
postgresContext(calls, [{ ID: '1' }, { ID: '2' }]),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(result.ErrorMessage).not.toContain('No read-only data source');
|
|
100
|
+
expect(result.Success).toBe(true);
|
|
101
|
+
expect(calls).toHaveLength(1);
|
|
102
|
+
expect(calls[0].SQL).toBe('SELECT ID FROM __mj.vwUsers');
|
|
103
|
+
expect(JSON.parse(result.Results)).toEqual([{ ID: '1' }, { ID: '2' }]);
|
|
104
|
+
// The SQL is authorized (and so rendered) against the same PostgreSQL provider it runs on.
|
|
105
|
+
expect((authorize.mock.calls[0][1] as { PlatformKey: string }).PlatformKey).toBe('postgresql');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('ExecuteAdhocQuery refusal path', () => {
|
|
110
|
+
it('still refuses when there is no read-only provider at all', async () => {
|
|
111
|
+
const resolver = new AdhocQueryResolver();
|
|
112
|
+
|
|
113
|
+
const result = await resolver.ExecuteAdhocQuery(
|
|
114
|
+
{ SQL: 'SELECT ID FROM __mj.vwUsers' },
|
|
115
|
+
{ dataSources: [], providers: [], userPayload: { userRecord: { ID: 'u-1' } } } as unknown as AppContext,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(result.Success).toBe(false);
|
|
119
|
+
expect(result.ErrorMessage).toContain('No read-only data source available for ad-hoc query execution');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('does NOT fall back to a read-write provider', async () => {
|
|
123
|
+
const resolver = new AdhocQueryResolver();
|
|
124
|
+
|
|
125
|
+
const result = await resolver.ExecuteAdhocQuery(
|
|
126
|
+
{ SQL: 'SELECT ID FROM __mj.vwUsers' },
|
|
127
|
+
{
|
|
128
|
+
dataSources: [],
|
|
129
|
+
providers: [{ type: 'Read-Write', provider: { PlatformKey: 'postgresql', RunQuery: async () => ({ Success: true, Results: [] }) } }],
|
|
130
|
+
userPayload: { userRecord: { ID: 'u-1' } },
|
|
131
|
+
} as unknown as AppContext,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(result.Success).toBe(false);
|
|
135
|
+
expect(result.ErrorMessage).toContain('No read-only data source available');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -29,6 +29,7 @@ vi.mock('@memberjunction/graphql-dataprovider', () => ({
|
|
|
29
29
|
import type { ExecuteAgentResult } from '@memberjunction/ai-core-plus';
|
|
30
30
|
import { ConversationStreaming, type IConversationsRuntimeContext } from '@memberjunction/conversations-runtime';
|
|
31
31
|
import { AgentRunStatusPublisher } from '../resolvers/AgentRunStatusPublisher.js';
|
|
32
|
+
import { StatusUpdatesFilter, type PushStatusNotificationPayload } from '../generic/PushStatusResolver.js';
|
|
32
33
|
import type { UserPayload } from '../types.js';
|
|
33
34
|
|
|
34
35
|
const run = {
|
|
@@ -56,6 +57,31 @@ function envelopes(publish: ReturnType<typeof vi.fn>): Array<Record<string, unkn
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
describe('AgentRunStatusPublisher', () => {
|
|
60
|
+
it('stamps the reply row on every message when it has one, so a reconnecting client can find its row', () => {
|
|
61
|
+
const publish = vi.fn().mockResolvedValue(undefined);
|
|
62
|
+
const pubSub = { publish } as unknown as PubSubEngine;
|
|
63
|
+
const userPayload = { sessionId: 'session-1', userRecord: { ID: 'user-1' } } as UserPayload;
|
|
64
|
+
const made = new AgentRunStatusPublisher(pubSub, userPayload, 'session-1', 'reply-7');
|
|
65
|
+
const runWithoutDetail = { ...run, ConversationDetailID: undefined, GetAll: () => ({ ID: 'run-1' }) };
|
|
66
|
+
|
|
67
|
+
made.OnProgress({ step: 'prompt_execution', message: 'Writing', metadata: { agentRun: runWithoutDetail } });
|
|
68
|
+
made.OnStreaming({ content: 'Hel', isComplete: false, stepType: 'prompt', kind: 'final-response' });
|
|
69
|
+
made.PublishFinal({ success: true, agentRun: runWithoutDetail } as unknown as ExecuteAgentResult);
|
|
70
|
+
|
|
71
|
+
const details = envelopes(publish).map((row) => (row.data as { conversationDetailId?: string }).conversationDetailId);
|
|
72
|
+
expect(details).toEqual(['reply-7', 'reply-7', 'reply-7', 'reply-7']);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('leaves progress and streaming messages without a reply row when it has none, as before', () => {
|
|
76
|
+
const { publish, made } = publisher();
|
|
77
|
+
|
|
78
|
+
made.OnProgress({ step: 'prompt_execution', message: 'Writing', metadata: { agentRun: run } });
|
|
79
|
+
made.OnStreaming({ content: 'Hel', isComplete: false, stepType: 'prompt', kind: 'final-response' });
|
|
80
|
+
|
|
81
|
+
const sent = envelopes(publish).map((row) => row.data as Record<string, unknown>);
|
|
82
|
+
expect(sent.every((data) => !('conversationDetailId' in data))).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
59
85
|
it('publishes the resolver sequence: a significant step, streamed text, the partial result, then the completion', () => {
|
|
60
86
|
const { publish, made } = publisher();
|
|
61
87
|
|
|
@@ -173,3 +199,26 @@ describe('AgentRunStatusPublisher', () => {
|
|
|
173
199
|
expect(streaming.GetRecentCompletion('detail-1')?.agentRunId).toBe('run-1');
|
|
174
200
|
});
|
|
175
201
|
});
|
|
202
|
+
|
|
203
|
+
describe('AgentRunStatusPublisher — a widget guest elevated to the system user (#4931)', () => {
|
|
204
|
+
const GUEST = { ID: 'anonymous-user', IsMagicLinkAnonymous: true, AnonymousVisitorID: 'sid-a', MagicLinkScope: { ResourceID: 'sid-a' } };
|
|
205
|
+
const SYSTEM = { ID: 'system-user' };
|
|
206
|
+
|
|
207
|
+
it("stamps the guest (ElevatedFrom), not the elevated user, so the guest's own socket receives the run's events", () => {
|
|
208
|
+
const publish = vi.fn().mockResolvedValue(undefined);
|
|
209
|
+
const elevated = { sessionId: 'session-1', userRecord: SYSTEM, isSystemUser: true, ElevatedFrom: GUEST } as unknown as UserPayload;
|
|
210
|
+
const made = new AgentRunStatusPublisher({ publish } as unknown as PubSubEngine, elevated, 'session-1');
|
|
211
|
+
|
|
212
|
+
made.PublishFinal({ success: true, agentRun: run, payload: { text: 'Hello' } } as ExecuteAgentResult, 'detail-1', '{"text":"Hello"}');
|
|
213
|
+
made.PublishFailure('detail-1', 'boom');
|
|
214
|
+
|
|
215
|
+
const owners = publish.mock.calls.map((call) => (call[1] as { ownerUserId: string }).ownerUserId);
|
|
216
|
+
expect(owners.length).toBeGreaterThan(0);
|
|
217
|
+
// An anonymous visitor's owner key carries its scope identity, so another visitor cannot receive these events.
|
|
218
|
+
expect(owners.every((owner) => owner === 'anonymous-user|sid-a')).toBe(true);
|
|
219
|
+
const guestConnection = { userPayload: { sessionId: 'session-1', userRecord: GUEST } as unknown as UserPayload };
|
|
220
|
+
for (const call of publish.mock.calls) {
|
|
221
|
+
expect(StatusUpdatesFilter({ payload: call[1] as PushStatusNotificationPayload, args: { sessionId: 'session-1' }, context: guestConnection })).toBe(true);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
3
|
+
import type { AppContext } from '../types.js';
|
|
4
|
+
|
|
5
|
+
vi.mock('type-graphql', () => {
|
|
6
|
+
const noopDecorator = () => () => undefined;
|
|
7
|
+
return {
|
|
8
|
+
Resolver: noopDecorator,
|
|
9
|
+
Mutation: noopDecorator,
|
|
10
|
+
Arg: noopDecorator,
|
|
11
|
+
Ctx: noopDecorator,
|
|
12
|
+
ObjectType: noopDecorator,
|
|
13
|
+
Field: noopDecorator,
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
vi.mock('../generic/ResolverBase.js', () => ({
|
|
18
|
+
ResolverBase: class {
|
|
19
|
+
protected GetUserFromPayload(payload?: { userRecord?: UserInfo }): UserInfo | undefined {
|
|
20
|
+
return payload?.userRecord;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const closeSessionMock = vi.fn(async () => true);
|
|
26
|
+
const heartbeatMock = vi.fn(async () => true);
|
|
27
|
+
|
|
28
|
+
vi.mock('../agentSessions/index.js', () => ({
|
|
29
|
+
SessionManager: class {
|
|
30
|
+
CloseSession = closeSessionMock;
|
|
31
|
+
Heartbeat = heartbeatMock;
|
|
32
|
+
},
|
|
33
|
+
SessionAuthorizationError: class extends Error {},
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
import { AgentSessionResolver } from '../resolvers/AgentSessionResolver.js';
|
|
37
|
+
|
|
38
|
+
const uuid = (a: string, b: string) =>
|
|
39
|
+
[a.repeat(8), b.repeat(4), '4' + b.repeat(3), '8' + b.repeat(3), b.repeat(12)].join('-');
|
|
40
|
+
const ANON = uuid('A', '0');
|
|
41
|
+
const SESSION = uuid('1', '1');
|
|
42
|
+
const CONVERSATION = uuid('2', '2');
|
|
43
|
+
|
|
44
|
+
const guest = (visitorId: string) =>
|
|
45
|
+
({
|
|
46
|
+
ID: ANON,
|
|
47
|
+
IsMagicLinkAnonymous: true,
|
|
48
|
+
// An unscoped multi-use invite: redemptions are told apart by mj_sid (a resource scope would be shared).
|
|
49
|
+
AnonymousVisitorID: visitorId,
|
|
50
|
+
}) as unknown as UserInfo;
|
|
51
|
+
|
|
52
|
+
function makeProvider(sessionFound = true, externalId = 'visitor-1'): IMetadataProvider {
|
|
53
|
+
return {
|
|
54
|
+
GetEntityObject: vi.fn(async (entityName: string) =>
|
|
55
|
+
entityName === 'MJ: AI Agent Sessions'
|
|
56
|
+
? { UserID: ANON, ConversationID: CONVERSATION, Load: vi.fn(async () => sessionFound) }
|
|
57
|
+
: { ExternalID: externalId, Load: vi.fn(async () => true) },
|
|
58
|
+
),
|
|
59
|
+
} as unknown as IMetadataProvider;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function makeCtx(user: UserInfo, provider: IMetadataProvider): AppContext {
|
|
63
|
+
return {
|
|
64
|
+
userPayload: { email: 'anonymous@magic-link.local', sessionId: 's', userRecord: user },
|
|
65
|
+
providers: [{ provider, type: 'Read-Write' }],
|
|
66
|
+
} as unknown as AppContext;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe('AgentSessionResolver anonymous visitor ownership', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
closeSessionMock.mockClear();
|
|
72
|
+
heartbeatMock.mockClear();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('CloseAgentSession allows the matching anonymous visitor and refuses a second anonymous visitor', async () => {
|
|
76
|
+
const resolver = new AgentSessionResolver();
|
|
77
|
+
const provider = makeProvider(true, 'visitor-1');
|
|
78
|
+
|
|
79
|
+
await expect(resolver.CloseAgentSession(SESSION, makeCtx(guest('visitor-1'), provider))).resolves.toBe(true);
|
|
80
|
+
expect(closeSessionMock).toHaveBeenCalledOnce();
|
|
81
|
+
|
|
82
|
+
await expect(resolver.CloseAgentSession(SESSION, makeCtx(guest('visitor-2'), provider))).rejects.toThrow(
|
|
83
|
+
'Not authorized: you do not own this agent session',
|
|
84
|
+
);
|
|
85
|
+
expect(closeSessionMock).toHaveBeenCalledOnce();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('AgentSessionHeartbeat allows the matching anonymous visitor and refuses a second anonymous visitor', async () => {
|
|
89
|
+
const resolver = new AgentSessionResolver();
|
|
90
|
+
const provider = makeProvider(true, 'visitor-1');
|
|
91
|
+
|
|
92
|
+
await expect(resolver.AgentSessionHeartbeat(SESSION, makeCtx(guest('visitor-1'), provider))).resolves.toBe(true);
|
|
93
|
+
expect(heartbeatMock).toHaveBeenCalledOnce();
|
|
94
|
+
|
|
95
|
+
await expect(resolver.AgentSessionHeartbeat(SESSION, makeCtx(guest('visitor-2'), provider))).rejects.toThrow(
|
|
96
|
+
'Not authorized: you do not own this agent session',
|
|
97
|
+
);
|
|
98
|
+
expect(heartbeatMock).toHaveBeenCalledOnce();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('throws the expected not-found message when the session does not exist', async () => {
|
|
102
|
+
const resolver = new AgentSessionResolver();
|
|
103
|
+
const provider = makeProvider(false);
|
|
104
|
+
|
|
105
|
+
await expect(resolver.CloseAgentSession(SESSION, makeCtx(guest('visitor-1'), provider))).rejects.toThrow(
|
|
106
|
+
`Agent session ${SESSION} not found`,
|
|
107
|
+
);
|
|
108
|
+
await expect(resolver.AgentSessionHeartbeat(SESSION, makeCtx(guest('visitor-1'), provider))).rejects.toThrow(
|
|
109
|
+
`Agent session ${SESSION} not found`,
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('CloseAgentSession close reason (MJ#6013)', () => {
|
|
115
|
+
beforeEach(() => closeSessionMock.mockClear());
|
|
116
|
+
|
|
117
|
+
it('passes a client-reportable reason through, so a start that failed is recorded as an Error', async () => {
|
|
118
|
+
const resolver = new AgentSessionResolver();
|
|
119
|
+
const provider = makeProvider(true, 'visitor-1');
|
|
120
|
+
await expect(resolver.CloseAgentSession(SESSION, makeCtx(guest('visitor-1'), provider), 'Error')).resolves.toBe(true);
|
|
121
|
+
expect(closeSessionMock).toHaveBeenCalledWith(SESSION, expect.anything(), provider, 'Error');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('defaults to Explicit when the client sends no reason (older clients)', async () => {
|
|
125
|
+
const resolver = new AgentSessionResolver();
|
|
126
|
+
const provider = makeProvider(true, 'visitor-1');
|
|
127
|
+
await expect(resolver.CloseAgentSession(SESSION, makeCtx(guest('visitor-1'), provider))).resolves.toBe(true);
|
|
128
|
+
expect(closeSessionMock).toHaveBeenCalledWith(SESSION, expect.anything(), provider, 'Explicit');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('refuses a reason only the server may stamp', async () => {
|
|
132
|
+
const resolver = new AgentSessionResolver();
|
|
133
|
+
const provider = makeProvider(true, 'visitor-1');
|
|
134
|
+
await expect(resolver.CloseAgentSession(SESSION, makeCtx(guest('visitor-1'), provider), 'Janitor')).rejects.toThrow(/close reason/i);
|
|
135
|
+
expect(closeSessionMock).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -14,10 +14,19 @@
|
|
|
14
14
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
15
15
|
|
|
16
16
|
const runPipelineBatchMock = vi.fn();
|
|
17
|
+
/**
|
|
18
|
+
* MJ-APPLY-6 — the BARE, non-retrying entry point. Bound separately so a regression back to it is
|
|
19
|
+
* an assertion failure rather than an invisible loss of the retry. Promotion runs unattended
|
|
20
|
+
* mid-sync; a transient pipeline step failing there drops the whole promotion silently.
|
|
21
|
+
*/
|
|
22
|
+
const runPipelineBatchBareMock = vi.fn();
|
|
17
23
|
vi.mock('@memberjunction/schema-engine', () => ({
|
|
18
24
|
RuntimeSchemaManager: {
|
|
19
25
|
get Instance() {
|
|
20
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
RunPipelineBatch: runPipelineBatchBareMock,
|
|
28
|
+
RunPipelineBatchWithRetry: runPipelineBatchMock,
|
|
29
|
+
};
|
|
21
30
|
},
|
|
22
31
|
},
|
|
23
32
|
}));
|
|
@@ -104,6 +113,7 @@ function stubPromoter(
|
|
|
104
113
|
|
|
105
114
|
beforeEach(() => {
|
|
106
115
|
runPipelineBatchMock.mockReset();
|
|
116
|
+
runPipelineBatchBareMock.mockReset();
|
|
107
117
|
});
|
|
108
118
|
|
|
109
119
|
describe('PromoteForSync — one batched RSU pass', () => {
|
|
@@ -122,6 +132,10 @@ describe('PromoteForSync — one batched RSU pass', () => {
|
|
|
122
132
|
const result = await promoter.PromoteForSync('ci-1', ['A', 'B', 'C']);
|
|
123
133
|
|
|
124
134
|
expect(runPipelineBatchMock).toHaveBeenCalledTimes(1);
|
|
135
|
+
// MJ-APPLY-6 — the ONE pass has to be the RETRYING one. Promotion is unattended: a
|
|
136
|
+
// transient ExecuteMigration/RunCodeGen/Compile/Restart failure with no replay drops every
|
|
137
|
+
// accepted column back into overflow and nothing reruns it.
|
|
138
|
+
expect(runPipelineBatchBareMock).not.toHaveBeenCalled();
|
|
125
139
|
expect(runPipelineBatchMock.mock.calls[0][0]).toHaveLength(3);
|
|
126
140
|
expect(result.Promoted).toBe(true);
|
|
127
141
|
expect(result.ColumnsAdded).toEqual([
|