@memberjunction/server 0.9.164 → 0.9.166
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/build.log.json +6 -0
- package/dist/generated/generated.js +138 -41
- package/dist/generated/generated.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +145 -43
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/package.json +6 -6
- package/src/generated/generated.ts +99 -37
- package/src/resolvers/AskSkipResolver.ts +178 -47
|
@@ -10,7 +10,7 @@ import { promisify } from 'util';
|
|
|
10
10
|
const gzip = promisify(zlib.gzip);
|
|
11
11
|
|
|
12
12
|
import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver';
|
|
13
|
-
import { ConversationDetailEntity, ConversationEntity, UserNotificationEntity, UserViewEntityExtended } from '@memberjunction/core-entities';
|
|
13
|
+
import { ConversationDetailEntity, ConversationEntity, DataContextEntity, DataContextItemEntity, UserNotificationEntity, UserViewEntity, UserViewEntityExtended } from '@memberjunction/core-entities';
|
|
14
14
|
import { DataSource } from 'typeorm';
|
|
15
15
|
import { ___skipAPIOrgId, ___skipAPIurl } from '../config';
|
|
16
16
|
|
|
@@ -64,24 +64,83 @@ export class AskSkipResolver {
|
|
|
64
64
|
@Query(() => AskSkipResultType)
|
|
65
65
|
async ExecuteAskSkipAnalysisQuery(
|
|
66
66
|
@Arg('UserQuestion', () => String) UserQuestion: string,
|
|
67
|
-
@Arg('ViewId', () => Int) ViewId: number,
|
|
68
67
|
@Arg('ConversationId', () => Int) ConversationId: number,
|
|
69
68
|
@Ctx() { dataSource, userPayload }: AppContext,
|
|
70
|
-
@PubSub() pubSub: PubSubEngine
|
|
69
|
+
@PubSub() pubSub: PubSubEngine,
|
|
70
|
+
@Arg('DataContextId', () => Int, { nullable: true }) DataContextId?: number
|
|
71
71
|
) {
|
|
72
72
|
const md = new Metadata();
|
|
73
73
|
const user = UserCache.Instance.Users.find((u) => u.Email === userPayload.email);
|
|
74
74
|
if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
|
|
75
75
|
|
|
76
|
+
const {convoEntity, dataContextEntity, convoDetailEntity, dataContext} = await this.HandleSkipInitialObjectLoading(dataSource, ConversationId, UserQuestion, user, userPayload, md, DataContextId);
|
|
77
|
+
|
|
78
|
+
const OrganizationId = ___skipAPIOrgId;
|
|
79
|
+
|
|
80
|
+
// now load up the messages. We will load up ALL of the messages for this conversation, and then pass them to the Skip API
|
|
81
|
+
const messages: SkipMessage[] = await this.LoadConversationDetailsIntoSkipMessages(dataSource, convoEntity.ID, AskSkipResolver._maxHistoricalMessages);
|
|
82
|
+
|
|
83
|
+
const input: SkipAPIRequest = {
|
|
84
|
+
messages: messages,
|
|
85
|
+
conversationID: ConversationId.toString(),
|
|
86
|
+
dataContext: dataContext,
|
|
87
|
+
organizationID: !isNaN(parseInt(OrganizationId)) ? parseInt(OrganizationId) : 0,
|
|
88
|
+
requestPhase: 'initial_request'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
92
|
+
message: JSON.stringify({
|
|
93
|
+
type: 'AskSkip',
|
|
94
|
+
status: 'OK',
|
|
95
|
+
message: 'I will be happy to help and will start by analyzing your request...',
|
|
96
|
+
}),
|
|
97
|
+
sessionId: userPayload.sessionId,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return this.HandleSkipRequest(input, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
protected async HandleSkipInitialObjectLoading(dataSource: DataSource,
|
|
105
|
+
ConversationId: number,
|
|
106
|
+
UserQuestion: string,
|
|
107
|
+
user: UserInfo,
|
|
108
|
+
userPayload: UserPayload,
|
|
109
|
+
md: Metadata,
|
|
110
|
+
DataContextId: number): Promise<{convoEntity: ConversationEntity,
|
|
111
|
+
dataContextEntity: DataContextEntity,
|
|
112
|
+
convoDetailEntity: ConversationDetailEntity,
|
|
113
|
+
dataContext: SkipDataContext}> {
|
|
76
114
|
const convoEntity = <ConversationEntity>await md.GetEntityObject('Conversations', user);
|
|
115
|
+
let dataContextEntity: DataContextEntity;
|
|
116
|
+
|
|
77
117
|
if (!ConversationId || ConversationId <= 0) {
|
|
78
118
|
// create a new conversation id
|
|
79
119
|
convoEntity.NewRecord();
|
|
80
120
|
if (user) {
|
|
81
121
|
convoEntity.UserID = user.ID;
|
|
82
122
|
convoEntity.Name = AskSkipResolver._defaultNewChatName;
|
|
83
|
-
|
|
123
|
+
|
|
124
|
+
dataContextEntity = await md.GetEntityObject<DataContextEntity>('Data Contexts', user);
|
|
125
|
+
if (!DataContextId || DataContextId <= 0) {
|
|
126
|
+
dataContextEntity.NewRecord();
|
|
127
|
+
dataContextEntity.UserID = user.ID;
|
|
128
|
+
dataContextEntity.Name = 'Data Context for Skip Conversation';
|
|
129
|
+
if (!await dataContextEntity.Save())
|
|
130
|
+
throw new Error(`Creating a new data context failed`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
await dataContextEntity.Load(DataContextId);
|
|
134
|
+
}
|
|
135
|
+
convoEntity.DataContextID = dataContextEntity.ID;
|
|
136
|
+
if (await convoEntity.Save()) {
|
|
84
137
|
ConversationId = convoEntity.ID;
|
|
138
|
+
if (!DataContextId || dataContextEntity.ID <= 0) {
|
|
139
|
+
// only do this if we created a new data context for this conversation
|
|
140
|
+
dataContextEntity.Name += ` ${ConversationId}`;
|
|
141
|
+
await dataContextEntity.Save();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
85
144
|
else
|
|
86
145
|
throw new Error(`Creating a new conversation failed`);
|
|
87
146
|
}
|
|
@@ -90,8 +149,16 @@ export class AskSkipResolver {
|
|
|
90
149
|
}
|
|
91
150
|
} else {
|
|
92
151
|
await convoEntity.Load(ConversationId); // load the existing conversation, will need it later
|
|
152
|
+
dataContextEntity = await md.GetEntityObject<DataContextEntity>('Data Contexts', user);
|
|
153
|
+
|
|
154
|
+
// note - we ignore the parameter DataContextId if it is passed in, we will use the data context from the conversation that is saved. If a user wants to change the data context for a convo, they can do that elsewhere
|
|
155
|
+
if (DataContextId && DataContextId > 0 && DataContextId !== convoEntity.DataContextID)
|
|
156
|
+
console.log(`AskSkipResolver: DataContextId ${DataContextId} was passed in but it was ignored because it was different than the DataContextID in the conversation ${convoEntity.DataContextID}`);
|
|
157
|
+
|
|
158
|
+
await dataContextEntity.Load(convoEntity.DataContextID);
|
|
93
159
|
}
|
|
94
160
|
|
|
161
|
+
|
|
95
162
|
// now, create a conversation detail record for the user message
|
|
96
163
|
const convoDetailEntity = await md.GetEntityObject<ConversationDetailEntity>('Conversation Details', user);
|
|
97
164
|
convoDetailEntity.NewRecord();
|
|
@@ -101,38 +168,65 @@ export class AskSkipResolver {
|
|
|
101
168
|
convoDetailEntity.Set('Sequence', 1); // using weakly typed here because we're going to get rid of this field soon
|
|
102
169
|
await convoDetailEntity.Save();
|
|
103
170
|
|
|
104
|
-
//const OrganizationId = 2 //HG 8/1/2023 TODO: Pull this from an environment variable
|
|
105
|
-
const OrganizationId = ___skipAPIOrgId;
|
|
106
171
|
const dataContext: SkipDataContext = new SkipDataContext();
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
172
|
+
const dciEntityInfo = md.Entities.find((e) => e.Name === 'Data Context Items');
|
|
173
|
+
if (!dciEntityInfo)
|
|
174
|
+
throw new Error(`Data Context Items entity not found`);
|
|
175
|
+
|
|
176
|
+
const sql = `SELECT * FROM ${dciEntityInfo.SchemaName}.${dciEntityInfo.BaseView} WHERE DataContextID = ${dataContextEntity.ID}`;
|
|
177
|
+
const result = await dataSource.query(sql);
|
|
178
|
+
if (!result)
|
|
179
|
+
throw new Error(`Error running SQL: ${sql}`);
|
|
180
|
+
else {
|
|
181
|
+
for (const r of result) {
|
|
182
|
+
const item = new SkipDataContextItem();
|
|
183
|
+
item.Type = r.Type;
|
|
184
|
+
switch (item.Type) {
|
|
185
|
+
case 'full_entity':
|
|
186
|
+
item.EntityID = r.EntityID;
|
|
187
|
+
break;
|
|
188
|
+
case 'single_record':
|
|
189
|
+
item.RecordID = r.RecordID;
|
|
190
|
+
item.EntityID = r.EntityID;
|
|
191
|
+
break;
|
|
192
|
+
case 'query':
|
|
193
|
+
item.QueryID = r.QueryID; // map the QueryID in our database to the RecordID field in the object model for runtime use
|
|
194
|
+
break;
|
|
195
|
+
case 'sql':
|
|
196
|
+
item.SQL = r.SQL;
|
|
197
|
+
break;
|
|
198
|
+
case 'view':
|
|
199
|
+
item.ViewID = r.ViewID;
|
|
200
|
+
item.EntityID = r.EntityID;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
if (item.EntityID) {
|
|
204
|
+
item.Entity = md.Entities.find((e) => e.ID === item.EntityID);
|
|
205
|
+
item.EntityName = item.Entity.Name;
|
|
206
|
+
if (item.Type === 'full_entity')
|
|
207
|
+
item.RecordName = item.EntityName;
|
|
208
|
+
}
|
|
209
|
+
if (item.Type === 'query' && item.QueryID) {
|
|
210
|
+
const q = md.Queries.find((q) => q.ID === item.QueryID);
|
|
211
|
+
item.RecordName = q?.Name;
|
|
212
|
+
}
|
|
213
|
+
if (item.Type === 'view' && item.ViewID) {
|
|
214
|
+
const v = await md.GetEntityObject<UserViewEntityExtended>('User Views', user);
|
|
215
|
+
await v.Load(item.ViewID);
|
|
216
|
+
item.RecordName = v.Name;
|
|
217
|
+
item.ViewEntity = v;
|
|
218
|
+
}
|
|
219
|
+
item.Data = r.Data && r.Data.length > 0 ? JSON.parse(r.Data) : item.Data; // parse the stored data if it was saved, otherwise leave it to whatever the object's default is
|
|
220
|
+
item.AdditionalDescription = r.AdditionalDescription;
|
|
221
|
+
item.DataContextItemID = r.ID;
|
|
222
|
+
dataContext.Items.push(item);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
114
225
|
|
|
115
|
-
// now load up the messages. We will load up ALL of the messages for this conversation, and then pass them to the Skip API
|
|
116
|
-
const messages: SkipMessage[] = await this.LoadConversationDetailsIntoSkipMessages(dataSource, convoEntity.ID, AskSkipResolver._maxHistoricalMessages);
|
|
117
226
|
|
|
118
|
-
|
|
119
|
-
messages: messages,
|
|
120
|
-
conversationID: ConversationId.toString(),
|
|
121
|
-
dataContext: dataContext,
|
|
122
|
-
organizationID: !isNaN(parseInt(OrganizationId)) ? parseInt(OrganizationId) : 0,
|
|
123
|
-
requestPhase: 'initial_request'
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
127
|
-
message: JSON.stringify({
|
|
128
|
-
type: 'AskSkip',
|
|
129
|
-
status: 'OK',
|
|
130
|
-
message: 'I will be happy to help and will start by analyzing your request...',
|
|
131
|
-
}),
|
|
132
|
-
sessionId: userPayload.sessionId,
|
|
133
|
-
});
|
|
227
|
+
/// TODO next up we need to modify MJExplorer to handle the data context stuff and then we can finish this method
|
|
134
228
|
|
|
135
|
-
return
|
|
229
|
+
return {dataContext, convoEntity, dataContextEntity, convoDetailEntity};
|
|
136
230
|
}
|
|
137
231
|
|
|
138
232
|
protected async LoadConversationDetailsIntoSkipMessages(dataSource: DataSource, ConversationId: number, maxHistoricalMessages?: number): Promise<SkipMessage[]> {
|
|
@@ -191,7 +285,8 @@ export class AskSkipResolver {
|
|
|
191
285
|
|
|
192
286
|
protected async HandleSkipRequest(input: SkipAPIRequest, UserQuestion: string, user: UserInfo, dataSource: DataSource,
|
|
193
287
|
ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, md: Metadata,
|
|
194
|
-
convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity
|
|
288
|
+
convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
|
|
289
|
+
dataContext: SkipDataContext, dataContextEntity: DataContextEntity): Promise<AskSkipResultType> {
|
|
195
290
|
LogStatus(`Sending request to Skip API: ${___skipAPIurl}`)
|
|
196
291
|
|
|
197
292
|
// Convert JSON payload to a Buffer and compress it
|
|
@@ -212,14 +307,14 @@ export class AskSkipResolver {
|
|
|
212
307
|
|
|
213
308
|
// now, based on the result type, we will either wait for the next phase or we will process the results
|
|
214
309
|
if (apiResponse.responsePhase === 'data_request') {
|
|
215
|
-
return await this.HandleDataRequestPhase(input, <SkipAPIDataRequestResponse>apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity);
|
|
310
|
+
return await this.HandleDataRequestPhase(input, <SkipAPIDataRequestResponse>apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity);
|
|
216
311
|
}
|
|
217
312
|
else if (apiResponse.responsePhase === 'clarifying_question') {
|
|
218
313
|
// need to send the request back to the user for a clarifying question
|
|
219
314
|
return await this.HandleClarifyingQuestionPhase(input, <SkipAPIClarifyingQuestionResponse>apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity);
|
|
220
315
|
}
|
|
221
316
|
else if (apiResponse.responsePhase === 'analysis_complete') {
|
|
222
|
-
return await this.HandleAnalysisComplete(input, <SkipAPIAnalysisCompleteResponse>apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity);
|
|
317
|
+
return await this.HandleAnalysisComplete(input, <SkipAPIAnalysisCompleteResponse>apiResponse, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, convoEntity, convoDetailEntity, dataContext, dataContextEntity);
|
|
223
318
|
}
|
|
224
319
|
else {
|
|
225
320
|
// unknown response phase
|
|
@@ -274,11 +369,12 @@ export class AskSkipResolver {
|
|
|
274
369
|
}
|
|
275
370
|
|
|
276
371
|
protected async HandleAnalysisComplete(apiRequest: SkipAPIRequest, apiResponse: SkipAPIAnalysisCompleteResponse, UserQuestion: string, user: UserInfo, dataSource: DataSource,
|
|
277
|
-
ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity
|
|
372
|
+
ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
|
|
373
|
+
dataContext: SkipDataContext, dataContextEntity: DataContextEntity): Promise<AskSkipResultType> {
|
|
278
374
|
// analysis is complete
|
|
279
375
|
// all done, wrap things up
|
|
280
376
|
const md = new Metadata();
|
|
281
|
-
const {AIMessageConversationDetailID} = await this.FinishConversationAndNotifyUser(apiResponse, md, user, convoEntity, pubSub, userPayload);
|
|
377
|
+
const {AIMessageConversationDetailID} = await this.FinishConversationAndNotifyUser(apiResponse, dataContext, dataContextEntity, md, user, convoEntity, pubSub, userPayload);
|
|
282
378
|
|
|
283
379
|
return {
|
|
284
380
|
Success: true,
|
|
@@ -325,7 +421,8 @@ export class AskSkipResolver {
|
|
|
325
421
|
}
|
|
326
422
|
|
|
327
423
|
protected async HandleDataRequestPhase(apiRequest: SkipAPIRequest, apiResponse: SkipAPIDataRequestResponse, UserQuestion: string, user: UserInfo, dataSource: DataSource,
|
|
328
|
-
ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity
|
|
424
|
+
ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
|
|
425
|
+
dataContext: SkipDataContext, dataContextEntity: DataContextEntity): Promise<AskSkipResultType> {
|
|
329
426
|
// our job in this method is to go through each of the data requests from the Skip API, get the data, and then go back to the Skip API again and to the next phase
|
|
330
427
|
try {
|
|
331
428
|
const _maxDataGatheringRetries = 5;
|
|
@@ -352,9 +449,9 @@ export class AskSkipResolver {
|
|
|
352
449
|
const item = new SkipDataContextItem();
|
|
353
450
|
item.Type = 'sql';
|
|
354
451
|
item.Data = result;
|
|
355
|
-
item.
|
|
452
|
+
item.SQL = dr.text;
|
|
356
453
|
item.AdditionalDescription = dr.description;
|
|
357
|
-
|
|
454
|
+
dataContext.Items.push(item);
|
|
358
455
|
break;
|
|
359
456
|
case "stored_query":
|
|
360
457
|
const queryName = dr.text;
|
|
@@ -366,10 +463,10 @@ export class AskSkipResolver {
|
|
|
366
463
|
const item = new SkipDataContextItem();
|
|
367
464
|
item.Type = 'query';
|
|
368
465
|
item.Data = result.Results;
|
|
369
|
-
item.
|
|
466
|
+
item.QueryID = query.ID;
|
|
370
467
|
item.RecordName = query.Name;
|
|
371
468
|
item.AdditionalDescription = dr.description;
|
|
372
|
-
|
|
469
|
+
dataContext.Items.push(item);
|
|
373
470
|
}
|
|
374
471
|
else
|
|
375
472
|
throw new Error(`Error running query ${queryName}`);
|
|
@@ -415,7 +512,7 @@ export class AskSkipResolver {
|
|
|
415
512
|
apiRequest.requestPhase = 'data_gathering_response';
|
|
416
513
|
}
|
|
417
514
|
// we have all of the data now, add it to the data context and then submit it back to the Skip API
|
|
418
|
-
return this.HandleSkipRequest(apiRequest, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity);
|
|
515
|
+
return this.HandleSkipRequest(apiRequest, UserQuestion, user, dataSource, ConversationId, userPayload, pubSub, md, convoEntity, convoDetailEntity, dataContext, dataContextEntity);
|
|
419
516
|
}
|
|
420
517
|
catch (e) {
|
|
421
518
|
LogError(e);
|
|
@@ -424,7 +521,7 @@ export class AskSkipResolver {
|
|
|
424
521
|
}
|
|
425
522
|
|
|
426
523
|
/**
|
|
427
|
-
* This method will handle the process for an end of request where a user is notified of an AI message. The AI message is either the finished report or a clarifying question.
|
|
524
|
+
* This method will handle the process for an end of successful request where a user is notified of an AI message. The AI message is either the finished report or a clarifying question.
|
|
428
525
|
* @param apiResponse
|
|
429
526
|
* @param md
|
|
430
527
|
* @param user
|
|
@@ -433,11 +530,11 @@ export class AskSkipResolver {
|
|
|
433
530
|
* @param userPayload
|
|
434
531
|
* @returns
|
|
435
532
|
*/
|
|
436
|
-
protected async FinishConversationAndNotifyUser(apiResponse: SkipAPIAnalysisCompleteResponse, md: Metadata, user: UserInfo, convoEntity: ConversationEntity, pubSub: PubSubEngine, userPayload: UserPayload): Promise<{AIMessageConversationDetailID: number}> {
|
|
533
|
+
protected async FinishConversationAndNotifyUser(apiResponse: SkipAPIAnalysisCompleteResponse, dataContext: SkipDataContext, dataContextEntity: DataContextEntity, md: Metadata, user: UserInfo, convoEntity: ConversationEntity, pubSub: PubSubEngine, userPayload: UserPayload): Promise<{AIMessageConversationDetailID: number}> {
|
|
437
534
|
const sTitle = apiResponse.reportTitle;
|
|
438
535
|
const sResult = JSON.stringify(apiResponse);
|
|
439
536
|
|
|
440
|
-
//
|
|
537
|
+
// Create a conversation detail record for the Skip response
|
|
441
538
|
const convoDetailEntityAI = <ConversationDetailEntity>await md.GetEntityObject('Conversation Details', user);
|
|
442
539
|
convoDetailEntityAI.NewRecord();
|
|
443
540
|
convoDetailEntityAI.ConversationID = convoEntity.ID;
|
|
@@ -447,7 +544,7 @@ export class AskSkipResolver {
|
|
|
447
544
|
await convoDetailEntityAI.Save();
|
|
448
545
|
|
|
449
546
|
// finally update the convo name if it is still the default
|
|
450
|
-
if (convoEntity.Name === AskSkipResolver._defaultNewChatName && sTitle) {
|
|
547
|
+
if (convoEntity.Name === AskSkipResolver._defaultNewChatName && sTitle && sTitle !== AskSkipResolver._defaultNewChatName) {
|
|
451
548
|
convoEntity.Name = sTitle; // use the title from the response
|
|
452
549
|
await convoEntity.Save();
|
|
453
550
|
}
|
|
@@ -464,6 +561,39 @@ export class AskSkipResolver {
|
|
|
464
561
|
conversationId: convoEntity.ID,
|
|
465
562
|
});
|
|
466
563
|
await userNotification.Save();
|
|
564
|
+
|
|
565
|
+
// now, persist the data context items, first let's get
|
|
566
|
+
for (const item of dataContext.Items) {
|
|
567
|
+
const dciEntity = <DataContextItemEntity>await md.GetEntityObject('Data Context Items', user);
|
|
568
|
+
if (item.DataContextItemID > 0)
|
|
569
|
+
await dciEntity.Load(item.DataContextItemID);
|
|
570
|
+
else
|
|
571
|
+
dciEntity.NewRecord();
|
|
572
|
+
dciEntity.DataContextID = dataContextEntity.ID;
|
|
573
|
+
dciEntity.Type = item.Type;
|
|
574
|
+
switch (item.Type) {
|
|
575
|
+
case 'full_entity':
|
|
576
|
+
case 'single_record':
|
|
577
|
+
const e = item.Entity || md.Entities.find((e) => e.Name === item.EntityName);
|
|
578
|
+
dciEntity.EntityID = e.ID;
|
|
579
|
+
if (item.Type === 'single_record')
|
|
580
|
+
dciEntity.RecordID = item.RecordID;
|
|
581
|
+
break;
|
|
582
|
+
case 'view':
|
|
583
|
+
dciEntity.ViewID = item.ViewID;
|
|
584
|
+
break;
|
|
585
|
+
case 'query':
|
|
586
|
+
dciEntity.QueryID = item.QueryID;
|
|
587
|
+
break;
|
|
588
|
+
case 'sql':
|
|
589
|
+
dciEntity.SQL = item.SQL;
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
dciEntity.DataJSON = JSON.stringify(item.Data);
|
|
593
|
+
await dciEntity.Save();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// send a UI update trhough pub-sub
|
|
467
597
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
|
|
468
598
|
message: JSON.stringify({
|
|
469
599
|
type: 'UserNotifications',
|
|
@@ -475,6 +605,7 @@ export class AskSkipResolver {
|
|
|
475
605
|
}),
|
|
476
606
|
sessionId: userPayload.sessionId,
|
|
477
607
|
});
|
|
608
|
+
|
|
478
609
|
return {
|
|
479
610
|
AIMessageConversationDetailID: convoDetailEntityAI.ID
|
|
480
611
|
};
|