@memberjunction/server 6.2.0-edge.4 → 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/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 +5 -1
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +13 -1
- 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 +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/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- 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/config.d.ts +136 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -4
- package/dist/config.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 +369 -3
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +1750 -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/PushStatusResolver.js +1 -1
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +103 -44
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +264 -305
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +12 -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 +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +112 -160
- package/dist/index.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/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 +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 +6 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +7 -3
- package/dist/realtimeSessions/sessionAccess.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/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 +18 -3
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +100 -35
- 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/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
- package/dist/resolvers/IntegrationDiscoveryResolver.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/RealtimeClientSessionResolver.d.ts +2 -8
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +10 -26
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +35 -6
- package/dist/resolvers/RunAIAgentResolver.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/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/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/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 +54 -2
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +598 -15
- 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/package.json +99 -98
- package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +163 -15
- 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__/RecordingAssemblyScheduler.test.ts +178 -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 +209 -6
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +1 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +5 -3
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -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 +27 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +356 -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__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -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__/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__/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__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
- package/src/__tests__/sharedCache.test.ts +5 -3
- package/src/agentSessions/SessionJanitor.ts +369 -93
- package/src/agentSessions/SessionManager.ts +13 -1
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +26 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
- package/src/config.ts +46 -5
- package/src/coreRoutes.ts +166 -0
- package/src/generated/graphql-schemas/__mj.ts +1273 -10
- package/src/generic/CacheInvalidationResolver.ts +73 -18
- package/src/generic/PushStatusResolver.ts +1 -1
- package/src/generic/ResolverBase.ts +304 -325
- package/src/generic/RunViewResolver.ts +17 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +116 -174
- package/src/multiTenancy/index.ts +24 -2
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/index.ts +2 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
- package/src/realtimeSessions/sessionAccess.ts +7 -3
- package/src/resolvers/ArtifactFileResolver.ts +9 -8
- 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 +121 -39
- package/src/resolvers/FileResolver.ts +321 -106
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
- package/src/resolvers/QueryResolver.ts +11 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +10 -33
- package/src/resolvers/RunAIAgentResolver.ts +39 -4
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +68 -13
- package/src/resolvers/TransactionGroupResolver.ts +131 -27
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -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 +704 -14
- package/src/sharedCache.ts +3 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// ResolverBase transitively pulls in type-graphql decorators, which need the
|
|
2
|
+
// Reflect.metadata polyfill at import time.
|
|
3
|
+
import 'reflect-metadata';
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { CompositeKey, DatabaseProviderBase, EntityInfo, EntityPermissionType, Metadata, RowLevelSecurityFilterInfo, UserInfo } from '@memberjunction/core';
|
|
6
|
+
import type { DeleteSQLResult, IMetadataProvider, QueryExecutionSpec, RunQueryResult, SaveSQLResult } from '@memberjunction/core';
|
|
7
|
+
import { EntityRecordNameResolver } from '../resolvers/EntityRecordNameResolver.js';
|
|
8
|
+
import type { AppContext, UserPayload } from '../types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The record-name queries against a real provider whose database only records what it is sent.
|
|
12
|
+
* The lookup the resolver causes carries the acting user's read row filter, binds the key value,
|
|
13
|
+
* refuses a key that is not the entity's primary key, and never runs without an acting user.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const CLIENTS_ID = 'E0000000-0000-0000-0000-0000000000C1';
|
|
17
|
+
const ACCOUNT_MANAGER_ROLE_ID = 'B0000000-0000-0000-0000-0000000000C1';
|
|
18
|
+
const OWN_CLIENTS_FILTER_ID = 'F0000000-0000-0000-0000-0000000000C1';
|
|
19
|
+
const MANAGER_ID = 'C0000000-0000-0000-0000-0000000000C1';
|
|
20
|
+
const CLIENT_ID_VALUE = '33333333-3333-3333-3333-333333333333';
|
|
21
|
+
|
|
22
|
+
const ROW_FILTERS: RowLevelSecurityFilterInfo[] = [
|
|
23
|
+
new RowLevelSecurityFilterInfo({ ID: OWN_CLIENTS_FILTER_ID, Name: 'Own clients', FilterText: "AccountManagerID = '{{UserID}}'" }),
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/** `Clients`: account managers read only the clients they manage. */
|
|
27
|
+
function clientsEntity(): EntityInfo {
|
|
28
|
+
return new EntityInfo({
|
|
29
|
+
ID: CLIENTS_ID, Name: 'Clients', SchemaName: 'dbo', BaseTable: 'Client', BaseView: 'vwClients',
|
|
30
|
+
Permissions: [
|
|
31
|
+
{ ID: 'perm-manager', EntityID: CLIENTS_ID, RoleID: ACCOUNT_MANAGER_ROLE_ID, CanRead: true, ReadRLSFilterID: OWN_CLIENTS_FILTER_ID, Type: 'Allow' },
|
|
32
|
+
],
|
|
33
|
+
Fields: [
|
|
34
|
+
{ ID: 'cl-id', EntityID: CLIENTS_ID, Sequence: 1, Name: 'ID', Type: 'uniqueidentifier', IsPrimaryKey: true, NeedsQuotes: true },
|
|
35
|
+
{ ID: 'cl-name', EntityID: CLIENTS_ID, Sequence: 2, Name: 'Name', Type: 'nvarchar', IsNameField: true, NeedsQuotes: true },
|
|
36
|
+
{ ID: 'cl-tier', EntityID: CLIENTS_ID, Sequence: 3, Name: 'Tier', Type: 'int', NeedsQuotes: false },
|
|
37
|
+
{ ID: 'cl-manager', EntityID: CLIENTS_ID, Sequence: 4, Name: 'AccountManagerID', Type: 'uniqueidentifier', NeedsQuotes: true },
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface SentQuery {
|
|
43
|
+
SQL: string;
|
|
44
|
+
Parameters: unknown[] | undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A SQL Server-shaped provider over `Clients`, whose database answers every query with one row. */
|
|
48
|
+
class RecordingProvider extends DatabaseProviderBase {
|
|
49
|
+
public Sent: SentQuery[] = [];
|
|
50
|
+
private readonly entities = [clientsEntity()];
|
|
51
|
+
|
|
52
|
+
public override get Entities(): EntityInfo[] {
|
|
53
|
+
return this.entities;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public override get RowLevelSecurityFilters(): RowLevelSecurityFilterInfo[] {
|
|
57
|
+
return ROW_FILTERS;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async ExecuteSQL<T>(sql: string, parameters?: unknown[]): Promise<Array<T>> {
|
|
61
|
+
this.Sent.push({ SQL: sql, Parameters: parameters });
|
|
62
|
+
return [{ Name: 'Acme Manufacturing' }] as T[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public override BuildParameterPlaceholder(index: number): string { return `@p${index}`; }
|
|
66
|
+
protected get UUIDFunctionPattern(): RegExp { return /^newid\(\)$/i; }
|
|
67
|
+
protected get DBDefaultFunctionPattern(): RegExp { return /^getdate\(\)$/i; }
|
|
68
|
+
public QuoteIdentifier(name: string): string { return `[${name}]`; }
|
|
69
|
+
public QuoteSchemaAndView(schema: string, obj: string): string { return `[${schema}].[${obj}]`; }
|
|
70
|
+
protected BuildChildDiscoverySQL(): string { return ''; }
|
|
71
|
+
protected BuildHardLinkDependencySQL(): string { return ''; }
|
|
72
|
+
protected BuildSoftLinkDependencySQL(): string { return ''; }
|
|
73
|
+
protected async GenerateSaveSQL(): Promise<SaveSQLResult> { return { fullSQL: '' }; }
|
|
74
|
+
protected GenerateDeleteSQL(): DeleteSQLResult { return { fullSQL: '' }; }
|
|
75
|
+
protected BuildRecordChangeSQL(): { sql: string; parameters?: unknown[] } | null { return null; }
|
|
76
|
+
protected BuildSiblingRecordChangeSQL(): string { return ''; }
|
|
77
|
+
async BeginTransaction(): Promise<void> {}
|
|
78
|
+
async CommitTransaction(): Promise<void> {}
|
|
79
|
+
async RollbackTransaction(): Promise<void> {}
|
|
80
|
+
protected async InternalExecuteQueryFromSpec(_spec: QueryExecutionSpec): Promise<RunQueryResult> {
|
|
81
|
+
throw new Error('Not supported.');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const MANAGER = new UserInfo(null, {
|
|
86
|
+
ID: MANAGER_ID, Name: 'Morgan', Email: 'morgan@example.com', IsActive: true,
|
|
87
|
+
UserRoles: [{ UserID: MANAGER_ID, RoleID: ACCOUNT_MANAGER_ROLE_ID, Role: 'Account Managers' }],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
function key(fieldName: string, value: string): CompositeKey {
|
|
91
|
+
return new CompositeKey([{ FieldName: fieldName, Value: value }]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
describe('EntityRecordNameResolver lookups under row-level security', () => {
|
|
95
|
+
let provider: RecordingProvider;
|
|
96
|
+
let savedProvider: IMetadataProvider;
|
|
97
|
+
let context: AppContext;
|
|
98
|
+
const resolver = new EntityRecordNameResolver();
|
|
99
|
+
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
provider = new RecordingProvider();
|
|
102
|
+
// Row filters resolve through the global provider.
|
|
103
|
+
savedProvider = Metadata.Provider;
|
|
104
|
+
Metadata.Provider = provider;
|
|
105
|
+
const userPayload: UserPayload = { email: MANAGER.Email, userRecord: MANAGER, sessionId: 'session-1' };
|
|
106
|
+
context = { dataSource: undefined, dataSources: [], providers: [{ provider, type: 'Read-Only' }], userPayload };
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
afterEach(() => {
|
|
110
|
+
Metadata.Provider = savedProvider;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
function managerRowFilter(): string {
|
|
114
|
+
return provider.EntityByName('Clients').GetEffectiveRowFilterWhereClause(MANAGER, EntityPermissionType.Read, '');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
it("looks the name up under the acting user's read row filter, with the key value bound", async () => {
|
|
118
|
+
const rowFilter = managerRowFilter();
|
|
119
|
+
expect(rowFilter).toContain(MANAGER_ID);
|
|
120
|
+
|
|
121
|
+
const result = await resolver.GetEntityRecordName('Clients', key('ID', CLIENT_ID_VALUE), context);
|
|
122
|
+
|
|
123
|
+
expect(result.Success).toBe(true);
|
|
124
|
+
expect(provider.Sent).toEqual([
|
|
125
|
+
{ SQL: `SELECT [Name] FROM [dbo].[vwClients] WHERE [ID]=@p0 AND (${rowFilter})`, Parameters: [CLIENT_ID_VALUE] },
|
|
126
|
+
]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('applies the row filter to each item of a batch', async () => {
|
|
130
|
+
const rowFilter = managerRowFilter();
|
|
131
|
+
|
|
132
|
+
const results = await resolver.GetEntityRecordNames([{ EntityName: 'Clients', CompositeKey: key('ID', CLIENT_ID_VALUE) }], context);
|
|
133
|
+
|
|
134
|
+
expect(results.map(r => r.Success)).toEqual([true]);
|
|
135
|
+
expect(provider.Sent.map(q => q.SQL)).toEqual([`SELECT [Name] FROM [dbo].[vwClients] WHERE [ID]=@p0 AND (${rowFilter})`]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('refuses a key field that is not a primary key, without querying', async () => {
|
|
139
|
+
const result = await resolver.GetEntityRecordName('Clients', key('Tier', '1'), context);
|
|
140
|
+
|
|
141
|
+
expect(result.Success).toBe(false);
|
|
142
|
+
expect(result.RecordName).toBeUndefined();
|
|
143
|
+
expect(provider.Sent).toHaveLength(0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('looks no name up when there is no acting user', async () => {
|
|
147
|
+
const result = await resolver.InnerGetEntityRecordName(provider, 'Clients', key('ID', CLIENT_ID_VALUE));
|
|
148
|
+
|
|
149
|
+
expect(result.Success).toBe(false);
|
|
150
|
+
expect(provider.Sent).toHaveLength(0);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -22,6 +22,8 @@ vi.mock('type-graphql', () => {
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
const runViewCalls: Array<{ params: RunViewParams; contextUser: UserInfo | undefined }> = [];
|
|
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) => {
|
|
@@ -31,6 +33,10 @@ vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
|
31
33
|
runViewCalls.push({ params, contextUser });
|
|
32
34
|
return runViewHandler(params, contextUser);
|
|
33
35
|
}
|
|
36
|
+
async RunViews(params: RunViewParams[], contextUser?: UserInfo): Promise<ViewResult[]> {
|
|
37
|
+
runViewsBatches.push(params.map((p) => p.EntityName ?? ''));
|
|
38
|
+
return Promise.all(params.map((p) => this.RunView(p, contextUser)));
|
|
39
|
+
}
|
|
34
40
|
}
|
|
35
41
|
return { ...actual, RunView: MockRunView, LogStatus: () => undefined, LogError: () => undefined };
|
|
36
42
|
});
|
|
@@ -123,6 +129,25 @@ const memberRows = [
|
|
|
123
129
|
{ ID: 'ccc', FirstName: 'Alan', LastName: null },
|
|
124
130
|
];
|
|
125
131
|
|
|
132
|
+
/**
|
|
133
|
+
* A fake `MJ: Entity Record Documents` view over `rows`: returns only the requested `Fields`, and
|
|
134
|
+
* only the rows named by an `ID IN ('a','b')` filter when one is given.
|
|
135
|
+
*/
|
|
136
|
+
function erdView(rows: Array<Record<string, string>>) {
|
|
137
|
+
return (params: RunViewParams): ViewResult => {
|
|
138
|
+
const idList = /^ID IN \((.*)\)$/.exec(params.ExtraFilter ?? '');
|
|
139
|
+
const ids = idList ? new Set(idList[1].split(',').map((v) => v.trim().replace(/^'|'$/g, ''))) : undefined;
|
|
140
|
+
const picked = ids ? rows.filter((r) => ids.has(r.ID)) : rows;
|
|
141
|
+
const fields = params.Fields ?? Object.keys(rows[0] ?? {});
|
|
142
|
+
return { Success: true, Results: picked.map((r) => Object.fromEntries(fields.map((f) => [f, r[f]]))) };
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The VectorJSON fetches: Entity Record Documents views filtered by ID. */
|
|
147
|
+
function vectorFetches() {
|
|
148
|
+
return callsFor('MJ: Entity Record Documents').filter((c) => c.params.ExtraFilter?.startsWith('ID IN ('));
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
function callsFor(entityName: string) {
|
|
127
152
|
return runViewCalls.filter((c) => c.params.EntityName === entityName);
|
|
128
153
|
}
|
|
@@ -134,6 +159,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
134
159
|
|
|
135
160
|
beforeEach(() => {
|
|
136
161
|
runViewCalls.length = 0;
|
|
162
|
+
runViewsBatches.length = 0;
|
|
137
163
|
});
|
|
138
164
|
|
|
139
165
|
afterEach(() => {
|
|
@@ -150,7 +176,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
150
176
|
it('reads VectorJSON for the document with the context user instead of calling QueryIndex', async () => {
|
|
151
177
|
const { queryIndex } = useVectorDB(true);
|
|
152
178
|
routeViews('Members', {
|
|
153
|
-
'MJ: Entity Record Documents': (
|
|
179
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
154
180
|
Members: () => ({ Success: true, Results: memberRows }),
|
|
155
181
|
});
|
|
156
182
|
|
|
@@ -163,18 +189,38 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
163
189
|
expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2', 'ERD-3']);
|
|
164
190
|
expect(result.Results.map((r) => r.Values)).toEqual([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]);
|
|
165
191
|
|
|
166
|
-
const [
|
|
167
|
-
expect(
|
|
168
|
-
expect(
|
|
169
|
-
expect(
|
|
170
|
-
expect(
|
|
171
|
-
expect(
|
|
192
|
+
const [listCall, vectorCall] = callsFor('MJ: Entity Record Documents');
|
|
193
|
+
expect(listCall.contextUser).toBe(contextUser);
|
|
194
|
+
expect(listCall.params.ExtraFilter).toBe(`EntityDocumentID='${DOC_ID}' AND VectorJSON IS NOT NULL`);
|
|
195
|
+
expect(listCall.params.Fields).toEqual(['ID', 'RecordID']);
|
|
196
|
+
expect(listCall.params.ResultType).toBe('simple');
|
|
197
|
+
expect(listCall.params.IgnoreMaxRows).toBe(true);
|
|
198
|
+
expect(vectorCall.contextUser).toBe(contextUser);
|
|
199
|
+
expect(vectorCall.params.ExtraFilter).toBe("ID IN ('ERD-1','ERD-2','ERD-3')");
|
|
200
|
+
expect(vectorCall.params.Fields).toEqual(['ID', 'VectorJSON']);
|
|
201
|
+
expect(vectorCall.params.ResultType).toBe('simple');
|
|
202
|
+
expect(vectorCall.params.IgnoreMaxRows).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('bypasses the server RunView cache for both Entity Record Documents views, so Fields is honoured', async () => {
|
|
206
|
+
useVectorDB(true);
|
|
207
|
+
routeViews('Members', {
|
|
208
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
209
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
213
|
+
|
|
214
|
+
const erdCalls = callsFor('MJ: Entity Record Documents');
|
|
215
|
+
expect(erdCalls).toHaveLength(2);
|
|
216
|
+
expect(erdCalls.map((c) => c.params.BypassCache)).toEqual([true, true]);
|
|
217
|
+
expect(callsFor('Members')[0].params.BypassCache).toBeUndefined();
|
|
172
218
|
});
|
|
173
219
|
|
|
174
220
|
it('labels each item with Entity, RecordID and the record name fields (RecordID matched case-insensitively)', async () => {
|
|
175
221
|
useVectorDB(true);
|
|
176
222
|
routeViews('Members', {
|
|
177
|
-
'MJ: Entity Record Documents': (
|
|
223
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
178
224
|
Members: () => ({ Success: true, Results: memberRows }),
|
|
179
225
|
});
|
|
180
226
|
|
|
@@ -196,7 +242,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
196
242
|
useVectorDB(true);
|
|
197
243
|
const filter = "LastName = 'Hopper'";
|
|
198
244
|
routeViews('Members', {
|
|
199
|
-
'MJ: Entity Record Documents': (
|
|
245
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
200
246
|
Members: (params) => ({
|
|
201
247
|
Success: true,
|
|
202
248
|
Results: params.ExtraFilter === filter ? memberRows.filter((r) => r.LastName === 'Hopper') : memberRows,
|
|
@@ -215,7 +261,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
215
261
|
it('stops at maxRecords', async () => {
|
|
216
262
|
useVectorDB(true);
|
|
217
263
|
routeViews('Members', {
|
|
218
|
-
'MJ: Entity Record Documents': (
|
|
264
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
219
265
|
Members: () => ({ Success: true, Results: memberRows }),
|
|
220
266
|
});
|
|
221
267
|
|
|
@@ -223,15 +269,74 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
223
269
|
|
|
224
270
|
expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2']);
|
|
225
271
|
expect(result.TotalCount).toBe(2);
|
|
272
|
+
expect(vectorFetches().map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('ERD-1','ERD-2')"]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('fetches VectorJSON only for vectors whose record passed the filter', async () => {
|
|
276
|
+
useVectorDB(true);
|
|
277
|
+
const filter = "LastName = 'Hopper'";
|
|
278
|
+
routeViews('Members', {
|
|
279
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
280
|
+
Members: () => ({ Success: true, Results: memberRows.filter((r) => r.LastName === 'Hopper') }),
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, filter, appContext(membersEntity));
|
|
284
|
+
|
|
285
|
+
expect(result.Results.map((r) => r.Values)).toEqual([[0.3, 0.4]]);
|
|
286
|
+
expect(vectorFetches().map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('ERD-2')"]);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('runs no VectorJSON fetch when no stored vector belongs to a readable record', async () => {
|
|
290
|
+
useVectorDB(true);
|
|
291
|
+
routeViews('Members', {
|
|
292
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
293
|
+
Members: () => ({ Success: true, Results: [] }),
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
297
|
+
|
|
298
|
+
expect(result.Success).toBe(true);
|
|
299
|
+
expect(result.Results).toEqual([]);
|
|
300
|
+
expect(vectorFetches()).toHaveLength(0);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('splits a large VectorJSON fetch into ID lists of at most 1000, sent in one RunViews batch', async () => {
|
|
304
|
+
useVectorDB(true);
|
|
305
|
+
const ids = Array.from({ length: 1500 }, (_, i) => `id-${i}`);
|
|
306
|
+
routeViews('Members', {
|
|
307
|
+
'MJ: Entity Record Documents': erdView(ids.map((id, i) => ({ ID: `ERD-${i}`, RecordID: id, VectorJSON: `[${i}]` }))),
|
|
308
|
+
Members: () => ({ Success: true, Results: ids.map((id) => ({ ID: id, FirstName: id, LastName: null })) }),
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 1500, undefined, appContext(membersEntity));
|
|
312
|
+
|
|
313
|
+
expect(result.TotalCount).toBe(1500);
|
|
314
|
+
expect(result.Results[1499]).toMatchObject({ ID: 'ERD-1499', Values: [1499] });
|
|
315
|
+
const fetchSizes = vectorFetches().map((c) => (c.params.ExtraFilter ?? '').split(',').length);
|
|
316
|
+
expect(fetchSizes).toEqual([1000, 500]);
|
|
317
|
+
expect(runViewsBatches[1]).toEqual(['MJ: Entity Record Documents', 'MJ: Entity Record Documents']);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('returns an error result when the VectorJSON fetch fails', async () => {
|
|
321
|
+
useVectorDB(true);
|
|
322
|
+
const listing = erdView(storedRows);
|
|
323
|
+
routeViews('Members', {
|
|
324
|
+
'MJ: Entity Record Documents': (params) =>
|
|
325
|
+
params.ExtraFilter?.startsWith('ID IN (') ? { Success: false, Results: [], ErrorMessage: 'deadlock' } : listing(params),
|
|
326
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
330
|
+
|
|
331
|
+
expect(result.Success).toBe(false);
|
|
332
|
+
expect(result.Results).toEqual([]);
|
|
333
|
+
expect(result.ErrorMessage).toBe('Failed to load vector values: deadlock');
|
|
226
334
|
});
|
|
227
335
|
|
|
228
336
|
it('matches composite-key records by the compact RecordID segment and falls back to NameField for labels', async () => {
|
|
229
337
|
useVectorDB(true);
|
|
230
338
|
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
|
-
}),
|
|
339
|
+
'MJ: Entity Record Documents': erdView([{ ID: 'ERD-9', RecordID: 'OrderID|o-1||LineNo|2', VectorJSON: '[1,0]' }]),
|
|
235
340
|
'Order Lines': () => ({ Success: true, Results: [{ OrderID: 'o-1', LineNo: 2, Description: 'Brie wheel' }] }),
|
|
236
341
|
});
|
|
237
342
|
|
|
@@ -250,7 +355,7 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
250
355
|
it('returns an error result when the entity records cannot be loaded (e.g. an invalid filter)', async () => {
|
|
251
356
|
useVectorDB(true);
|
|
252
357
|
routeViews('Members', {
|
|
253
|
-
'MJ: Entity Record Documents': (
|
|
358
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
254
359
|
Members: () => ({ Success: false, Results: [], ErrorMessage: 'bad filter' }),
|
|
255
360
|
});
|
|
256
361
|
|
|
@@ -260,6 +365,49 @@ describe('FetchEntityVectorsResolver', () => {
|
|
|
260
365
|
expect(result.Results).toEqual([]);
|
|
261
366
|
expect(result.ErrorMessage).toBe('Failed to load "Members" records: bad filter');
|
|
262
367
|
});
|
|
368
|
+
|
|
369
|
+
it('returns an error result when the stored vectors cannot be loaded', async () => {
|
|
370
|
+
useVectorDB(true);
|
|
371
|
+
routeViews('Members', {
|
|
372
|
+
'MJ: Entity Record Documents': () => ({ Success: false, Results: [], ErrorMessage: 'timeout' }),
|
|
373
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
377
|
+
|
|
378
|
+
expect(result.Success).toBe(false);
|
|
379
|
+
expect(result.Results).toEqual([]);
|
|
380
|
+
expect(result.TotalCount).toBe(0);
|
|
381
|
+
expect(result.ErrorMessage).toBe('Failed to load stored vectors: timeout');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('returns an error result, and runs neither view, when the document names an entity missing from metadata', async () => {
|
|
385
|
+
const { queryIndex } = useVectorDB(true);
|
|
386
|
+
routeViews('Retired Entity', {
|
|
387
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
391
|
+
|
|
392
|
+
expect(result.Success).toBe(false);
|
|
393
|
+
expect(result.Results).toEqual([]);
|
|
394
|
+
expect(result.ErrorMessage).toBe('Entity "Retired Entity" not found in metadata');
|
|
395
|
+
expect(queryIndex).not.toHaveBeenCalled();
|
|
396
|
+
expect(callsFor('MJ: Entity Record Documents')).toHaveLength(0);
|
|
397
|
+
expect(runViewsBatches).toHaveLength(0);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('lists the entity records and the stored vectors in one RunViews batch, then fetches the vector values', async () => {
|
|
401
|
+
useVectorDB(true);
|
|
402
|
+
routeViews('Members', {
|
|
403
|
+
'MJ: Entity Record Documents': erdView(storedRows),
|
|
404
|
+
Members: () => ({ Success: true, Results: memberRows }),
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
|
|
408
|
+
|
|
409
|
+
expect(runViewsBatches).toEqual([['Members', 'MJ: Entity Record Documents'], ['MJ: Entity Record Documents']]);
|
|
410
|
+
});
|
|
263
411
|
});
|
|
264
412
|
|
|
265
413
|
describe('index-keyed providers (Pinecone path, unchanged)', () => {
|