@memberjunction/server 1.7.0 → 1.8.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.
@@ -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
- dataObject[f.CodeName] = dataObject[f.Name];
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
- } else return null;
241
- } catch (err) {
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
- if (clientOldValues[key] !== dbValues[key] && f && f.AllowUpdateAPI && !f.IsPrimaryKey ) {
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
- const cd = ExternalChangeDetectorEngine.Instance;
96
- await cd.Config(false, UserCache.Users[0]);
97
-
98
- // don't wait for this, just run it and show in console whenever done.
99
- cd.DetectChangesForAllEligibleEntities().then(result => console.log(result));
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) =>
@@ -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
- return super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { ID }));
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
- return super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { EmployeeID }));
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;