@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
|
@@ -21,16 +21,26 @@ vi.mock('type-graphql', () => {
|
|
|
21
21
|
return Object.fromEntries(exportNames.map((name) => [name, decoratorFactory]));
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
const runViewCalls: Array<{ params: RunViewParams; contextUser: UserInfo | undefined }> = [];
|
|
24
|
+
const runViewCalls: Array<{ params: RunViewParams; contextUser: UserInfo | undefined; provider: unknown }> = [];
|
|
25
|
+
/** Each RunViews call, as the list of entity names it batched. */
|
|
26
|
+
const runViewsBatches: string[][] = [];
|
|
25
27
|
let runViewHandler: RunViewHandler = () => ({ Success: true, Results: [] });
|
|
26
28
|
|
|
27
29
|
vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
28
30
|
const actual = await importOriginal<typeof import('@memberjunction/core')>();
|
|
29
31
|
class MockRunView {
|
|
32
|
+
constructor(private readonly provider?: unknown) {}
|
|
33
|
+
static FromMetadataProvider(provider: unknown): MockRunView {
|
|
34
|
+
return new MockRunView(provider);
|
|
35
|
+
}
|
|
30
36
|
async RunView(params: RunViewParams, contextUser?: UserInfo): Promise<ViewResult> {
|
|
31
|
-
runViewCalls.push({ params, contextUser });
|
|
37
|
+
runViewCalls.push({ params, contextUser, provider: this.provider });
|
|
32
38
|
return runViewHandler(params, contextUser);
|
|
33
39
|
}
|
|
40
|
+
async RunViews(params: RunViewParams[], contextUser?: UserInfo): Promise<ViewResult[]> {
|
|
41
|
+
runViewsBatches.push(params.map((p) => p.EntityName ?? ''));
|
|
42
|
+
return Promise.all(params.map((p) => this.RunView(p, contextUser)));
|
|
43
|
+
}
|
|
34
44
|
}
|
|
35
45
|
return { ...actual, RunView: MockRunView, LogStatus: () => undefined, LogError: () => undefined };
|
|
36
46
|
});
|
|
@@ -54,9 +64,13 @@ import type { AppContext } from '../types.js';
|
|
|
54
64
|
const DOC_ID = 'DOC-1';
|
|
55
65
|
const contextUser = new UserInfo();
|
|
56
66
|
|
|
67
|
+
/** Entity read and field-level security the external-index path checks, per test. */
|
|
68
|
+
const access = { canRead: true, deniedReadFields: new Set<string>() };
|
|
69
|
+
|
|
57
70
|
const membersEntity = {
|
|
58
71
|
Name: 'Members',
|
|
59
72
|
PrimaryKeys: [{ Name: 'ID' }],
|
|
73
|
+
FirstPrimaryKey: { Name: 'ID' },
|
|
60
74
|
Fields: [
|
|
61
75
|
{ Name: 'ID', IsNameField: false },
|
|
62
76
|
{ Name: 'FirstName', IsNameField: true },
|
|
@@ -64,11 +78,16 @@ const membersEntity = {
|
|
|
64
78
|
{ Name: 'Email', IsNameField: false },
|
|
65
79
|
],
|
|
66
80
|
NameField: undefined,
|
|
81
|
+
GetUserPermisions: (user: UserInfo) => ({ CanRead: user === contextUser && access.canRead }),
|
|
82
|
+
GetDeniedReadFields: (user: UserInfo) => (user === contextUser ? access.deniedReadFields : new Set<string>()),
|
|
67
83
|
} as unknown as EntityInfo;
|
|
68
84
|
|
|
69
85
|
const orderLinesEntity = {
|
|
70
86
|
Name: 'Order Lines',
|
|
71
87
|
PrimaryKeys: [{ Name: 'OrderID' }, { Name: 'LineNo' }],
|
|
88
|
+
FirstPrimaryKey: { Name: 'OrderID' },
|
|
89
|
+
GetUserPermisions: (user: UserInfo) => ({ CanRead: user === contextUser && access.canRead }),
|
|
90
|
+
GetDeniedReadFields: () => new Set<string>(),
|
|
72
91
|
Fields: [
|
|
73
92
|
{ Name: 'OrderID', IsNameField: false },
|
|
74
93
|
{ Name: 'LineNo', IsNameField: false },
|
|
@@ -123,6 +142,38 @@ const memberRows = [
|
|
|
123
142
|
{ ID: 'ccc', FirstName: 'Alan', LastName: null },
|
|
124
143
|
];
|
|
125
144
|
|
|
145
|
+
/**
|
|
146
|
+
* A fake `MJ: Entity Record Documents` view over `rows`: returns only the requested `Fields`, and
|
|
147
|
+
* only the rows named by an `ID IN ('a','b')` filter when one is given.
|
|
148
|
+
*/
|
|
149
|
+
function erdView(rows: Array<Record<string, string>>) {
|
|
150
|
+
return (params: RunViewParams): ViewResult => {
|
|
151
|
+
const idList = /^ID IN \((.*)\)$/.exec(params.ExtraFilter ?? '');
|
|
152
|
+
const ids = idList ? new Set(idList[1].split(',').map((v) => v.trim().replace(/^'|'$/g, ''))) : undefined;
|
|
153
|
+
const picked = ids ? rows.filter((r) => ids.has(r.ID)) : rows;
|
|
154
|
+
const fields = params.Fields ?? Object.keys(rows[0] ?? {});
|
|
155
|
+
return { Success: true, Results: picked.map((r) => Object.fromEntries(fields.map((f) => [f, r[f]]))) };
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A fake `Members` view of the rows the caller may read (row-level security already applied):
|
|
161
|
+
* returns the requested `Fields` of the rows named by an `ID IN ('a','b')` filter.
|
|
162
|
+
*/
|
|
163
|
+
function readableMembersView(readableIDs: string[]) {
|
|
164
|
+
return (params: RunViewParams): ViewResult => {
|
|
165
|
+
const idList = /^ID IN \((.*)\)$/.exec(params.ExtraFilter ?? '');
|
|
166
|
+
const requested = idList ? idList[1].split(',').map((v) => v.trim().replace(/^'|'$/g, '')) : [];
|
|
167
|
+
const rows = requested.filter((id) => readableIDs.includes(id)).map((id) => ({ ID: id }));
|
|
168
|
+
return { Success: true, Results: rows.map((r) => Object.fromEntries((params.Fields ?? ['ID']).map((f) => [f, r[f as 'ID']]))) };
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** The VectorJSON fetches: Entity Record Documents views filtered by ID. */
|
|
173
|
+
function vectorFetches() {
|
|
174
|
+
return callsFor('MJ: Entity Record Documents').filter((c) => c.params.ExtraFilter?.startsWith('ID IN ('));
|
|
175
|
+
}
|
|
176
|
+
|
|
126
177
|
function callsFor(entityName: string) {
|
|
127
178
|
return runViewCalls.filter((c) => c.params.EntityName === entityName);
|
|
128
179
|
}
|
|
@@ -134,6 +185,9 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
134
185
|
|
|
135
186
|
beforeEach(() => {
|
|
136
187
|
runViewCalls.length = 0;
|
|
188
|
+
runViewsBatches.length = 0;
|
|
189
|
+
access.canRead = true;
|
|
190
|
+
access.deniedReadFields = new Set<string>();
|
|
137
191
|
});
|
|
138
192
|
|
|
139
193
|
afterEach(() => {
|
|
@@ -150,7 +204,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
150
204
|
it('reads VectorJSON for the document with the context user instead of calling QueryIndex', async () => {
|
|
151
205
|
const { queryIndex } = useVectorDB(true);
|
|
152
206
|
routeViews('Members', {
|
|
153
|
-
'MJ: Entity Record Documents': (
|
|
207
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
154
208
|
Members: () => ({ Success: true, Results: memberRows }),
|
|
155
209
|
});
|
|
156
210
|
|
|
@@ -163,18 +217,53 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
163
217
|
expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2', 'ERD-3']);
|
|
164
218
|
expect(result.Results.map((r) => r.Values)).toEqual([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]);
|
|
165
219
|
|
|
166
|
-
const [
|
|
167
|
-
expect(
|
|
168
|
-
expect(
|
|
169
|
-
expect(
|
|
170
|
-
expect(
|
|
171
|
-
expect(
|
|
220
|
+
const [listCall, vectorCall] = callsFor('MJ: Entity Record Documents');
|
|
221
|
+
expect(listCall.contextUser).toBe(contextUser);
|
|
222
|
+
expect(listCall.params.ExtraFilter).toBe(`EntityDocumentID='${DOC_ID}' AND VectorJSON IS NOT NULL`);
|
|
223
|
+
expect(listCall.params.Fields).toEqual(['ID', 'RecordID']);
|
|
224
|
+
expect(listCall.params.ResultType).toBe('simple');
|
|
225
|
+
expect(listCall.params.IgnoreMaxRows).toBe(true);
|
|
226
|
+
expect(vectorCall.contextUser).toBe(contextUser);
|
|
227
|
+
expect(vectorCall.params.ExtraFilter).toBe("ID IN ('ERD-1','ERD-2','ERD-3')");
|
|
228
|
+
expect(vectorCall.params.Fields).toEqual(['ID', 'VectorJSON']);
|
|
229
|
+
expect(vectorCall.params.ResultType).toBe('simple');
|
|
230
|
+
expect(vectorCall.params.IgnoreMaxRows).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("REGRESSION: runs every view on the request's provider", async () => {
|
|
234
|
+
useVectorDB(true);
|
|
235
|
+
routeViews('Members', {
|
|
236
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
237
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
238
|
+
});
|
|
239
|
+
const context = appContext(membersEntity);
|
|
240
|
+
|
|
241
|
+
await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, context);
|
|
242
|
+
|
|
243
|
+
const requestProvider = context.providers[0].provider;
|
|
244
|
+
expect(callsFor('MJ: Entity Record Documents')).toHaveLength(2);
|
|
245
|
+
expect(runViewCalls.every((c) => c.provider === requestProvider)).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('bypasses the server RunView cache for both Entity Record Documents views, so Fields is honoured', async () => {
|
|
249
|
+
useVectorDB(true);
|
|
250
|
+
routeViews('Members', {
|
|
251
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
252
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
256
|
+
|
|
257
|
+
const erdCalls = callsFor('MJ: Entity Record Documents');
|
|
258
|
+
expect(erdCalls).toHaveLength(2);
|
|
259
|
+
expect(erdCalls.map((c) => c.params.BypassCache)).toEqual([true, true]);
|
|
260
|
+
expect(callsFor('Members')[0].params.BypassCache).toBeUndefined();
|
|
172
261
|
});
|
|
173
262
|
|
|
174
263
|
it('labels each item with Entity, RecordID and the record name fields (RecordID matched case-insensitively)', async () => {
|
|
175
264
|
useVectorDB(true);
|
|
176
265
|
routeViews('Members', {
|
|
177
|
-
'MJ: Entity Record Documents': (
|
|
266
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
178
267
|
Members: () => ({ Success: true, Results: memberRows }),
|
|
179
268
|
});
|
|
180
269
|
|
|
@@ -196,7 +285,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
196
285
|
useVectorDB(true);
|
|
197
286
|
const filter = "LastName = 'Hopper'";
|
|
198
287
|
routeViews('Members', {
|
|
199
|
-
'MJ: Entity Record Documents': (
|
|
288
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
200
289
|
Members: (params) => ({
|
|
201
290
|
Success: true,
|
|
202
291
|
Results: params.ExtraFilter === filter ? memberRows.filter((r) => r.LastName === 'Hopper') : memberRows,
|
|
@@ -215,7 +304,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
215
304
|
it('stops at maxRecords', async () => {
|
|
216
305
|
useVectorDB(true);
|
|
217
306
|
routeViews('Members', {
|
|
218
|
-
'MJ: Entity Record Documents': (
|
|
307
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
219
308
|
Members: () => ({ Success: true, Results: memberRows }),
|
|
220
309
|
});
|
|
221
310
|
|
|
@@ -223,15 +312,74 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
223
312
|
|
|
224
313
|
expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2']);
|
|
225
314
|
expect(result.TotalCount).toBe(2);
|
|
315
|
+
expect(vectorFetches().map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('ERD-1','ERD-2')"]);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('fetches VectorJSON only for vectors whose record passed the filter', async () => {
|
|
319
|
+
useVectorDB(true);
|
|
320
|
+
const filter = "LastName = 'Hopper'";
|
|
321
|
+
routeViews('Members', {
|
|
322
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
323
|
+
Members: () => ({ Success: true, Results: memberRows.filter((r) => r.LastName === 'Hopper') }),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, filter, appContext(membersEntity));
|
|
327
|
+
|
|
328
|
+
expect(result.Results.map((r) => r.Values)).toEqual([[0.3, 0.4]]);
|
|
329
|
+
expect(vectorFetches().map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('ERD-2')"]);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('runs no VectorJSON fetch when no stored vector belongs to a readable record', async () => {
|
|
333
|
+
useVectorDB(true);
|
|
334
|
+
routeViews('Members', {
|
|
335
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
336
|
+
Members: () => ({ Success: true, Results: [] }),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
340
|
+
|
|
341
|
+
expect(result.Success).toBe(true);
|
|
342
|
+
expect(result.Results).toEqual([]);
|
|
343
|
+
expect(vectorFetches()).toHaveLength(0);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('splits a large VectorJSON fetch into ID lists of at most 1000, sent in one RunViews batch', async () => {
|
|
347
|
+
useVectorDB(true);
|
|
348
|
+
const ids = Array.from({ length: 1500 }, (_, i) => `id-${i}`);
|
|
349
|
+
routeViews('Members', {
|
|
350
|
+
'MJ: Entity Record Documents': erdView(ids.map((id, i) => ({ ID: `ERD-${i}`, RecordID: id, VectorJSON: `[${i}]` }))),
|
|
351
|
+
Members: () => ({ Success: true, Results: ids.map((id) => ({ ID: id, FirstName: id, LastName: null })) }),
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 1500, undefined, appContext(membersEntity));
|
|
355
|
+
|
|
356
|
+
expect(result.TotalCount).toBe(1500);
|
|
357
|
+
expect(result.Results[1499]).toMatchObject({ ID: 'ERD-1499', Values: [1499] });
|
|
358
|
+
const fetchSizes = vectorFetches().map((c) => (c.params.ExtraFilter ?? '').split(',').length);
|
|
359
|
+
expect(fetchSizes).toEqual([1000, 500]);
|
|
360
|
+
expect(runViewsBatches[1]).toEqual(['MJ: Entity Record Documents', 'MJ: Entity Record Documents']);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('returns an error result when the VectorJSON fetch fails', async () => {
|
|
364
|
+
useVectorDB(true);
|
|
365
|
+
const listing = erdView(storedRows);
|
|
366
|
+
routeViews('Members', {
|
|
367
|
+
'MJ: Entity Record Documents': (params) =>
|
|
368
|
+
params.ExtraFilter?.startsWith('ID IN (') ? { Success: false, Results: [], ErrorMessage: 'deadlock' } : listing(params),
|
|
369
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
373
|
+
|
|
374
|
+
expect(result.Success).toBe(false);
|
|
375
|
+
expect(result.Results).toEqual([]);
|
|
376
|
+
expect(result.ErrorMessage).toBe('Failed to load vector values: deadlock');
|
|
226
377
|
});
|
|
227
378
|
|
|
228
379
|
it('matches composite-key records by the compact RecordID segment and falls back to NameField for labels', async () => {
|
|
229
380
|
useVectorDB(true);
|
|
230
381
|
routeViews('Order Lines', {
|
|
231
|
-
'MJ: Entity Record Documents': (
|
|
232
|
-
Success: true,
|
|
233
|
-
Results: [{ ID: 'ERD-9', RecordID: 'OrderID|o-1||LineNo|2', VectorJSON: '[1,0]' }],
|
|
234
|
-
}),
|
|
382
|
+
'MJ: Entity Record Documents': erdView([{ ID: 'ERD-9', RecordID: 'OrderID|o-1||LineNo|2', VectorJSON: '[1,0]' }]),
|
|
235
383
|
'Order Lines': () => ({ Success: true, Results: [{ OrderID: 'o-1', LineNo: 2, Description: 'Brie wheel' }] }),
|
|
236
384
|
});
|
|
237
385
|
|
|
@@ -250,7 +398,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
250
398
|
it('returns an error result when the entity records cannot be loaded (e.g. an invalid filter)', async () => {
|
|
251
399
|
useVectorDB(true);
|
|
252
400
|
routeViews('Members', {
|
|
253
|
-
'MJ: Entity Record Documents': (
|
|
401
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
254
402
|
Members: () => ({ Success: false, Results: [], ErrorMessage: 'bad filter' }),
|
|
255
403
|
});
|
|
256
404
|
|
|
@@ -260,12 +408,55 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
260
408
|
expect(result.Results).toEqual([]);
|
|
261
409
|
expect(result.ErrorMessage).toBe('Failed to load "Members" records: bad filter');
|
|
262
410
|
});
|
|
411
|
+
|
|
412
|
+
it('returns an error result when the stored vectors cannot be loaded', async () => {
|
|
413
|
+
useVectorDB(true);
|
|
414
|
+
routeViews('Members', {
|
|
415
|
+
'MJ: Entity Record Documents': () => ({ Success: false, Results: [], ErrorMessage: 'timeout' }),
|
|
416
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
420
|
+
|
|
421
|
+
expect(result.Success).toBe(false);
|
|
422
|
+
expect(result.Results).toEqual([]);
|
|
423
|
+
expect(result.TotalCount).toBe(0);
|
|
424
|
+
expect(result.ErrorMessage).toBe('Failed to load stored vectors: timeout');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('returns an error result, and runs neither view, when the document names an entity missing from metadata', async () => {
|
|
428
|
+
const { queryIndex } = useVectorDB(true);
|
|
429
|
+
routeViews('Retired Entity', {
|
|
430
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
434
|
+
|
|
435
|
+
expect(result.Success).toBe(false);
|
|
436
|
+
expect(result.Results).toEqual([]);
|
|
437
|
+
expect(result.ErrorMessage).toBe('Entity "Retired Entity" not found in metadata');
|
|
438
|
+
expect(queryIndex).not.toHaveBeenCalled();
|
|
439
|
+
expect(callsFor('MJ: Entity Record Documents')).toHaveLength(0);
|
|
440
|
+
expect(runViewsBatches).toHaveLength(0);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('lists the entity records and the stored vectors in one RunViews batch, then fetches the vector values', async () => {
|
|
444
|
+
useVectorDB(true);
|
|
445
|
+
routeViews('Members', {
|
|
446
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
447
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
451
|
+
|
|
452
|
+
expect(runViewsBatches).toEqual([['Members', 'MJ: Entity Record Documents'], ['MJ: Entity Record Documents']]);
|
|
453
|
+
});
|
|
263
454
|
});
|
|
264
455
|
|
|
265
456
|
describe('index-keyed providers (Pinecone path, unchanged)', () => {
|
|
266
457
|
it('queries the provider index by name and never reads Entity Record Documents', async () => {
|
|
267
458
|
const { queryIndex } = useVectorDB(false);
|
|
268
|
-
routeViews('Members', {});
|
|
459
|
+
routeViews('Members', { Members: readableMembersView(['a']) });
|
|
269
460
|
|
|
270
461
|
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
271
462
|
|
|
@@ -279,7 +470,167 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
279
470
|
expect(query.vector).toHaveLength(2);
|
|
280
471
|
expect(result.Results).toEqual([{ ID: 'pc-1', Values: [0.5, 0.5], Metadata: JSON.stringify({ Entity: 'Members', RecordID: 'a' }) }]);
|
|
281
472
|
expect(callsFor('MJ: Entity Record Documents')).toHaveLength(0);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('removes metadata keys naming fields the caller may not read', async () => {
|
|
476
|
+
const { queryIndex } = useVectorDB(false);
|
|
477
|
+
queryIndex.mockResolvedValueOnce({
|
|
478
|
+
success: true,
|
|
479
|
+
data: { matches: [{ id: 'pc-1', values: [0.5, 0.5], metadata: { Entity: 'Members', RecordID: 'a', FirstName: 'Ada', Email: 'ada@secret.org' } }] },
|
|
480
|
+
});
|
|
481
|
+
access.deniedReadFields = new Set(['email']);
|
|
482
|
+
routeViews('Members', { Members: readableMembersView(['a']) });
|
|
483
|
+
|
|
484
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
485
|
+
|
|
486
|
+
expect(result.Success).toBe(true);
|
|
487
|
+
expect(JSON.parse(result.Results[0].Metadata)).toEqual({ Entity: 'Members', RecordID: 'a', FirstName: 'Ada' });
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('keeps every metadata key for a caller denied nothing', async () => {
|
|
491
|
+
const { queryIndex } = useVectorDB(false);
|
|
492
|
+
const metadata = { Entity: 'Members', RecordID: 'a', FirstName: 'Ada', Email: 'ada@secret.org' };
|
|
493
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: [{ id: 'pc-1', values: [0.5, 0.5], metadata }] } });
|
|
494
|
+
routeViews('Members', { Members: readableMembersView(['a']) });
|
|
495
|
+
|
|
496
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
497
|
+
|
|
498
|
+
expect(JSON.parse(result.Results[0].Metadata)).toEqual(metadata);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('refuses, without querying the index, a caller who may not read the source entity', async () => {
|
|
502
|
+
const { queryIndex } = useVectorDB(false);
|
|
503
|
+
access.canRead = false;
|
|
504
|
+
routeViews('Members', {});
|
|
505
|
+
|
|
506
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
507
|
+
|
|
508
|
+
expect(result.Success).toBe(false);
|
|
509
|
+
expect(result.Results).toEqual([]);
|
|
510
|
+
expect(result.ErrorMessage).toBe('Entity "Members" does not exist or you do not have permission to read it');
|
|
511
|
+
expect(queryIndex).not.toHaveBeenCalled();
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
describe('index-keyed providers: rows the caller cannot read', () => {
|
|
516
|
+
function matchesFor(recordIDs: Array<string | undefined>) {
|
|
517
|
+
return recordIDs.map((recordID, i) => ({
|
|
518
|
+
id: `pc-${i}`,
|
|
519
|
+
values: [i, i],
|
|
520
|
+
score: 1 - i / 10,
|
|
521
|
+
metadata: recordID === undefined ? { Entity: 'Members' } : { Entity: 'Members', RecordID: recordID },
|
|
522
|
+
}));
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
it('drops matches whose record the caller cannot read, keeping the order of the rest', async () => {
|
|
526
|
+
const { queryIndex } = useVectorDB(false);
|
|
527
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa', 'ID|bbb', 'ID|ccc']) } });
|
|
528
|
+
routeViews('Members', { Members: readableMembersView(['ccc', 'aaa']) });
|
|
529
|
+
|
|
530
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
531
|
+
|
|
532
|
+
expect(result.Success).toBe(true);
|
|
533
|
+
expect(result.Results.map((r) => r.ID)).toEqual(['pc-0', 'pc-2']);
|
|
534
|
+
expect(result.Results.map((r) => r.Values)).toEqual([[0, 0], [2, 2]]);
|
|
535
|
+
expect(result.TotalCount).toBe(2);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("REGRESSION: runs every view, the key check included, on the request's provider", async () => {
|
|
539
|
+
const { queryIndex } = useVectorDB(false);
|
|
540
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa']) } });
|
|
541
|
+
routeViews('Members', { Members: readableMembersView(['aaa']) });
|
|
542
|
+
const context = appContext(membersEntity);
|
|
543
|
+
|
|
544
|
+
await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, context);
|
|
545
|
+
|
|
546
|
+
const requestProvider = context.providers[0].provider;
|
|
547
|
+
expect(callsFor('Members')).toHaveLength(1);
|
|
548
|
+
expect(runViewCalls.length).toBeGreaterThan(1);
|
|
549
|
+
expect(runViewCalls.every((c) => c.provider === requestProvider)).toBe(true);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('checks the matched keys with a simple view of the primary key, as the caller', async () => {
|
|
553
|
+
const { queryIndex } = useVectorDB(false);
|
|
554
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa', 'bbb']) } });
|
|
555
|
+
routeViews('Members', { Members: readableMembersView(['aaa', 'bbb']) });
|
|
556
|
+
|
|
557
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
558
|
+
|
|
559
|
+
expect(result.Results.map((r) => r.ID)).toEqual(['pc-0', 'pc-1']);
|
|
560
|
+
const [check] = callsFor('Members');
|
|
561
|
+
expect(check.contextUser).toBe(contextUser);
|
|
562
|
+
expect(check.params.ExtraFilter).toBe("ID IN ('aaa','bbb')");
|
|
563
|
+
expect(check.params.Fields).toEqual(['ID']);
|
|
564
|
+
expect(check.params.ResultType).toBe('simple');
|
|
565
|
+
expect(check.params.IgnoreMaxRows).toBe(true);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('drops a match with no record ID, or one naming a field that is not the primary key, without querying for it', async () => {
|
|
569
|
+
const { queryIndex } = useVectorDB(false);
|
|
570
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor([undefined, "Salary|1' OR 1=1 --", 'ID|aaa']) } });
|
|
571
|
+
routeViews('Members', { Members: readableMembersView(['aaa']) });
|
|
572
|
+
|
|
573
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
574
|
+
|
|
575
|
+
expect(result.Results.map((r) => r.ID)).toEqual(['pc-2']);
|
|
576
|
+
expect(callsFor('Members').map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('aaa')"]);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('returns no matches and runs no check when none carries a usable record ID', async () => {
|
|
580
|
+
const { queryIndex } = useVectorDB(false);
|
|
581
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor([undefined]) } });
|
|
582
|
+
routeViews('Members', { Members: readableMembersView([]) });
|
|
583
|
+
|
|
584
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
585
|
+
|
|
586
|
+
expect(result.Success).toBe(true);
|
|
587
|
+
expect(result.Results).toEqual([]);
|
|
282
588
|
expect(callsFor('Members')).toHaveLength(0);
|
|
283
589
|
});
|
|
590
|
+
|
|
591
|
+
it('checks composite keys with one clause per record, using the real key columns', async () => {
|
|
592
|
+
const { queryIndex } = useVectorDB(false);
|
|
593
|
+
queryIndex.mockResolvedValueOnce({
|
|
594
|
+
success: true,
|
|
595
|
+
data: { matches: [
|
|
596
|
+
{ id: 'pc-a', values: [1], metadata: { Entity: 'Order Lines', RecordID: 'OrderID|o-1||LineNo|2' } },
|
|
597
|
+
{ id: 'pc-b', values: [2], metadata: { Entity: 'Order Lines', RecordID: 'OrderID|o-2||LineNo|1' } },
|
|
598
|
+
] },
|
|
599
|
+
});
|
|
600
|
+
routeViews('Order Lines', {
|
|
601
|
+
'Order Lines': () => ({ Success: true, Results: [{ OrderID: 'o-2', LineNo: 1 }] }),
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(orderLinesEntity));
|
|
605
|
+
|
|
606
|
+
expect(result.Results.map((r) => r.ID)).toEqual(['pc-b']);
|
|
607
|
+
const [check] = callsFor('Order Lines');
|
|
608
|
+
expect(check.params.ExtraFilter).toBe("(OrderID='o-1' AND LineNo='2') OR (OrderID='o-2' AND LineNo='1')");
|
|
609
|
+
expect(check.params.Fields).toEqual(['OrderID', 'LineNo']);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it('splits a large key check into lists of at most 1000, sent in one RunViews batch', async () => {
|
|
613
|
+
const { queryIndex } = useVectorDB(false);
|
|
614
|
+
const ids = Array.from({ length: 1500 }, (_, i) => `id-${i}`);
|
|
615
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(ids.map((id) => `ID|${id}`)) } });
|
|
616
|
+
routeViews('Members', { Members: readableMembersView(ids) });
|
|
617
|
+
|
|
618
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 1500, undefined, appContext(membersEntity));
|
|
619
|
+
|
|
620
|
+
expect(result.TotalCount).toBe(1500);
|
|
621
|
+
expect(callsFor('Members').map((c) => (c.params.ExtraFilter ?? '').split(',').length)).toEqual([1000, 500]);
|
|
622
|
+
expect(runViewsBatches).toEqual([['Members', 'Members']]);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it('returns an error result when the key check fails', async () => {
|
|
626
|
+
const { queryIndex } = useVectorDB(false);
|
|
627
|
+
queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa']) } });
|
|
628
|
+
routeViews('Members', { Members: () => ({ Success: false, Results: [], ErrorMessage: 'timeout' }) });
|
|
629
|
+
|
|
630
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
|
|
631
|
+
|
|
632
|
+
expect(result.Success).toBe(false);
|
|
633
|
+
expect(result.Results).toEqual([]);
|
|
634
|
+
});
|
|
284
635
|
});
|
|
285
636
|
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-level security on FileResolver's own create path and responses.
|
|
3
|
+
*
|
|
4
|
+
* `CreateFile` applies its input with `NewRecord` rather than through the generated create
|
|
5
|
+
* resolver, so it gates the input itself: a restricted caller's key for a field it may not read,
|
|
6
|
+
* or a key that names no field, is refused with the same message, before any storage work. The
|
|
7
|
+
* `File` it returns (and `UploadStorageFile`'s) is the record just saved or reloaded, which is
|
|
8
|
+
* projected for the caller. The file entity here stands in with the fixture's restricted fields.
|
|
9
|
+
*/
|
|
10
|
+
import 'reflect-metadata';
|
|
11
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
12
|
+
|
|
13
|
+
const mocks = vi.hoisted(() => ({ createUploadUrl: vi.fn() }));
|
|
14
|
+
|
|
15
|
+
vi.mock('@memberjunction/server', () => {
|
|
16
|
+
const noopDecorator = () => () => undefined;
|
|
17
|
+
return {
|
|
18
|
+
Arg: noopDecorator, Ctx: noopDecorator, Field: noopDecorator, FieldResolver: noopDecorator, InputType: noopDecorator,
|
|
19
|
+
Mutation: noopDecorator, ObjectType: noopDecorator, PubSub: noopDecorator, Query: noopDecorator, Resolver: noopDecorator,
|
|
20
|
+
Root: noopDecorator, Int: {}, Float: {}, DeleteOptionsInput: class {},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
vi.mock('../generated/generated.js', () => ({
|
|
24
|
+
CreateMJFileInput: class {},
|
|
25
|
+
UpdateMJFileInput: class {},
|
|
26
|
+
MJFile_: class {},
|
|
27
|
+
MJFileResolver: class {
|
|
28
|
+
protected GetUserFromPayload(userPayload: { userRecord?: object } | undefined) {
|
|
29
|
+
return userPayload?.userRecord;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
vi.mock('@memberjunction/ai-agents', () => ({ readRealtimeRecordingFile: vi.fn() }));
|
|
34
|
+
vi.mock('@memberjunction/graphql-dataprovider', () => ({ FieldMapper: class { MapFields(o: object) { return o; } } }));
|
|
35
|
+
vi.mock('../config.js', () => ({ configInfo: {} }));
|
|
36
|
+
vi.mock('../rest/MediaAccessKeys.js', () => ({ MediaAccessKeyManager: { Instance: {} } }));
|
|
37
|
+
vi.mock('../rest/UploadTokenManager.js', () => ({ UploadTokenManager: { Instance: { Consume: vi.fn() } } }));
|
|
38
|
+
vi.mock('@memberjunction/storage', async () => {
|
|
39
|
+
const actual = await vi.importActual<typeof import('@memberjunction/storage')>('@memberjunction/storage');
|
|
40
|
+
return { ...actual, createUploadUrl: mocks.createUploadUrl };
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
import type { PubSubEngine } from '@memberjunction/server';
|
|
44
|
+
import { FileResolver } from '../resolvers/FileResolver.js';
|
|
45
|
+
import type { AppContext } from '../types.js';
|
|
46
|
+
import { EmployeeEntity, FullWidthEmployeeRow, Intern, NoRecordChangesMetadata, RefusalFor, SECRET_SALARY } from './fieldSecurityFixtures.js';
|
|
47
|
+
|
|
48
|
+
function fileEntity() {
|
|
49
|
+
return { EntityInfo: EmployeeEntity(), CheckPermissions: vi.fn(), NewRecord: vi.fn(() => true), GetAll: () => FullWidthEmployeeRow() };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function contextFor(entity: ReturnType<typeof fileEntity>): AppContext {
|
|
53
|
+
const provider = { GetEntityObject: vi.fn(async () => entity), RunView: vi.fn(), RunViews: vi.fn() };
|
|
54
|
+
return { providers: [{ provider, type: 'Read-Write' }], userPayload: { userRecord: Intern() } } as unknown as AppContext;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
describe('FileResolver.CreateFile: client input is gated for a restricted caller', () => {
|
|
58
|
+
it.each([
|
|
59
|
+
['a denied field', 'Salary'],
|
|
60
|
+
['a key that names no field', 'Bogus'],
|
|
61
|
+
])('REGRESSION: refuses %s with the same message, before any storage work', async (_label, key) => {
|
|
62
|
+
const entity = fileEntity();
|
|
63
|
+
const input = { Name: 'new.pdf', [key]: 1 }; // keys of the stand-in entity
|
|
64
|
+
await expect(new FileResolver().CreateFile(input as never, contextFor(entity), {} as PubSubEngine))
|
|
65
|
+
.rejects.toThrow(RefusalFor(key));
|
|
66
|
+
expect(entity.NewRecord).not.toHaveBeenCalled();
|
|
67
|
+
expect(mocks.createUploadUrl).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('FileResolver responses are projected for the caller', () => {
|
|
72
|
+
it('REGRESSION: the File returned never carries a denied value of a full-width record', () => {
|
|
73
|
+
const respond = (FileResolver.prototype as unknown as {
|
|
74
|
+
fileResponse(entity: ReturnType<typeof fileEntity>, user: unknown, provider: unknown): Record<string, unknown>;
|
|
75
|
+
}).fileResponse;
|
|
76
|
+
|
|
77
|
+
const file = respond.call(new FileResolver(), fileEntity(), Intern(), NoRecordChangesMetadata);
|
|
78
|
+
|
|
79
|
+
expect(JSON.stringify(file)).not.toContain(String(SECRET_SALARY));
|
|
80
|
+
expect(file).toMatchObject({ ID: 'emp-1', Name: 'Ada' });
|
|
81
|
+
});
|
|
82
|
+
});
|