@memberjunction/server 6.1.0-edge.0 → 6.1.0-edge.2
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/dist/auth/exampleNewUserSubClass.js +1 -1
- package/dist/auth/exampleNewUserSubClass.js.map +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +9 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +1 -1
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.js +1 -1
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/context.js +2 -2
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +208 -646
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +1123 -3773
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +19 -3
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +51 -17
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +100 -42
- package/dist/index.js.map +1 -1
- package/dist/logging/secretRedactor.d.ts +9 -0
- package/dist/logging/secretRedactor.d.ts.map +1 -1
- package/dist/logging/secretRedactor.js +39 -0
- package/dist/logging/secretRedactor.js.map +1 -1
- package/dist/middleware/BaseServerMiddleware.d.ts +5 -0
- package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
- package/dist/middleware/BaseServerMiddleware.js +5 -0
- package/dist/middleware/BaseServerMiddleware.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +12 -0
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.js +1 -1
- package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +9 -0
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +3 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +5 -0
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/SearchEntitiesResolver.js +1 -1
- package/dist/resolvers/SearchEntitiesResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +2 -2
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +85 -0
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -0
- package/dist/resolvers/TaskGraphFrameResolver.js +163 -0
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -0
- package/dist/resolvers/UserFavoriteResolver.js +1 -1
- package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +1 -1
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts +0 -4
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +1 -15
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/SignatureWebhookHandler.js +1 -1
- package/dist/rest/SignatureWebhookHandler.js.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts +26 -0
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -0
- package/dist/services/DurableEntityActionTaskSubmitter.js +55 -0
- package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -0
- package/dist/services/ScheduledJobsService.js +1 -1
- package/dist/services/ScheduledJobsService.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.d.ts +33 -0
- package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -0
- package/dist/services/StartTaskGraphDispatcher.js +46 -0
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -0
- package/dist/services/TaskGraphActionRunner.d.ts +34 -0
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphActionRunner.js +95 -0
- package/dist/services/TaskGraphActionRunner.js.map +1 -0
- package/dist/services/TaskGraphAgentRunner.d.ts +28 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphAgentRunner.js +104 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +70 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js +162 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -0
- package/dist/services/TaskGraphPromptRunner.d.ts +31 -0
- package/dist/services/TaskGraphPromptRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphPromptRunner.js +134 -0
- package/dist/services/TaskGraphPromptRunner.js.map +1 -0
- package/dist/services/TaskGraphProviderFactory.d.ts +39 -0
- package/dist/services/TaskGraphProviderFactory.d.ts.map +1 -0
- package/dist/services/TaskGraphProviderFactory.js +28 -0
- package/dist/services/TaskGraphProviderFactory.js.map +1 -0
- package/dist/telephony/RingCentralTelephonyService.js +1 -1
- package/dist/telephony/RingCentralTelephonyService.js.map +1 -1
- package/dist/telephony/TwilioTelephonyRouter.js +1 -1
- package/dist/telephony/TwilioTelephonyRouter.js.map +1 -1
- package/dist/telephony/VonageTelephonyRouter.js +1 -1
- package/dist/telephony/VonageTelephonyRouter.js.map +1 -1
- package/package.json +92 -89
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +1 -1
- package/src/__tests__/ResolverBase.frozenRecordMapping.test.ts +133 -0
- package/src/__tests__/ResolverBase.transportMapping.test.ts +289 -0
- package/src/__tests__/SessionManager.test.ts +1 -1
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +249 -0
- package/src/__tests__/TaskGraphFrameResolver.test.ts +136 -0
- package/src/__tests__/WidgetSessionService.test.ts +1 -1
- package/src/__tests__/databaseAbstraction.test.ts +1 -1
- package/src/__tests__/magicLinkService.provisionLog.test.ts +1 -1
- package/src/__tests__/resolverBase.rls.test.ts +3 -0
- package/src/__tests__/secretRedactor.failOpen.test.ts +135 -0
- package/src/__tests__/widgetGuestElevation.test.ts +1 -1
- package/src/auth/exampleNewUserSubClass.ts +1 -1
- package/src/auth/index.ts +10 -6
- package/src/auth/magicLink/MagicLinkService.ts +1 -1
- package/src/auth/newUsers.ts +1 -1
- package/src/context.ts +2 -2
- package/src/generated/generated.ts +955 -2735
- package/src/generic/ResolverBase.ts +52 -17
- package/src/index.ts +108 -43
- package/src/logging/secretRedactor.ts +43 -0
- package/src/middleware/BaseServerMiddleware.ts +5 -0
- package/src/realtimeWidget/WidgetSessionService.ts +1 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +1 -1
- package/src/resolvers/ActionResolver.ts +13 -0
- package/src/resolvers/ComponentRegistryResolver.ts +1 -1
- package/src/resolvers/EntityPermissionResolver.ts +1 -1
- package/src/resolvers/IntegrationDiscoveryResolver.ts +12 -2
- package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
- package/src/resolvers/RealtimeClientSessionResolver.ts +13 -0
- package/src/resolvers/SearchEntitiesResolver.ts +1 -1
- package/src/resolvers/SqlLoggingConfigResolver.ts +1 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +2 -2
- package/src/resolvers/TaskGraphFrameResolver.ts +160 -0
- package/src/resolvers/UserFavoriteResolver.ts +1 -1
- package/src/rest/OAuthCallbackHandler.ts +1 -1
- package/src/rest/RESTEndpointHandler.ts +1 -15
- package/src/rest/SignatureWebhookHandler.ts +1 -1
- package/src/services/DurableEntityActionTaskSubmitter.ts +78 -0
- package/src/services/ScheduledJobsService.ts +1 -1
- package/src/services/StartTaskGraphDispatcher.ts +78 -0
- package/src/services/TaskGraphActionRunner.ts +99 -0
- package/src/services/TaskGraphAgentRunner.ts +115 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +181 -0
- package/src/services/TaskGraphPromptRunner.ts +144 -0
- package/src/services/TaskGraphProviderFactory.ts +53 -0
- package/src/telephony/RingCentralTelephonyService.ts +1 -1
- package/src/telephony/TwilioTelephonyRouter.ts +1 -1
- package/src/telephony/VonageTelephonyRouter.ts +1 -1
- package/dist/resolvers/ReportResolver.d.ts +0 -24
- package/dist/resolvers/ReportResolver.d.ts.map +0 -1
- package/dist/resolvers/ReportResolver.js +0 -198
- package/dist/resolvers/ReportResolver.js.map +0 -1
- package/dist/resolvers/TaskResolver.d.ts +0 -31
- package/dist/resolvers/TaskResolver.d.ts.map +0 -1
- package/dist/resolvers/TaskResolver.js +0 -157
- package/dist/resolvers/TaskResolver.js.map +0 -1
- package/dist/services/TaskOrchestrator.d.ts +0 -130
- package/dist/services/TaskOrchestrator.d.ts.map +0 -1
- package/dist/services/TaskOrchestrator.js +0 -692
- package/dist/services/TaskOrchestrator.js.map +0 -1
- package/src/resolvers/ReportResolver.ts +0 -165
- package/src/resolvers/TaskResolver.ts +0 -148
- package/src/resolvers/__tests__/ReportResolver.test.ts +0 -279
- package/src/services/TaskOrchestrator.ts +0 -873
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
UserInfo,
|
|
21
21
|
} from '@memberjunction/core';
|
|
22
22
|
import { MJAuditLogEntity, MJErrorLogEntity, MJUserViewEntityExtended } from '@memberjunction/core-entities';
|
|
23
|
-
import { SQLServerDataProvider
|
|
23
|
+
import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
|
|
24
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
24
25
|
import { PubSubEngine, AuthorizationError } from 'type-graphql';
|
|
25
26
|
import { GraphQLError } from 'graphql';
|
|
26
27
|
import { GetAPIKeyEngine } from '@memberjunction/api-keys';
|
|
@@ -62,16 +63,28 @@ export class ResolverBase {
|
|
|
62
63
|
* - AllowDecryptInAPI=false + SendEncryptedValue=true: Keep encrypted ciphertext
|
|
63
64
|
* - AllowDecryptInAPI=false + SendEncryptedValue=false: Replace with sentinel
|
|
64
65
|
*
|
|
66
|
+
* Returns a COPY — `dataObject` is never written to. Callers routinely pass rows straight
|
|
67
|
+
* from `findBy`/`RunView`, which are the server cache's own objects held by reference, and
|
|
68
|
+
* `LocalCacheManager` deep-freezes them. Renaming in place therefore threw
|
|
69
|
+
* `Cannot add property _mj__CreatedAt, object is not extensible` on every `UserByEmail` /
|
|
70
|
+
* `UserByID` / `UserByEmployeeID` call and on every generated single-record resolver whose
|
|
71
|
+
* entity has caching enabled. (Before the freeze it did something worse but quieter: it
|
|
72
|
+
* rewrote the cached row's keys, so later readers were served transport-shaped rows that
|
|
73
|
+
* `BaseEntity.SetMany` rejects.) Copying here fixes every call site at once and makes the
|
|
74
|
+
* hazard unreachable for future ones.
|
|
75
|
+
*
|
|
65
76
|
* @param entityName - The entity name
|
|
66
|
-
* @param dataObject - The data object with field values
|
|
77
|
+
* @param dataObject - The data object with field values. Not modified.
|
|
67
78
|
* @param contextUser - Optional user context for decryption (required for encrypted fields)
|
|
68
|
-
* @returns
|
|
79
|
+
* @returns A new object in transport shape, or null when there is nothing to map
|
|
69
80
|
*/
|
|
70
81
|
protected async MapFieldNamesToCodeNames(entityName: string, dataObject: any, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<any> {
|
|
71
82
|
// Return null for empty objects (e.g. when no rows found due to RLS filtering)
|
|
72
83
|
if (!dataObject || Object.keys(dataObject).length === 0) {
|
|
73
84
|
return null;
|
|
74
85
|
}
|
|
86
|
+
// Shallow copy up front so every write below lands on our object, never the caller's.
|
|
87
|
+
dataObject = { ...dataObject };
|
|
75
88
|
|
|
76
89
|
// for the given entity name provided, check to see if there are any fields
|
|
77
90
|
// where the code name is different from the field name, and for just those
|
|
@@ -184,14 +197,21 @@ export class ResolverBase {
|
|
|
184
197
|
return true;
|
|
185
198
|
}
|
|
186
199
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Array form of {@link MapFieldNamesToCodeNames}. Returns a NEW array of NEW objects; neither
|
|
202
|
+
* the input array nor its rows are modified. Both matter: the cache freezes the array as well
|
|
203
|
+
* as the rows it contains, so collecting the mapped copies (rather than mapping in place and
|
|
204
|
+
* returning the original) is what makes this safe on cache-served input.
|
|
205
|
+
*/
|
|
206
|
+
protected async ArrayMapFieldNamesToCodeNames(entityName: string, dataObjectArray: any[], contextUser?: UserInfo, provider?: IMetadataProvider): Promise<any[]> {
|
|
207
|
+
if (!dataObjectArray || dataObjectArray.length === 0) {
|
|
208
|
+
return dataObjectArray;
|
|
193
209
|
}
|
|
194
|
-
|
|
210
|
+
const mapped: any[] = [];
|
|
211
|
+
for (const element of dataObjectArray) {
|
|
212
|
+
mapped.push(await this.MapFieldNamesToCodeNames(entityName, element, contextUser, provider));
|
|
213
|
+
}
|
|
214
|
+
return mapped;
|
|
195
215
|
}
|
|
196
216
|
|
|
197
217
|
/**
|
|
@@ -826,12 +846,27 @@ export class ResolverBase {
|
|
|
826
846
|
LogStatus(`[ResolverBase] RunView result aggregate info: entityName=${viewInfo.Entity}, hasAggregateResults=${!!result?.AggregateResults}, aggregateResultCount=${result?.AggregateResults?.length || 0}, aggregateExecutionTime=${result?.AggregateExecutionTime}, aggregateResults=${JSON.stringify(result?.AggregateResults)}`);
|
|
827
847
|
}
|
|
828
848
|
|
|
829
|
-
// Process results for GraphQL transport
|
|
849
|
+
// Process results for GraphQL transport.
|
|
850
|
+
//
|
|
851
|
+
// Map onto COPIES, never in place. `FieldMapper.MapFields` renames keys by
|
|
852
|
+
// mutating (`obj[mapped] = obj[k]; delete obj[k]`), and these rows are the
|
|
853
|
+
// provider's own result objects — which the server cache holds BY REFERENCE
|
|
854
|
+
// under a reference-sharing storage provider. Mapping them in place rewrote
|
|
855
|
+
// `__mj_CreatedAt` to the transport alias `_mj__CreatedAt` inside the live
|
|
856
|
+
// cache, and because that cache is process-wide, one GraphQL response made
|
|
857
|
+
// every later read hand back transport-shaped rows that `BaseEntity.SetMany`
|
|
858
|
+
// rejects. `ArrayFilterEncryptedFieldsForAPI` mutates too, so it must also
|
|
859
|
+
// see the copies. (FileResolver already maps a spread copy.)
|
|
860
|
+
//
|
|
861
|
+
// These copies are LOAD-BEARING, not merely defensive: LocalCacheManager now
|
|
862
|
+
// deep-freezes cached rows (see ILocalStorageProvider.SharesReferences), so
|
|
863
|
+
// mapping in place would throw rather than silently corrupt. `{ ...r }` is a
|
|
864
|
+
// SHALLOW copy, which is sufficient here because the only post-map mutators
|
|
865
|
+
// rename top-level keys and redact scalar fields — and the cache's freeze is
|
|
866
|
+
// deep, so a nested value can no longer be reached through the copy either.
|
|
830
867
|
const mapper = new FieldMapper();
|
|
831
868
|
if (result?.Success && result.Results?.length) {
|
|
832
|
-
|
|
833
|
-
mapper.MapFields(r);
|
|
834
|
-
}
|
|
869
|
+
result.Results = result.Results.map(r => mapper.MapFields({ ...r }));
|
|
835
870
|
// Filter encrypted fields before sending to API client
|
|
836
871
|
await this.ArrayFilterEncryptedFieldsForAPI(
|
|
837
872
|
viewInfo.Entity,
|
|
@@ -948,9 +983,9 @@ export class ResolverBase {
|
|
|
948
983
|
for (let i = 0; i < runViewResults.length; i++) {
|
|
949
984
|
const runViewResult = runViewResults[i];
|
|
950
985
|
if (runViewResult?.Success && runViewResult.Results?.length) {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
}
|
|
986
|
+
// Copy-then-map, same reason as the single-view path above: these rows
|
|
987
|
+
// are cache-held references and MapFields renames keys in place.
|
|
988
|
+
runViewResult.Results = runViewResult.Results.map(r => mapper.MapFields({ ...r }));
|
|
954
989
|
// Filter encrypted fields before sending to API client
|
|
955
990
|
// Use the corresponding param's entity name
|
|
956
991
|
const entityName = params[i]?.viewInfo?.Entity;
|
package/src/index.ts
CHANGED
|
@@ -5,9 +5,9 @@ dotenv.config({ quiet: true });
|
|
|
5
5
|
import { expressMiddleware } from '@as-integrations/express5';
|
|
6
6
|
import { mergeSchemas } from '@graphql-tools/schema';
|
|
7
7
|
import { Metadata, DatabasePlatform, SetProvider, StartupManager as StartupManagerImport, BaseEntity, BaseEntityEvent, RunView, DatabaseProviderBase, ResolveStartupMode } from '@memberjunction/core';
|
|
8
|
-
import { resolveDbPlatformFromEnv } from '@memberjunction/generic-database-provider';
|
|
8
|
+
import { UserCache, resolveDbPlatformFromEnv } from '@memberjunction/generic-database-provider';
|
|
9
9
|
import { MJGlobal, MJEventType, UUIDsEqual, ShutdownRegistry } from '@memberjunction/global';
|
|
10
|
-
import { setupSQLServerClient, SQLServerDataProvider, SQLServerProviderConfigData
|
|
10
|
+
import { setupSQLServerClient, SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
|
|
11
11
|
import { extendConnectionPoolWithQuery } from './util.js';
|
|
12
12
|
import { registerIntegrationCustomColumnPromoter } from './integration/CustomColumnPromoter.js';
|
|
13
13
|
import { DisableUnselectedEntityMaps, ReenableFieldMapsForEntityMap } from './integration/EntityMapLifecycle.js';
|
|
@@ -19,11 +19,12 @@ import { default as fg } from 'fast-glob';
|
|
|
19
19
|
import { useServer } from 'graphql-ws/lib/use/ws';
|
|
20
20
|
import { createServer } from 'node:http';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
|
-
import { readFileSync } from 'node:fs';
|
|
22
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { Session as InspectorSession } from 'node:inspector';
|
|
23
24
|
import { sep } from 'node:path';
|
|
24
25
|
import 'reflect-metadata';
|
|
25
26
|
import { ReplaySubject } from 'rxjs';
|
|
26
|
-
import { BuildSchemaOptions, buildSchemaSync, GraphQLTimestamp, PubSubEngine } from 'type-graphql';
|
|
27
|
+
import { BuildSchemaOptions, buildSchemaSync, getMetadataStorage, GraphQLTimestamp, PubSubEngine } from 'type-graphql';
|
|
27
28
|
import { PubSub } from 'graphql-subscriptions';
|
|
28
29
|
import sql from 'mssql';
|
|
29
30
|
import { WebSocketServer } from 'ws';
|
|
@@ -66,6 +67,7 @@ import { IntegrationProgressEmitter } from '@memberjunction/integration-progress
|
|
|
66
67
|
import { PublishIntegrationProgress } from './resolvers/IntegrationProgressResolver.js';
|
|
67
68
|
import { ClientToolRequestManager, AgentRunWatchdog } from '@memberjunction/ai-agents';
|
|
68
69
|
import { SessionJanitor } from './agentSessions/index.js';
|
|
70
|
+
import { StartTaskGraphDispatcher } from './services/StartTaskGraphDispatcher.js';
|
|
69
71
|
import { CACHE_INVALIDATION_TOPIC } from './generic/CacheInvalidationResolver.js';
|
|
70
72
|
import { ConnectorFactory, IntegrationEngine, IntegrationSyncOptions } from '@memberjunction/integration-engine';
|
|
71
73
|
import { CronExpressionHelper } from '@memberjunction/scheduling-engine';
|
|
@@ -136,7 +138,6 @@ export * from './resolvers/IntegrationProgressResolver.js';
|
|
|
136
138
|
export * from './resolvers/ClientToolRequestResolver.js';
|
|
137
139
|
export * from './resolvers/AutotagPipelineResolver.js';
|
|
138
140
|
export * from './resolvers/TagGovernanceResolver.js';
|
|
139
|
-
export * from './resolvers/TaskResolver.js';
|
|
140
141
|
export * from './generic/KeyValuePairInput.js';
|
|
141
142
|
export * from './generic/KeyInputOutputTypes.js';
|
|
142
143
|
export * from './generic/DeleteOptionsInput.js';
|
|
@@ -149,8 +150,13 @@ export * from './resolvers/ComponentRegistryResolver.js';
|
|
|
149
150
|
export * from './resolvers/DatasetResolver.js';
|
|
150
151
|
export * from './resolvers/EntityRecordNameResolver.js';
|
|
151
152
|
export * from './resolvers/MergeRecordsResolver.js';
|
|
152
|
-
export * from './resolvers/ReportResolver.js';
|
|
153
153
|
export * from './resolvers/QueryResolver.js';
|
|
154
|
+
export * from './services/TaskGraphProviderFactory.js';
|
|
155
|
+
export * from './services/TaskGraphAgentRunner.js';
|
|
156
|
+
export * from './services/StartTaskGraphDispatcher.js';
|
|
157
|
+
export * from './services/TaskGraphPromptRunner.js';
|
|
158
|
+
export * from './services/TaskGraphContinuationDeliverer.js';
|
|
159
|
+
export * from './resolvers/TaskGraphFrameResolver.js';
|
|
154
160
|
export * from './resolvers/TestQuerySQLResolver.js';
|
|
155
161
|
export * from './resolvers/SqlLoggingConfigResolver.js';
|
|
156
162
|
export * from './resolvers/SyncRolesUsersResolver.js';
|
|
@@ -324,8 +330,8 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
|
|
|
324
330
|
await provider.Config(pgConfigData);
|
|
325
331
|
SetProvider(provider);
|
|
326
332
|
|
|
327
|
-
//
|
|
328
|
-
await
|
|
333
|
+
// Warm the user cache — dialect-neutral, same call the SQL Server path makes
|
|
334
|
+
await UserCache.Instance.Refresh(provider);
|
|
329
335
|
|
|
330
336
|
// Run startup actions — same 'full' entry-point default as the SQL Server path
|
|
331
337
|
const sysUser = UserCache.Instance.GetSystemUser();
|
|
@@ -854,23 +860,73 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
|
|
|
854
860
|
lap('Resolver + Middleware Discovery', tServe);
|
|
855
861
|
tServe = startupLog.BeginPhase('Building GraphQL schema');
|
|
856
862
|
|
|
863
|
+
// ── Scale-runtime instrumentation (fix #1 profiling) ─────────────────────────
|
|
864
|
+
// Split the previously-monolithic "Schema Build" phase into its three real
|
|
865
|
+
// sub-steps so we can see which one dominates at high entity counts, and capture
|
|
866
|
+
// the type-graphql metadata cardinality (the O(entities×fields) signal). Guarded
|
|
867
|
+
// by MJ_SCHEMA_PROFILE, and emits ONE explicit console line (independent of the
|
|
868
|
+
// startup logger's level, and NOT via EndPhase, so the "schema" summary token and
|
|
869
|
+
// its total math stay clean). No-op in normal deployments.
|
|
870
|
+
const schemaProfile = process.env.MJ_SCHEMA_PROFILE === '1';
|
|
871
|
+
let objectTypeCount = -1, fieldCount = -1;
|
|
872
|
+
if (schemaProfile) {
|
|
873
|
+
try {
|
|
874
|
+
const meta = getMetadataStorage();
|
|
875
|
+
// Read BEFORE buildSchemaSync — buildSchemaSync's internal build() resets these arrays.
|
|
876
|
+
objectTypeCount = meta.objectTypes.length;
|
|
877
|
+
fieldCount = meta.fields.length;
|
|
878
|
+
} catch {
|
|
879
|
+
/* metadata cardinality is best-effort; leave sentinels */
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Surgical CPU profile of ONLY buildSchemaSync (behind MJ_SCHEMA_CPUPROF=1), so we can see
|
|
884
|
+
// whether the ~66s at scale is uniform per-field work (cache is the only fix) or a patchable
|
|
885
|
+
// hotspot in type-graphql's generator. Deterministic flush (writes before serve continues) —
|
|
886
|
+
// no dependency on --cpu-prof surviving a process kill.
|
|
887
|
+
const cpuProf = process.env.MJ_SCHEMA_CPUPROF === '1';
|
|
888
|
+
let profSession: InspectorSession | undefined;
|
|
889
|
+
if (cpuProf) {
|
|
890
|
+
profSession = new InspectorSession();
|
|
891
|
+
profSession.connect();
|
|
892
|
+
await new Promise<void>((res, rej) => profSession!.post('Profiler.enable', (e) => (e ? rej(e) : res())));
|
|
893
|
+
await new Promise<void>((res, rej) => profSession!.post('Profiler.start', (e) => (e ? rej(e) : res())));
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const tBuild = performance.now();
|
|
897
|
+
const builtSchema = buildSchemaSync({
|
|
898
|
+
resolvers: allResolvers,
|
|
899
|
+
validate: false,
|
|
900
|
+
scalarsMap: [{ type: Date, scalar: GraphQLTimestamp }],
|
|
901
|
+
emitSchemaFile: websiteRunFromPackage !== 1,
|
|
902
|
+
pubSub,
|
|
903
|
+
globalMiddlewares: [variablesLoggingMiddleware],
|
|
904
|
+
});
|
|
905
|
+
const buildMs = performance.now() - tBuild;
|
|
906
|
+
|
|
907
|
+
if (cpuProf && profSession) {
|
|
908
|
+
const profile = await new Promise<unknown>((res, rej) =>
|
|
909
|
+
profSession!.post('Profiler.stop', (e, r) => (e ? rej(e) : res((r as { profile: unknown }).profile)))
|
|
910
|
+
);
|
|
911
|
+
const outPath = `/tmp/mjapi_schema_build_${process.pid}.cpuprofile`;
|
|
912
|
+
writeFileSync(outPath, JSON.stringify(profile));
|
|
913
|
+
profSession.disconnect();
|
|
914
|
+
// eslint-disable-next-line no-console
|
|
915
|
+
console.log(`[SCHEMA-CPUPROF] wrote ${outPath} (buildSchemaSync=${buildMs.toFixed(0)}ms)`);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const tMerge = performance.now();
|
|
857
919
|
let schema = mergeSchemas({
|
|
858
|
-
schemas: [
|
|
859
|
-
buildSchemaSync({
|
|
860
|
-
resolvers: allResolvers,
|
|
861
|
-
validate: false,
|
|
862
|
-
scalarsMap: [{ type: Date, scalar: GraphQLTimestamp }],
|
|
863
|
-
emitSchemaFile: websiteRunFromPackage !== 1,
|
|
864
|
-
pubSub,
|
|
865
|
-
globalMiddlewares: [variablesLoggingMiddleware],
|
|
866
|
-
}),
|
|
867
|
-
],
|
|
920
|
+
schemas: [builtSchema],
|
|
868
921
|
typeDefs: [requireSystemUserDirective.typeDefs, publicDirective.typeDefs],
|
|
869
922
|
});
|
|
923
|
+
const mergeMs = performance.now() - tMerge;
|
|
870
924
|
|
|
871
925
|
// Verbose-mode-only diagnostic: name custom-resolver args that aren't metadata-bound
|
|
872
926
|
// and aren't @NoLog-marked. No-op in default config (logVariables=false).
|
|
873
927
|
auditResolversForUndecoratedArgs();
|
|
928
|
+
|
|
929
|
+
const tTransform = performance.now();
|
|
874
930
|
schema = requireSystemUserDirective.transformer(schema);
|
|
875
931
|
schema = publicDirective.transformer(schema);
|
|
876
932
|
|
|
@@ -878,6 +934,17 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
|
|
|
878
934
|
for (const transformer of mwSchemaTransformers) {
|
|
879
935
|
schema = transformer(schema);
|
|
880
936
|
}
|
|
937
|
+
const transformMs = performance.now() - tTransform;
|
|
938
|
+
|
|
939
|
+
if (schemaProfile) {
|
|
940
|
+
const typeMapSize = Object.keys(schema.getTypeMap()).length;
|
|
941
|
+
// eslint-disable-next-line no-console
|
|
942
|
+
console.log(
|
|
943
|
+
`[SCHEMA-PROFILE] resolvers=${allResolvers.length} objectTypes=${objectTypeCount} fields=${fieldCount} ` +
|
|
944
|
+
`typeMap=${typeMapSize} | buildSchemaSync=${buildMs.toFixed(0)}ms mergeSchemas=${mergeMs.toFixed(0)}ms ` +
|
|
945
|
+
`transformers=${transformMs.toFixed(0)}ms`
|
|
946
|
+
);
|
|
947
|
+
}
|
|
881
948
|
|
|
882
949
|
lap('Schema Build', tServe);
|
|
883
950
|
tServe = startupLog.BeginPhase('Starting HTTP server');
|
|
@@ -1341,6 +1408,29 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
|
|
|
1341
1408
|
.catch(err => console.warn(`[SessionJanitor] Startup failed: ${err}`));
|
|
1342
1409
|
}
|
|
1343
1410
|
|
|
1411
|
+
// Launch the durable task-graph dispatcher: claim Pending tasks, execute them with a fresh
|
|
1412
|
+
// provider each, and reconcile claims orphaned by a crash. Without this nothing ever picks up a
|
|
1413
|
+
// submitted graph — submission would be durable and inert, which is strictly worse than the old
|
|
1414
|
+
// client-driven path it replaced. Gated on SQL Server because the provider factory mints
|
|
1415
|
+
// SQLServerDataProvider; the PG branch lands with PG parity. Self-registers with ShutdownRegistry.
|
|
1416
|
+
//
|
|
1417
|
+
// `MJ_DISABLE_TASK_GRAPH_DISPATCHER=1` suppresses it, for the one case where a second dispatcher
|
|
1418
|
+
// is actively harmful: the integration suite's task-graph bundle drives its OWN dispatcher against
|
|
1419
|
+
// a stub runner and asserts exactly-once execution. A dispatcher claims from the whole table, not
|
|
1420
|
+
// from "its own" graphs, so a server sharing that database races the suite for every claim and
|
|
1421
|
+
// executes the suite's tasks with the real agent runner. The bundle then reports tasks that never
|
|
1422
|
+
// ran and graphs that settled to the wrong status — symptoms that read as engine defects and cost
|
|
1423
|
+
// a release cycle to trace back to here. The suite still needs MJAPI up for its client-transport
|
|
1424
|
+
// members, so "stop the server" is not the remedy; this is.
|
|
1425
|
+
const taskGraphPool = dataSources[0]?.dataSource;
|
|
1426
|
+
const taskGraphDispatcherDisabled = process.env.MJ_DISABLE_TASK_GRAPH_DISPATCHER === '1';
|
|
1427
|
+
if (taskGraphDispatcherDisabled) {
|
|
1428
|
+
LogStatus('[TaskGraphDispatcher] Disabled by MJ_DISABLE_TASK_GRAPH_DISPATCHER=1 — submitted graphs will not be executed by this process.');
|
|
1429
|
+
} else if (resumeUser && taskGraphPool instanceof sql.ConnectionPool) {
|
|
1430
|
+
StartTaskGraphDispatcher(taskGraphPool, resumeUser)
|
|
1431
|
+
.catch(err => console.warn(`[TaskGraphDispatcher] Startup failed: ${err}`));
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1344
1434
|
// Launch the calendar / scheduled-bridge loop (M2): poll agent calendars for meeting invites and
|
|
1345
1435
|
// start due meeting bridges. Mirrors the SessionJanitor lifecycle (run-once + interval, timer
|
|
1346
1436
|
// unref'd). Gated on Teams meetings being enabled (the provider whose scheduled-join is wired) and
|
|
@@ -1730,31 +1820,6 @@ function createMSSQLCompatPool(pgPool: import('pg').Pool): sql.ConnectionPool {
|
|
|
1730
1820
|
return wrapper as unknown as sql.ConnectionPool;
|
|
1731
1821
|
}
|
|
1732
1822
|
|
|
1733
|
-
/**
|
|
1734
|
-
* Refreshes the UserCache using PostgreSQL queries instead of MSSQL.
|
|
1735
|
-
* This mirrors the logic in UserCache.Refresh() but uses pg.Pool.
|
|
1736
|
-
*/
|
|
1737
|
-
async function refreshUserCacheFromPG(pgPool: import('pg').Pool, coreSchema: string): Promise<void> {
|
|
1738
|
-
const { UserInfo } = await import('@memberjunction/core');
|
|
1739
|
-
const uResult = await pgPool.query(`SELECT * FROM ${coreSchema}."vwUsers"`);
|
|
1740
|
-
const rResult = await pgPool.query(`SELECT * FROM ${coreSchema}."vwUserRoles"`);
|
|
1741
|
-
const users = uResult.rows;
|
|
1742
|
-
const roles = rResult.rows;
|
|
1743
|
-
|
|
1744
|
-
if (users) {
|
|
1745
|
-
const userInfos = users.map((user: Record<string, unknown>) => {
|
|
1746
|
-
const userWithRoles = {
|
|
1747
|
-
...user,
|
|
1748
|
-
UserRoles: roles.filter((role: Record<string, unknown>) => UUIDsEqual(role.UserID as string, user.ID as string)),
|
|
1749
|
-
};
|
|
1750
|
-
return new UserInfo(Metadata.Provider, userWithRoles); // global-provider-ok: bootstrap (UserCache initialization)
|
|
1751
|
-
});
|
|
1752
|
-
// Access the UserCache internals to set users
|
|
1753
|
-
const cache = UserCache.Instance;
|
|
1754
|
-
(cache as unknown as Record<string, unknown>)['_users'] = userInfos;
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
1823
|
/**
|
|
1759
1824
|
* Translates SQL Server bracket-quoted identifiers to PostgreSQL double-quoted identifiers.
|
|
1760
1825
|
* Converts [schema].[table] to "schema"."table" and handles common T-SQL patterns.
|
|
@@ -51,6 +51,20 @@ export function redactArg(ctx: RedactionContext): unknown {
|
|
|
51
51
|
const entity = ctx.provider.Entities.find((e) => e.ClassName === match.groups!.name);
|
|
52
52
|
if (entity) {
|
|
53
53
|
encryptedFieldNames = new Set(entity.EncryptedFields.map((f) => f.Name));
|
|
54
|
+
} else {
|
|
55
|
+
// The name matched the CRUD convention but resolves to no entity — so the
|
|
56
|
+
// metadata half of the redactor contributes nothing and, absent any @NoLog
|
|
57
|
+
// field, this arg falls through to shortenForLog with its values intact.
|
|
58
|
+
//
|
|
59
|
+
// This is the silent case: the boot audit tests only the NAME pattern, so an
|
|
60
|
+
// input like `CreateConnectionInput` is classified as metadata-bound and never
|
|
61
|
+
// warned about, while the lookup here quietly fails. Every hand-written
|
|
62
|
+
// `Create*Input` in the resolvers hits this — codegen inputs embed the entity
|
|
63
|
+
// ClassName (`CreateMJCredentialInput`), hand-written ones do not.
|
|
64
|
+
//
|
|
65
|
+
// Report it once per input type so the operator who enabled variables logging
|
|
66
|
+
// learns which args are unprotected, rather than discovering it in a log file.
|
|
67
|
+
WarnUnboundCrudInput(ctx.inputTypeName);
|
|
54
68
|
}
|
|
55
69
|
}
|
|
56
70
|
|
|
@@ -80,3 +94,32 @@ export function redactArg(ctx: RedactionContext): unknown {
|
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
const EMPTY_SET: ReadonlySet<string> = new Set<string>();
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Input type names already reported by {@link WarnUnboundCrudInput}. Warning once
|
|
100
|
+
* per type keeps a hot mutation from flooding the log with the same diagnostic.
|
|
101
|
+
*/
|
|
102
|
+
const warnedUnboundInputTypes = new Set<string>();
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Reports, once per input type, that an arg named like a CRUD input resolves to no
|
|
106
|
+
* entity and is therefore logged unredacted unless its fields carry `@NoLog`.
|
|
107
|
+
*
|
|
108
|
+
* Exported for tests; not part of the redaction path's contract.
|
|
109
|
+
*/
|
|
110
|
+
export function WarnUnboundCrudInput(inputTypeName: string): void {
|
|
111
|
+
if (warnedUnboundInputTypes.has(inputTypeName)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
warnedUnboundInputTypes.add(inputTypeName);
|
|
115
|
+
console.warn(
|
|
116
|
+
`[MJServer] Variables logging: '${inputTypeName}' matches the Create/Update/Delete input ` +
|
|
117
|
+
`naming convention but maps to no entity, so encrypted-field redaction cannot apply to it. ` +
|
|
118
|
+
`Its values are being logged. Mark any sensitive @Field() on this input with @NoLog.`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Test seam — clears the once-per-type warning state. */
|
|
123
|
+
export function ResetUnboundCrudInputWarnings(): void {
|
|
124
|
+
warnedUnboundInputTypes.clear();
|
|
125
|
+
}
|
|
@@ -126,6 +126,11 @@ export abstract class BaseServerMiddleware {
|
|
|
126
126
|
/**
|
|
127
127
|
* Hook that runs after a RunView operation completes. Can modify the result
|
|
128
128
|
* (e.g., filtering or augmenting data) before it is returned to the caller.
|
|
129
|
+
*
|
|
130
|
+
* ⚠️ `results.Results` and its rows may be FROZEN shared cache state (LocalCacheManager
|
|
131
|
+
* freeze-on-write) — writing into a row throws `TypeError`. To modify, build copies and
|
|
132
|
+
* either reassign `results.Results = results.Results.map(r => ({ ...r, ... }))` or return
|
|
133
|
+
* a new result; never mutate rows in place.
|
|
129
134
|
*/
|
|
130
135
|
PostRunView(params: RunViewParams, results: RunViewResult, contextUser: UserInfo | undefined): RunViewResult | Promise<RunViewResult> {
|
|
131
136
|
return results;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { Metadata, RunView, UserInfo, LogError, LogStatus } from '@memberjunction/core';
|
|
18
18
|
import { MJLruCache, UUIDsEqual } from '@memberjunction/global';
|
|
19
19
|
import type { MJConversationWidgetInstanceEntity, MJConversationEntity } from '@memberjunction/core-entities';
|
|
20
|
-
import { UserCache } from '@memberjunction/
|
|
20
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
21
21
|
import { MagicLinkKeyManager } from '../auth/magicLink/MagicLinkKeys.js';
|
|
22
22
|
import { generateSessionId } from '../auth/magicLink/magicLinkCore.js';
|
|
23
23
|
import { MagicLinkService } from '../auth/magicLink/MagicLinkService.js';
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { RunView, LogError, type UserInfo, type DatabaseProviderBase } from '@memberjunction/core';
|
|
17
17
|
import type { MJConversationWidgetInstanceEntity } from '@memberjunction/core-entities';
|
|
18
|
-
import { UserCache } from '@memberjunction/
|
|
18
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
19
19
|
import type { UserPayload } from '../types.js';
|
|
20
20
|
|
|
21
21
|
const WIDGET_ENTITY = 'MJ: Conversation Widget Instances';
|
|
@@ -366,6 +366,19 @@ export class ActionResolver extends ResolverBase {
|
|
|
366
366
|
// Run the entity action
|
|
367
367
|
const result = await EntityActionEngineServer.Instance.RunEntityAction(params);
|
|
368
368
|
|
|
369
|
+
if (!result) {
|
|
370
|
+
// The binding is scoped (ScopeEntityID/ScopeRecordID) and this record falls outside it, so
|
|
371
|
+
// the action never ran. That is a legitimate answer, not a failure — dereferencing the null
|
|
372
|
+
// here turned "this binding does not apply to that record" into a server error for the
|
|
373
|
+
// caller. Reported as a successful no-op with an explicit message, so a client can tell it
|
|
374
|
+
// apart from an action that ran and did nothing.
|
|
375
|
+
return {
|
|
376
|
+
Success: true,
|
|
377
|
+
Message: 'The entity action did not run: its binding is scoped to different records.',
|
|
378
|
+
ResultData: JSON.stringify({ Ran: false, Reason: 'OutOfScope' })
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
369
382
|
// Return the result
|
|
370
383
|
return {
|
|
371
384
|
Success: result.Success,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, InputType, ObjectType, Query, Mutation, Resolver } from 'type-graphql';
|
|
2
2
|
import { UserInfo, LogError, LogStatus } from '@memberjunction/core';
|
|
3
3
|
import { UUIDsEqual, MJLruCache } from '@memberjunction/global';
|
|
4
|
-
import { UserCache } from '@memberjunction/
|
|
4
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
5
5
|
import { MJComponentRegistryEntity, ComponentMetadataEngine } from '@memberjunction/core-entities';
|
|
6
6
|
import { CredentialEngine } from '@memberjunction/credentials';
|
|
7
7
|
import { ComponentSpec } from '@memberjunction/interactive-component-types';
|
|
@@ -3,7 +3,7 @@ import { LogError } from '@memberjunction/core';
|
|
|
3
3
|
import { AppContext } from '../types.js';
|
|
4
4
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
5
5
|
import { GetReadOnlyProvider } from '../util.js';
|
|
6
|
-
import { UserCache } from '@memberjunction/
|
|
6
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
7
7
|
|
|
8
8
|
@ObjectType()
|
|
9
9
|
class EntityPermissionResult {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Resolver, Query, Mutation, Arg, Ctx, ObjectType, Field, InputType, Int, Float } from "type-graphql";
|
|
2
2
|
import { CompositeKey, DatabaseProviderBase, EntityInfo, LocalCacheManager, Metadata, RunView, UserInfo, LogError, LogStatus, IMetadataProvider, TransactionGroupBase } from "@memberjunction/core";
|
|
3
3
|
import { GetReadOnlyProvider, GetReadWriteProvider } from "../util.js";
|
|
4
|
+
import { NoLog } from "../logging/NoLog.js";
|
|
4
5
|
import { UUIDsEqual } from "@memberjunction/global";
|
|
5
6
|
import { CronExpressionHelper } from "@memberjunction/scheduling-engine";
|
|
6
7
|
import {
|
|
@@ -56,7 +57,7 @@ import { IntegrationCustomColumnPromoter } from "../integration/CustomColumnProm
|
|
|
56
57
|
import { ComputeCascadeRemovalSet, ComputeRemovedDependencyWarnings, DisableUnselectedEntityMaps, ReenableFieldMapsForEntityMap, ResetPullWatermarks, SetEntityMapEnabled } from "../integration/EntityMapLifecycle.js";
|
|
57
58
|
import { AppContext } from "../types.js";
|
|
58
59
|
import { RequireSystemUser } from "../directives/RequireSystemUser.js";
|
|
59
|
-
import { UserCache } from "@memberjunction/
|
|
60
|
+
import { UserCache } from "@memberjunction/generic-database-provider";
|
|
60
61
|
|
|
61
62
|
// ─── RSU Pipeline Output Types ──────────────────────────────────────────────
|
|
62
63
|
|
|
@@ -551,7 +552,16 @@ class CreateConnectionInput {
|
|
|
551
552
|
@Field() CompanyID: string;
|
|
552
553
|
@Field() CredentialTypeID: string;
|
|
553
554
|
@Field() CredentialName: string;
|
|
554
|
-
|
|
555
|
+
/**
|
|
556
|
+
* The credential payload being connected, as a JSON string.
|
|
557
|
+
*
|
|
558
|
+
* `@NoLog` is load-bearing here, not decorative. This field does NOT map to an
|
|
559
|
+
* entity column — the resolver assigns it onto `MJ: Credentials`.`Values`
|
|
560
|
+
* (which IS `Encrypt=true`) in procedural code further down. The metadata-driven
|
|
561
|
+
* half of the redactor keys off entity columns, so it cannot see this field, and
|
|
562
|
+
* without the mark the variables log emits the credential in plaintext.
|
|
563
|
+
*/
|
|
564
|
+
@Field() @NoLog CredentialValues: string;
|
|
555
565
|
@Field({ nullable: true }) ExternalSystemID?: string;
|
|
556
566
|
@Field({ nullable: true }) Configuration?: string;
|
|
557
567
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
PotentialDuplicateResult,
|
|
9
9
|
} from '@memberjunction/core';
|
|
10
10
|
import { AppContext } from '../types.js';
|
|
11
|
-
import { UserCache } from '@memberjunction/
|
|
11
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
12
12
|
|
|
13
13
|
import { CompositeKeyInputType, CompositeKeyOutputType, KeyValuePairOutputType } from '../generic/KeyInputOutputTypes.js';
|
|
14
14
|
import { GetReadOnlyProvider } from '../util.js';
|
|
@@ -227,6 +227,18 @@ export class StartRealtimeClientSessionResult {
|
|
|
227
227
|
@Field(() => String, { nullable: true })
|
|
228
228
|
ModelName?: string;
|
|
229
229
|
|
|
230
|
+
/**
|
|
231
|
+
* `DriverClass` of the vendor that actually runs the session (e.g. `OpenAIRealtime`,
|
|
232
|
+
* `ElevenLabsRealtime`). Null when unknown.
|
|
233
|
+
*
|
|
234
|
+
* Surfaced to the BROWSER because on the default-model path the framework picks the vendor itself:
|
|
235
|
+
* without this the caller cannot tell which vendor spoke, and so cannot diagnose a voice that did
|
|
236
|
+
* not land (issue #3530). `ModelName` alone is not enough — one model can resolve to different
|
|
237
|
+
* vendors depending on which API keys a deployment configures.
|
|
238
|
+
*/
|
|
239
|
+
@Field(() => String, { nullable: true })
|
|
240
|
+
DriverClass?: string;
|
|
241
|
+
|
|
230
242
|
/**
|
|
231
243
|
* DB-driven progress-narration instruction template (contains a `{{ progressMessage }}`
|
|
232
244
|
* placeholder). Null when the narration prompt is not present in this deployment's metadata —
|
|
@@ -1677,6 +1689,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
1677
1689
|
ExpiresAt: cfg.ExpiresAt,
|
|
1678
1690
|
SessionConfigJson: JSON.stringify(cfg.SessionConfig),
|
|
1679
1691
|
ModelName: prep.ModelName,
|
|
1692
|
+
DriverClass: prep.DriverClass,
|
|
1680
1693
|
NarrationInstructionsTemplate: prep.NarrationInstructionsTemplate,
|
|
1681
1694
|
NarrationPaceMs: prep.NarrationPaceMs,
|
|
1682
1695
|
EffectiveConfigJson: prep.EffectiveConfig ? JSON.stringify(prep.EffectiveConfig) : undefined,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, Float, InputType, Int, ObjectType, Query, Resolver } from 'type-graphql';
|
|
2
2
|
import { EntitySearchResult, SearchEntityParams } from '@memberjunction/core';
|
|
3
3
|
import { AppContext } from '../types.js';
|
|
4
|
-
import { UserCache } from '@memberjunction/
|
|
4
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
5
5
|
import { GetReadOnlyProvider } from '../util.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -2,14 +2,13 @@ import { Arg, Ctx, Field, InputType, Mutation, ObjectType, Query, Int, Resolver
|
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
3
|
import { Metadata, UserInfo } from '@memberjunction/core';
|
|
4
4
|
import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
|
|
5
|
-
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
6
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
6
|
import * as path from 'path';
|
|
8
7
|
import * as fs from 'fs/promises';
|
|
9
8
|
import { loadConfig } from '../config.js';
|
|
10
9
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
11
10
|
import { GetReadOnlyProvider } from '../util.js';
|
|
12
|
-
import { SqlLoggingOptions as ProviderSqlLoggingOptions } from '@memberjunction/generic-database-provider';
|
|
11
|
+
import { UserCache, SqlLoggingOptions as ProviderSqlLoggingOptions } from '@memberjunction/generic-database-provider';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Configuration options for SQL logging sessions.
|
|
@@ -4,7 +4,7 @@ import { DatabaseProviderBase, EntityDeleteOptions, EntitySaveOptions, IMetadata
|
|
|
4
4
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
5
5
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
6
6
|
import { MJRoleEntity, MJUserEntity, MJUserRoleEntity } from '@memberjunction/core-entities';
|
|
7
|
-
import { UserCache } from '@memberjunction/
|
|
7
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
8
8
|
import { GetReadWriteProvider } from '../util.js';
|
|
9
9
|
|
|
10
10
|
@ObjectType()
|
|
@@ -112,7 +112,7 @@ export class SyncRolesAndUsersResolver {
|
|
|
112
112
|
await provider.CommitTransaction();
|
|
113
113
|
|
|
114
114
|
// refresh the user cache — one-time, since the normal auto-refresh is already scheduled
|
|
115
|
-
await UserCache.Instance.Refresh(
|
|
115
|
+
await UserCache.Instance.Refresh(provider);
|
|
116
116
|
return usersResult;
|
|
117
117
|
} catch (txErr) {
|
|
118
118
|
await provider.RollbackTransaction();
|