@memberjunction/server 6.2.0-edge.3 → 6.2.0-edge.5
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 +20 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
- package/dist/agentSessions/BridgeHostInstance.js +30 -0
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
- package/dist/agentSessions/HostInstance.d.ts +75 -11
- package/dist/agentSessions/HostInstance.d.ts.map +1 -1
- package/dist/agentSessions/HostInstance.js +99 -28
- package/dist/agentSessions/HostInstance.js.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +236 -27
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +682 -54
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +48 -3
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +112 -9
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
- package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
- package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
- package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
- package/dist/agentSessions/index.d.ts +2 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +10 -3
- package/dist/agentSessions/index.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 +25 -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/actionAuthorization.d.ts +40 -0
- package/dist/auth/actionAuthorization.d.ts.map +1 -0
- package/dist/auth/actionAuthorization.js +94 -0
- package/dist/auth/actionAuthorization.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 +4 -10
- package/dist/auth/index.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/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +6 -3
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.d.ts +10 -0
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +80 -11
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/config.d.ts +2514 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +166 -4
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +14 -11
- 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 +142 -0
- package/dist/coreRoutes.js.map +1 -0
- package/dist/generated/graphql-schemas/__mj.d.ts +504 -3
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +2456 -7
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +43 -8
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +59 -19
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PushStatusResolver.js +1 -1
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +123 -48
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +293 -305
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +16 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- 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 +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +219 -278
- package/dist/index.js.map +1 -1
- package/dist/isolatedProvider.d.ts +20 -0
- package/dist/isolatedProvider.d.ts.map +1 -0
- package/dist/isolatedProvider.js +22 -0
- package/dist/isolatedProvider.js.map +1 -0
- 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/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
- package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
- package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +139 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +99 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
- 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 +61 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
- package/dist/realtimeSessions/index.d.ts +19 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -0
- package/dist/realtimeSessions/index.js +19 -0
- package/dist/realtimeSessions/index.js.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +30 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js +47 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +92 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionAccess.js +94 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
- package/dist/realtimeSessions/verificationCore.d.ts +243 -0
- package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationCore.js +356 -0
- package/dist/realtimeSessions/verificationCore.js.map +1 -0
- package/dist/realtimeSessions/verificationPages.d.ts +66 -0
- package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPages.js +110 -0
- package/dist/realtimeSessions/verificationPages.js.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.js +249 -0
- package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.js +435 -0
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +31 -5
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +75 -14
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +14 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +21 -5
- package/dist/resolvers/AdhocQueryResolver.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/CacheStatsResolver.d.ts +27 -0
- package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
- package/dist/resolvers/CacheStatsResolver.js +113 -2
- package/dist/resolvers/CacheStatsResolver.js.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
- package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.js +28 -10
- package/dist/resolvers/ClientToolRequestResolver.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/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 +27 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +148 -2
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +82 -11
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +279 -101
- 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/HumanHandoffResolver.d.ts.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.js +7 -1
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +9 -4
- package/dist/resolvers/MCPResolver.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 +13 -0
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +157 -33
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +404 -116
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +100 -14
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +12 -11
- package/dist/resolvers/RunTemplateResolver.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 +6 -2
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
- package/dist/resolvers/SyncRolesUsersResolver.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 +65 -13
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +19 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +102 -24
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
- package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
- package/dist/resolvers/UserAvatarResolver.js +124 -0
- package/dist/resolvers/UserAvatarResolver.js.map +1 -0
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +2 -17
- package/dist/resolvers/UserResolver.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/resolvers/agentVisionConsentAudit.d.ts +37 -0
- package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
- package/dist/resolvers/agentVisionConsentAudit.js +63 -0
- package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
- package/dist/resolvers/avatarInputValidation.d.ts +40 -0
- package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
- package/dist/resolvers/avatarInputValidation.js +97 -0
- package/dist/resolvers/avatarInputValidation.js.map +1 -0
- package/dist/resolvers/currentUserRoles.d.ts +29 -0
- package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
- package/dist/resolvers/currentUserRoles.js +30 -0
- package/dist/resolvers/currentUserRoles.js.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.d.ts +6 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +21 -0
- package/dist/resolvers/meetings/meetingMutations.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 +3 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +21 -12
- 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/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 +57 -4
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +607 -18
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts +105 -0
- package/dist/sharedCache.d.ts.map +1 -0
- package/dist/sharedCache.js +279 -0
- package/dist/sharedCache.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +99 -96
- package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
- package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
- package/src/__tests__/BridgeHostInstance.test.ts +232 -0
- package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
- package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +433 -0
- package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
- package/src/__tests__/HostInstance.test.ts +84 -0
- package/src/__tests__/HumanHandoffResolver.test.ts +17 -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__/MeetingResolver.test.ts +75 -1
- package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
- package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
- package/src/__tests__/RealtimeRelay.test.ts +876 -0
- package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
- 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 +212 -6
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +170 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +6 -4
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
- package/src/__tests__/RunTemplateResolver.test.ts +132 -0
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
- package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
- package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
- package/src/__tests__/SessionJanitor.test.ts +1203 -21
- package/src/__tests__/SessionManager.test.ts +240 -17
- package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
- package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +403 -22
- package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
- package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
- package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/UserAvatarResolver.test.ts +255 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -0
- package/src/__tests__/avatarInputValidation.test.ts +160 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +64 -0
- package/src/__tests__/conversationRunEvents.test.ts +56 -14
- package/src/__tests__/globalCors.test.ts +217 -0
- package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
- package/src/__tests__/magicLinkService.consume.test.ts +6 -0
- package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -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__/newUsers.template.test.ts +151 -0
- package/src/__tests__/newUsers.test.ts +74 -5
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
- package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +7 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +652 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
- package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
- package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
- package/src/__tests__/realtimeSessions/helpers.ts +4 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
- package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
- package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
- package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
- package/src/__tests__/resolverBase.fls.test.ts +87 -4
- package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -96
- package/src/__tests__/sharedCache.test.ts +312 -0
- package/src/__tests__/widgetGuestElevation.test.ts +44 -0
- package/src/agentSessions/BridgeHostInstance.ts +32 -0
- package/src/agentSessions/HostInstance.ts +124 -28
- package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
- package/src/agentSessions/SessionJanitor.ts +821 -59
- package/src/agentSessions/SessionManager.ts +138 -26
- package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
- package/src/agentSessions/index.ts +10 -3
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +26 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/actionAuthorization.ts +116 -0
- package/src/auth/index.ts +4 -8
- package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
- package/src/auth/magicLink/MagicLinkService.ts +6 -3
- package/src/auth/newUsers.ts +99 -11
- package/src/config.ts +171 -5
- package/src/context.ts +15 -10
- package/src/coreRoutes.ts +166 -0
- package/src/generated/graphql-schemas/__mj.ts +1815 -57
- package/src/generic/CacheInvalidationResolver.ts +73 -18
- package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
- package/src/generic/PushStatusResolver.ts +1 -1
- package/src/generic/ResolverBase.ts +335 -326
- package/src/generic/RunViewResolver.ts +21 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +231 -300
- package/src/isolatedProvider.ts +22 -0
- package/src/multiTenancy/index.ts +24 -2
- package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
- package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
- package/src/realtimeSessions/README.md +179 -0
- package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +168 -0
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +109 -0
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
- package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
- package/src/realtimeSessions/index.ts +18 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
- package/src/realtimeSessions/sessionAccess.ts +145 -0
- package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
- package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
- package/src/realtimeSessions/verificationCore.ts +502 -0
- package/src/realtimeSessions/verificationPages.ts +149 -0
- package/src/realtimeSessions/verificationPolicy.ts +321 -0
- package/src/realtimeSessions/verificationWorkflow.ts +702 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
- package/src/resolvers/ActionResolver.ts +84 -21
- package/src/resolvers/AdhocQueryResolver.ts +24 -6
- package/src/resolvers/ArtifactFileResolver.ts +9 -8
- package/src/resolvers/CacheStatsResolver.ts +89 -2
- package/src/resolvers/ClientToolRequestResolver.ts +25 -7
- package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityRecordNameResolver.ts +3 -2
- package/src/resolvers/FetchEntityVectorsResolver.ts +179 -2
- package/src/resolvers/FileResolver.ts +321 -106
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/HumanHandoffResolver.ts +8 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
- package/src/resolvers/MCPResolver.ts +8 -5
- package/src/resolvers/QueryResolver.ts +11 -0
- package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
- package/src/resolvers/RealtimeClientSessionResolver.ts +511 -129
- package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
- package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
- package/src/resolvers/RunAIAgentResolver.ts +116 -14
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/SyncDataResolver.ts +6 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +68 -13
- package/src/resolvers/TransactionGroupResolver.ts +131 -27
- package/src/resolvers/UserAvatarResolver.ts +111 -0
- package/src/resolvers/UserResolver.ts +2 -16
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -0
- package/src/resolvers/agentVisionConsentAudit.ts +82 -0
- package/src/resolvers/avatarInputValidation.ts +123 -0
- package/src/resolvers/currentUserRoles.ts +32 -1
- package/src/resolvers/meetings/meetingMutations.ts +21 -0
- package/src/rest/RealtimeRecordingHandler.ts +464 -0
- package/src/rest/ViewOperationsHandler.ts +29 -13
- package/src/rest/setupRESTEndpoints.ts +6 -0
- package/src/serverExtensionReservedRoots.ts +14 -17
- 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 +713 -17
- package/src/sharedCache.ts +305 -0
- package/src/types.ts +7 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// type-graphql decorators call `Reflect.getMetadata`, which only exists once this polyfill is
|
|
2
|
+
// loaded. MUST precede any import that pulls in the resolver file.
|
|
3
|
+
import 'reflect-metadata';
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
|
|
9
|
+
vi.mock('type-graphql', () => {
|
|
10
|
+
const noopDecorator = () => () => undefined;
|
|
11
|
+
return {
|
|
12
|
+
Resolver: noopDecorator, Mutation: noopDecorator, Query: noopDecorator, Subscription: noopDecorator,
|
|
13
|
+
ObjectType: noopDecorator, InputType: noopDecorator, Field: noopDecorator, Arg: noopDecorator,
|
|
14
|
+
Args: noopDecorator, Ctx: noopDecorator, PubSub: noopDecorator, Root: noopDecorator,
|
|
15
|
+
Directive: noopDecorator, Authorized: noopDecorator, UseMiddleware: noopDecorator,
|
|
16
|
+
createUnionType: () => class {},
|
|
17
|
+
AuthorizationError: class AuthorizationError extends Error {},
|
|
18
|
+
Float: class {}, Int: class {}, ID: class {},
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
vi.mock('../config.js', () => ({ configInfo: { databaseSettings: { requestTimeout: 120_000 } } }));
|
|
23
|
+
|
|
24
|
+
import { UserInfo } from '@memberjunction/core';
|
|
25
|
+
import type { DatabaseProviderBase } from '@memberjunction/core';
|
|
26
|
+
import { TestQuerySQLResolver, TestQuerySQLInput } from '../resolvers/TestQuerySQLResolver.js';
|
|
27
|
+
import type { AppContext } from '../types.js';
|
|
28
|
+
|
|
29
|
+
interface EntityFixture {
|
|
30
|
+
Name: string;
|
|
31
|
+
SchemaName: string;
|
|
32
|
+
BaseView: string;
|
|
33
|
+
canRead?: boolean;
|
|
34
|
+
rowFilter?: string;
|
|
35
|
+
deniedFields?: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createReadOnlyProvider(entities: EntityFixture[]): DatabaseProviderBase {
|
|
39
|
+
return {
|
|
40
|
+
PlatformKey: 'sqlserver',
|
|
41
|
+
Entities: entities.map((e) => ({
|
|
42
|
+
Name: e.Name,
|
|
43
|
+
SchemaName: e.SchemaName,
|
|
44
|
+
BaseView: e.BaseView,
|
|
45
|
+
GetUserPermisions: () => ({ CanRead: e.canRead ?? true }),
|
|
46
|
+
GetEffectiveRowFilterWhereClause: () => e.rowFilter ?? '',
|
|
47
|
+
GetDeniedReadFields: () => new Set<string>(e.deniedFields ?? []),
|
|
48
|
+
})),
|
|
49
|
+
} as unknown as DatabaseProviderBase;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createPlainUser(): UserInfo {
|
|
53
|
+
const u = new UserInfo();
|
|
54
|
+
u.ID = 'D1F1A0C4-0000-4000-8000-000000000001';
|
|
55
|
+
u.Email = 'user@example.com';
|
|
56
|
+
return u;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const RUBRIC_EVALUATIONS: EntityFixture = {
|
|
60
|
+
Name: 'MJ: Rubric Evaluations',
|
|
61
|
+
SchemaName: '__mj',
|
|
62
|
+
BaseView: 'vwRubricEvaluations',
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const AI_MODELS: EntityFixture = {
|
|
66
|
+
Name: 'MJ: AI Models',
|
|
67
|
+
SchemaName: '__mj',
|
|
68
|
+
BaseView: 'vwAIModels',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
describe('TestQuerySQLResolver scope-limited principal guard', () => {
|
|
72
|
+
const resolverSrc = readFileSync(resolve(__dirname, '../resolvers/TestQuerySQLResolver.ts'), 'utf8');
|
|
73
|
+
|
|
74
|
+
it('checks IsScopeLimitedPrincipal before acquiring the read-only provider or authorizing SQL', () => {
|
|
75
|
+
const scopeGuardIdx = resolverSrc.indexOf('IsScopeLimitedPrincipal(');
|
|
76
|
+
const providerIdx = resolverSrc.indexOf('GetReadOnlyProvider(');
|
|
77
|
+
const authorizeIdx = resolverSrc.indexOf('this.AuthorizeRenderedTestSQL(');
|
|
78
|
+
|
|
79
|
+
expect(scopeGuardIdx).toBeGreaterThan(-1);
|
|
80
|
+
expect(providerIdx).toBeGreaterThan(-1);
|
|
81
|
+
expect(authorizeIdx).toBeGreaterThan(-1);
|
|
82
|
+
expect(scopeGuardIdx).toBeLessThan(providerIdx);
|
|
83
|
+
expect(providerIdx).toBeLessThan(authorizeIdx);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('refuses anonymous magic-link guests, resource-scoped sessions, and missing principals', async () => {
|
|
87
|
+
const resolver = new TestQuerySQLResolver();
|
|
88
|
+
const input: TestQuerySQLInput = { SQL: 'SELECT ID FROM [__mj].[vwRubricEvaluations]' };
|
|
89
|
+
|
|
90
|
+
const anonGuest = createPlainUser();
|
|
91
|
+
anonGuest.IsMagicLinkAnonymous = true;
|
|
92
|
+
|
|
93
|
+
const scopedUser = createPlainUser();
|
|
94
|
+
scopedUser.MagicLinkScope = { ResourceID: 'A0000000-0000-4000-8000-000000000009' };
|
|
95
|
+
|
|
96
|
+
for (const userRecord of [anonGuest, scopedUser, undefined]) {
|
|
97
|
+
const context = {
|
|
98
|
+
userPayload: { userRecord, email: 'guest@example.com', sessionId: 'sess-1' },
|
|
99
|
+
providers: [],
|
|
100
|
+
} as unknown as AppContext;
|
|
101
|
+
|
|
102
|
+
const result = await resolver.TestQuerySQL(input, context);
|
|
103
|
+
expect(result.Success).toBe(false);
|
|
104
|
+
expect(result.RowCount).toBe(0);
|
|
105
|
+
expect(result.ErrorMessage).toBe('Ad-hoc SQL execution is not permitted for scope-limited sessions.');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('TestQuerySQLResolver.AuthorizeRenderedTestSQL', () => {
|
|
111
|
+
const resolver = new TestQuerySQLResolver();
|
|
112
|
+
const user = createPlainUser();
|
|
113
|
+
|
|
114
|
+
it('admits a read query referencing only unscoped readable entity base views', () => {
|
|
115
|
+
const provider = createReadOnlyProvider([RUBRIC_EVALUATIONS, AI_MODELS]);
|
|
116
|
+
const refusal = resolver.AuthorizeRenderedTestSQL(
|
|
117
|
+
{ SQL: 'SELECT r.ID, m.Name FROM [__mj].[vwRubricEvaluations] r INNER JOIN [__mj].[vwAIModels] m ON r.ID = m.ID' },
|
|
118
|
+
provider,
|
|
119
|
+
user,
|
|
120
|
+
);
|
|
121
|
+
expect(refusal).toBeNull();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('allows a table-free query that reads niladic user functions (CURRENT_USER, SESSION_USER, SYSTEM_USER)', () => {
|
|
125
|
+
const provider = createReadOnlyProvider([RUBRIC_EVALUATIONS]);
|
|
126
|
+
const refusal = resolver.AuthorizeRenderedTestSQL(
|
|
127
|
+
{ SQL: 'SELECT CURRENT_USER AS UserName, SESSION_USER AS SessionUser, SYSTEM_USER AS SystemUser' },
|
|
128
|
+
provider,
|
|
129
|
+
user,
|
|
130
|
+
);
|
|
131
|
+
expect(refusal).toBeNull();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('refuses a query that reads a base table instead of an entity base view', () => {
|
|
135
|
+
const provider = createReadOnlyProvider([RUBRIC_EVALUATIONS]);
|
|
136
|
+
const refusal = resolver.AuthorizeRenderedTestSQL(
|
|
137
|
+
{ SQL: 'SELECT * FROM [__mj].[User]' },
|
|
138
|
+
provider,
|
|
139
|
+
user,
|
|
140
|
+
);
|
|
141
|
+
expect(refusal).toMatch(/entity base view/);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('refuses a query that reads an entity the caller does not have CanRead permission for', () => {
|
|
145
|
+
const provider = createReadOnlyProvider([{ ...RUBRIC_EVALUATIONS, canRead: false }]);
|
|
146
|
+
const refusal = resolver.AuthorizeRenderedTestSQL(
|
|
147
|
+
{ SQL: 'SELECT ID FROM [__mj].[vwRubricEvaluations]' },
|
|
148
|
+
provider,
|
|
149
|
+
user,
|
|
150
|
+
);
|
|
151
|
+
expect(refusal).toMatch(/not permitted to read|permission/i);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('refuses a query that reads a row-level-security filtered entity', () => {
|
|
155
|
+
const provider = createReadOnlyProvider([
|
|
156
|
+
{ ...RUBRIC_EVALUATIONS, rowFilter: "EvaluatorUserID = 'user-1'" },
|
|
157
|
+
]);
|
|
158
|
+
const refusal = resolver.AuthorizeRenderedTestSQL(
|
|
159
|
+
{ SQL: 'SELECT ID FROM [__mj].[vwRubricEvaluations]' },
|
|
160
|
+
provider,
|
|
161
|
+
user,
|
|
162
|
+
);
|
|
163
|
+
expect(refusal).toMatch(/row-level-security filtered/);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('refuses a query that reads an entity with denied read fields for the caller', () => {
|
|
167
|
+
const provider = createReadOnlyProvider([
|
|
168
|
+
{ ...RUBRIC_EVALUATIONS, deniedFields: ['SecretScore'] },
|
|
169
|
+
]);
|
|
170
|
+
const refusal = resolver.AuthorizeRenderedTestSQL(
|
|
171
|
+
{ SQL: 'SELECT ID FROM [__mj].[vwRubricEvaluations]' },
|
|
172
|
+
provider,
|
|
173
|
+
user,
|
|
174
|
+
);
|
|
175
|
+
expect(refusal).toMatch(/fields you are not permitted to read/);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('refuses write statements and stacked SQL batches at render time', () => {
|
|
179
|
+
const provider = createReadOnlyProvider([RUBRIC_EVALUATIONS]);
|
|
180
|
+
|
|
181
|
+
const deleteRefusal = resolver.AuthorizeRenderedTestSQL(
|
|
182
|
+
{ SQL: 'DELETE FROM [__mj].[vwRubricEvaluations]' },
|
|
183
|
+
provider,
|
|
184
|
+
user,
|
|
185
|
+
);
|
|
186
|
+
expect(deleteRefusal).toMatch(/single read query/i);
|
|
187
|
+
|
|
188
|
+
const stackedRefusal = resolver.AuthorizeRenderedTestSQL(
|
|
189
|
+
{ SQL: 'SELECT ID FROM [__mj].[vwRubricEvaluations]; SELECT * FROM [__mj].[User]' },
|
|
190
|
+
provider,
|
|
191
|
+
user,
|
|
192
|
+
);
|
|
193
|
+
expect(stackedRefusal).toMatch(/single read query/i);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('screens the rendered SQL after Nunjucks template expansion', () => {
|
|
197
|
+
const provider = createReadOnlyProvider([RUBRIC_EVALUATIONS]);
|
|
198
|
+
|
|
199
|
+
// Safe parameter substitution on an allowed view passes
|
|
200
|
+
const safeRefusal = resolver.AuthorizeRenderedTestSQL(
|
|
201
|
+
{
|
|
202
|
+
SQL: 'SELECT ID FROM [__mj].[vwRubricEvaluations] WHERE Name = {{ targetName | sqlString }}',
|
|
203
|
+
UsesTemplate: true,
|
|
204
|
+
Parameters: { targetName: 'Q1 Eval' },
|
|
205
|
+
},
|
|
206
|
+
provider,
|
|
207
|
+
user,
|
|
208
|
+
);
|
|
209
|
+
expect(safeRefusal).toBeNull();
|
|
210
|
+
|
|
211
|
+
// Template expansion that introduces a base table reference is refused
|
|
212
|
+
const injectedTableRefusal = resolver.AuthorizeRenderedTestSQL(
|
|
213
|
+
{
|
|
214
|
+
SQL: 'SELECT * FROM {{ targetTable }}',
|
|
215
|
+
UsesTemplate: true,
|
|
216
|
+
Parameters: { targetTable: '[__mj].[User]' },
|
|
217
|
+
},
|
|
218
|
+
provider,
|
|
219
|
+
user,
|
|
220
|
+
);
|
|
221
|
+
expect(injectedTableRefusal).toMatch(/entity base view/);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('screens the rendered SQL after inline Dependencies ({{query:"..."}}) composition', () => {
|
|
225
|
+
const provider = createReadOnlyProvider([RUBRIC_EVALUATIONS, AI_MODELS]);
|
|
226
|
+
|
|
227
|
+
// Inline dependency reading an unscoped readable entity base view is admitted
|
|
228
|
+
const allowedDepRefusal = resolver.AuthorizeRenderedTestSQL(
|
|
229
|
+
{
|
|
230
|
+
SQL: 'SELECT sub.ID FROM {{query:"Analytics/Evals/Base"}} sub',
|
|
231
|
+
Dependencies: [
|
|
232
|
+
{
|
|
233
|
+
Name: 'Base',
|
|
234
|
+
CategoryPath: '/Analytics/Evals/',
|
|
235
|
+
SQL: 'SELECT ID FROM [__mj].[vwRubricEvaluations]',
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
provider,
|
|
240
|
+
user,
|
|
241
|
+
);
|
|
242
|
+
expect(allowedDepRefusal).toBeNull();
|
|
243
|
+
|
|
244
|
+
// Inline dependency reading a base table is refused when composed into the outer query
|
|
245
|
+
const forbiddenDepRefusal = resolver.AuthorizeRenderedTestSQL(
|
|
246
|
+
{
|
|
247
|
+
SQL: 'SELECT sub.ID FROM {{query:"Analytics/Evals/LeakedUsers"}} sub',
|
|
248
|
+
Dependencies: [
|
|
249
|
+
{
|
|
250
|
+
Name: 'LeakedUsers',
|
|
251
|
+
CategoryPath: '/Analytics/Evals/',
|
|
252
|
+
SQL: 'SELECT ID FROM [__mj].[User]',
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
provider,
|
|
257
|
+
user,
|
|
258
|
+
);
|
|
259
|
+
expect(forbiddenDepRefusal).toMatch(/entity base view/);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('IntegrationDiscoveryResolver webhook SSRF guard', () => {
|
|
264
|
+
const discoverySrc = readFileSync(resolve(__dirname, '../resolvers/IntegrationDiscoveryResolver.ts'), 'utf8');
|
|
265
|
+
|
|
266
|
+
it('routes sendWebhook through SafeFetch from @memberjunction/network-utils instead of bare fetch', () => {
|
|
267
|
+
expect(discoverySrc).toMatch(/import\s*\{[^}]*\bSafeFetch\b[^}]*\}\s*from\s*['"]@memberjunction\/network-utils['"]/);
|
|
268
|
+
const sendWebhookStart = discoverySrc.indexOf('private async sendWebhook(');
|
|
269
|
+
expect(sendWebhookStart).toBeGreaterThan(-1);
|
|
270
|
+
const sendWebhookBody = discoverySrc.slice(sendWebhookStart, sendWebhookStart + 800);
|
|
271
|
+
expect(sendWebhookBody).toContain('await SafeFetch(url,');
|
|
272
|
+
expect(sendWebhookBody).not.toMatch(/(?<![A-Za-z])fetch\s*\(/);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
TransactionInputType,
|
|
42
42
|
TransactionItemInputType,
|
|
43
43
|
TransactionOperationType,
|
|
44
|
+
TransactionVariableType,
|
|
44
45
|
} from '../resolvers/TransactionGroupResolver.js';
|
|
45
46
|
import type { AppContext } from '../types.js';
|
|
46
47
|
|
|
@@ -57,7 +58,10 @@ class FakeTransactionGroup {
|
|
|
57
58
|
public AddTransaction(label: string): void {
|
|
58
59
|
this.Enrolled.push(label);
|
|
59
60
|
}
|
|
60
|
-
public
|
|
61
|
+
public Variables: unknown[] = [];
|
|
62
|
+
public AddVariable(variable: unknown): void {
|
|
63
|
+
this.Variables.push(variable);
|
|
64
|
+
}
|
|
61
65
|
public async Submit(): Promise<boolean> {
|
|
62
66
|
this.SubmitCalled = true;
|
|
63
67
|
// TransactionGroupBase.Submit(): an empty group is "nothing to do" and returns true.
|
|
@@ -287,4 +291,97 @@ describe('ExecuteTransactionGroup — rows refused server-side (issue #4309)', (
|
|
|
287
291
|
expect(result.Success).toBe(true);
|
|
288
292
|
expect(group.Enrolled).toHaveLength(0);
|
|
289
293
|
});
|
|
294
|
+
|
|
295
|
+
it("registers the group's variables before any row's Save(), so a server-side check can see them", async () => {
|
|
296
|
+
// A 'Use' variable sets its field at Submit(), after every Save()-time check has passed. A check
|
|
297
|
+
// that depends on a field's final value (who a conversation message is from) can refuse such a
|
|
298
|
+
// row only if the variable is already on the group when its Save() runs.
|
|
299
|
+
const variablesAtSave: number[] = [];
|
|
300
|
+
class RecordingEntity extends FakeEntity {
|
|
301
|
+
public override async Save(): Promise<boolean> {
|
|
302
|
+
variablesAtSave.push(this.TransactionGroup?.Variables.length ?? -1);
|
|
303
|
+
return super.Save();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const entities = [
|
|
307
|
+
new RecordingEntity('MJ: User Roles', { accepts: true }),
|
|
308
|
+
new RecordingEntity('MJ: User Roles', { accepts: true }),
|
|
309
|
+
];
|
|
310
|
+
const { context } = buildContext(entities);
|
|
311
|
+
const input = buildGroupInput(2);
|
|
312
|
+
input.Variables = [
|
|
313
|
+
{ Name: 'v', ItemIndex: 0, FieldName: 'ID', Type: TransactionVariableType.Define },
|
|
314
|
+
{ Name: 'v', ItemIndex: 1, FieldName: 'UserID', Type: TransactionVariableType.Use },
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(input, context);
|
|
318
|
+
|
|
319
|
+
expect(result.Success).toBe(true);
|
|
320
|
+
expect(variablesAtSave).toEqual([2, 2]);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('returns delta-only ResultsJSON on Update (PK, client-sent fields, datestamps, and server-changed fields), omitting unchanged columns', async () => {
|
|
324
|
+
const preLoadedRow: Record<string, unknown> = {
|
|
325
|
+
ID: 'a0000000-0000-0000-0000-000000000099',
|
|
326
|
+
Name: 'Action One',
|
|
327
|
+
Description: 'Initial description',
|
|
328
|
+
DriverClass: 'OriginalDriver',
|
|
329
|
+
Category: 'Utilities',
|
|
330
|
+
__mj_CreatedAt: '2026-01-01T00:00:00.000Z',
|
|
331
|
+
__mj_UpdatedAt: '2026-02-01T00:00:00.000Z',
|
|
332
|
+
};
|
|
333
|
+
for (let i = 1; i <= 15; i++) {
|
|
334
|
+
preLoadedRow[`UnchangedField${i}`] = `Constant-${i}`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
class ServerMutatingEntity extends FakeEntity {
|
|
338
|
+
public override async Save(): Promise<boolean> {
|
|
339
|
+
const ok = await super.Save();
|
|
340
|
+
if (ok) {
|
|
341
|
+
// Simulate server-side DB update refreshing __mj_UpdatedAt and a computed/trigger column
|
|
342
|
+
this.SetMany({
|
|
343
|
+
__mj_UpdatedAt: '2026-03-01T00:00:00.000Z',
|
|
344
|
+
Category: 'Automation (Server Refreshed)',
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return ok;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const entity = new ServerMutatingEntity('MJ: Actions', {
|
|
352
|
+
accepts: true,
|
|
353
|
+
loadedValues: preLoadedRow,
|
|
354
|
+
});
|
|
355
|
+
const { context } = buildContext([entity]);
|
|
356
|
+
const input: TransactionInputType = {
|
|
357
|
+
Items: [
|
|
358
|
+
{
|
|
359
|
+
EntityName: 'MJ: Actions',
|
|
360
|
+
EntityObjectJSON: JSON.stringify({
|
|
361
|
+
ID: 'a0000000-0000-0000-0000-000000000099',
|
|
362
|
+
Description: 'Updated description',
|
|
363
|
+
}),
|
|
364
|
+
OperationType: TransactionOperationType.Update,
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
Variables: [],
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(input, context);
|
|
371
|
+
|
|
372
|
+
expect(result.Success).toBe(true);
|
|
373
|
+
const returnedObj = JSON.parse(result.ResultsJSON[0]) as Record<string, unknown>;
|
|
374
|
+
expect(returnedObj).toEqual({
|
|
375
|
+
ID: 'a0000000-0000-0000-0000-000000000099',
|
|
376
|
+
Description: 'Updated description',
|
|
377
|
+
Category: 'Automation (Server Refreshed)',
|
|
378
|
+
__mj_CreatedAt: '2026-01-01T00:00:00.000Z',
|
|
379
|
+
__mj_UpdatedAt: '2026-03-01T00:00:00.000Z',
|
|
380
|
+
});
|
|
381
|
+
expect(returnedObj).not.toHaveProperty('Name');
|
|
382
|
+
expect(returnedObj).not.toHaveProperty('DriverClass');
|
|
383
|
+
for (let i = 1; i <= 15; i++) {
|
|
384
|
+
expect(returnedObj).not.toHaveProperty(`UnchangedField${i}`);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
290
387
|
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// type-graphql decorators in the resolver graph need the Reflect.metadata polyfill at import time.
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
/**
|
|
4
|
+
* `UpdateMJDataContextItem` goes through `ResolverBase.UpdateRecord`. `MJ: Data Context Items` does
|
|
5
|
+
* not track record changes, so without special handling a client that sends `OldValues___` decides
|
|
6
|
+
* the record's starting state. It could repeat its new SQL as the old value, so the SQL field does
|
|
7
|
+
* not look changed. Or it could leave `Type` and `SQL` out entirely, so they are never loaded and
|
|
8
|
+
* the stored procedure keeps the stored SQL item. The server entity class for the item must still
|
|
9
|
+
* refuse a non-Owner's save of a SQL item on every one of these paths.
|
|
10
|
+
*
|
|
11
|
+
* The resolver base and the entity class are real. The provider hands out real entity objects,
|
|
12
|
+
* loads a fixed stored row, and records each save instead of writing it.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
15
|
+
import { EntityInfo, Metadata, UserInfo, UserRoleInfo } from '@memberjunction/core';
|
|
16
|
+
import type { BaseEntity, DatabaseProviderBase, IEntityDataProvider, IMetadataProvider } from '@memberjunction/core';
|
|
17
|
+
import { MJDataContextItemEntityServer } from '@memberjunction/core-entities-server';
|
|
18
|
+
import type { PubSubEngine } from 'type-graphql';
|
|
19
|
+
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
20
|
+
import type { UserPayload } from '../types.js';
|
|
21
|
+
|
|
22
|
+
const ENTITY_ID = 'E0000000-0000-4000-8000-0000000000D2';
|
|
23
|
+
const UI_ROLE_ID = 'E1000000-0000-4000-8000-000000000001';
|
|
24
|
+
const DATA_CONTEXT_ID = 'B0000000-0000-4000-8000-000000000001';
|
|
25
|
+
const OTHER_DATA_CONTEXT_ID = 'B0000000-0000-4000-8000-000000000002';
|
|
26
|
+
const ITEM_ID = 'C0000000-0000-4000-8000-000000000001';
|
|
27
|
+
const STORED_SQL = 'SELECT 1 AS One';
|
|
28
|
+
const NEW_SQL = 'SELECT 2 AS Two';
|
|
29
|
+
|
|
30
|
+
interface StoredRow {
|
|
31
|
+
ID: string;
|
|
32
|
+
DataContextID: string;
|
|
33
|
+
Type: string;
|
|
34
|
+
SQL: string | null;
|
|
35
|
+
DataJSON: string | null;
|
|
36
|
+
Description: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A SQL item in someone else's data context. */
|
|
40
|
+
const STORED_SQL_ITEM: StoredRow = {
|
|
41
|
+
ID: ITEM_ID, DataContextID: DATA_CONTEXT_ID, Type: 'sql', SQL: STORED_SQL, DataJSON: null, Description: 'Revenue',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** An item of another type, with no SQL. */
|
|
45
|
+
const STORED_ENTITY_ITEM: StoredRow = {
|
|
46
|
+
ID: ITEM_ID, DataContextID: DATA_CONTEXT_ID, Type: 'full_entity', SQL: null, DataJSON: null, Description: 'Notes',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `MJ: Data Context Items` as the database describes it: record changes and field-level security
|
|
51
|
+
* off, the API open, and the UI role holding every permission with no row-level filter.
|
|
52
|
+
*/
|
|
53
|
+
const ITEM_ENTITY = new EntityInfo({
|
|
54
|
+
ID: ENTITY_ID,
|
|
55
|
+
Name: 'MJ: Data Context Items',
|
|
56
|
+
SchemaName: '__mj',
|
|
57
|
+
BaseTable: 'DataContextItem',
|
|
58
|
+
BaseView: 'vwDataContextItems',
|
|
59
|
+
Status: 'Active',
|
|
60
|
+
IncludeInAPI: true,
|
|
61
|
+
AllowCreateAPI: true,
|
|
62
|
+
AllowUpdateAPI: true,
|
|
63
|
+
AllowDeleteAPI: true,
|
|
64
|
+
TrackRecordChanges: false,
|
|
65
|
+
EnableFieldLevelSecurity: false,
|
|
66
|
+
EntityPermissions: [{
|
|
67
|
+
ID: 'P0000000-0000-4000-8000-000000000001', EntityID: ENTITY_ID, RoleID: UI_ROLE_ID,
|
|
68
|
+
CanCreate: true, CanRead: true, CanUpdate: true, CanDelete: true,
|
|
69
|
+
}],
|
|
70
|
+
EntityFields: [
|
|
71
|
+
{ Name: 'ID', Type: 'uniqueidentifier', AllowsNull: false, IsPrimaryKey: true },
|
|
72
|
+
{ Name: 'DataContextID', Type: 'uniqueidentifier', AllowsNull: false },
|
|
73
|
+
{ Name: 'Type', Type: 'nvarchar', AllowsNull: false },
|
|
74
|
+
{ Name: 'SQL', Type: 'nvarchar', AllowsNull: true },
|
|
75
|
+
{ Name: 'DataJSON', Type: 'nvarchar', AllowsNull: true },
|
|
76
|
+
{ Name: 'Description', Type: 'nvarchar', AllowsNull: true },
|
|
77
|
+
].map((c, i) => ({
|
|
78
|
+
ID: `F0000000-0000-4000-8000-00000000000${i}`,
|
|
79
|
+
EntityID: ENTITY_ID,
|
|
80
|
+
Sequence: i + 1,
|
|
81
|
+
Length: 0,
|
|
82
|
+
Precision: 0,
|
|
83
|
+
Scale: 0,
|
|
84
|
+
AllowUpdateAPI: true,
|
|
85
|
+
...c,
|
|
86
|
+
})),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/** A UI-role user who is not an administrator. */
|
|
90
|
+
const UI_USER = new UserInfo(null, {
|
|
91
|
+
ID: 'A0000000-0000-4000-8000-000000000002',
|
|
92
|
+
Email: 'ui-user@example.com',
|
|
93
|
+
Type: 'User',
|
|
94
|
+
UserRoles: [new UserRoleInfo({ UserID: 'A0000000-0000-4000-8000-000000000002', RoleID: UI_ROLE_ID })],
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const PAYLOAD: UserPayload = { email: UI_USER.Email, userRecord: UI_USER, sessionId: 'test-session' };
|
|
98
|
+
const PUB_SUB = { publish: async (): Promise<void> => undefined } as unknown as PubSubEngine;
|
|
99
|
+
|
|
100
|
+
/** What one save would have written. */
|
|
101
|
+
interface RecordedSave {
|
|
102
|
+
DataContextID: string;
|
|
103
|
+
SQL: string | null;
|
|
104
|
+
DataJSON: string | null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A database provider whose load returns `stored`, and which records each save it receives. */
|
|
108
|
+
function recordingDatabase(stored: StoredRow): { Saves: RecordedSave[]; Provider: DatabaseProviderBase } {
|
|
109
|
+
const saves: RecordedSave[] = [];
|
|
110
|
+
const entityData = {
|
|
111
|
+
async Load(): Promise<Record<string, unknown>> {
|
|
112
|
+
return { ...stored };
|
|
113
|
+
},
|
|
114
|
+
async Save(entity: BaseEntity): Promise<Record<string, unknown>> {
|
|
115
|
+
const item = entity as MJDataContextItemEntityServer;
|
|
116
|
+
saves.push({ DataContextID: item.DataContextID, SQL: item.SQL, DataJSON: item.DataJSON });
|
|
117
|
+
return entity.GetAll();
|
|
118
|
+
},
|
|
119
|
+
} as unknown as IEntityDataProvider;
|
|
120
|
+
const database = {
|
|
121
|
+
async GetEntityObject(_entityName: string, user: UserInfo): Promise<MJDataContextItemEntityServer> {
|
|
122
|
+
const item = new MJDataContextItemEntityServer(ITEM_ENTITY, entityData);
|
|
123
|
+
item.ContextCurrentUser = user;
|
|
124
|
+
return item;
|
|
125
|
+
},
|
|
126
|
+
} as unknown as DatabaseProviderBase;
|
|
127
|
+
return { Saves: saves, Provider: database };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Builds an `OldValues___` array from a plain object. */
|
|
131
|
+
function oldValues(values: Record<string, string | null>): Array<{ Key: string; Value: string | null }> {
|
|
132
|
+
return Object.entries(values).map(([Key, Value]) => ({ Key, Value }));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Exposes ResolverBase's generic update path, which the generated `UpdateMJDataContextItem` mutation calls. */
|
|
136
|
+
class UpdateHarness extends ResolverBase {
|
|
137
|
+
/** Runs the update and reports how it ended: 'saved', or the message it was refused with. */
|
|
138
|
+
public async Update(input: Record<string, unknown>, provider: DatabaseProviderBase): Promise<string> {
|
|
139
|
+
try {
|
|
140
|
+
await this.UpdateRecord('MJ: Data Context Items', input, provider, PAYLOAD, PUB_SUB);
|
|
141
|
+
return 'saved';
|
|
142
|
+
} catch (e) {
|
|
143
|
+
return e instanceof Error ? e.message : String(e);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const harness = new UpdateHarness();
|
|
149
|
+
|
|
150
|
+
describe('UpdateMJDataContextItem — a non-Owner may not change a SQL item', () => {
|
|
151
|
+
it('refuses new SQL when the update loads the stored row (no OldValues___)', async () => {
|
|
152
|
+
const db = recordingDatabase(STORED_SQL_ITEM);
|
|
153
|
+
|
|
154
|
+
const outcome = await harness.Update({ ...STORED_SQL_ITEM, SQL: NEW_SQL }, db.Provider);
|
|
155
|
+
|
|
156
|
+
expect(db.Saves).toEqual([]);
|
|
157
|
+
expect(outcome).toMatch(/Owner-type/);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('refuses new SQL when OldValues___ repeats the new SQL, so the field does not look changed', async () => {
|
|
161
|
+
const db = recordingDatabase(STORED_SQL_ITEM);
|
|
162
|
+
const input = {
|
|
163
|
+
...STORED_SQL_ITEM,
|
|
164
|
+
SQL: NEW_SQL,
|
|
165
|
+
Description: 'Renamed',
|
|
166
|
+
OldValues___: oldValues({ ID: ITEM_ID, DataContextID: DATA_CONTEXT_ID, Type: 'sql', SQL: NEW_SQL, Description: 'Revenue' }),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const outcome = await harness.Update(input, db.Provider);
|
|
170
|
+
|
|
171
|
+
expect(db.Saves).toEqual([]);
|
|
172
|
+
expect(outcome).toMatch(/Owner-type/);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('refuses moving a SQL item to another data context when Type and SQL are left out of the input and OldValues___', async () => {
|
|
176
|
+
const db = recordingDatabase(STORED_SQL_ITEM);
|
|
177
|
+
const input = {
|
|
178
|
+
ID: ITEM_ID,
|
|
179
|
+
DataContextID: OTHER_DATA_CONTEXT_ID,
|
|
180
|
+
OldValues___: oldValues({ ID: ITEM_ID, DataContextID: DATA_CONTEXT_ID }),
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const outcome = await harness.Update(input, db.Provider);
|
|
184
|
+
|
|
185
|
+
expect(db.Saves).toEqual([]);
|
|
186
|
+
expect(outcome).toMatch(/Owner-type/);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('refuses changing the cached DataJSON of a SQL item when Type and SQL are left out of the input and OldValues___', async () => {
|
|
190
|
+
const db = recordingDatabase(STORED_SQL_ITEM);
|
|
191
|
+
const input = {
|
|
192
|
+
ID: ITEM_ID,
|
|
193
|
+
DataJSON: '[{"One":2}]',
|
|
194
|
+
OldValues___: oldValues({ ID: ITEM_ID, DataJSON: null }),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const outcome = await harness.Update(input, db.Provider);
|
|
198
|
+
|
|
199
|
+
expect(db.Saves).toEqual([]);
|
|
200
|
+
expect(outcome).toMatch(/Owner-type/);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('still saves a non-Owner\'s change to an item of another type that sends OldValues___', async () => {
|
|
204
|
+
// The success path maps the saved row back through the process-wide metadata.
|
|
205
|
+
vi.spyOn(Metadata, 'Provider', 'get').mockReturnValue(
|
|
206
|
+
{ Entities: [ITEM_ENTITY], EntityByName: () => ITEM_ENTITY } as unknown as IMetadataProvider,
|
|
207
|
+
);
|
|
208
|
+
const db = recordingDatabase(STORED_ENTITY_ITEM);
|
|
209
|
+
const input = {
|
|
210
|
+
ID: ITEM_ID,
|
|
211
|
+
Description: 'Renamed',
|
|
212
|
+
OldValues___: oldValues({ ID: ITEM_ID, Description: 'Notes' }),
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const outcome = await harness.Update(input, db.Provider);
|
|
216
|
+
|
|
217
|
+
expect(outcome).toBe('saved');
|
|
218
|
+
expect(db.Saves).toEqual([{ DataContextID: DATA_CONTEXT_ID, SQL: null, DataJSON: null }]);
|
|
219
|
+
});
|
|
220
|
+
});
|