@memberjunction/server 2.75.0 → 2.77.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/README.md +61 -2
- package/dist/apolloServer/index.d.ts.map +1 -1
- package/dist/apolloServer/index.js +1 -3
- package/dist/apolloServer/index.js.map +1 -1
- package/dist/auth/exampleNewUserSubClass.d.ts +1 -1
- package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
- package/dist/auth/exampleNewUserSubClass.js +1 -1
- package/dist/auth/exampleNewUserSubClass.js.map +1 -1
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/context.d.ts +5 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +34 -3
- package/dist/context.js.map +1 -1
- package/dist/entitySubclasses/entityPermissions.server.d.ts +2 -2
- package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
- package/dist/entitySubclasses/entityPermissions.server.js +2 -2
- package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
- package/dist/generated/generated.d.ts +1787 -1628
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +5871 -4367
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +20 -21
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +75 -59
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +8 -8
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +50 -48
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts +6 -6
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +33 -21
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/resolvers/CreateQueryResolver.d.ts +12 -3
- package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/CreateQueryResolver.js +121 -65
- package/dist/resolvers/CreateQueryResolver.js.map +1 -1
- package/dist/resolvers/EntityResolver.d.ts +1 -2
- package/dist/resolvers/EntityResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityResolver.js +17 -9
- package/dist/resolvers/EntityResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +9 -9
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +3 -1
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +3 -0
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +2 -1
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +4 -4
- package/dist/resolvers/QueryResolver.js +18 -18
- package/dist/resolvers/ReportResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts +8 -8
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +19 -19
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +10 -10
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +19 -19
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.d.ts +2 -2
- package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.js +7 -4
- package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
- package/dist/resolvers/UserResolver.d.ts +3 -3
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +10 -6
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts +2 -2
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js +11 -6
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
- package/dist/scheduler/LearningCycleScheduler.js +7 -1
- package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
- package/dist/types.d.ts +7 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +8 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +28 -0
- package/dist/util.js.map +1 -1
- package/package.json +34 -34
- package/src/apolloServer/index.ts +3 -3
- package/src/auth/exampleNewUserSubClass.ts +3 -2
- package/src/auth/newUsers.ts +1 -1
- package/src/context.ts +49 -9
- package/src/entitySubclasses/entityPermissions.server.ts +3 -3
- package/src/generated/generated.ts +5506 -4368
- package/src/generic/ResolverBase.ts +103 -86
- package/src/generic/RunViewResolver.ts +55 -54
- package/src/index.ts +1 -1
- package/src/resolvers/AskSkipResolver.ts +44 -23
- package/src/resolvers/CreateQueryResolver.ts +104 -62
- package/src/resolvers/EntityResolver.ts +18 -9
- package/src/resolvers/FileCategoryResolver.ts +12 -9
- package/src/resolvers/FileResolver.ts +4 -2
- package/src/resolvers/GetDataResolver.ts +3 -0
- package/src/resolvers/MergeRecordsResolver.ts +2 -1
- package/src/resolvers/QueryResolver.ts +14 -14
- package/src/resolvers/ReportResolver.ts +1 -1
- package/src/resolvers/SyncDataResolver.ts +21 -21
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -21
- package/src/resolvers/TransactionGroupResolver.ts +1 -1
- package/src/resolvers/UserFavoriteResolver.ts +7 -5
- package/src/resolvers/UserResolver.ts +10 -6
- package/src/resolvers/UserViewResolver.ts +13 -7
- package/src/scheduler/LearningCycleScheduler.ts +10 -4
- package/src/types.ts +14 -4
- package/src/util.ts +45 -2
- package/dist/apolloServer/TransactionPlugin.d.ts +0 -4
- package/dist/apolloServer/TransactionPlugin.d.ts.map +0 -1
- package/dist/apolloServer/TransactionPlugin.js +0 -46
- package/dist/apolloServer/TransactionPlugin.js.map +0 -1
- package/src/apolloServer/TransactionPlugin.ts +0 -53
package/src/index.ts
CHANGED
|
@@ -93,7 +93,7 @@ export * from './resolvers/GetDataResolver.js';
|
|
|
93
93
|
export * from './resolvers/GetDataContextDataResolver.js';
|
|
94
94
|
export * from './resolvers/TransactionGroupResolver.js';
|
|
95
95
|
export * from './resolvers/CreateQueryResolver.js';
|
|
96
|
-
export { GetReadOnlyDataSource, GetReadWriteDataSource } from './util.js';
|
|
96
|
+
export { GetReadOnlyDataSource, GetReadWriteDataSource, GetReadWriteProvider, GetReadOnlyProvider } from './util.js';
|
|
97
97
|
|
|
98
98
|
export * from './generated/generated.js';
|
|
99
99
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, Mutation, ObjectType, PubSub, PubSubEngine, Query, Resolver } from 'type-graphql';
|
|
2
|
-
import { LogError, LogStatus, Metadata, RunView, UserInfo, CompositeKey, EntityFieldInfo, EntityInfo, EntityRelationshipInfo } from '@memberjunction/core';
|
|
2
|
+
import { LogError, LogStatus, Metadata, RunView, UserInfo, CompositeKey, EntityFieldInfo, EntityInfo, EntityRelationshipInfo, EntitySaveOptions, EntityDeleteOptions } from '@memberjunction/core';
|
|
3
3
|
import { AppContext, UserPayload, MJ_SERVER_EVENT_CODE } from '../types.js';
|
|
4
4
|
import { BehaviorSubject } from 'rxjs';
|
|
5
5
|
import { take } from 'rxjs/operators';
|
|
@@ -390,6 +390,7 @@ export class AskSkipResolver {
|
|
|
390
390
|
const ck = new CompositeKey();
|
|
391
391
|
ck.KeyValuePairs = compositeKey.KeyValuePairs;
|
|
392
392
|
dci.RecordID = ck.Values();
|
|
393
|
+
|
|
393
394
|
let dciSaveResult: boolean = await dci.Save();
|
|
394
395
|
if (!dciSaveResult) {
|
|
395
396
|
LogError(`Error saving DataContextItemEntity for record chat: ${EntityName} ${ck.Values()}`, undefined, dci.LatestResult);
|
|
@@ -415,7 +416,7 @@ export class AskSkipResolver {
|
|
|
415
416
|
conversationDetailID: convoDetailEntity.ID,
|
|
416
417
|
});
|
|
417
418
|
|
|
418
|
-
return this.handleSimpleSkipChatPostRequest(input, convoEntity, convoDetailEntity, true, user);
|
|
419
|
+
return this.handleSimpleSkipChatPostRequest(input, convoEntity, convoDetailEntity, true, user, userPayload);
|
|
419
420
|
}
|
|
420
421
|
|
|
421
422
|
/**
|
|
@@ -536,7 +537,7 @@ export class AskSkipResolver {
|
|
|
536
537
|
}
|
|
537
538
|
else {
|
|
538
539
|
// Make the API request
|
|
539
|
-
const response = await this.handleSimpleSkipLearningPostRequest(input, user, learningCycleId, agentID);
|
|
540
|
+
const response = await this.handleSimpleSkipLearningPostRequest(input, user, learningCycleId, agentID, userPayload);
|
|
540
541
|
|
|
541
542
|
// Update learning cycle to completed
|
|
542
543
|
const endTime = new Date();
|
|
@@ -594,7 +595,8 @@ export class AskSkipResolver {
|
|
|
594
595
|
input: SkipAPILearningCycleRequest,
|
|
595
596
|
user: UserInfo,
|
|
596
597
|
learningCycleId: string,
|
|
597
|
-
agentID: string
|
|
598
|
+
agentID: string,
|
|
599
|
+
userPayload: UserPayload
|
|
598
600
|
): Promise<SkipAPILearningCycleResponse> {
|
|
599
601
|
const skipConfigInfo = configInfo.askSkip;
|
|
600
602
|
LogStatus(` >>> HandleSimpleSkipLearningPostRequest Sending request to Skip API: ${skipConfigInfo.learningCycleURL}`);
|
|
@@ -608,7 +610,7 @@ export class AskSkipResolver {
|
|
|
608
610
|
|
|
609
611
|
// Process any note changes, if any
|
|
610
612
|
if (apiResponse.noteChanges && apiResponse.noteChanges.length > 0) {
|
|
611
|
-
await this.processLearningCycleNoteChanges(apiResponse.noteChanges, agentID, user);
|
|
613
|
+
await this.processLearningCycleNoteChanges(apiResponse.noteChanges, agentID, user, userPayload);
|
|
612
614
|
}
|
|
613
615
|
|
|
614
616
|
// Not yet implemented
|
|
@@ -653,7 +655,8 @@ export class AskSkipResolver {
|
|
|
653
655
|
convoEntity: ConversationEntity = null,
|
|
654
656
|
convoDetailEntity: ConversationDetailEntity = null,
|
|
655
657
|
createAIMessageConversationDetail: boolean = false,
|
|
656
|
-
user: UserInfo = null
|
|
658
|
+
user: UserInfo = null,
|
|
659
|
+
userPayload: UserPayload = null
|
|
657
660
|
): Promise<AskSkipResultType> {
|
|
658
661
|
const skipConfigInfo = configInfo.askSkip;
|
|
659
662
|
LogStatus(` >>> HandleSimpleSkipChatPostRequest Sending request to Skip API: ${skipConfigInfo.chatURL}`);
|
|
@@ -665,7 +668,7 @@ export class AskSkipResolver {
|
|
|
665
668
|
// the last object in the response array is the final response from the Skip API
|
|
666
669
|
const apiResponse = <SkipAPIResponse>response[response.length - 1].value;
|
|
667
670
|
const AIMessageConversationDetailID = createAIMessageConversationDetail && convoEntity
|
|
668
|
-
? await this.CreateAIMessageConversationDetail(apiResponse, convoEntity.ID, user)
|
|
671
|
+
? await this.CreateAIMessageConversationDetail(apiResponse, convoEntity.ID, user, userPayload)
|
|
669
672
|
: '';
|
|
670
673
|
// const apiResponse = <SkipAPIResponse>response.data;
|
|
671
674
|
LogStatus(` Skip API response: ${apiResponse.responsePhase}`);
|
|
@@ -681,7 +684,7 @@ export class AskSkipResolver {
|
|
|
681
684
|
} else {
|
|
682
685
|
// Set conversation status to Available on failure so user can try again (if conversation exists)
|
|
683
686
|
if (convoEntity) {
|
|
684
|
-
await this.setConversationStatus(convoEntity, 'Available');
|
|
687
|
+
await this.setConversationStatus(convoEntity, 'Available', userPayload);
|
|
685
688
|
}
|
|
686
689
|
|
|
687
690
|
return {
|
|
@@ -697,7 +700,7 @@ export class AskSkipResolver {
|
|
|
697
700
|
} catch (error) {
|
|
698
701
|
// Set conversation status to Available on error so user can try again (if conversation exists)
|
|
699
702
|
if (convoEntity) {
|
|
700
|
-
await this.setConversationStatus(convoEntity, 'Available');
|
|
703
|
+
await this.setConversationStatus(convoEntity, 'Available', userPayload);
|
|
701
704
|
}
|
|
702
705
|
|
|
703
706
|
// Log the error for debugging
|
|
@@ -720,7 +723,8 @@ export class AskSkipResolver {
|
|
|
720
723
|
protected async processLearningCycleNoteChanges(
|
|
721
724
|
noteChanges: SkipLearningCycleNoteChange[],
|
|
722
725
|
agentID: string,
|
|
723
|
-
user: UserInfo
|
|
726
|
+
user: UserInfo,
|
|
727
|
+
userPayload: UserPayload
|
|
724
728
|
): Promise<void> {
|
|
725
729
|
const md = new Metadata();
|
|
726
730
|
|
|
@@ -739,9 +743,9 @@ export class AskSkipResolver {
|
|
|
739
743
|
await Promise.all(validNoteChanges.map(async (change) => {
|
|
740
744
|
try {
|
|
741
745
|
if (change.changeType === 'add' || change.changeType === 'update') {
|
|
742
|
-
await this.processAddOrUpdateSkipNote(change, agentID, user);
|
|
746
|
+
await this.processAddOrUpdateSkipNote(change, agentID, user, userPayload);
|
|
743
747
|
} else if (change.changeType === 'delete') {
|
|
744
|
-
await this.processDeleteSkipNote(change, user);
|
|
748
|
+
await this.processDeleteSkipNote(change, user, userPayload);
|
|
745
749
|
}
|
|
746
750
|
} catch (e) {
|
|
747
751
|
LogError(`Error processing note change: ${e}`);
|
|
@@ -758,7 +762,7 @@ export class AskSkipResolver {
|
|
|
758
762
|
* @param user User context for the operation
|
|
759
763
|
* @returns Whether the operation was successful
|
|
760
764
|
*/
|
|
761
|
-
protected async processAddOrUpdateSkipNote(change: SkipLearningCycleNoteChange, agentID: string, user: UserInfo): Promise<boolean> {
|
|
765
|
+
protected async processAddOrUpdateSkipNote(change: SkipLearningCycleNoteChange, agentID: string, user: UserInfo, userPayload: UserPayload): Promise<boolean> {
|
|
762
766
|
try {
|
|
763
767
|
// Get the note entity object
|
|
764
768
|
const md = new Metadata();
|
|
@@ -805,7 +809,7 @@ export class AskSkipResolver {
|
|
|
805
809
|
* @param user User context for the operation
|
|
806
810
|
* @returns Whether the deletion was successful
|
|
807
811
|
*/
|
|
808
|
-
protected async processDeleteSkipNote(change: SkipLearningCycleNoteChange, user: UserInfo): Promise<boolean> {
|
|
812
|
+
protected async processDeleteSkipNote(change: SkipLearningCycleNoteChange, user: UserInfo, userPayload: UserPayload): Promise<boolean> {
|
|
809
813
|
// Get the note entity object
|
|
810
814
|
const md = new Metadata();
|
|
811
815
|
const noteEntity = await md.GetEntityObject<AIAgentNoteEntity>('AI Agent Notes', user);
|
|
@@ -843,7 +847,7 @@ cycle.`);
|
|
|
843
847
|
* @param user User context for the operation
|
|
844
848
|
* @returns ID of the created conversation detail, or empty string if creation failed
|
|
845
849
|
*/
|
|
846
|
-
protected async CreateAIMessageConversationDetail(apiResponse: SkipAPIResponse, conversationID: string, user: UserInfo): Promise<string> {
|
|
850
|
+
protected async CreateAIMessageConversationDetail(apiResponse: SkipAPIResponse, conversationID: string, user: UserInfo, userPayload: UserPayload): Promise<string> {
|
|
847
851
|
const md = new Metadata();
|
|
848
852
|
const convoDetailEntityAI = <ConversationDetailEntity>await md.GetEntityObject('Conversation Details', user);
|
|
849
853
|
convoDetailEntityAI.NewRecord();
|
|
@@ -853,6 +857,7 @@ cycle.`);
|
|
|
853
857
|
const lastSystemMessage = systemMessages[systemMessages.length - 1];
|
|
854
858
|
convoDetailEntityAI.Message = lastSystemMessage?.content;
|
|
855
859
|
convoDetailEntityAI.Role = 'AI';
|
|
860
|
+
|
|
856
861
|
if (await convoDetailEntityAI.Save()) {
|
|
857
862
|
return convoDetailEntityAI.ID;
|
|
858
863
|
} else {
|
|
@@ -1287,7 +1292,7 @@ cycle.`);
|
|
|
1287
1292
|
await dataContext.Load(DataContextId, dataSource, true, false, 0, user);
|
|
1288
1293
|
const input = <SkipAPIRunScriptRequest>await this.buildSkipChatAPIRequest([], '', dataContext, 'run_existing_script', false, false, false, false, user, dataSource, false, false);
|
|
1289
1294
|
input.scriptText = ScriptText;
|
|
1290
|
-
return this.handleSimpleSkipChatPostRequest(input);
|
|
1295
|
+
return this.handleSimpleSkipChatPostRequest(input, undefined, undefined, undefined, userPayload.userRecord, userPayload);
|
|
1291
1296
|
}
|
|
1292
1297
|
|
|
1293
1298
|
/**
|
|
@@ -1366,7 +1371,7 @@ cycle.`);
|
|
|
1366
1371
|
);
|
|
1367
1372
|
|
|
1368
1373
|
// Set the conversation status to 'Processing' when a request is initiated
|
|
1369
|
-
await this.setConversationStatus(convoEntity, 'Processing');
|
|
1374
|
+
await this.setConversationStatus(convoEntity, 'Processing', userPayload);
|
|
1370
1375
|
|
|
1371
1376
|
// now load up the messages. We will load up ALL of the messages for this conversation, and then pass them to the Skip API
|
|
1372
1377
|
const messages: SkipMessage[] = await this.LoadConversationDetailsIntoSkipMessages(
|
|
@@ -1455,7 +1460,19 @@ cycle.`);
|
|
|
1455
1460
|
updatedAt: f.__mj_UpdatedAt,
|
|
1456
1461
|
};
|
|
1457
1462
|
}),
|
|
1458
|
-
|
|
1463
|
+
params: q.Parameters.map((p) => {
|
|
1464
|
+
return {
|
|
1465
|
+
id: p.ID,
|
|
1466
|
+
name: p.Name,
|
|
1467
|
+
description: p.Description,
|
|
1468
|
+
type: p.Type,
|
|
1469
|
+
isRequired: p.IsRequired,
|
|
1470
|
+
defaultValue: p.DefaultValue,
|
|
1471
|
+
createdAt: p.__mj_CreatedAt,
|
|
1472
|
+
updatedAt: p.__mj_UpdatedAt,
|
|
1473
|
+
};
|
|
1474
|
+
})
|
|
1475
|
+
}
|
|
1459
1476
|
});
|
|
1460
1477
|
}
|
|
1461
1478
|
|
|
@@ -1986,6 +2003,7 @@ cycle.`);
|
|
|
1986
2003
|
convoDetailEntity.Message = UserQuestion;
|
|
1987
2004
|
convoDetailEntity.Role = 'User';
|
|
1988
2005
|
convoDetailEntity.HiddenToUser = false;
|
|
2006
|
+
|
|
1989
2007
|
let convoDetailSaveResult: boolean = await convoDetailEntity.Save();
|
|
1990
2008
|
if (!convoDetailSaveResult) {
|
|
1991
2009
|
LogError(`Error saving conversation detail entity for user message: ${UserQuestion}`, undefined, convoDetailEntity.LatestResult);
|
|
@@ -2162,7 +2180,7 @@ cycle.`);
|
|
|
2162
2180
|
|
|
2163
2181
|
if (conversationDetailCount > 10) {
|
|
2164
2182
|
// Set status of conversation to Available since we still want to allow the user to ask questions
|
|
2165
|
-
await this.setConversationStatus(convoEntity, 'Available');
|
|
2183
|
+
await this.setConversationStatus(convoEntity, 'Available', userPayload);
|
|
2166
2184
|
|
|
2167
2185
|
// At this point it is likely that we are stuck in a loop, so we stop here
|
|
2168
2186
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
@@ -2222,7 +2240,7 @@ cycle.`);
|
|
|
2222
2240
|
);
|
|
2223
2241
|
} catch (error) {
|
|
2224
2242
|
// Set conversation status to Available on error so user can try again
|
|
2225
|
-
await this.setConversationStatus(convoEntity, 'Available');
|
|
2243
|
+
await this.setConversationStatus(convoEntity, 'Available', userPayload);
|
|
2226
2244
|
|
|
2227
2245
|
// Log the error for debugging
|
|
2228
2246
|
LogError(`Error in HandleSkipChatRequest sendPostRequest: ${error}`);
|
|
@@ -2305,7 +2323,7 @@ cycle.`);
|
|
|
2305
2323
|
}
|
|
2306
2324
|
} else {
|
|
2307
2325
|
// Set status of conversation to Available since we still want to allow the user to ask questions
|
|
2308
|
-
await this.setConversationStatus(convoEntity, 'Available');
|
|
2326
|
+
await this.setConversationStatus(convoEntity, 'Available', userPayload);
|
|
2309
2327
|
|
|
2310
2328
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
2311
2329
|
message: JSON.stringify({
|
|
@@ -2477,7 +2495,7 @@ cycle.`);
|
|
|
2477
2495
|
convoDetailEntityAI.CompletionTime = endTime.getTime() - startTime.getTime();
|
|
2478
2496
|
|
|
2479
2497
|
// Set conversation status back to Available since we need user input for the clarifying question
|
|
2480
|
-
await this.setConversationStatus(convoEntity, 'Available');
|
|
2498
|
+
await this.setConversationStatus(convoEntity, 'Available', userPayload);
|
|
2481
2499
|
|
|
2482
2500
|
if (await convoDetailEntityAI.Save()) {
|
|
2483
2501
|
return {
|
|
@@ -2765,6 +2783,7 @@ cycle.`);
|
|
|
2765
2783
|
artifactVersionEntity.ConversationArtifactID = artifactId;
|
|
2766
2784
|
artifactVersionEntity.Version = newVersion;
|
|
2767
2785
|
artifactVersionEntity.Configuration = sResult; // store the full response here
|
|
2786
|
+
|
|
2768
2787
|
if (await artifactVersionEntity.Save()) {
|
|
2769
2788
|
// success saving the new version, set the artifactVersionId
|
|
2770
2789
|
artifactVersionId = artifactVersionEntity.ID;
|
|
@@ -2792,6 +2811,7 @@ cycle.`);
|
|
|
2792
2811
|
convoDetailEntityAI.ArtifactVersionID = artifactVersionId;
|
|
2793
2812
|
}
|
|
2794
2813
|
}
|
|
2814
|
+
|
|
2795
2815
|
const convoDetailSaveResult: boolean = await convoDetailEntityAI.Save();
|
|
2796
2816
|
if (!convoDetailSaveResult) {
|
|
2797
2817
|
LogError(`Error saving conversation detail entity for AI message: ${sResult}`, undefined, convoDetailEntityAI.LatestResult);
|
|
@@ -2872,9 +2892,10 @@ cycle.`);
|
|
|
2872
2892
|
};
|
|
2873
2893
|
}
|
|
2874
2894
|
|
|
2875
|
-
private async setConversationStatus(convoEntity: ConversationEntity, status: 'Processing' | 'Available'): Promise<boolean> {
|
|
2895
|
+
private async setConversationStatus(convoEntity: ConversationEntity, status: 'Processing' | 'Available', userPayload: UserPayload): Promise<boolean> {
|
|
2876
2896
|
if (convoEntity.Status !== status) {
|
|
2877
2897
|
convoEntity.Status = status;
|
|
2898
|
+
|
|
2878
2899
|
const convoSaveResult = await convoEntity.Save();
|
|
2879
2900
|
if (!convoSaveResult) {
|
|
2880
2901
|
LogError(`Error updating conversation status to '${status}'`, undefined, convoEntity.LatestResult);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType } from 'type-graphql';
|
|
2
|
-
import { AppContext } from '../types.js';
|
|
3
|
-
import { LogError, Metadata, RunView, UserInfo } from '@memberjunction/core';
|
|
1
|
+
import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType, Resolver, PubSub, PubSubEngine } from 'type-graphql';
|
|
2
|
+
import { AppContext, UserPayload } from '../types.js';
|
|
3
|
+
import { LogError, Metadata, RunView, UserInfo, CompositeKey, EntitySaveOptions } from '@memberjunction/core';
|
|
4
4
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
5
|
-
import {
|
|
5
|
+
import { QueryCategoryEntity } from '@memberjunction/core-entities';
|
|
6
|
+
import { QueryResolver } from '../generated/generated.js';
|
|
7
|
+
import { GetReadWriteProvider } from '../util.js';
|
|
8
|
+
import { DeleteOptionsInput } from '../generic/DeleteOptionsInput.js';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Query status enumeration for GraphQL
|
|
@@ -20,7 +23,7 @@ registerEnumType(QueryStatus, {
|
|
|
20
23
|
});
|
|
21
24
|
|
|
22
25
|
@InputType()
|
|
23
|
-
export class
|
|
26
|
+
export class CreateQuerySystemUserInput {
|
|
24
27
|
@Field(() => String)
|
|
25
28
|
Name!: string;
|
|
26
29
|
|
|
@@ -73,88 +76,127 @@ export class CreateQueryResultType {
|
|
|
73
76
|
QueryData?: string;
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
@ObjectType()
|
|
80
|
+
export class DeleteQueryResultType {
|
|
81
|
+
@Field(() => Boolean)
|
|
82
|
+
Success!: boolean;
|
|
83
|
+
|
|
84
|
+
@Field(() => String, { nullable: true })
|
|
85
|
+
ErrorMessage?: string;
|
|
86
|
+
|
|
87
|
+
@Field(() => String, { nullable: true })
|
|
88
|
+
QueryData?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Resolver()
|
|
92
|
+
export class QueryResolverExtended extends QueryResolver {
|
|
77
93
|
/**
|
|
78
94
|
* Creates a new query with the provided attributes. This mutation is restricted to system users only.
|
|
79
|
-
* @param input -
|
|
95
|
+
* @param input - CreateQuerySystemUserInput containing all the query attributes
|
|
80
96
|
* @param context - Application context containing user information
|
|
81
97
|
* @returns CreateQueryResultType with success status and query data
|
|
82
98
|
*/
|
|
83
99
|
@RequireSystemUser()
|
|
84
100
|
@Mutation(() => CreateQueryResultType)
|
|
85
|
-
async
|
|
86
|
-
@Arg('input', () =>
|
|
87
|
-
@Ctx() context: AppContext
|
|
101
|
+
async CreateQuerySystemUser(
|
|
102
|
+
@Arg('input', () => CreateQuerySystemUserInput) input: CreateQuerySystemUserInput,
|
|
103
|
+
@Ctx() context: AppContext,
|
|
104
|
+
@PubSub() pubSub: PubSubEngine
|
|
88
105
|
): Promise<CreateQueryResultType> {
|
|
89
106
|
try {
|
|
90
|
-
const md = new Metadata();
|
|
91
|
-
const newQuery = await md.GetEntityObject<QueryEntity>("Queries", context.userPayload.userRecord);
|
|
92
|
-
|
|
93
107
|
// Handle CategoryPath if provided
|
|
94
108
|
let finalCategoryID = input.CategoryID;
|
|
95
109
|
if (input.CategoryPath) {
|
|
96
|
-
|
|
110
|
+
const md = new Metadata();
|
|
111
|
+
finalCategoryID = await this.findOrCreateCategoryPath(input.CategoryPath, md, context.userPayload.userRecord, context.userPayload);
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (input.SQL != null) {
|
|
115
|
-
newQuery.SQL = input.SQL;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (input.TechnicalDescription != null) {
|
|
119
|
-
newQuery.TechnicalDescription = input.TechnicalDescription;
|
|
120
|
-
}
|
|
114
|
+
// Create input for the inherited CreateRecord method
|
|
115
|
+
const createInput = {
|
|
116
|
+
Name: input.Name,
|
|
117
|
+
CategoryID: finalCategoryID,
|
|
118
|
+
UserQuestion: input.UserQuestion,
|
|
119
|
+
Description: input.Description,
|
|
120
|
+
SQL: input.SQL,
|
|
121
|
+
TechnicalDescription: input.TechnicalDescription,
|
|
122
|
+
OriginalSQL: input.OriginalSQL,
|
|
123
|
+
Feedback: input.Feedback,
|
|
124
|
+
Status: input.Status || 'Approved',
|
|
125
|
+
QualityRank: input.QualityRank || 0,
|
|
126
|
+
ExecutionCostRank: input.ExecutionCostRank,
|
|
127
|
+
UsesTemplate: input.UsesTemplate || false
|
|
128
|
+
};
|
|
121
129
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
// Use inherited CreateRecord method which bypasses AI processing
|
|
131
|
+
const provider = GetReadWriteProvider(context.providers);
|
|
132
|
+
const createdQuery = await this.CreateRecord('Queries', createInput, provider, context.userPayload, pubSub);
|
|
125
133
|
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
if (createdQuery) {
|
|
135
|
+
return {
|
|
136
|
+
Success: true,
|
|
137
|
+
QueryData: JSON.stringify(createdQuery)
|
|
138
|
+
};
|
|
139
|
+
} else {
|
|
140
|
+
return {
|
|
141
|
+
Success: false,
|
|
142
|
+
ErrorMessage: 'Failed to create query using CreateRecord method'
|
|
143
|
+
};
|
|
132
144
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
|
|
146
|
+
} catch (err) {
|
|
147
|
+
LogError(err);
|
|
148
|
+
return {
|
|
149
|
+
Success: false,
|
|
150
|
+
ErrorMessage: `QueryResolverExtended::CreateQuerySystemUser --- Error creating query: ${err instanceof Error ? err.message : String(err)}`
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Deletes a query by ID. This mutation is restricted to system users only.
|
|
157
|
+
* @param ID - The ID of the query to delete
|
|
158
|
+
* @param options - Delete options controlling action execution
|
|
159
|
+
* @param context - Application context containing user information
|
|
160
|
+
* @returns DeleteQueryResultType with success status and deleted query data
|
|
161
|
+
*/
|
|
162
|
+
@RequireSystemUser()
|
|
163
|
+
@Mutation(() => DeleteQueryResultType)
|
|
164
|
+
async DeleteQuerySystemResolver(
|
|
165
|
+
@Arg('ID', () => String) ID: string,
|
|
166
|
+
@Arg('options', () => DeleteOptionsInput, { nullable: true }) options: DeleteOptionsInput | null,
|
|
167
|
+
@Ctx() context: AppContext,
|
|
168
|
+
@PubSub() pubSub: PubSubEngine
|
|
169
|
+
): Promise<DeleteQueryResultType> {
|
|
170
|
+
try {
|
|
171
|
+
// Validate ID is not null/undefined/empty
|
|
172
|
+
if (!ID || ID.trim() === '') {
|
|
173
|
+
return {
|
|
174
|
+
Success: false,
|
|
175
|
+
ErrorMessage: 'QueryResolverExtended::DeleteQuerySystemResolver --- Invalid query ID: ID cannot be null or empty'
|
|
176
|
+
};
|
|
136
177
|
}
|
|
178
|
+
|
|
179
|
+
const provider = GetReadWriteProvider(context.providers);
|
|
180
|
+
const key = new CompositeKey([{FieldName: 'ID', Value: ID}]);
|
|
137
181
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
182
|
+
// Provide default options if none provided
|
|
183
|
+
const deleteOptions = options || {
|
|
184
|
+
SkipEntityAIActions: false,
|
|
185
|
+
SkipEntityActions: false
|
|
186
|
+
};
|
|
141
187
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Save the query
|
|
147
|
-
const saveResult = await newQuery.Save();
|
|
188
|
+
// Use inherited DeleteRecord method from ResolverBase
|
|
189
|
+
const deletedQuery = await this.DeleteRecord('Queries', key, deleteOptions, provider, context.userPayload, pubSub);
|
|
148
190
|
|
|
149
|
-
if (
|
|
191
|
+
if (deletedQuery) {
|
|
150
192
|
return {
|
|
151
193
|
Success: true,
|
|
152
|
-
QueryData: JSON.stringify(
|
|
194
|
+
QueryData: JSON.stringify(deletedQuery)
|
|
153
195
|
};
|
|
154
196
|
} else {
|
|
155
197
|
return {
|
|
156
198
|
Success: false,
|
|
157
|
-
ErrorMessage:
|
|
199
|
+
ErrorMessage: 'Failed to delete query using DeleteRecord method'
|
|
158
200
|
};
|
|
159
201
|
}
|
|
160
202
|
|
|
@@ -162,7 +204,7 @@ export class CreateQueryResolver {
|
|
|
162
204
|
LogError(err);
|
|
163
205
|
return {
|
|
164
206
|
Success: false,
|
|
165
|
-
ErrorMessage: `
|
|
207
|
+
ErrorMessage: `QueryResolverExtended::DeleteQuerySystemResolver --- Error deleting query: ${err instanceof Error ? err.message : String(err)}`
|
|
166
208
|
};
|
|
167
209
|
}
|
|
168
210
|
}
|
|
@@ -175,7 +217,7 @@ export class CreateQueryResolver {
|
|
|
175
217
|
* @param contextUser - User context for operations
|
|
176
218
|
* @returns The ID of the final category in the path
|
|
177
219
|
*/
|
|
178
|
-
private async findOrCreateCategoryPath(categoryPath: string, md: Metadata, contextUser: UserInfo): Promise<string> {
|
|
220
|
+
private async findOrCreateCategoryPath(categoryPath: string, md: Metadata, contextUser: UserInfo, userPayload: UserPayload): Promise<string> {
|
|
179
221
|
if (!categoryPath || categoryPath.trim() === '') {
|
|
180
222
|
throw new Error('CategoryPath cannot be empty');
|
|
181
223
|
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import { EntityPermissionType } from '@memberjunction/core';
|
|
1
|
+
import { EntityPermissionType, IRunViewProvider } from '@memberjunction/core';
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
3
|
import { Arg, Ctx, Query, Resolver, InputType, Field } from 'type-graphql';
|
|
4
4
|
import { Entity_, EntityResolverBase } from '../generated/generated.js';
|
|
5
5
|
import sql from 'mssql';
|
|
6
|
+
import { GetReadOnlyProvider } from '../util.js';
|
|
6
7
|
|
|
7
8
|
@Resolver(Entity_)
|
|
8
9
|
export class EntityResolver extends EntityResolverBase {
|
|
9
10
|
@Query(() => [Entity_])
|
|
10
11
|
async EntitiesBySchemas(
|
|
11
|
-
@Ctx() {
|
|
12
|
+
@Ctx() { providers, userPayload }: AppContext,
|
|
12
13
|
@Arg('IncludeSchemas', () => [String], { nullable: true }) IncludeSchemas?: string[],
|
|
13
14
|
@Arg('ExcludeSchemas', () => [String], { nullable: true }) ExcludeSchemas?: string[]
|
|
14
15
|
) {
|
|
15
16
|
this.CheckUserReadPermissions('Entities', userPayload);
|
|
16
|
-
const
|
|
17
|
+
const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
18
|
+
const rlsWhere = this.getRowLevelSecurityWhereClause(provider, 'Entities', userPayload, EntityPermissionType.Read, ' WHERE');
|
|
17
19
|
const includeSchemaSQL =
|
|
18
20
|
IncludeSchemas && IncludeSchemas.length > 0 ? `SchemaName IN (${IncludeSchemas.map((s) => `'${s}'`).join(',')})` : '';
|
|
19
21
|
const excludeSchemaSQL =
|
|
@@ -25,14 +27,21 @@ export class EntityResolver extends EntityResolverBase {
|
|
|
25
27
|
else schemaSQL = excludeSchemaSQL;
|
|
26
28
|
}
|
|
27
29
|
let totalWhere = '';
|
|
28
|
-
if (schemaSQL) totalWhere =
|
|
30
|
+
if (schemaSQL) totalWhere = `${schemaSQL}`;
|
|
29
31
|
if (rlsWhere) {
|
|
30
32
|
if (totalWhere) totalWhere = `${totalWhere} AND ${rlsWhere}`;
|
|
31
|
-
else totalWhere = `
|
|
33
|
+
else totalWhere = ` ${rlsWhere}`;
|
|
34
|
+
}
|
|
35
|
+
const rv = provider as any as IRunViewProvider;
|
|
36
|
+
const result = await rv.RunView({
|
|
37
|
+
EntityName: 'Entities',
|
|
38
|
+
ExtraFilter: totalWhere,
|
|
39
|
+
}, userPayload.userRecord);
|
|
40
|
+
if (result && result.Success) {
|
|
41
|
+
return result.Results;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new Error(`Failed to fetch entities: ${result?.ErrorMessage || 'Unknown error'}`);
|
|
32
45
|
}
|
|
33
|
-
const sSQL = `SELECT * FROM [${this.MJCoreSchema}].vwEntities${totalWhere}`;
|
|
34
|
-
const request = new sql.Request(dataSource);
|
|
35
|
-
const result = await request.query(sSQL);
|
|
36
|
-
return result.recordset;
|
|
37
46
|
}
|
|
38
47
|
}
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import { CompositeKey, EntityPermissionType, Metadata, RunView } from '@memberjunction/core';
|
|
1
|
+
import { CompositeKey, EntityDeleteOptions, EntityPermissionType, EntitySaveOptions, Metadata, RunView } from '@memberjunction/core';
|
|
2
2
|
import { FileCategoryEntity, FileEntity } from '@memberjunction/core-entities';
|
|
3
3
|
import { AppContext, Arg, Ctx, DeleteOptionsInput, Int, Mutation } from '@memberjunction/server';
|
|
4
4
|
import { mj_core_schema } from '../config.js';
|
|
5
5
|
import { FileCategoryResolver as FileCategoryResolverBase, FileCategory_ } from '../generated/generated.js';
|
|
6
6
|
import sql from 'mssql';
|
|
7
|
+
import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
|
|
8
|
+
import { GetReadWriteProvider } from '../util.js';
|
|
7
9
|
|
|
8
10
|
export class FileResolver extends FileCategoryResolverBase {
|
|
9
11
|
@Mutation(() => FileCategory_)
|
|
10
12
|
async DeleteFileCategory(
|
|
11
13
|
@Arg('ID', () => String) ID: string,
|
|
12
14
|
@Arg('options___', () => DeleteOptionsInput) options: DeleteOptionsInput,
|
|
13
|
-
@Ctx() {
|
|
15
|
+
@Ctx() { providers, userPayload }: AppContext
|
|
14
16
|
) {
|
|
15
17
|
const key = new CompositeKey();
|
|
16
18
|
key.LoadFromSingleKeyValuePair('ID', ID);
|
|
17
|
-
|
|
19
|
+
const provider = GetReadWriteProvider(providers);
|
|
20
|
+
|
|
21
|
+
if (!(await this.BeforeDelete(provider, key))) {
|
|
18
22
|
return null;
|
|
19
23
|
}
|
|
20
24
|
|
|
@@ -30,8 +34,7 @@ export class FileResolver extends FileCategoryResolverBase {
|
|
|
30
34
|
const returnValue = fileCategoryEntity.GetAll();
|
|
31
35
|
|
|
32
36
|
// Any files using the deleted category fall back to its parent
|
|
33
|
-
|
|
34
|
-
await transaction.begin();
|
|
37
|
+
await provider.BeginTransaction();
|
|
35
38
|
try {
|
|
36
39
|
// SHOULD USE BaseEntity for each of these records to ensure object model
|
|
37
40
|
// is used everywhere - new code below. The below is SLOWER than a single
|
|
@@ -63,14 +66,14 @@ export class FileResolver extends FileCategoryResolverBase {
|
|
|
63
66
|
await fileEntity.Save();
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
|
-
await fileCategoryEntity.Delete();
|
|
67
|
-
await
|
|
69
|
+
await fileCategoryEntity.Delete(options);
|
|
70
|
+
await provider.CommitTransaction();
|
|
68
71
|
} catch (error) {
|
|
69
|
-
await
|
|
72
|
+
await provider.RollbackTransaction();
|
|
70
73
|
throw error;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
await this.AfterDelete(
|
|
76
|
+
await this.AfterDelete(provider, key); // fire event
|
|
74
77
|
return returnValue;
|
|
75
78
|
}
|
|
76
79
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityPermissionType, Metadata, FieldValueCollection } from '@memberjunction/core';
|
|
1
|
+
import { EntityPermissionType, Metadata, FieldValueCollection, EntitySaveOptions } from '@memberjunction/core';
|
|
2
2
|
import { FileEntity, FileStorageProviderEntity } from '@memberjunction/core-entities';
|
|
3
3
|
import {
|
|
4
4
|
AppContext,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import { createDownloadUrl, createUploadUrl, deleteObject, moveObject } from '@memberjunction/storage';
|
|
20
20
|
import { CreateFileInput, FileResolver as FileResolverBase, File_, UpdateFileInput } from '../generated/generated.js';
|
|
21
21
|
import { FieldMapper } from '@memberjunction/graphql-dataprovider';
|
|
22
|
+
import { GetReadOnlyProvider } from '../util.js';
|
|
22
23
|
|
|
23
24
|
@InputType()
|
|
24
25
|
export class CreateUploadURLInput {
|
|
@@ -57,8 +58,9 @@ export class FileResolver extends FileResolverBase {
|
|
|
57
58
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
58
59
|
|
|
59
60
|
// Check to see if there's already an object with that name
|
|
61
|
+
const provider = GetReadOnlyProvider(context.providers, {allowFallbackToReadWrite: true})
|
|
60
62
|
const [sameName] = await this.findBy(
|
|
61
|
-
|
|
63
|
+
provider,
|
|
62
64
|
'Files',
|
|
63
65
|
{ Name: input.Name, ProviderID: input.ProviderID },
|
|
64
66
|
context.userPayload.userRecord
|
|
@@ -264,6 +264,9 @@ export function isTokenValid(token: string) {
|
|
|
264
264
|
export function recordTokenUse(token: string, usePayload: any) {
|
|
265
265
|
const t = __accessTokens.find((t) => t.Token === token);
|
|
266
266
|
if (t) {
|
|
267
|
+
if (!t.TokenUses) {
|
|
268
|
+
t.TokenUses = [];
|
|
269
|
+
}
|
|
267
270
|
t.TokenUses.push({ Token: token, UsedAt: new Date(), UsePayload: usePayload });
|
|
268
271
|
}
|
|
269
272
|
else {
|
|
@@ -163,7 +163,8 @@ export class RecordMergeResolver {
|
|
|
163
163
|
) {
|
|
164
164
|
try {
|
|
165
165
|
const md = new Metadata();
|
|
166
|
-
const
|
|
166
|
+
const options = {};
|
|
167
|
+
const result = await md.MergeRecords(request, userPayload.userRecord, options);
|
|
167
168
|
return result;
|
|
168
169
|
} catch (e) {
|
|
169
170
|
LogError(e);
|