@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,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `RunAIAgentFromConversationDetail` after A19: only the server writes agent replies.
|
|
3
|
+
*
|
|
4
|
+
* The mutation names the user's message. The server loads the history, creates the agent's reply row as the person
|
|
5
|
+
* asking (marked as server-authored by `AgentRunner.CreateAgentReplyDetail`), and runs the agent into it. The
|
|
6
|
+
* fire-and-forget acknowledgement names the row (`conversationDetailId`, `createdAt`) and is returned before the run
|
|
7
|
+
* publishes anything; the run's status messages carry the same ID (`AgentRunStatusPublisher`, its own tests).
|
|
8
|
+
*/
|
|
9
|
+
import 'reflect-metadata'; // must precede any type-graphql decorator evaluation
|
|
10
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
11
|
+
import type { PubSubEngine } from 'type-graphql';
|
|
12
|
+
|
|
13
|
+
vi.mock('type-graphql', () => {
|
|
14
|
+
const decoratorFactory = (..._args: unknown[]) => (..._decorated: unknown[]) => undefined;
|
|
15
|
+
const exportNames = [
|
|
16
|
+
'Resolver', 'Query', 'Mutation', 'Subscription', 'Arg', 'Args', 'ArgsType', 'Ctx', 'Root',
|
|
17
|
+
'Info', 'Field', 'FieldResolver', 'ObjectType', 'InputType', 'InterfaceType', 'Authorized',
|
|
18
|
+
'UseMiddleware', 'Extensions', 'Directive', 'ID', 'Int', 'Float', 'GraphQLISODateTime',
|
|
19
|
+
'GraphQLTimestamp', 'registerEnumType', 'createMethodDecorator', 'createParamDecorator',
|
|
20
|
+
'buildSchema', 'buildSchemaSync', 'PubSub',
|
|
21
|
+
];
|
|
22
|
+
return Object.fromEntries(exportNames.map((name) => [name, decoratorFactory]));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const hoisted = vi.hoisted(() => ({
|
|
26
|
+
createReply: vi.fn(),
|
|
27
|
+
runAgentInConversation: vi.fn(),
|
|
28
|
+
widgetContext: vi.fn(),
|
|
29
|
+
agents: new Map<string, { ID: string; Name: string; Status: string }>(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock('@memberjunction/aiengine', () => ({
|
|
33
|
+
AIEngine: {
|
|
34
|
+
Instance: {
|
|
35
|
+
Config: async () => undefined,
|
|
36
|
+
GetAgentByID: (id: string) => hoisted.agents.get(id),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
GetAttachmentService: () => ({}),
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
vi.mock('@memberjunction/ai-agents', () => ({
|
|
43
|
+
AgentRunner: class {
|
|
44
|
+
CreateAgentReplyDetail(args: unknown) {
|
|
45
|
+
return hoisted.createReply(args);
|
|
46
|
+
}
|
|
47
|
+
RunAgentInConversation(...args: unknown[]) {
|
|
48
|
+
return hoisted.runAgentInConversation(...args);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
ArtifactToolManager: class {},
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
vi.mock('../realtimeWidget/widgetGuestElevation.js', () => ({
|
|
55
|
+
ResolveWidgetGuestRunContext: (...args: unknown[]) => hoisted.widgetContext(...args),
|
|
56
|
+
ElevateUserPayload: (payload: Record<string, unknown>, user: unknown) => ({ ...payload, userRecord: user }),
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
import { RunAIAgentResolver } from '../resolvers/RunAIAgentResolver.js';
|
|
60
|
+
import type { AppContext, UserPayload } from '../types.js';
|
|
61
|
+
|
|
62
|
+
const USER = { ID: 'user-1', Email: 'user@example.com' };
|
|
63
|
+
const SYSTEM = { ID: 'system-1', Email: 'system@example.com' };
|
|
64
|
+
const CREATED_AT = new Date('2026-10-12T10:00:00.000Z');
|
|
65
|
+
const REPLY = { ID: 'reply-1', ConversationID: 'conv-1', __mj_CreatedAt: CREATED_AT };
|
|
66
|
+
|
|
67
|
+
type Fn = ReturnType<typeof vi.fn>;
|
|
68
|
+
|
|
69
|
+
interface ResolverSeams {
|
|
70
|
+
CheckAPIKeyScopeAuthorization: Fn;
|
|
71
|
+
GetUserFromPayload: Fn;
|
|
72
|
+
loadConversationHistoryWithAttachments: Fn;
|
|
73
|
+
executeAIAgent: Fn;
|
|
74
|
+
executeAgentInBackground: Fn;
|
|
75
|
+
validateAgent: Fn;
|
|
76
|
+
persistInFlightAgentFailure: Fn;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The order the turn's steps happened in. */
|
|
80
|
+
let steps: string[] = [];
|
|
81
|
+
|
|
82
|
+
function makeResolver(): { resolver: RunAIAgentResolver; seams: ResolverSeams } {
|
|
83
|
+
const resolver = new RunAIAgentResolver();
|
|
84
|
+
const seams = resolver as unknown as ResolverSeams;
|
|
85
|
+
seams.CheckAPIKeyScopeAuthorization = vi.fn().mockResolvedValue(undefined);
|
|
86
|
+
seams.GetUserFromPayload = vi.fn((payload: { userRecord: unknown }) => payload.userRecord);
|
|
87
|
+
seams.loadConversationHistoryWithAttachments = vi.fn(async () => {
|
|
88
|
+
steps.push('history');
|
|
89
|
+
return [];
|
|
90
|
+
});
|
|
91
|
+
seams.executeAIAgent = vi.fn(async () => {
|
|
92
|
+
steps.push('run');
|
|
93
|
+
return { success: true, result: '{}' };
|
|
94
|
+
});
|
|
95
|
+
seams.executeAgentInBackground = vi.fn(() => {
|
|
96
|
+
steps.push('run');
|
|
97
|
+
});
|
|
98
|
+
return { resolver, seams };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A provider whose conversation details are the user's message ('user-msg-1', in conv-1) plus `others`: message ID →
|
|
103
|
+
* its conversation. A load of any other ID finds nothing, as when the caller can't read it.
|
|
104
|
+
*/
|
|
105
|
+
function makeContext(userMessageFound = true, others: Record<string, string> = {}): { ctx: AppContext; loads: Fn } {
|
|
106
|
+
const conversations: Record<string, string> = { ...(userMessageFound ? { 'user-msg-1': 'conv-1' } : {}), ...others };
|
|
107
|
+
const loads = vi.fn();
|
|
108
|
+
const provider = {
|
|
109
|
+
GetEntityObject: vi.fn(async () => {
|
|
110
|
+
const row = { ConversationID: '', Load: async (id: string) => false };
|
|
111
|
+
row.Load = async (id: string) => {
|
|
112
|
+
loads(id);
|
|
113
|
+
row.ConversationID = conversations[id] ?? '';
|
|
114
|
+
return id in conversations;
|
|
115
|
+
};
|
|
116
|
+
return row;
|
|
117
|
+
}),
|
|
118
|
+
};
|
|
119
|
+
const ctx = {
|
|
120
|
+
userPayload: { sessionId: 'session-1', userRecord: USER } as unknown as UserPayload,
|
|
121
|
+
providers: [{ type: 'Read-Write', provider }],
|
|
122
|
+
dataSource: {},
|
|
123
|
+
} as unknown as AppContext;
|
|
124
|
+
return { ctx, loads };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const pubSub = { publish: vi.fn() } as unknown as PubSubEngine;
|
|
128
|
+
|
|
129
|
+
function runMutation(
|
|
130
|
+
resolver: RunAIAgentResolver,
|
|
131
|
+
ctx: AppContext,
|
|
132
|
+
fireAndForget: boolean,
|
|
133
|
+
threading: { replyParentId?: string; announcedByAgentId?: string } = {}
|
|
134
|
+
) {
|
|
135
|
+
return resolver.RunAIAgentFromConversationDetail(
|
|
136
|
+
'user-msg-1', 'agent-1', ctx, 'session-1', pubSub,
|
|
137
|
+
undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined,
|
|
138
|
+
undefined, undefined, fireAndForget, undefined, undefined, undefined,
|
|
139
|
+
threading.replyParentId, threading.announcedByAgentId
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
beforeEach(() => {
|
|
144
|
+
steps = [];
|
|
145
|
+
hoisted.agents.clear();
|
|
146
|
+
hoisted.agents.set('agent-1', { ID: 'agent-1', Name: 'Research', Status: 'Active' });
|
|
147
|
+
hoisted.agents.set('manager-1', { ID: 'manager-1', Name: 'Sage', Status: 'Active' });
|
|
148
|
+
hoisted.agents.set('retired-1', { ID: 'retired-1', Name: 'Old', Status: 'Disabled' });
|
|
149
|
+
hoisted.widgetContext.mockReset().mockResolvedValue(null);
|
|
150
|
+
hoisted.createReply.mockReset().mockImplementation(async () => {
|
|
151
|
+
steps.push('reply');
|
|
152
|
+
return REPLY;
|
|
153
|
+
});
|
|
154
|
+
vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
afterEach(() => vi.restoreAllMocks());
|
|
158
|
+
|
|
159
|
+
describe('RunAIAgentFromConversationDetail — the server creates the reply row', () => {
|
|
160
|
+
it("acks the reply row it created, with its ID and creation time, before the run publishes anything", async () => {
|
|
161
|
+
const { resolver, seams } = makeResolver();
|
|
162
|
+
const { ctx } = makeContext();
|
|
163
|
+
|
|
164
|
+
const result = await runMutation(resolver, ctx, true);
|
|
165
|
+
|
|
166
|
+
expect(result.success).toBe(true);
|
|
167
|
+
expect(JSON.parse(result.result)).toEqual({
|
|
168
|
+
accepted: true,
|
|
169
|
+
fireAndForget: true,
|
|
170
|
+
conversationDetailId: 'reply-1',
|
|
171
|
+
createdAt: CREATED_AT.toISOString(),
|
|
172
|
+
});
|
|
173
|
+
// History first (so it never holds the empty reply), then the reply row, then the run.
|
|
174
|
+
expect(steps).toEqual(['history', 'reply', 'run']);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("creates the reply as the person asking, answering their message, for the agent that runs", async () => {
|
|
178
|
+
const { resolver } = makeResolver();
|
|
179
|
+
const { ctx } = makeContext();
|
|
180
|
+
|
|
181
|
+
await runMutation(resolver, ctx, true);
|
|
182
|
+
|
|
183
|
+
expect(hoisted.createReply).toHaveBeenCalledOnce();
|
|
184
|
+
expect(hoisted.createReply.mock.calls[0][0]).toMatchObject({
|
|
185
|
+
conversationId: 'conv-1',
|
|
186
|
+
agentId: 'agent-1',
|
|
187
|
+
contextUser: USER,
|
|
188
|
+
parentId: 'user-msg-1',
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('runs the agent into that row on both paths: its ID and the row itself reach the run', async () => {
|
|
193
|
+
const background = makeResolver();
|
|
194
|
+
await runMutation(background.resolver, makeContext().ctx, true);
|
|
195
|
+
const bgArgs = background.seams.executeAgentInBackground.mock.calls[0];
|
|
196
|
+
expect(bgArgs[12]).toBe('reply-1');
|
|
197
|
+
expect(bgArgs.at(-1)).toBe(REPLY);
|
|
198
|
+
|
|
199
|
+
const sync = makeResolver();
|
|
200
|
+
await runMutation(sync.resolver, makeContext().ctx, false);
|
|
201
|
+
const syncArgs = sync.seams.executeAIAgent.mock.calls[0];
|
|
202
|
+
expect(syncArgs[13]).toBe('reply-1');
|
|
203
|
+
expect(syncArgs.at(-1)).toBe(REPLY);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('hands agentResponseDetail through the real executeAIAgent into RunAgentInConversation', async () => {
|
|
207
|
+
const resolver = new RunAIAgentResolver();
|
|
208
|
+
const seams = resolver as unknown as ResolverSeams;
|
|
209
|
+
seams.validateAgent = vi.fn().mockResolvedValue({ ID: 'agent-1', Name: 'Research' });
|
|
210
|
+
seams.persistInFlightAgentFailure = vi.fn().mockResolvedValue(undefined);
|
|
211
|
+
hoisted.runAgentInConversation.mockReset().mockRejectedValue(new Error('stop here'));
|
|
212
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
213
|
+
|
|
214
|
+
const execute = (resolver as unknown as { executeAIAgent(...args: unknown[]): Promise<unknown> }).executeAIAgent.bind(resolver);
|
|
215
|
+
const userPayload = { sessionId: 'session-1', userRecord: USER };
|
|
216
|
+
await execute(
|
|
217
|
+
{}, {}, 'agent-1', userPayload, '[{"role":"user","content":"hi"}]', 'session-1', pubSub,
|
|
218
|
+
undefined, undefined, undefined, undefined, undefined, undefined, 'reply-1', false, false,
|
|
219
|
+
undefined, undefined, 'conv-1', undefined, undefined, undefined, undefined, undefined,
|
|
220
|
+
false, null, REPLY
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(hoisted.runAgentInConversation).toHaveBeenCalledOnce();
|
|
224
|
+
expect(hoisted.runAgentInConversation.mock.calls[0][1]).toMatchObject({
|
|
225
|
+
conversationDetailId: 'reply-1',
|
|
226
|
+
agentResponseDetail: REPLY,
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('hands agentResponseDetail through the real executeAgentInBackground into executeAIAgent', async () => {
|
|
231
|
+
const { resolver, seams } = makeResolver();
|
|
232
|
+
seams.executeAgentInBackground = (
|
|
233
|
+
RunAIAgentResolver.prototype as unknown as { executeAgentInBackground: ResolverSeams['executeAgentInBackground'] }
|
|
234
|
+
).executeAgentInBackground.bind(resolver);
|
|
235
|
+
|
|
236
|
+
await runMutation(resolver, makeContext().ctx, true);
|
|
237
|
+
|
|
238
|
+
expect(seams.executeAIAgent).toHaveBeenCalledOnce();
|
|
239
|
+
const bgToExecArgs = seams.executeAIAgent.mock.calls[0];
|
|
240
|
+
expect(bgToExecArgs[13]).toBe('reply-1');
|
|
241
|
+
expect(bgToExecArgs.at(-1)).toBe(REPLY);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("loads the user's message as the caller, and creates nothing when they can't read it", async () => {
|
|
245
|
+
const { resolver, seams } = makeResolver();
|
|
246
|
+
const { ctx, loads } = makeContext(false);
|
|
247
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
248
|
+
|
|
249
|
+
const result = await runMutation(resolver, ctx, true);
|
|
250
|
+
|
|
251
|
+
expect(loads).toHaveBeenCalledWith('user-msg-1');
|
|
252
|
+
expect(result.success).toBe(false);
|
|
253
|
+
expect(result.errorMessage).toContain('user-msg-1');
|
|
254
|
+
expect(hoisted.createReply).not.toHaveBeenCalled();
|
|
255
|
+
expect(seams.executeAgentInBackground).not.toHaveBeenCalled();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('fails the request with the reason when the reply row is refused, and starts no run', async () => {
|
|
259
|
+
const { resolver, seams } = makeResolver();
|
|
260
|
+
hoisted.createReply.mockRejectedValue(new Error('Failed to create agent response conversation detail: You have view-only access to this conversation.'));
|
|
261
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
262
|
+
|
|
263
|
+
const result = await runMutation(resolver, makeContext().ctx, true);
|
|
264
|
+
|
|
265
|
+
expect(result.success).toBe(false);
|
|
266
|
+
expect(result.errorMessage).toContain('view-only access');
|
|
267
|
+
expect(seams.executeAgentInBackground).not.toHaveBeenCalled();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("writes a widget guest's reply as the guest, while the agent runs under the elevated principal", async () => {
|
|
271
|
+
hoisted.widgetContext.mockResolvedValue({ ElevatedUser: SYSTEM, PinnedAgentId: 'pinned-agent' });
|
|
272
|
+
const { resolver, seams } = makeResolver();
|
|
273
|
+
|
|
274
|
+
await runMutation(resolver, makeContext().ctx, true);
|
|
275
|
+
|
|
276
|
+
expect(hoisted.createReply.mock.calls[0][0]).toMatchObject({ contextUser: USER, agentId: 'pinned-agent' });
|
|
277
|
+
const runPayload = seams.executeAgentInBackground.mock.calls[0][3] as { userRecord: unknown };
|
|
278
|
+
expect(runPayload.userRecord).toBe(SYSTEM);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('RunAIAgentFromConversationDetail — where the reply sits', () => {
|
|
283
|
+
it("threads the reply under a message of the same conversation the caller names", async () => {
|
|
284
|
+
const { resolver } = makeResolver();
|
|
285
|
+
|
|
286
|
+
const result = await runMutation(resolver, makeContext(true, { 'manager-reply-1': 'conv-1' }).ctx, true, { replyParentId: 'manager-reply-1' });
|
|
287
|
+
|
|
288
|
+
expect(result.success).toBe(true);
|
|
289
|
+
expect(hoisted.createReply.mock.calls[0][0]).toMatchObject({ parentId: 'manager-reply-1' });
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('refuses a parent from another conversation, or one the caller cannot read, and creates nothing', async () => {
|
|
293
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
294
|
+
for (const parent of ['elsewhere-1', 'hidden-1']) {
|
|
295
|
+
const { resolver, seams } = makeResolver();
|
|
296
|
+
hoisted.createReply.mockClear();
|
|
297
|
+
|
|
298
|
+
const result = await runMutation(resolver, makeContext(true, { 'elsewhere-1': 'conv-2' }).ctx, true, { replyParentId: parent });
|
|
299
|
+
|
|
300
|
+
expect(result.success).toBe(false);
|
|
301
|
+
expect(result.errorMessage).toContain(parent);
|
|
302
|
+
expect(hoisted.createReply).not.toHaveBeenCalled();
|
|
303
|
+
expect(seams.executeAgentInBackground).not.toHaveBeenCalled();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("posts the announcing agent's fixed line first, threads the reply under it, and names it in the ack", async () => {
|
|
308
|
+
const { resolver, seams } = makeResolver();
|
|
309
|
+
hoisted.createReply.mockImplementation(async (args: { message?: string }) => (
|
|
310
|
+
args.message ? { ID: 'announcement-1', ConversationID: 'conv-1', __mj_CreatedAt: CREATED_AT } : REPLY
|
|
311
|
+
));
|
|
312
|
+
|
|
313
|
+
const result = await runMutation(resolver, makeContext().ctx, true, { announcedByAgentId: 'manager-1' });
|
|
314
|
+
|
|
315
|
+
expect(seams.CheckAPIKeyScopeAuthorization).toHaveBeenCalledWith('agent:execute', 'manager-1', expect.anything());
|
|
316
|
+
expect(hoisted.createReply.mock.calls[0][0]).toMatchObject({
|
|
317
|
+
agentId: 'manager-1',
|
|
318
|
+
contextUser: USER,
|
|
319
|
+
parentId: 'user-msg-1',
|
|
320
|
+
message: 'Continuing with **Research** for refinement...',
|
|
321
|
+
status: 'Complete',
|
|
322
|
+
});
|
|
323
|
+
expect(hoisted.createReply.mock.calls[1][0]).toMatchObject({ agentId: 'agent-1', parentId: 'announcement-1' });
|
|
324
|
+
expect(JSON.parse(result.result)).toMatchObject({ conversationDetailId: 'reply-1', announcementDetailId: 'announcement-1' });
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('ignores announcedByAgentId when elevated under a widget guest token', async () => {
|
|
328
|
+
hoisted.widgetContext.mockResolvedValue({ ElevatedUser: SYSTEM, PinnedAgentId: 'pinned-agent' });
|
|
329
|
+
const { resolver } = makeResolver();
|
|
330
|
+
|
|
331
|
+
const result = await runMutation(resolver, makeContext().ctx, true, { announcedByAgentId: 'manager-1' });
|
|
332
|
+
|
|
333
|
+
expect(result.success).toBe(true);
|
|
334
|
+
expect(hoisted.createReply).toHaveBeenCalledOnce();
|
|
335
|
+
expect(hoisted.createReply.mock.calls[0][0]).toMatchObject({ agentId: 'pinned-agent', parentId: 'user-msg-1' });
|
|
336
|
+
expect(JSON.parse(result.result)).not.toHaveProperty('announcementDetailId');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('refuses an announcement by an agent that is not active, and creates nothing', async () => {
|
|
340
|
+
const { resolver } = makeResolver();
|
|
341
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
342
|
+
|
|
343
|
+
const result = await runMutation(resolver, makeContext().ctx, true, { announcedByAgentId: 'retired-1' });
|
|
344
|
+
|
|
345
|
+
expect(result.success).toBe(false);
|
|
346
|
+
expect(hoisted.createReply).not.toHaveBeenCalled();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('without threading, answers the user message directly and the ack names no announcement', async () => {
|
|
350
|
+
const { resolver } = makeResolver();
|
|
351
|
+
|
|
352
|
+
const result = await runMutation(resolver, makeContext().ctx, true);
|
|
353
|
+
|
|
354
|
+
expect(hoisted.createReply).toHaveBeenCalledOnce();
|
|
355
|
+
expect(hoisted.createReply.mock.calls[0][0]).toMatchObject({ parentId: 'user-msg-1' });
|
|
356
|
+
expect(JSON.parse(result.result)).not.toHaveProperty('announcementDetailId');
|
|
357
|
+
});
|
|
358
|
+
});
|
|
@@ -29,6 +29,9 @@ vi.mock('@memberjunction/ai-agents', () => ({
|
|
|
29
29
|
RunAgentInConversation() {
|
|
30
30
|
return Promise.resolve({ agentResult: { success: true } });
|
|
31
31
|
}
|
|
32
|
+
CreateAgentReplyDetail() {
|
|
33
|
+
return Promise.resolve({ ID: 'reply-1', __mj_CreatedAt: new Date() }); // the server creates the reply row (A19)
|
|
34
|
+
}
|
|
32
35
|
},
|
|
33
36
|
ArtifactToolManager: class {},
|
|
34
37
|
}));
|
|
@@ -140,7 +143,7 @@ describe('RunAIAgentFromConversationDetail — continuing from a user-stopped ru
|
|
|
140
143
|
expect(args[BG_AUTO]).toBe(true);
|
|
141
144
|
});
|
|
142
145
|
|
|
143
|
-
it('looks up the predecessor on the branch the
|
|
146
|
+
it('looks up the predecessor on the branch the person\'s message was posted on', async () => {
|
|
144
147
|
await runMutation(resolver, { branchId: 'B' });
|
|
145
148
|
expect(seams.findUserStoppedPredecessorRunId).toHaveBeenCalledWith('conv-1', 'agent-1', 'B', expect.anything(), expect.anything());
|
|
146
149
|
await runMutation(resolver, {});
|
|
@@ -7,7 +7,7 @@ import 'reflect-metadata';
|
|
|
7
7
|
* magic-link and widget sessions, so the resolver applies its own rule. These tests pin that it
|
|
8
8
|
* refuses scope-limited sessions and callers who cannot read templates before it loads anything.
|
|
9
9
|
*/
|
|
10
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
10
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
11
11
|
|
|
12
12
|
// As in RunDecisionResolver.test.ts: vitest's esbuild transform does not apply
|
|
13
13
|
// `emitDecoratorMetadata`, so the decorators are no-ops and the resolver runs as a plain class.
|
|
@@ -29,7 +29,8 @@ vi.mock('type-graphql', () => {
|
|
|
29
29
|
};
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
import { EntityInfo, UserInfo, UserRoleInfo } from '@memberjunction/core';
|
|
32
|
+
import { EntityInfo, RunView, UserInfo, UserRoleInfo } from '@memberjunction/core';
|
|
33
|
+
import { TemplateEngineServer } from '@memberjunction/templates';
|
|
33
34
|
import { RunTemplateResolver } from '../resolvers/RunTemplateResolver.js';
|
|
34
35
|
import type { AppContext, UserPayload } from '../types.js';
|
|
35
36
|
|
|
@@ -130,3 +131,35 @@ describe('RunTemplate authorization', () => {
|
|
|
130
131
|
expect(provider.EntityObjectRequests).toEqual(['MJ: Templates']);
|
|
131
132
|
});
|
|
132
133
|
});
|
|
134
|
+
|
|
135
|
+
/** Metadata plus a template that loads, so the resolver reaches the render. */
|
|
136
|
+
class RenderingProvider {
|
|
137
|
+
public readonly Template = { Load: async () => true, IsSaved: true, Name: 'T' };
|
|
138
|
+
constructor(private readonly entities: EntityInfo[]) {}
|
|
139
|
+
public EntityByName(name: string): EntityInfo | undefined {
|
|
140
|
+
return this.entities.find((e) => e.Name.trim().toLowerCase() === name.trim().toLowerCase());
|
|
141
|
+
}
|
|
142
|
+
public async GetEntityObject(): Promise<object> {
|
|
143
|
+
return this.Template;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
describe('RunTemplate render user', () => {
|
|
148
|
+
afterEach(() => vi.restoreAllMocks());
|
|
149
|
+
|
|
150
|
+
it('REGRESSION: renders as the calling user, not whoever last configured the engine', async () => {
|
|
151
|
+
const provider = new RenderingProvider(readableEverywhere());
|
|
152
|
+
const content = { ID: 'content-1', TemplateText: 'x' };
|
|
153
|
+
vi.spyOn(RunView, 'FromMetadataProvider').mockReturnValue({
|
|
154
|
+
RunView: async () => ({ Success: true, Results: [content] }),
|
|
155
|
+
} as unknown as RunView);
|
|
156
|
+
vi.spyOn(TemplateEngineServer.Instance, 'Config').mockResolvedValue(undefined);
|
|
157
|
+
const render = vi.spyOn(TemplateEngineServer.Instance, 'RenderTemplate').mockResolvedValue({ Success: true, Output: 'ok', Message: undefined });
|
|
158
|
+
const caller = makeUser([UI_ROLE_ID]);
|
|
159
|
+
|
|
160
|
+
const result = await new RunTemplateResolver().RunTemplate(TEMPLATE_ID, contextFor(caller, provider as unknown as FakeProvider));
|
|
161
|
+
|
|
162
|
+
expect(result).toMatchObject({ success: true, output: 'ok' });
|
|
163
|
+
expect(render.mock.calls[0][5]).toEqual({ ContextUser: caller });
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -60,4 +60,15 @@ describe('RunViewsWithCacheCheck resolver — param forwarding', () => {
|
|
|
60
60
|
expect(forwarded.DataSource).toBe('Materialized');
|
|
61
61
|
expect(forwarded.Aggregates).toEqual([{ expression: 'COUNT(*)', alias: 'total' }]);
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
it('forwards AlternateBaseView (including empty-string override) and leaves it undefined when unset', async () => {
|
|
65
|
+
const withNamed = await forwardedParams({ EntityName: 'Customers', AlternateBaseView: 'Active Customers' });
|
|
66
|
+
expect(withNamed.AlternateBaseView).toBe('Active Customers');
|
|
67
|
+
|
|
68
|
+
const withEmptyOverride = await forwardedParams({ EntityName: 'Customers', AlternateBaseView: '' });
|
|
69
|
+
expect(withEmptyOverride.AlternateBaseView).toBe('');
|
|
70
|
+
|
|
71
|
+
const unset = await forwardedParams({ EntityName: 'Customers' });
|
|
72
|
+
expect(unset.AlternateBaseView).toBeUndefined();
|
|
73
|
+
});
|
|
63
74
|
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// ResolverBase pulls in type-graphql decorators, which need the Reflect.metadata polyfill first.
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `RunViewsWithCacheCheck` is the transport `RunViews` switches to when any view in the batch is
|
|
7
|
+
* `CacheLocal`, so it carries the same client-supplied clauses as `RunViews`. These tests pin that
|
|
8
|
+
* it applies the same GraphQL-boundary protections before the provider is called: the base-view
|
|
9
|
+
* AST screen on ExtraFilter / OrderBy / OverrideExcludeFilter, and the API-key scope check.
|
|
10
|
+
*
|
|
11
|
+
* Every payload here is harmless (stacked `SELECT`s); the point is only whether it reaches the
|
|
12
|
+
* provider. The provider's own keyword denylist accepts the bracket-identifier payload, because a
|
|
13
|
+
* quote inside `[a']` hides the rest of the text from its literal stripper.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { authorize } = vi.hoisted(() => ({ authorize: vi.fn() }));
|
|
17
|
+
|
|
18
|
+
vi.mock('@memberjunction/api-keys', async (importOriginal) => {
|
|
19
|
+
const actual = await importOriginal<typeof import('@memberjunction/api-keys')>();
|
|
20
|
+
return { ...actual, GetAPIKeyEngine: () => ({ Authorize: authorize }) };
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
import type { DatabaseProviderBase, EntityInfo, RunViewWithCacheCheckParams, UserInfo } from '@memberjunction/core';
|
|
24
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
25
|
+
import { RunDynamicViewInput, RunViewResolver, RunViewWithCacheCheckInput } from '../generic/RunViewResolver.js';
|
|
26
|
+
import type { AppContext, UserPayload } from '../types.js';
|
|
27
|
+
|
|
28
|
+
/** A stacked statement hidden from the keyword denylist by a quote inside a bracket identifier. */
|
|
29
|
+
const BRACKET_STACKED =
|
|
30
|
+
"1 = (SELECT 1 AS [a'])) ; SELECT 1 AS [x] ; SELECT * FROM __mj.vwUserViews WHERE (1 = (SELECT 1 AS [b'])";
|
|
31
|
+
|
|
32
|
+
const USER = { ID: 'user-1', Email: 'reader@example.com', Name: 'Reader' } as UserInfo;
|
|
33
|
+
|
|
34
|
+
function entity(name: string, schema: string, baseView: string): EntityInfo {
|
|
35
|
+
return {
|
|
36
|
+
Name: name,
|
|
37
|
+
SchemaName: schema,
|
|
38
|
+
BaseView: baseView,
|
|
39
|
+
PrimaryKeys: [{ Name: 'ID' }],
|
|
40
|
+
GetUserPermisions: () => ({ CanRead: true }),
|
|
41
|
+
GetEffectiveRowFilterWhereClause: () => '',
|
|
42
|
+
GetDeniedReadFields: () => new Set<string>(),
|
|
43
|
+
} as unknown as EntityInfo;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Provider double: the metadata the screen reads, and a cache-check method that records calls. */
|
|
47
|
+
function fakeProvider() {
|
|
48
|
+
const calls: RunViewWithCacheCheckParams[][] = [];
|
|
49
|
+
const entities = [
|
|
50
|
+
entity('MJ: User Views', '__mj', 'vwUserViews'),
|
|
51
|
+
entity('MJ: Users', '__mj', 'vwUsers'),
|
|
52
|
+
];
|
|
53
|
+
const provider = {
|
|
54
|
+
Entities: entities,
|
|
55
|
+
EntityByName: (name: string) => entities.find((e) => e.Name === name),
|
|
56
|
+
RunViewsWithCacheCheck: async (params: RunViewWithCacheCheckParams[]) => {
|
|
57
|
+
calls.push(params);
|
|
58
|
+
return { success: true, results: [] };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
return { provider: provider as unknown as DatabaseProviderBase, calls };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function context(provider: DatabaseProviderBase, payload: Partial<UserPayload> = {}): AppContext {
|
|
65
|
+
const userPayload = { email: USER.Email, userRecord: USER, ...payload } as UserPayload;
|
|
66
|
+
return { providers: [{ type: 'Read-Only', provider }], userPayload } as unknown as AppContext;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function cacheCheckInput(params: Partial<RunDynamicViewInput>): RunViewWithCacheCheckInput[] {
|
|
70
|
+
const dynamic = Object.assign(new RunDynamicViewInput(), { EntityName: 'MJ: User Views', ...params });
|
|
71
|
+
return [Object.assign(new RunViewWithCacheCheckInput(), { params: dynamic })];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('RunViewsWithCacheCheck — GraphQL-boundary clause screen', () => {
|
|
75
|
+
it('refuses a stacked statement hidden behind a bracket identifier, before the provider runs', async () => {
|
|
76
|
+
const { provider, calls } = fakeProvider();
|
|
77
|
+
|
|
78
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
79
|
+
cacheCheckInput({ ExtraFilter: BRACKET_STACKED }),
|
|
80
|
+
context(provider),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(calls).toHaveLength(0);
|
|
84
|
+
expect(result.success).toBe(false);
|
|
85
|
+
expect(result.errorMessage).toMatch(/Invalid ExtraFilter: multiple statements/);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('refuses a subquery against a base table', async () => {
|
|
89
|
+
const { provider, calls } = fakeProvider();
|
|
90
|
+
|
|
91
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
92
|
+
cacheCheckInput({ ExtraFilter: `EXISTS (SELECT 1 FROM __mj.[User] WHERE [Type] = 'Owner')` }),
|
|
93
|
+
context(provider),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(calls).toHaveLength(0);
|
|
97
|
+
expect(result.success).toBe(false);
|
|
98
|
+
expect(result.errorMessage).toMatch(/entity base view/);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('screens OrderBy and OverrideExcludeFilter too', async () => {
|
|
102
|
+
for (const params of [
|
|
103
|
+
{ OrderBy: "[a'] ; SELECT 1 AS [x] ; SELECT 1 AS [b']" },
|
|
104
|
+
{ OverrideExcludeFilter: BRACKET_STACKED },
|
|
105
|
+
]) {
|
|
106
|
+
const { provider, calls } = fakeProvider();
|
|
107
|
+
|
|
108
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(cacheCheckInput(params), context(provider));
|
|
109
|
+
|
|
110
|
+
expect(calls).toHaveLength(0);
|
|
111
|
+
expect(result.success).toBe(false);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('passes a filter that only reads entity base views through to the provider', async () => {
|
|
116
|
+
const { provider, calls } = fakeProvider();
|
|
117
|
+
const filter = `UserID IN (SELECT ID FROM [__mj].[vwUsers] WHERE Email = 'o''brien@example.com')`;
|
|
118
|
+
|
|
119
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
120
|
+
cacheCheckInput({ ExtraFilter: filter, OrderBy: '[Name] DESC' }),
|
|
121
|
+
context(provider),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(result.success).toBe(true);
|
|
125
|
+
expect(calls).toHaveLength(1);
|
|
126
|
+
expect(calls[0][0].params.ExtraFilter).toBe(filter);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('RunViewsWithCacheCheck — API-key scope', () => {
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
authorize.mockReset();
|
|
133
|
+
vi.spyOn(UserCache.Instance, 'GetSystemUser').mockReturnValue({ ID: 'system', Email: 'system@example.com' } as UserInfo);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('refuses an API key without the batch view scope, before the provider runs', async () => {
|
|
137
|
+
authorize.mockResolvedValue({ Allowed: false, Reason: 'scope not granted' });
|
|
138
|
+
const { provider, calls } = fakeProvider();
|
|
139
|
+
|
|
140
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
141
|
+
cacheCheckInput({ ExtraFilter: `Name = 'x'` }),
|
|
142
|
+
context(provider, { apiKeyHash: 'hash-of-a-narrow-key' }),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
expect(calls).toHaveLength(0);
|
|
146
|
+
expect(result.success).toBe(false);
|
|
147
|
+
expect(result.errorMessage).toMatch(/'view:batch' scope/);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('lets an API key holding the scope through', async () => {
|
|
151
|
+
authorize.mockImplementation(async (_hash: string, _app: string, scope: string) => ({
|
|
152
|
+
Allowed: scope === 'view:batch',
|
|
153
|
+
Reason: scope === 'view:batch' ? 'granted' : 'not full access',
|
|
154
|
+
}));
|
|
155
|
+
const { provider, calls } = fakeProvider();
|
|
156
|
+
|
|
157
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
158
|
+
cacheCheckInput({ ExtraFilter: `Name = 'x'` }),
|
|
159
|
+
context(provider, { apiKeyHash: 'hash-of-a-batch-key' }),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(result.success).toBe(true);
|
|
163
|
+
expect(calls).toHaveLength(1);
|
|
164
|
+
});
|
|
165
|
+
});
|