@memberjunction/server 1.7.1 → 1.8.1
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/CHANGELOG.json +381 -1
- package/CHANGELOG.md +79 -2
- package/dist/generated/generated.d.ts +718 -438
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +2015 -559
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +2 -2
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +42 -8
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +8 -8
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts +4 -4
- package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.js +4 -4
- package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +1 -1
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +5 -3
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/package.json +21 -21
- package/src/generated/generated.ts +1594 -543
- package/src/generic/ResolverBase.ts +52 -11
- package/src/index.ts +15 -8
- package/src/resolvers/AskSkipResolver.ts +9 -9
- package/src/resolvers/EntityCommunicationsResolver.ts +5 -5
- package/src/resolvers/UserResolver.ts +5 -3
|
@@ -24,10 +24,16 @@ export class ResolverBase {
|
|
|
24
24
|
const entityInfo = md.Entities.find((e) => e.Name === entityName);
|
|
25
25
|
if (!entityInfo)
|
|
26
26
|
throw new Error(`Entity ${entityName} not found in metadata`);
|
|
27
|
-
const fields = entityInfo.Fields.filter((f) => f.Name !== f.CodeName);
|
|
27
|
+
const fields = entityInfo.Fields.filter((f) => f.Name !== f.CodeName || f.Name.startsWith('__mj_'));
|
|
28
28
|
fields.forEach((f) => {
|
|
29
29
|
if (dataObject.hasOwnProperty(f.Name)) {
|
|
30
|
-
|
|
30
|
+
if (f.CodeName.startsWith('__mj_')) { // GraphQL doesn't allow us to pass back fields with __ so we are mapping our special field cases that start with __mj_ to _mj__ for transport - they are converted back on the other side automatically
|
|
31
|
+
const newCodeName = `_mj__${f.CodeName.substring(5)}`;
|
|
32
|
+
dataObject[newCodeName] = dataObject[f.Name];
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
dataObject[f.CodeName] = dataObject[f.Name];
|
|
36
|
+
}
|
|
31
37
|
delete dataObject[f.Name];
|
|
32
38
|
}
|
|
33
39
|
});
|
|
@@ -205,14 +211,12 @@ export class ResolverBase {
|
|
|
205
211
|
// figure out the result type from the input string (if provided)
|
|
206
212
|
let rt: 'simple' | 'entity_object' | 'count_only' = 'simple';
|
|
207
213
|
switch (resultType?.trim().toLowerCase()) {
|
|
208
|
-
case 'entity_object':
|
|
209
|
-
rt = 'entity_object';
|
|
210
|
-
break;
|
|
211
214
|
case 'count_only':
|
|
212
215
|
rt = 'count_only';
|
|
213
216
|
break;
|
|
217
|
+
case 'entity_object':
|
|
214
218
|
default:
|
|
215
|
-
rt = 'simple';
|
|
219
|
+
rt = 'simple'; // use simple as the default AND for entity_object becuase on teh server we don't really pass back a true entity_object anyway, just passing back the simple object anyway
|
|
216
220
|
break;
|
|
217
221
|
}
|
|
218
222
|
|
|
@@ -236,9 +240,25 @@ export class ResolverBase {
|
|
|
236
240
|
},
|
|
237
241
|
user
|
|
238
242
|
);
|
|
243
|
+
// go through the result and convert all fields that start with __mj_*** to _mj__*** for GraphQL transport
|
|
244
|
+
if (result && result.Success) {
|
|
245
|
+
for (const r of result.Results) {
|
|
246
|
+
const keys = Object.keys(r);
|
|
247
|
+
keys.forEach((k) => {
|
|
248
|
+
if (k.trim().toLowerCase().startsWith('__mj_')) {
|
|
249
|
+
r[`_mj__${k.substring(5)}`] = r[k];
|
|
250
|
+
delete r[k];
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
}
|
|
255
|
+
}
|
|
239
256
|
return result;
|
|
240
|
-
}
|
|
241
|
-
|
|
257
|
+
}
|
|
258
|
+
else
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
242
262
|
console.log(err);
|
|
243
263
|
throw err;
|
|
244
264
|
}
|
|
@@ -385,7 +405,7 @@ export class ResolverBase {
|
|
|
385
405
|
if (await entityObject.Save()) {
|
|
386
406
|
// save worked, fire the AfterCreate event and then return all the data
|
|
387
407
|
await this.AfterCreate(dataSource, input); // fire event
|
|
388
|
-
return entityObject.GetAll();
|
|
408
|
+
return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
|
|
389
409
|
}
|
|
390
410
|
else
|
|
391
411
|
// save failed, return null
|
|
@@ -458,7 +478,7 @@ export class ResolverBase {
|
|
|
458
478
|
if (await entityObject.Save()) {
|
|
459
479
|
// save worked, fire afterevent and return all the data
|
|
460
480
|
await this.AfterUpdate(dataSource, input); // fire event
|
|
461
|
-
return entityObject.GetAll();
|
|
481
|
+
return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
|
|
462
482
|
}
|
|
463
483
|
else {
|
|
464
484
|
throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
|
|
@@ -499,6 +519,10 @@ export class ResolverBase {
|
|
|
499
519
|
val = (val === null || val === undefined || val === 'false' || val === '0' || parseInt(val) === 0) ? false : true;
|
|
500
520
|
break;
|
|
501
521
|
case EntityFieldTSType.Date:
|
|
522
|
+
// first, if val is a string and it is actually a number (milliseconds since epoch), convert it to a number.
|
|
523
|
+
if (val !== null && val !== undefined && val.toString().trim() !== '' && !isNaN(val))
|
|
524
|
+
val = parseInt(val);
|
|
525
|
+
|
|
502
526
|
val = val !== null && val !== undefined ? new Date(val) : null;
|
|
503
527
|
break;
|
|
504
528
|
default:
|
|
@@ -516,7 +540,24 @@ export class ResolverBase {
|
|
|
516
540
|
const dbDifferences = [];
|
|
517
541
|
Object.keys(clientOldValues).forEach((key) => {
|
|
518
542
|
const f = entityObject.EntityInfo.Fields.find((f) => f.CodeName === key);
|
|
519
|
-
|
|
543
|
+
let different = false;
|
|
544
|
+
switch (typeof clientOldValues[key]) {
|
|
545
|
+
case 'number':
|
|
546
|
+
different = clientOldValues[key] !== dbValues[key];
|
|
547
|
+
break;
|
|
548
|
+
case 'boolean':
|
|
549
|
+
different = clientOldValues[key] !== dbValues[key];
|
|
550
|
+
break;
|
|
551
|
+
case 'object':
|
|
552
|
+
if (clientOldValues[key] instanceof Date) {
|
|
553
|
+
different = clientOldValues[key].getTime() !== dbValues[key].getTime();
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
556
|
+
default:
|
|
557
|
+
different = clientOldValues[key] !== dbValues[key];
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
if (different && f && !f.ReadOnly ) {
|
|
520
561
|
// only include updateable fields
|
|
521
562
|
dbDifferences.push({
|
|
522
563
|
FieldName: key,
|
package/src/index.ts
CHANGED
|
@@ -89,18 +89,25 @@ export const serve = async (resolverPaths: Array<string>) => {
|
|
|
89
89
|
const config = new SQLServerProviderConfigData(dataSource, '', mj_core_schema, cacheRefreshInterval);
|
|
90
90
|
await setupSQLServerClient(config); // datasource is already initialized, so we can setup the client right away
|
|
91
91
|
const md = new Metadata();
|
|
92
|
-
|
|
92
|
+
console.log(`Data Source has been initialized. ${md?.Entities ? md.Entities.length : 0} entities loaded.`);
|
|
93
|
+
setupComplete$.next(true);
|
|
93
94
|
|
|
94
95
|
/******TEST HARNESS FOR CHANGE DETECTION */
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
/******TEST HARNESS FOR CHANGE DETECTION */
|
|
97
|
+
// const cd = ExternalChangeDetectorEngine.Instance;
|
|
98
|
+
// await cd.Config(false, UserCache.Users[0]);
|
|
99
|
+
|
|
100
|
+
// // don't wait for this, just run it and show in console whenever done.
|
|
101
|
+
// cd.DetectChangesForAllEligibleEntities().then(result => {
|
|
102
|
+
// console.log(result)
|
|
103
|
+
// cd.ReplayChanges(result.Changes).then(replayResult => {
|
|
104
|
+
// console.log(replayResult)
|
|
105
|
+
// });
|
|
106
|
+
// });
|
|
107
|
+
/******TEST HARNESS FOR CHANGE DETECTION */
|
|
108
|
+
/******TEST HARNESS FOR CHANGE DETECTION */
|
|
100
109
|
|
|
101
|
-
console.log(`Data Source has been initialized. ${md?.Entities ? md.Entities.length : 0} entities loaded.`);
|
|
102
110
|
|
|
103
|
-
setupComplete$.next(true);
|
|
104
111
|
|
|
105
112
|
const dynamicModules = await Promise.all(paths.map((modulePath) => import(modulePath.replace(/\.[jt]s$/, ''))));
|
|
106
113
|
const resolvers = dynamicModules.flatMap((module) =>
|
|
@@ -275,8 +275,8 @@ export class AskSkipResolver {
|
|
|
275
275
|
feedback: q.Feedback,
|
|
276
276
|
status: q.Status,
|
|
277
277
|
qualityRank: q.QualityRank,
|
|
278
|
-
createdAt: q.
|
|
279
|
-
updatedAt: q.
|
|
278
|
+
createdAt: q.__mj_CreatedAt,
|
|
279
|
+
updatedAt: q.__mj_UpdatedAt,
|
|
280
280
|
categoryID: q.CategoryID,
|
|
281
281
|
fields: q.Fields.map((f) => {
|
|
282
282
|
return {
|
|
@@ -294,8 +294,8 @@ export class AskSkipResolver {
|
|
|
294
294
|
computationDescription: f.ComputationDescription,
|
|
295
295
|
isSummary: f.IsSummary,
|
|
296
296
|
summaryDescription: f.SummaryDescription,
|
|
297
|
-
createdAt: f.
|
|
298
|
-
updatedAt: f.
|
|
297
|
+
createdAt: f.__mj_CreatedAt,
|
|
298
|
+
updatedAt: f.__mj_UpdatedAt,
|
|
299
299
|
}
|
|
300
300
|
})
|
|
301
301
|
}
|
|
@@ -470,23 +470,23 @@ export class AskSkipResolver {
|
|
|
470
470
|
const md = new Metadata();
|
|
471
471
|
const e = md.Entities.find((e) => e.Name === 'Conversation Details');
|
|
472
472
|
const sql = `SELECT
|
|
473
|
-
${maxHistoricalMessages ? 'TOP ' + maxHistoricalMessages : ''} ID, Message, Role,
|
|
473
|
+
${maxHistoricalMessages ? 'TOP ' + maxHistoricalMessages : ''} ID, Message, Role, __mj_CreatedAt
|
|
474
474
|
FROM
|
|
475
475
|
${e.SchemaName}.${e.BaseView}
|
|
476
476
|
WHERE
|
|
477
477
|
ConversationID = ${ConversationId}
|
|
478
478
|
ORDER
|
|
479
|
-
BY
|
|
479
|
+
BY __mj_CreatedAt DESC`;
|
|
480
480
|
const result = await dataSource.query(sql);
|
|
481
481
|
if (!result)
|
|
482
482
|
throw new Error(`Error running SQL: ${sql}`);
|
|
483
483
|
else {
|
|
484
|
-
// first, let's sort the result array into a local variable called returnData and in that we will sort by
|
|
484
|
+
// first, let's sort the result array into a local variable called returnData and in that we will sort by __mj_CreatedAt in ASCENDING order so we have the right chronological order
|
|
485
485
|
// the reason we're doing a LOCAL sort here is because in the SQL query above, we're sorting in DESCENDING order so we can use the TOP clause to limit the number of records and get the
|
|
486
486
|
// N most recent records. We want to sort in ASCENDING order because we want to send the messages to the Skip API in the order they were created.
|
|
487
487
|
const returnData = result.sort((a: any, b: any) => {
|
|
488
|
-
const aDate = new Date(a.
|
|
489
|
-
const bDate = new Date(b.
|
|
488
|
+
const aDate = new Date(a.__mj_CreatedAt);
|
|
489
|
+
const bDate = new Date(b.__mj_CreatedAt);
|
|
490
490
|
return aDate.getTime() - bDate.getTime();
|
|
491
491
|
});
|
|
492
492
|
|
|
@@ -29,10 +29,10 @@ export class CommunicationProviderMessageType {
|
|
|
29
29
|
AdditionalAttributes: string;
|
|
30
30
|
|
|
31
31
|
@Field()
|
|
32
|
-
|
|
32
|
+
_mj_CreatedAt: Date;
|
|
33
33
|
|
|
34
34
|
@Field()
|
|
35
|
-
|
|
35
|
+
_mj_UpdatedAt: Date;
|
|
36
36
|
|
|
37
37
|
@Field()
|
|
38
38
|
CommunicationProvider?: string;
|
|
@@ -71,10 +71,10 @@ export class TemplateInputType {
|
|
|
71
71
|
IsActive: boolean;
|
|
72
72
|
|
|
73
73
|
@Field()
|
|
74
|
-
|
|
74
|
+
_mj_CreatedAt: Date;
|
|
75
75
|
|
|
76
76
|
@Field()
|
|
77
|
-
|
|
77
|
+
_mj_UpdatedAt: Date;
|
|
78
78
|
|
|
79
79
|
@Field({ nullable: true})
|
|
80
80
|
Category?: string;
|
|
@@ -169,7 +169,7 @@ export class ReportResolver {
|
|
|
169
169
|
@Ctx() { userPayload }: AppContext): Promise<RunEntityCommunicationResultType> {
|
|
170
170
|
try {
|
|
171
171
|
await EntityCommunicationsEngine.Instance.Config(false, userPayload.userRecord);
|
|
172
|
-
const newMessage = new Message(
|
|
172
|
+
const newMessage = new Message(message as unknown as Message);
|
|
173
173
|
await TemplateEngineServer.Instance.Config(false, userPayload.userRecord);
|
|
174
174
|
// for the templates, replace the values from the input with the objects from the Template Engine we have here
|
|
175
175
|
if (newMessage.BodyTemplate) {
|
|
@@ -10,12 +10,14 @@ export class UserResolver extends UserResolverBase {
|
|
|
10
10
|
|
|
11
11
|
@Query(() => User_)
|
|
12
12
|
async UserByID(@Arg('ID', () => Int) ID: number, @Ctx() { dataSource }: AppContext) {
|
|
13
|
-
|
|
13
|
+
const retVal = super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { ID }));
|
|
14
|
+
return this.MapFieldNamesToCodeNames('Users', retVal);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
@Query(() => User_)
|
|
17
18
|
async UserByEmployeeID(@Arg('EmployeeID', () => Int) EmployeeID: number, @Ctx() { dataSource }: AppContext) {
|
|
18
|
-
|
|
19
|
+
const retVal = super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { EmployeeID }));
|
|
20
|
+
return this.MapFieldNamesToCodeNames('Users', retVal);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
@Query(() => User_)
|
|
@@ -23,7 +25,7 @@ export class UserResolver extends UserResolverBase {
|
|
|
23
25
|
// const searchEmail = userEmailMap[Email] ?? Email;
|
|
24
26
|
const searchEmail = Email;
|
|
25
27
|
const returnVal = super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { Email: searchEmail }));
|
|
26
|
-
return returnVal;
|
|
28
|
+
return this.MapFieldNamesToCodeNames('Users', returnVal);
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
export default UserResolver;
|