@memberjunction/server 5.30.1 → 5.31.0
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/agents/skip-sdk.d.ts +17 -1
- package/dist/agents/skip-sdk.d.ts.map +1 -1
- package/dist/agents/skip-sdk.js +18 -5
- package/dist/agents/skip-sdk.js.map +1 -1
- package/dist/auth/exampleNewUserSubClass.js +1 -1
- package/dist/auth/exampleNewUserSubClass.js.map +1 -1
- package/dist/auth/index.js +2 -2
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/newUsers.js +2 -2
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/context.js +3 -3
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +218 -8
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +1267 -52
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +5 -5
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +21 -18
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/multiTenancy/index.js +1 -1
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/resolvers/APIKeyResolver.d.ts.map +1 -1
- package/dist/resolvers/APIKeyResolver.js +5 -3
- package/dist/resolvers/APIKeyResolver.js.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts +3 -3
- package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.js +18 -12
- package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.d.ts +1 -1
- package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +6 -4
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.js +2 -2
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +1 -2
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/ISAEntityResolver.d.ts.map +1 -1
- package/dist/resolvers/ISAEntityResolver.js +2 -5
- package/dist/resolvers/ISAEntityResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +75 -66
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts +4 -4
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +9 -8
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +6 -6
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +22 -18
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/TagGovernanceResolver.d.ts +43 -0
- package/dist/resolvers/TagGovernanceResolver.d.ts.map +1 -0
- package/dist/resolvers/TagGovernanceResolver.js +245 -0
- package/dist/resolvers/TagGovernanceResolver.js.map +1 -0
- package/dist/resolvers/TaskResolver.d.ts +1 -1
- package/dist/resolvers/TaskResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskResolver.js +4 -2
- package/dist/resolvers/TaskResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +2 -1
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +4 -4
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +9 -9
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +4 -4
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/services/TaskOrchestrator.d.ts +4 -2
- package/dist/services/TaskOrchestrator.d.ts.map +1 -1
- package/dist/services/TaskOrchestrator.js +16 -12
- package/dist/services/TaskOrchestrator.js.map +1 -1
- package/package.json +68 -66
- package/src/__tests__/TagGovernanceResolver.test.ts +255 -0
- package/src/agents/skip-sdk.ts +30 -7
- package/src/auth/exampleNewUserSubClass.ts +1 -1
- package/src/auth/index.ts +2 -2
- package/src/auth/newUsers.ts +2 -2
- package/src/context.ts +3 -3
- package/src/generated/generated.ts +872 -41
- package/src/generic/ResolverBase.ts +28 -21
- package/src/index.ts +9 -9
- package/src/multiTenancy/index.ts +1 -1
- package/src/resolvers/APIKeyResolver.ts +7 -4
- package/src/resolvers/AutotagPipelineResolver.ts +20 -11
- package/src/resolvers/ComponentRegistryResolver.ts +8 -5
- package/src/resolvers/FileResolver.ts +2 -2
- package/src/resolvers/GetDataContextDataResolver.ts +1 -2
- package/src/resolvers/ISAEntityResolver.ts +3 -5
- package/src/resolvers/IntegrationDiscoveryResolver.ts +83 -66
- package/src/resolvers/SyncDataResolver.ts +12 -11
- package/src/resolvers/SyncRolesUsersResolver.ts +23 -19
- package/src/resolvers/TagGovernanceResolver.ts +189 -0
- package/src/resolvers/TaskResolver.ts +5 -3
- package/src/resolvers/TransactionGroupResolver.ts +3 -2
- package/src/rest/EntityCRUDHandler.ts +4 -4
- package/src/rest/RESTEndpointHandler.ts +9 -9
- package/src/rest/ViewOperationsHandler.ts +4 -4
- package/src/services/TaskOrchestrator.ts +18 -13
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType } from 'type-graphql';
|
|
2
2
|
import { AppContext, UserPayload } from '../types.js';
|
|
3
|
-
import { DatabaseProviderBase, EntityDeleteOptions, EntitySaveOptions, LogError, Metadata, RunView, UserInfo } from '@memberjunction/core';
|
|
3
|
+
import { DatabaseProviderBase, EntityDeleteOptions, EntitySaveOptions, IMetadataProvider, LogError, Metadata, RunView, UserInfo } from '@memberjunction/core';
|
|
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
7
|
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
8
|
+
import { GetReadWriteProvider } from '../util.js';
|
|
8
9
|
|
|
9
10
|
@ObjectType()
|
|
10
11
|
export class SyncRolesAndUsersResultType {
|
|
@@ -92,16 +93,17 @@ export class SyncRolesAndUsersResolver {
|
|
|
92
93
|
// attempted this with a TransactionGroup but ran into nesting issues —
|
|
93
94
|
// direct DB transactions per the plan doc avoid that.
|
|
94
95
|
try {
|
|
95
|
-
const
|
|
96
|
+
const md = GetReadWriteProvider(context.providers, { allowFallbackToReadOnly: true }) as unknown as IMetadataProvider;
|
|
97
|
+
const provider = md as unknown as DatabaseProviderBase;
|
|
96
98
|
await provider.BeginTransaction();
|
|
97
99
|
try {
|
|
98
|
-
const roleResult = await this.DoSyncRoles(data.Roles, context.userPayload.userRecord, context.userPayload);
|
|
100
|
+
const roleResult = await this.DoSyncRoles(data.Roles, context.userPayload.userRecord, context.userPayload, md);
|
|
99
101
|
if (!roleResult.Success) {
|
|
100
102
|
await provider.RollbackTransaction();
|
|
101
103
|
return roleResult;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
const usersResult = await this.DoSyncUsers(data.Users, context.userPayload.userRecord, context.userPayload);
|
|
106
|
+
const usersResult = await this.DoSyncUsers(data.Users, context.userPayload.userRecord, context.userPayload, md);
|
|
105
107
|
if (!usersResult.Success) {
|
|
106
108
|
await provider.RollbackTransaction();
|
|
107
109
|
return usersResult;
|
|
@@ -136,10 +138,11 @@ export class SyncRolesAndUsersResolver {
|
|
|
136
138
|
// Wrap delete + add + update of roles in one DB transaction so any failure
|
|
137
139
|
// rolls back the whole batch. Keeps the sync idempotent across retries.
|
|
138
140
|
try {
|
|
139
|
-
const
|
|
141
|
+
const md = GetReadWriteProvider(context.providers, { allowFallbackToReadOnly: true }) as unknown as IMetadataProvider;
|
|
142
|
+
const provider = md as unknown as DatabaseProviderBase;
|
|
140
143
|
await provider.BeginTransaction();
|
|
141
144
|
try {
|
|
142
|
-
const result = await this.DoSyncRoles(roles, context.userPayload.userRecord, context.userPayload);
|
|
145
|
+
const result = await this.DoSyncRoles(roles, context.userPayload.userRecord, context.userPayload, md);
|
|
143
146
|
if (result.Success) {
|
|
144
147
|
await provider.CommitTransaction();
|
|
145
148
|
} else {
|
|
@@ -161,7 +164,7 @@ export class SyncRolesAndUsersResolver {
|
|
|
161
164
|
* Throws on any Save/Delete failure so the outer transaction rolls back. A returned
|
|
162
165
|
* `{ Success: true }` means every operation succeeded.
|
|
163
166
|
*/
|
|
164
|
-
protected async DoSyncRoles(roles: RoleInputType[], user: UserInfo, userPayload: UserPayload): Promise<SyncRolesAndUsersResultType> {
|
|
167
|
+
protected async DoSyncRoles(roles: RoleInputType[], user: UserInfo, userPayload: UserPayload, provider?: IMetadataProvider): Promise<SyncRolesAndUsersResultType> {
|
|
165
168
|
const rv = new RunView();
|
|
166
169
|
const result = await rv.RunView<MJRoleEntity>({
|
|
167
170
|
EntityName: "MJ: Roles",
|
|
@@ -174,7 +177,7 @@ export class SyncRolesAndUsersResolver {
|
|
|
174
177
|
|
|
175
178
|
const currentRoles = result.Results;
|
|
176
179
|
await this.DeleteRemovedRoles(currentRoles, roles, user, userPayload);
|
|
177
|
-
await this.AddNewRoles(currentRoles, roles, user, userPayload);
|
|
180
|
+
await this.AddNewRoles(currentRoles, roles, user, userPayload, provider);
|
|
178
181
|
await this.UpdateExistingRoles(currentRoles, roles, userPayload);
|
|
179
182
|
return { Success: true };
|
|
180
183
|
}
|
|
@@ -192,9 +195,9 @@ export class SyncRolesAndUsersResolver {
|
|
|
192
195
|
}
|
|
193
196
|
}
|
|
194
197
|
|
|
195
|
-
protected async AddNewRoles(currentRoles: MJRoleEntity[], futureRoles: RoleInputType[], user: UserInfo, userPayload: UserPayload): Promise<void> {
|
|
198
|
+
protected async AddNewRoles(currentRoles: MJRoleEntity[], futureRoles: RoleInputType[], user: UserInfo, userPayload: UserPayload, provider?: IMetadataProvider): Promise<void> {
|
|
196
199
|
// go through the future roles and add any that are not in the current roles
|
|
197
|
-
const md = new Metadata();
|
|
200
|
+
const md = provider ?? new Metadata();
|
|
198
201
|
|
|
199
202
|
for (const add of futureRoles) {
|
|
200
203
|
if (!currentRoles.find(r => r.Name.trim().toLowerCase() === add.Name.trim().toLowerCase())) {
|
|
@@ -262,10 +265,11 @@ export class SyncRolesAndUsersResolver {
|
|
|
262
265
|
) : Promise<SyncRolesAndUsersResultType> {
|
|
263
266
|
// Wrap delete + add + update + role-sync of users in one DB transaction.
|
|
264
267
|
try {
|
|
265
|
-
const
|
|
268
|
+
const md = GetReadWriteProvider(context.providers, { allowFallbackToReadOnly: true }) as unknown as IMetadataProvider;
|
|
269
|
+
const provider = md as unknown as DatabaseProviderBase;
|
|
266
270
|
await provider.BeginTransaction();
|
|
267
271
|
try {
|
|
268
|
-
const result = await this.DoSyncUsers(users, context.userPayload.userRecord, context.userPayload);
|
|
272
|
+
const result = await this.DoSyncUsers(users, context.userPayload.userRecord, context.userPayload, md);
|
|
269
273
|
if (result.Success) {
|
|
270
274
|
await provider.CommitTransaction();
|
|
271
275
|
} else {
|
|
@@ -286,7 +290,7 @@ export class SyncRolesAndUsersResolver {
|
|
|
286
290
|
* Transaction-free core of SyncUsers — expected to be invoked inside an outer transaction.
|
|
287
291
|
* Throws on any Save/Delete failure so the outer transaction rolls back.
|
|
288
292
|
*/
|
|
289
|
-
protected async DoSyncUsers(users: UserInputType[], user: UserInfo, userPayload: UserPayload): Promise<SyncRolesAndUsersResultType> {
|
|
293
|
+
protected async DoSyncUsers(users: UserInputType[], user: UserInfo, userPayload: UserPayload, provider?: IMetadataProvider): Promise<SyncRolesAndUsersResultType> {
|
|
290
294
|
const rv = new RunView();
|
|
291
295
|
const result = await rv.RunView<MJUserEntity>({
|
|
292
296
|
EntityName: "MJ: Users",
|
|
@@ -299,9 +303,9 @@ export class SyncRolesAndUsersResolver {
|
|
|
299
303
|
|
|
300
304
|
const currentUsers = result.Results;
|
|
301
305
|
await this.DeleteRemovedUsers(currentUsers, users, user, userPayload);
|
|
302
|
-
await this.AddNewUsers(currentUsers, users, userPayload);
|
|
306
|
+
await this.AddNewUsers(currentUsers, users, userPayload, provider);
|
|
303
307
|
await this.UpdateExistingUsers(currentUsers, users, userPayload);
|
|
304
|
-
await this.SyncUserRoles(users, user, userPayload);
|
|
308
|
+
await this.SyncUserRoles(users, user, userPayload, provider);
|
|
305
309
|
return { Success: true };
|
|
306
310
|
}
|
|
307
311
|
|
|
@@ -322,9 +326,9 @@ export class SyncRolesAndUsersResolver {
|
|
|
322
326
|
}
|
|
323
327
|
}
|
|
324
328
|
|
|
325
|
-
protected async AddNewUsers(currentUsers: MJUserEntity[], futureUsers: UserInputType[], userPayload: UserPayload): Promise<void> {
|
|
329
|
+
protected async AddNewUsers(currentUsers: MJUserEntity[], futureUsers: UserInputType[], userPayload: UserPayload, provider?: IMetadataProvider): Promise<void> {
|
|
326
330
|
// add users that are not in the current users
|
|
327
|
-
const md = new Metadata();
|
|
331
|
+
const md = provider ?? new Metadata();
|
|
328
332
|
|
|
329
333
|
for (const add of futureUsers) {
|
|
330
334
|
const match = currentUsers.find(currentUser => currentUser.Email?.trim().toLowerCase() === add.Email?.trim().toLowerCase());
|
|
@@ -389,10 +393,10 @@ export class SyncRolesAndUsersResolver {
|
|
|
389
393
|
}
|
|
390
394
|
}
|
|
391
395
|
|
|
392
|
-
protected async SyncUserRoles(users: UserInputType[], u: UserInfo, userPayload: UserPayload): Promise<void> {
|
|
396
|
+
protected async SyncUserRoles(users: UserInputType[], u: UserInfo, userPayload: UserPayload, provider?: IMetadataProvider): Promise<void> {
|
|
393
397
|
// for each user in the users array, make sure there is a User Role that matches. First, get a list of all DATABASE user and roels so we have that for fast lookup in memory
|
|
394
398
|
const rv = new RunView();
|
|
395
|
-
const md = new Metadata();
|
|
399
|
+
const md = provider ?? new Metadata();
|
|
396
400
|
|
|
397
401
|
const p1 = rv.RunView<MJUserEntity>({
|
|
398
402
|
EntityName: "MJ: Users",
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Resolver, Mutation, Ctx, Arg, ObjectType, Field, Int } from 'type-graphql';
|
|
2
|
+
import { AppContext } from '../types.js';
|
|
3
|
+
import { LogError, LogStatus } from '@memberjunction/core';
|
|
4
|
+
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
5
|
+
import { TagGovernanceEngine, TagHealthJob, DEFAULT_TAG_HEALTH_THRESHOLDS, TagEngine } from '@memberjunction/tag-engine';
|
|
6
|
+
|
|
7
|
+
@ObjectType()
|
|
8
|
+
export class PromoteSuggestionResult {
|
|
9
|
+
@Field()
|
|
10
|
+
Success: boolean;
|
|
11
|
+
|
|
12
|
+
@Field({ nullable: true })
|
|
13
|
+
ResolvedTagID?: string;
|
|
14
|
+
|
|
15
|
+
@Field({ nullable: true })
|
|
16
|
+
ResolvedTagName?: string;
|
|
17
|
+
|
|
18
|
+
@Field({ nullable: true })
|
|
19
|
+
ErrorMessage?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@ObjectType()
|
|
23
|
+
export class RejectSuggestionResult {
|
|
24
|
+
@Field()
|
|
25
|
+
Success: boolean;
|
|
26
|
+
|
|
27
|
+
@Field({ nullable: true })
|
|
28
|
+
ErrorMessage?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@ObjectType()
|
|
32
|
+
export class RebuildTagEmbeddingsResult {
|
|
33
|
+
@Field()
|
|
34
|
+
Success: boolean;
|
|
35
|
+
|
|
36
|
+
@Field(() => Int)
|
|
37
|
+
Refreshed: number;
|
|
38
|
+
|
|
39
|
+
@Field(() => Int)
|
|
40
|
+
Total: number;
|
|
41
|
+
|
|
42
|
+
@Field({ nullable: true })
|
|
43
|
+
ErrorMessage?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@ObjectType()
|
|
47
|
+
export class RunTagHealthResult {
|
|
48
|
+
@Field()
|
|
49
|
+
Success: boolean;
|
|
50
|
+
|
|
51
|
+
@Field(() => Int)
|
|
52
|
+
MergeCount: number;
|
|
53
|
+
|
|
54
|
+
@Field(() => Int)
|
|
55
|
+
LowUsageCount: number;
|
|
56
|
+
|
|
57
|
+
@Field(() => Int)
|
|
58
|
+
WideNodeCount: number;
|
|
59
|
+
|
|
60
|
+
@Field(() => Int)
|
|
61
|
+
DurationMs: number;
|
|
62
|
+
|
|
63
|
+
@Field({ nullable: true })
|
|
64
|
+
ErrorMessage?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* GraphQL surface for the tag governance lifecycle that the Suggestion Inbox
|
|
69
|
+
* UI invokes. Wraps `TagGovernanceEngine` (PromoteSuggestion / RejectSuggestion),
|
|
70
|
+
* `TagEngine.RebuildTagEmbeddings`, and `TagHealthJob.Run` so the client gets a
|
|
71
|
+
* single transactional call per disposition instead of multiple BaseEntity saves.
|
|
72
|
+
*
|
|
73
|
+
* Multi-provider safety: each mutation reads the per-request user from the
|
|
74
|
+
* context and lets the engines use their default provider; if a non-default
|
|
75
|
+
* provider is in play it should be passed via `TagGovernanceEngine.Provider`.
|
|
76
|
+
*/
|
|
77
|
+
@Resolver()
|
|
78
|
+
export class TagGovernanceResolver extends ResolverBase {
|
|
79
|
+
@Mutation(() => PromoteSuggestionResult)
|
|
80
|
+
async PromoteTagSuggestion(
|
|
81
|
+
@Arg('suggestionID') suggestionID: string,
|
|
82
|
+
@Arg('strategy') strategy: 'create-new' | 'merge-into-existing',
|
|
83
|
+
@Arg('targetTagID', { nullable: true }) targetTagID: string | undefined,
|
|
84
|
+
@Ctx() { userPayload }: AppContext = {} as AppContext
|
|
85
|
+
): Promise<PromoteSuggestionResult> {
|
|
86
|
+
try {
|
|
87
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
88
|
+
if (!currentUser) {
|
|
89
|
+
return { Success: false, ErrorMessage: 'Unable to determine current user' };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (strategy !== 'create-new' && strategy !== 'merge-into-existing') {
|
|
93
|
+
return { Success: false, ErrorMessage: `Unknown strategy "${strategy}"` };
|
|
94
|
+
}
|
|
95
|
+
if (strategy === 'merge-into-existing' && !targetTagID) {
|
|
96
|
+
return { Success: false, ErrorMessage: 'targetTagID is required when strategy is "merge-into-existing".' };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const resolved = strategy === 'create-new'
|
|
100
|
+
? await TagGovernanceEngine.Instance.PromoteSuggestion(suggestionID, { kind: 'create-new' }, currentUser)
|
|
101
|
+
: await TagGovernanceEngine.Instance.PromoteSuggestion(suggestionID, { kind: 'merge-into-existing', targetTagID: targetTagID! }, currentUser);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
Success: true,
|
|
105
|
+
ResolvedTagID: resolved.ID,
|
|
106
|
+
ResolvedTagName: resolved.Name,
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
110
|
+
LogError(`PromoteTagSuggestion failed for ${suggestionID}: ${msg}`);
|
|
111
|
+
return { Success: false, ErrorMessage: msg };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Mutation(() => RejectSuggestionResult)
|
|
116
|
+
async RejectTagSuggestion(
|
|
117
|
+
@Arg('suggestionID') suggestionID: string,
|
|
118
|
+
@Arg('reviewerNotes', { nullable: true }) reviewerNotes: string | undefined,
|
|
119
|
+
@Ctx() { userPayload }: AppContext = {} as AppContext
|
|
120
|
+
): Promise<RejectSuggestionResult> {
|
|
121
|
+
try {
|
|
122
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
123
|
+
if (!currentUser) {
|
|
124
|
+
return { Success: false, ErrorMessage: 'Unable to determine current user' };
|
|
125
|
+
}
|
|
126
|
+
await TagGovernanceEngine.Instance.RejectSuggestion(suggestionID, reviewerNotes ?? null, currentUser);
|
|
127
|
+
return { Success: true };
|
|
128
|
+
} catch (error) {
|
|
129
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
130
|
+
LogError(`RejectTagSuggestion failed for ${suggestionID}: ${msg}`);
|
|
131
|
+
return { Success: false, ErrorMessage: msg };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@Mutation(() => RebuildTagEmbeddingsResult)
|
|
136
|
+
async RebuildTagEmbeddings(
|
|
137
|
+
@Ctx() { userPayload }: AppContext = {} as AppContext
|
|
138
|
+
): Promise<RebuildTagEmbeddingsResult> {
|
|
139
|
+
try {
|
|
140
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
141
|
+
if (!currentUser) {
|
|
142
|
+
return { Success: false, Refreshed: 0, Total: 0, ErrorMessage: 'Unable to determine current user' };
|
|
143
|
+
}
|
|
144
|
+
const result = await TagEngine.Instance.RebuildTagEmbeddings(currentUser);
|
|
145
|
+
LogStatus(`RebuildTagEmbeddings: refreshed ${result.refreshed}/${result.total} tags.`);
|
|
146
|
+
return { Success: true, Refreshed: result.refreshed, Total: result.total };
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
149
|
+
LogError(`RebuildTagEmbeddings failed: ${msg}`);
|
|
150
|
+
return { Success: false, Refreshed: 0, Total: 0, ErrorMessage: msg };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@Mutation(() => RunTagHealthResult)
|
|
155
|
+
async RunTagHealth(
|
|
156
|
+
@Arg('minCoOccurrence', () => Int, { nullable: true }) minCoOccurrence: number | undefined,
|
|
157
|
+
@Arg('minNameSimilarity', { nullable: true }) minNameSimilarity: number | undefined,
|
|
158
|
+
@Arg('minEmbeddingSimilarity', { nullable: true }) minEmbeddingSimilarity: number | undefined,
|
|
159
|
+
@Arg('maxUsage', () => Int, { nullable: true }) maxUsage: number | undefined,
|
|
160
|
+
@Arg('maxImplicitChildren', () => Int, { nullable: true }) maxImplicitChildren: number | undefined,
|
|
161
|
+
@Ctx() { userPayload }: AppContext = {} as AppContext
|
|
162
|
+
): Promise<RunTagHealthResult> {
|
|
163
|
+
try {
|
|
164
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
165
|
+
if (!currentUser) {
|
|
166
|
+
return { Success: false, MergeCount: 0, LowUsageCount: 0, WideNodeCount: 0, DurationMs: 0, ErrorMessage: 'Unable to determine current user' };
|
|
167
|
+
}
|
|
168
|
+
const summary = await TagHealthJob.Instance.Run({
|
|
169
|
+
...DEFAULT_TAG_HEALTH_THRESHOLDS,
|
|
170
|
+
...(minCoOccurrence != null ? { minCoOccurrence } : {}),
|
|
171
|
+
...(minNameSimilarity != null ? { minNameSimilarity } : {}),
|
|
172
|
+
...(minEmbeddingSimilarity != null ? { minEmbeddingSimilarity } : {}),
|
|
173
|
+
...(maxUsage != null ? { maxUsage } : {}),
|
|
174
|
+
...(maxImplicitChildren != null ? { maxImplicitChildren } : {}),
|
|
175
|
+
}, currentUser);
|
|
176
|
+
return {
|
|
177
|
+
Success: true,
|
|
178
|
+
MergeCount: summary.mergeCount,
|
|
179
|
+
LowUsageCount: summary.lowUsageCount,
|
|
180
|
+
WideNodeCount: summary.wideNodeCount,
|
|
181
|
+
DurationMs: summary.durationMs,
|
|
182
|
+
};
|
|
183
|
+
} catch (error) {
|
|
184
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
185
|
+
LogError(`RunTagHealth failed: ${msg}`);
|
|
186
|
+
return { Success: false, MergeCount: 0, LowUsageCount: 0, WideNodeCount: 0, DurationMs: 0, ErrorMessage: msg };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Resolver, Mutation, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine } from 'type-graphql';
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
|
-
import { LogError, LogStatus } from '@memberjunction/core';
|
|
3
|
+
import { IMetadataProvider, LogError, LogStatus } from '@memberjunction/core';
|
|
4
4
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
5
5
|
import { TaskOrchestrator, TaskGraphResponse, TaskExecutionResult } from '../services/TaskOrchestrator.js';
|
|
6
|
+
import { GetReadWriteProvider } from '../util.js';
|
|
6
7
|
|
|
7
8
|
@ObjectType()
|
|
8
9
|
export class TaskExecutionResultType {
|
|
@@ -53,7 +54,7 @@ export class TaskOrchestrationResolver extends ResolverBase {
|
|
|
53
54
|
@Arg('environmentId') environmentId: string,
|
|
54
55
|
@Arg('sessionId') sessionId: string,
|
|
55
56
|
@PubSub() pubSub: PubSubEngine,
|
|
56
|
-
@Ctx() { userPayload }: AppContext,
|
|
57
|
+
@Ctx() { userPayload, providers }: AppContext,
|
|
57
58
|
@Arg('createNotifications', { nullable: true }) createNotifications?: boolean
|
|
58
59
|
): Promise<ExecuteTaskGraphResult> {
|
|
59
60
|
// Check API key scope authorization for task execution
|
|
@@ -82,7 +83,8 @@ export class TaskOrchestrationResolver extends ResolverBase {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// Create task orchestrator with PubSub for progress updates
|
|
85
|
-
const
|
|
86
|
+
const provider = GetReadWriteProvider(providers, { allowFallbackToReadOnly: true }) as unknown as IMetadataProvider;
|
|
87
|
+
const orchestrator = new TaskOrchestrator(currentUser, pubSub, sessionId, userPayload, createNotifications || false, conversationDetailId, provider);
|
|
86
88
|
|
|
87
89
|
// Create parent task and child tasks with dependencies
|
|
88
90
|
const { parentTaskId, taskIdMap } = await orchestrator.createTasksFromGraph(
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, InputType, Int, Mutation, ObjectType, registerEnumType } from 'type-graphql';
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
|
-
import { CompositeKey, KeyValuePair, LogError, Metadata, TransactionVariable, BaseEntity, EntityDeleteOptions, EntitySaveOptions } from '@memberjunction/core';
|
|
3
|
+
import { CompositeKey, IMetadataProvider, KeyValuePair, LogError, Metadata, TransactionVariable, BaseEntity, EntityDeleteOptions, EntitySaveOptions } from '@memberjunction/core';
|
|
4
4
|
import { SafeJSONParse } from '@memberjunction/global';
|
|
5
|
+
import { GetReadWriteProvider } from '../util.js';
|
|
5
6
|
|
|
6
7
|
export enum TransactionVariableType {
|
|
7
8
|
Define = "Define",
|
|
@@ -84,7 +85,7 @@ export class TransactionResolver {
|
|
|
84
85
|
) {
|
|
85
86
|
try {
|
|
86
87
|
// we have received the transaction group information via the network, now we need to reconstruct our TransactionGroup object and run it
|
|
87
|
-
const md = new Metadata();
|
|
88
|
+
const md = (GetReadWriteProvider(context.providers, { allowFallbackToReadOnly: true }) as unknown as IMetadataProvider) ?? new Metadata();
|
|
88
89
|
const tg = await md.CreateTransactionGroup();
|
|
89
90
|
const entityObjects: BaseEntity[] = [];
|
|
90
91
|
const objectValues: any[] = [];
|
|
@@ -14,7 +14,7 @@ export class EntityCRUDHandler {
|
|
|
14
14
|
static async createEntity(entityName: string, data: any, user: UserInfo): Promise<{ success: boolean, entity?: any, error?: string, details?: any, validationErrors?: any[] }> {
|
|
15
15
|
try {
|
|
16
16
|
// Get entity object
|
|
17
|
-
const md = new Metadata();
|
|
17
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
18
18
|
const entity = await md.GetEntityObject(entityName, user);
|
|
19
19
|
|
|
20
20
|
// Check permissions
|
|
@@ -84,7 +84,7 @@ export class EntityCRUDHandler {
|
|
|
84
84
|
static async getEntity(entityName: string, id: string | number, relatedEntities: string[] = null, user: UserInfo): Promise<{ success: boolean, entity?: any, error?: string }> {
|
|
85
85
|
try {
|
|
86
86
|
// Get entity object
|
|
87
|
-
const md = new Metadata();
|
|
87
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
88
88
|
const entity = await md.GetEntityObject(entityName, user);
|
|
89
89
|
|
|
90
90
|
// Check permissions
|
|
@@ -123,7 +123,7 @@ export class EntityCRUDHandler {
|
|
|
123
123
|
static async updateEntity(entityName: string, id: string | number, data: any, user: UserInfo): Promise<{ success: boolean, entity?: any, error?: string, details?: any, validationErrors?: any[] }> {
|
|
124
124
|
try {
|
|
125
125
|
// Get entity object
|
|
126
|
-
const md = new Metadata();
|
|
126
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
127
127
|
const entity = await md.GetEntityObject(entityName, user);
|
|
128
128
|
|
|
129
129
|
// Check permissions
|
|
@@ -210,7 +210,7 @@ export class EntityCRUDHandler {
|
|
|
210
210
|
static async deleteEntity(entityName: string, id: string | number, options: EntityDeleteOptions, user: UserInfo): Promise<{ success: boolean, error?: string, details?: any }> {
|
|
211
211
|
try {
|
|
212
212
|
// Get entity object
|
|
213
|
-
const md = new Metadata();
|
|
213
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
214
214
|
const entity = await md.GetEntityObject(entityName, user);
|
|
215
215
|
|
|
216
216
|
// Check permissions
|
|
@@ -74,7 +74,7 @@ export class RESTEndpointHandler {
|
|
|
74
74
|
*/
|
|
75
75
|
private isEntityAllowed(entityName: string): boolean {
|
|
76
76
|
const name = entityName.toLowerCase();
|
|
77
|
-
const md = new Metadata();
|
|
77
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
78
78
|
const entity = md.Entities.find(e => e.Name.toLowerCase() === name);
|
|
79
79
|
|
|
80
80
|
// If entity not found in metadata, don't allow it
|
|
@@ -417,7 +417,7 @@ export class RESTEndpointHandler {
|
|
|
417
417
|
const user = req['mjUser'];
|
|
418
418
|
|
|
419
419
|
// Get the entity object
|
|
420
|
-
const md = new Metadata();
|
|
420
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
421
421
|
const entity = await md.GetEntityObject(entityName, user);
|
|
422
422
|
|
|
423
423
|
// Create a composite key
|
|
@@ -445,7 +445,7 @@ export class RESTEndpointHandler {
|
|
|
445
445
|
const user = req['mjUser'];
|
|
446
446
|
|
|
447
447
|
// Get the entity object
|
|
448
|
-
const md = new Metadata();
|
|
448
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
449
449
|
const entity = await md.GetEntityObject(entityName, user);
|
|
450
450
|
|
|
451
451
|
// Create a composite key
|
|
@@ -473,7 +473,7 @@ export class RESTEndpointHandler {
|
|
|
473
473
|
const user = req['mjUser'];
|
|
474
474
|
|
|
475
475
|
// Get the entity object
|
|
476
|
-
const md = new Metadata();
|
|
476
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
477
477
|
const entity = await md.GetEntityObject(entityName, user);
|
|
478
478
|
|
|
479
479
|
// Create a composite key
|
|
@@ -589,7 +589,7 @@ export class RESTEndpointHandler {
|
|
|
589
589
|
const user = req['mjUser'];
|
|
590
590
|
|
|
591
591
|
// Filter entities based on user permissions and REST API configuration
|
|
592
|
-
const md = new Metadata();
|
|
592
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
593
593
|
const entities = md.Entities.filter(e => {
|
|
594
594
|
// First check if entity is allowed based on configuration
|
|
595
595
|
if (!this.isEntityAllowed(e.Name)) {
|
|
@@ -629,7 +629,7 @@ export class RESTEndpointHandler {
|
|
|
629
629
|
|
|
630
630
|
const user = req['mjUser'];
|
|
631
631
|
|
|
632
|
-
const md = new Metadata();
|
|
632
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
633
633
|
const entity = md.Entities.find(e => e.Name === entityName);
|
|
634
634
|
if (!entity) {
|
|
635
635
|
res.status(404).json({ error: `Entity '${entityName}' not found` });
|
|
@@ -695,7 +695,7 @@ export class RESTEndpointHandler {
|
|
|
695
695
|
const user = req['mjUser'];
|
|
696
696
|
|
|
697
697
|
// Get the entity object
|
|
698
|
-
const md = new Metadata();
|
|
698
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
699
699
|
const entity = await md.GetEntityObject(entityName, user);
|
|
700
700
|
|
|
701
701
|
// Create a composite key
|
|
@@ -723,7 +723,7 @@ export class RESTEndpointHandler {
|
|
|
723
723
|
const user = req['mjUser'];
|
|
724
724
|
|
|
725
725
|
// Get the entity object
|
|
726
|
-
const md = new Metadata();
|
|
726
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
727
727
|
const entity = await md.GetEntityObject(entityName, user);
|
|
728
728
|
|
|
729
729
|
// Create a composite key
|
|
@@ -751,7 +751,7 @@ export class RESTEndpointHandler {
|
|
|
751
751
|
const user = req['mjUser'];
|
|
752
752
|
|
|
753
753
|
// Get the entity object
|
|
754
|
-
const md = new Metadata();
|
|
754
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
755
755
|
const entity = await md.GetEntityObject(entityName, user);
|
|
756
756
|
|
|
757
757
|
// Create a composite key
|
|
@@ -14,7 +14,7 @@ export class ViewOperationsHandler {
|
|
|
14
14
|
static async runView(params: RunViewParams, user: UserInfo): Promise<{ success: boolean, result?: RunViewResult, error?: string }> {
|
|
15
15
|
try {
|
|
16
16
|
// Validate entity exists
|
|
17
|
-
const md = new Metadata();
|
|
17
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
18
18
|
const entity = md.Entities.find(e => e.Name === params.EntityName);
|
|
19
19
|
if (!entity) {
|
|
20
20
|
return {
|
|
@@ -52,7 +52,7 @@ export class ViewOperationsHandler {
|
|
|
52
52
|
static async runViews(paramsArray: RunViewParams[], user: UserInfo): Promise<{ success: boolean, results?: RunViewResult[], error?: string }> {
|
|
53
53
|
try {
|
|
54
54
|
// Validate and sanitize each set of parameters
|
|
55
|
-
const md = new Metadata();
|
|
55
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
56
56
|
for (const params of paramsArray) {
|
|
57
57
|
// Validate entity exists
|
|
58
58
|
const entity = md.Entities.find(e => e.Name === params.EntityName);
|
|
@@ -93,7 +93,7 @@ export class ViewOperationsHandler {
|
|
|
93
93
|
static async listEntities(params: RunViewParams, user: UserInfo): Promise<RunViewResult> {
|
|
94
94
|
try {
|
|
95
95
|
// Check entity exists and user has permission
|
|
96
|
-
const md = new Metadata();
|
|
96
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
97
97
|
const entity = md.Entities.find(e => e.Name === params.EntityName);
|
|
98
98
|
if (!entity) {
|
|
99
99
|
throw new Error(`Entity '${params.EntityName}' not found`);
|
|
@@ -122,7 +122,7 @@ export class ViewOperationsHandler {
|
|
|
122
122
|
static async getEntityViews(entityName: string, user: UserInfo): Promise<{ success: boolean, views?: any[], error?: string }> {
|
|
123
123
|
try {
|
|
124
124
|
// Validate entity exists
|
|
125
|
-
const md = new Metadata();
|
|
125
|
+
const md = new Metadata(); // global-provider-ok: REST endpoint — no per-request provider injection in REST middleware yet
|
|
126
126
|
const entity = md.Entities.find(e => e.Name === entityName);
|
|
127
127
|
if (!entity) {
|
|
128
128
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DatabaseProviderBase, Metadata, RunView, UserInfo, LogError, LogStatus } from '@memberjunction/core';
|
|
1
|
+
import { DatabaseProviderBase, IMetadataProvider, Metadata, RunView, UserInfo, LogError, LogStatus } from '@memberjunction/core';
|
|
2
2
|
import { MJTaskEntity, MJTaskDependencyEntity, MJTaskTypeEntity, MJConversationDetailEntity, MJArtifactEntity, MJArtifactVersionEntity, MJConversationDetailArtifactEntity, MJUserNotificationEntity } from '@memberjunction/core-entities';
|
|
3
3
|
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
4
4
|
import { ChatMessageRole } from '@memberjunction/ai';
|
|
@@ -53,9 +53,14 @@ export class TaskOrchestrator {
|
|
|
53
53
|
private sessionId?: string,
|
|
54
54
|
private userPayload?: UserPayload,
|
|
55
55
|
private createNotifications: boolean = false,
|
|
56
|
-
private conversationDetailId?: string
|
|
56
|
+
private conversationDetailId?: string,
|
|
57
|
+
private provider?: IMetadataProvider
|
|
57
58
|
) {}
|
|
58
59
|
|
|
60
|
+
private getMetadata(): IMetadataProvider {
|
|
61
|
+
return this.provider ?? (new Metadata() as unknown as IMetadataProvider);
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
/**
|
|
60
65
|
* Initialize the orchestrator by finding/creating the AI Agent Task type
|
|
61
66
|
*/
|
|
@@ -77,7 +82,7 @@ export class TaskOrchestrator {
|
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
// Create the task type if it doesn't exist
|
|
80
|
-
const md =
|
|
85
|
+
const md = this.getMetadata();
|
|
81
86
|
const taskType = await md.GetEntityObject<MJTaskTypeEntity>('MJ: Task Types', this.contextUser);
|
|
82
87
|
taskType.Name = 'AI Agent Execution';
|
|
83
88
|
taskType.Description = 'Task executed by an AI agent as part of conversation workflow';
|
|
@@ -104,7 +109,7 @@ export class TaskOrchestrator {
|
|
|
104
109
|
environmentId: string
|
|
105
110
|
): Promise<{ parentTaskId: string; taskIdMap: Map<string, string> }> {
|
|
106
111
|
const taskTypeId = await this.ensureTaskType();
|
|
107
|
-
const md =
|
|
112
|
+
const md = this.getMetadata();
|
|
108
113
|
const tempIdToRealId = new Map<string, string>();
|
|
109
114
|
|
|
110
115
|
// Build the parent task, deduplicate the incoming task defs, and resolve agents
|
|
@@ -142,7 +147,7 @@ export class TaskOrchestrator {
|
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
// Persist parent + children + dependency graph in one transaction
|
|
145
|
-
const provider = Metadata.Provider as DatabaseProviderBase;
|
|
150
|
+
const provider = (this.provider ?? Metadata.Provider) as DatabaseProviderBase;
|
|
146
151
|
await provider.BeginTransaction();
|
|
147
152
|
try {
|
|
148
153
|
if (!await parentTask.Save()) {
|
|
@@ -302,7 +307,7 @@ export class TaskOrchestrator {
|
|
|
302
307
|
let hasMore = true;
|
|
303
308
|
|
|
304
309
|
// Get parent task for progress updates
|
|
305
|
-
const md =
|
|
310
|
+
const md = this.getMetadata();
|
|
306
311
|
const parentTask = await md.GetEntityObject<MJTaskEntity>('MJ: Tasks', this.contextUser);
|
|
307
312
|
await parentTask.Load(parentTaskId);
|
|
308
313
|
|
|
@@ -381,7 +386,7 @@ export class TaskOrchestrator {
|
|
|
381
386
|
* Update parent task progress based on child task completion
|
|
382
387
|
*/
|
|
383
388
|
private async updateParentTaskProgress(parentTaskId: string): Promise<void> {
|
|
384
|
-
const md =
|
|
389
|
+
const md = this.getMetadata();
|
|
385
390
|
const parentTask = await md.GetEntityObject<MJTaskEntity>('MJ: Tasks', this.contextUser);
|
|
386
391
|
const loaded = await parentTask.Load(parentTaskId);
|
|
387
392
|
if (!loaded) return;
|
|
@@ -414,7 +419,7 @@ export class TaskOrchestrator {
|
|
|
414
419
|
* Mark parent task as complete when all children are done
|
|
415
420
|
*/
|
|
416
421
|
private async completeParentTask(parentTaskId: string): Promise<void> {
|
|
417
|
-
const md =
|
|
422
|
+
const md = this.getMetadata();
|
|
418
423
|
const parentTask = await md.GetEntityObject<MJTaskEntity>('MJ: Tasks', this.contextUser);
|
|
419
424
|
const loaded = await parentTask.Load(parentTaskId);
|
|
420
425
|
if (!loaded) return;
|
|
@@ -464,7 +469,7 @@ export class TaskOrchestrator {
|
|
|
464
469
|
* Load a task by ID
|
|
465
470
|
*/
|
|
466
471
|
private async loadTask(taskId: string): Promise<MJTaskEntity | null> {
|
|
467
|
-
const md =
|
|
472
|
+
const md = this.getMetadata();
|
|
468
473
|
const task = await md.GetEntityObject<MJTaskEntity>('MJ: Tasks', this.contextUser);
|
|
469
474
|
const loaded = await task.Load(taskId);
|
|
470
475
|
return loaded ? task : null;
|
|
@@ -483,7 +488,7 @@ export class TaskOrchestrator {
|
|
|
483
488
|
await task.Save();
|
|
484
489
|
|
|
485
490
|
// Load the agent entity
|
|
486
|
-
const md =
|
|
491
|
+
const md = this.getMetadata();
|
|
487
492
|
const agentEntity = await md.GetEntityObject<MJAIAgentEntityExtended>('MJ: AI Agents', this.contextUser);
|
|
488
493
|
const loaded = await agentEntity.Load(task.AgentID!);
|
|
489
494
|
if (!loaded) {
|
|
@@ -707,8 +712,8 @@ export class TaskOrchestrator {
|
|
|
707
712
|
agent: MJAIAgentEntityExtended,
|
|
708
713
|
taskName: string
|
|
709
714
|
): Promise<void> {
|
|
710
|
-
const md =
|
|
711
|
-
const provider = Metadata.Provider as DatabaseProviderBase;
|
|
715
|
+
const md = this.getMetadata();
|
|
716
|
+
const provider = (this.provider ?? Metadata.Provider) as DatabaseProviderBase;
|
|
712
717
|
|
|
713
718
|
await provider.BeginTransaction();
|
|
714
719
|
try {
|
|
@@ -795,7 +800,7 @@ export class TaskOrchestrator {
|
|
|
795
800
|
return;
|
|
796
801
|
}
|
|
797
802
|
|
|
798
|
-
const md =
|
|
803
|
+
const md = this.getMetadata();
|
|
799
804
|
|
|
800
805
|
// Load conversation detail to get conversation ID
|
|
801
806
|
const detail = await md.GetEntityObject<MJConversationDetailEntity>(
|