@memberjunction/sqlserver-dataprovider 2.22.1 → 2.23.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.
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  /**************************************************************************************************************
3
- * The SQLServerDataProvider provides a data provider for the entities framework that uses SQL Server directly
4
- * In practice - this FILE will NOT exist in the entities library, we need to move to its own separate project
5
- * so it is only included by the consumer of the entities library if they want to use it.
6
- **************************************************************************************************************/
3
+ * The SQLServerDataProvider provides a data provider for the entities framework that uses SQL Server directly
4
+ * In practice - this FILE will NOT exist in the entities library, we need to move to its own separate project
5
+ * so it is only included by the consumer of the entities library if they want to use it.
6
+ **************************************************************************************************************/
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.SQLServerDataProvider = exports.SQLServerProviderConfigData = void 0;
9
9
  const core_1 = require("@memberjunction/core");
@@ -14,14 +14,20 @@ const SQLServerTransactionGroup_1 = require("./SQLServerTransactionGroup");
14
14
  const UserCache_1 = require("./UserCache");
15
15
  const actions_1 = require("@memberjunction/actions");
16
16
  class SQLServerProviderConfigData extends core_1.ProviderConfigDataBase {
17
- get DataSource() { return this.Data.DataSource; }
18
- get CurrentUserEmail() { return this.Data.CurrentUserEmail; }
19
- get CheckRefreshIntervalSeconds() { return this.Data.CheckRefreshIntervalSeconds; }
20
- constructor(dataSource, currentUserEmail, MJCoreSchemaName, checkRefreshIntervalSeconds = 90 /*default to 90 seconds between checks*/, includeSchemas, excludeSchemas) {
17
+ get DataSource() {
18
+ return this.Data.DataSource;
19
+ }
20
+ get CurrentUserEmail() {
21
+ return this.Data.CurrentUserEmail;
22
+ }
23
+ get CheckRefreshIntervalSeconds() {
24
+ return this.Data.CheckRefreshIntervalSeconds;
25
+ }
26
+ constructor(dataSource, currentUserEmail, MJCoreSchemaName, checkRefreshIntervalSeconds = 0 /*default to disabling auto refresh */, includeSchemas, excludeSchemas) {
21
27
  super({
22
28
  DataSource: dataSource,
23
29
  CurrentUserEmail: currentUserEmail,
24
- CheckRefreshIntervalSeconds: checkRefreshIntervalSeconds
30
+ CheckRefreshIntervalSeconds: checkRefreshIntervalSeconds,
25
31
  }, MJCoreSchemaName, includeSchemas, excludeSchemas);
26
32
  }
27
33
  }
@@ -32,7 +38,9 @@ class SQLServerDataProvider extends core_1.ProviderBase {
32
38
  super(...arguments);
33
39
  this._bAllowRefresh = true;
34
40
  }
35
- get ConfigData() { return super.ConfigData; }
41
+ get ConfigData() {
42
+ return super.ConfigData;
43
+ }
36
44
  async Config(configData) {
37
45
  try {
38
46
  this._dataSource = configData.DataSource;
@@ -41,7 +49,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
41
49
  }
42
50
  catch (e) {
43
51
  (0, core_1.LogError)(e);
44
- throw (e);
52
+ throw e;
45
53
  }
46
54
  }
47
55
  /**
@@ -62,7 +70,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
62
70
  host: dbOptions.host || '',
63
71
  port: dbOptions.port || '',
64
72
  instanceName: dbOptions.instanceName ? '/' + dbOptions.instanceName : '',
65
- database: dbOptions.database || ''
73
+ database: dbOptions.database || '',
66
74
  };
67
75
  return options.type + '://' + options.host + ':' + options.port + options.instanceName + '/' + options.database;
68
76
  }
@@ -86,9 +94,23 @@ class SQLServerDataProvider extends core_1.ProviderBase {
86
94
  const result = await this.ExecuteSQL(sql);
87
95
  const end = new Date().getTime();
88
96
  if (result)
89
- return { Success: true, ReportID: ReportID, Results: result, RowCount: result.length, ExecutionTime: end - start, ErrorMessage: '' };
97
+ return {
98
+ Success: true,
99
+ ReportID: ReportID,
100
+ Results: result,
101
+ RowCount: result.length,
102
+ ExecutionTime: end - start,
103
+ ErrorMessage: '',
104
+ };
90
105
  else
91
- return { Success: false, ReportID: ReportID, Results: [], RowCount: 0, ExecutionTime: end - start, ErrorMessage: 'Error running report SQL' };
106
+ return {
107
+ Success: false,
108
+ ReportID: ReportID,
109
+ Results: [],
110
+ RowCount: 0,
111
+ ExecutionTime: end - start,
112
+ ErrorMessage: 'Error running report SQL',
113
+ };
92
114
  }
93
115
  else
94
116
  return { Success: false, ReportID: ReportID, Results: [], RowCount: 0, ExecutionTime: 0, ErrorMessage: 'Report not found' };
@@ -111,9 +133,23 @@ class SQLServerDataProvider extends core_1.ProviderBase {
111
133
  const result = await this.ExecuteSQL(sql);
112
134
  const end = new Date().getTime();
113
135
  if (result)
114
- return { Success: true, QueryID: QueryID, Results: result, RowCount: result.length, ExecutionTime: end - start, ErrorMessage: '' };
136
+ return {
137
+ Success: true,
138
+ QueryID: QueryID,
139
+ Results: result,
140
+ RowCount: result.length,
141
+ ExecutionTime: end - start,
142
+ ErrorMessage: '',
143
+ };
115
144
  else
116
- return { Success: false, QueryID: QueryID, Results: [], RowCount: 0, ExecutionTime: end - start, ErrorMessage: 'Error running query SQL' };
145
+ return {
146
+ Success: false,
147
+ QueryID: QueryID,
148
+ Results: [],
149
+ RowCount: 0,
150
+ ExecutionTime: end - start,
151
+ ErrorMessage: 'Error running query SQL',
152
+ };
117
153
  }
118
154
  else
119
155
  return { Success: false, QueryID: QueryID, Results: [], RowCount: 0, ExecutionTime: 0, ErrorMessage: 'Query not found' };
@@ -183,7 +219,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
183
219
  }
184
220
  catch (e) {
185
221
  (0, core_1.LogError)(e);
186
- throw (e);
222
+ throw e;
187
223
  }
188
224
  }
189
225
  /**************************************************************************/
@@ -243,7 +279,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
243
279
  let countSQL = topSQL && topSQL.length > 0 ? `SELECT COUNT(*) AS TotalRowCount FROM [${entityInfo.SchemaName}].${entityInfo.BaseView}` : null;
244
280
  let whereSQL = '';
245
281
  let bHasWhere = false;
246
- let userViewRunID = "";
282
+ let userViewRunID = '';
247
283
  // The view may have a where clause that is part of the view definition. If so, we need to add it to the SQL
248
284
  if (viewEntity?.WhereClause && viewEntity?.WhereClause.length > 0) {
249
285
  const renderedWhere = await this.RenderViewWhereClause(viewEntity, contextUser);
@@ -280,8 +316,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
280
316
  }
281
317
  // now, check for an exclude UserViewRunID, or exclusion of ALL prior runs
282
318
  // if provided, we need to exclude the records that were part of that run (or all prior runs)
283
- if ((excludeUserViewRunID && excludeUserViewRunID.length > 0) ||
284
- (params.ExcludeDataFromAllPriorViewRuns === true)) {
319
+ if ((excludeUserViewRunID && excludeUserViewRunID.length > 0) || params.ExcludeDataFromAllPriorViewRuns === true) {
285
320
  let sExcludeSQL = `ID NOT IN (SELECT RecordID FROM [${this.MJCoreSchemaName}].vwUserViewRunDetails WHERE EntityID='${viewEntity.EntityID}' AND`;
286
321
  if (params.ExcludeDataFromAllPriorViewRuns === true)
287
322
  sExcludeSQL += ` UserViewID=${viewEntity.ID})`; // exclude ALL prior runs for this view, we do NOT need to also add the UserViewRunID even if it was provided because this will automatically filter that out too
@@ -302,7 +337,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
302
337
  bHasWhere = true;
303
338
  }
304
339
  }
305
- // NEXT, apply Row Level Security (RLS)
340
+ // NEXT, apply Row Level Security (RLS)
306
341
  if (!entityInfo.UserExemptFromRowLevelSecurity(user, core_1.EntityPermissionType.Read)) {
307
342
  // user is NOT exempt from RLS, so we need to apply it
308
343
  const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, core_1.EntityPermissionType.Read, '');
@@ -325,7 +360,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
325
360
  // first check params.OrderBy, that takes first priority
326
361
  // if that's not provided, then we check the view definition for its SortState
327
362
  // if that's not provided we do NOT sort
328
- const orderBy = params.OrderBy ? params.OrderBy : (viewEntity ? viewEntity.OrderByClause : '');
363
+ const orderBy = params.OrderBy ? params.OrderBy : viewEntity ? viewEntity.OrderByClause : '';
329
364
  // if we're saving the view results, we need to wrap the entire SQL statement
330
365
  if (viewEntity?.ID && viewEntity?.ID.length > 0 && saveViewResults && user) {
331
366
  const { executeViewSQL, runID } = await this.executeSQLForUserViewRunLogging(viewEntity.ID, viewEntity.EntityBaseView, whereSQL, orderBy, user);
@@ -333,7 +368,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
333
368
  userViewRunID = runID;
334
369
  }
335
370
  else if (orderBy && orderBy.length > 0) {
336
- // we only add order by if we're not doing run logging. This is becuase the run logging will
371
+ // we only add order by if we're not doing run logging. This is becuase the run logging will
337
372
  // add the order by to its SELECT query that pulls from the list of records that were returned
338
373
  // there is no point in ordering the rows as they are saved into an audit list anyway so no order-by above
339
374
  // just here for final step before we execute it.
@@ -341,9 +376,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
341
376
  throw new Error(`Invalid Order By clause: ${orderBy}, contains one more for forbidden keywords`);
342
377
  viewSQL += ` ORDER BY ${orderBy}`;
343
378
  }
344
- if (params.StartRow && params.StartRow > 0
345
- && params.MaxRows && params.MaxRows > 0
346
- && entityInfo.FirstPrimaryKey) {
379
+ if (params.StartRow && params.StartRow > 0 && params.MaxRows && params.MaxRows > 0 && entityInfo.FirstPrimaryKey) {
347
380
  viewSQL += ` ORDER BY ${entityInfo.FirstPrimaryKey.Name} `;
348
381
  viewSQL += ` OFFSET ${params.StartRow} ROWS FETCH NEXT ${params.MaxRows} ROWS ONLY`;
349
382
  }
@@ -360,26 +393,31 @@ class SQLServerDataProvider extends core_1.ProviderBase {
360
393
  }
361
394
  }
362
395
  const stopTime = new Date();
363
- if (params.ForceAuditLog || (viewEntity?.ID && (extraFilter === undefined || extraFilter === null || extraFilter?.trim().length === 0) && entityInfo.AuditViewRuns)) {
396
+ if (params.ForceAuditLog ||
397
+ (viewEntity?.ID &&
398
+ (extraFilter === undefined || extraFilter === null || extraFilter?.trim().length === 0) &&
399
+ entityInfo.AuditViewRuns)) {
364
400
  // ONLY LOG TOP LEVEL VIEW EXECUTION - this would be for views with an ID, and don't have ExtraFilter as ExtraFilter
365
401
  // is only used in the system on a tab or just for ad hoc view execution
366
- // we do NOT want to wait for this, so no await,
402
+ // we do NOT want to wait for this, so no await,
367
403
  this.createAuditLogRecord(user, 'Run View', 'Run View', 'Success', JSON.stringify({
368
404
  ViewID: viewEntity?.ID,
369
405
  ViewName: viewEntity?.Name,
370
406
  Description: params.AuditLogDescription,
371
407
  RowCount: retData.length,
372
- SQL: viewSQL
408
+ SQL: viewSQL,
373
409
  }), entityInfo.ID, null, params.AuditLogDescription);
374
410
  }
375
411
  return {
376
- RowCount: params.ResultType === 'count_only' ? rowCount : retData.length, /*this property should be total row count if the ResultType='count_only' otherwise it should be the row count of the returned rows */
412
+ RowCount: params.ResultType === 'count_only'
413
+ ? rowCount
414
+ : retData.length /*this property should be total row count if the ResultType='count_only' otherwise it should be the row count of the returned rows */,
377
415
  TotalRowCount: rowCount ? rowCount : retData.length,
378
416
  Results: retData,
379
417
  UserViewRunID: userViewRunID,
380
418
  ExecutionTime: stopTime.getTime() - startTime.getTime(),
381
419
  Success: true,
382
- ErrorMessage: null
420
+ ErrorMessage: null,
383
421
  };
384
422
  }
385
423
  else
@@ -392,10 +430,10 @@ class SQLServerDataProvider extends core_1.ProviderBase {
392
430
  RowCount: 0,
393
431
  TotalRowCount: 0,
394
432
  Results: [],
395
- UserViewRunID: "",
433
+ UserViewRunID: '',
396
434
  ExecutionTime: exceptionStopTime.getTime() - startTime.getTime(),
397
435
  Success: false,
398
- ErrorMessage: e.message
436
+ ErrorMessage: e.message,
399
437
  };
400
438
  }
401
439
  }
@@ -424,7 +462,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
424
462
  /\bunion\b/,
425
463
  /\bcast\b/,
426
464
  /\bxp_/,
427
- /;/
465
+ /;/,
428
466
  ];
429
467
  // Check for forbidden patterns
430
468
  for (const pattern of forbiddenPatterns) {
@@ -440,10 +478,12 @@ class SQLServerDataProvider extends core_1.ProviderBase {
440
478
  if (fieldList.length === 0)
441
479
  return '*';
442
480
  else
443
- return fieldList.map(f => {
481
+ return fieldList
482
+ .map((f) => {
444
483
  const asString = f.CodeName === f.Name ? '' : ` AS [${f.CodeName}]`;
445
484
  return `[${f.Name}]${asString}`;
446
- }).join(',');
485
+ })
486
+ .join(',');
447
487
  }
448
488
  getRunTimeViewFieldArray(params, viewEntity) {
449
489
  const fieldList = [];
@@ -460,7 +500,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
460
500
  if (params.Fields) {
461
501
  // fields provided, if primary key isn't included, add it first
462
502
  for (const ef of entityInfo.PrimaryKeys) {
463
- if (params.Fields.find(f => f.trim().toLowerCase() === ef.Name.toLowerCase()) === undefined)
503
+ if (params.Fields.find((f) => f.trim().toLowerCase() === ef.Name.toLowerCase()) === undefined)
464
504
  fieldList.push(ef); // always include the primary key fields in view run time field list
465
505
  }
466
506
  // now add the rest of the param.Fields to fields
@@ -479,7 +519,8 @@ class SQLServerDataProvider extends core_1.ProviderBase {
479
519
  if (viewEntity) {
480
520
  // saved view, figure out it's field list
481
521
  viewEntity.Columns.forEach((c) => {
482
- if (!c.hidden) { // only return the non-hidden fields
522
+ if (!c.hidden) {
523
+ // only return the non-hidden fields
483
524
  if (c.EntityField) {
484
525
  fieldList.push(c.EntityField);
485
526
  }
@@ -490,7 +531,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
490
531
  });
491
532
  // the below shouldn't happen as the pkey fields should always be included by now, but make SURE...
492
533
  for (const ef of entityInfo.PrimaryKeys) {
493
- if (fieldList.find(f => f.Name?.trim().toLowerCase() === ef.Name?.toLowerCase()) === undefined)
534
+ if (fieldList.find((f) => f.Name?.trim().toLowerCase() === ef.Name?.toLowerCase()) === undefined)
494
535
  fieldList.push(ef); // always include the primary key fields in view run time field list
495
536
  }
496
537
  }
@@ -512,13 +553,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
512
553
  `;
513
554
  const runIDResult = await this._dataSource.query(sSQL);
514
555
  const runID = runIDResult[0].UserViewRunID;
515
- const sRetSQL = `SELECT * FROM [${entityInfo.SchemaName}].${entityBaseView} WHERE ${entityInfo.FirstPrimaryKey.Name} IN
556
+ const sRetSQL = `SELECT * FROM [${entityInfo.SchemaName}].${entityBaseView} WHERE ${entityInfo.FirstPrimaryKey.Name} IN
516
557
  (SELECT RecordID FROM [${this.MJCoreSchemaName}].vwUserViewRunDetails WHERE UserViewRunID=${runID})
517
558
  ${orderBySQL && orderBySQL.length > 0 ? ' ORDER BY ' + orderBySQL : ''}`;
518
559
  return { executeViewSQL: sRetSQL, runID: runID };
519
560
  }
520
561
  createViewUserSearchSQL(entityInfo, userSearchString) {
521
- // we have a user search string.
562
+ // we have a user search string.
522
563
  // if we have full text search, we use that.
523
564
  // Otherwise, we need to manually construct the additional filter associated with this. The user search string is just text from the user
524
565
  // we need to apply it to one or more fields that are part of the entity that support being part of a user search.
@@ -535,11 +576,11 @@ class SQLServerDataProvider extends core_1.ProviderBase {
535
576
  const uUpper = u.toUpperCase();
536
577
  if (uUpper.includes(' AND ') || uUpper.includes(' OR ') || uUpper.includes(' NOT ')) {
537
578
  //replace all spaces with %, but add spaces inbetween the original and, or and not keywords
538
- u = uUpper.replace(/ /g, "%").replace(/%AND%/g, " AND ").replace(/%OR%/g, " OR ").replace(/%NOT%/g, " NOT ");
579
+ u = uUpper.replace(/ /g, '%').replace(/%AND%/g, ' AND ').replace(/%OR%/g, ' OR ').replace(/%NOT%/g, ' NOT ');
539
580
  }
540
581
  else if (uUpper.includes('AND') || uUpper.includes('OR') || uUpper.includes('NOT')) {
541
582
  //leave the string alone, except replacing spaces with %
542
- u = u.replace(/ /g, "%");
583
+ u = u.replace(/ /g, '%');
543
584
  }
544
585
  else if (u.includes(' ')) {
545
586
  if (u.startsWith('"') && u.endsWith('"')) {
@@ -577,8 +618,12 @@ class SQLServerDataProvider extends core_1.ProviderBase {
577
618
  }
578
619
  async createAuditLogRecord(user, authorizationName, auditLogTypeName, status, details, entityId, recordId, auditLogDescription) {
579
620
  try {
580
- const authorization = authorizationName ? this.Authorizations.find((a) => a?.Name?.trim().toLowerCase() === authorizationName.trim().toLowerCase()) : null;
581
- const auditLogType = auditLogTypeName ? this.AuditLogTypes.find((a) => a?.Name?.trim().toLowerCase() === auditLogTypeName.trim().toLowerCase()) : null;
621
+ const authorization = authorizationName
622
+ ? this.Authorizations.find((a) => a?.Name?.trim().toLowerCase() === authorizationName.trim().toLowerCase())
623
+ : null;
624
+ const auditLogType = auditLogTypeName
625
+ ? this.AuditLogTypes.find((a) => a?.Name?.trim().toLowerCase() === auditLogTypeName.trim().toLowerCase())
626
+ : null;
582
627
  if (!user)
583
628
  throw new Error(`User is a required parameter`);
584
629
  if (!auditLogType)
@@ -646,14 +691,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
646
691
  }
647
692
  catch (e) {
648
693
  (0, core_1.LogError)(e);
649
- throw (e);
694
+ throw e;
650
695
  }
651
696
  }
652
697
  async SetRecordFavoriteStatus(userId, entityName, CompositeKey, isFavorite, contextUser) {
653
698
  try {
654
699
  const currentFavoriteId = await this.GetRecordFavoriteID(userId, entityName, CompositeKey);
655
- if ((currentFavoriteId === null && isFavorite === false) ||
656
- (currentFavoriteId !== null && isFavorite === true))
700
+ if ((currentFavoriteId === null && isFavorite === false) || (currentFavoriteId !== null && isFavorite === true))
657
701
  return; // no change
658
702
  // if we're here that means we need to invert the status, which either means creating a record or deleting a record
659
703
  const e = this.Entities.find((e) => e.Name === entityName);
@@ -680,7 +724,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
680
724
  }
681
725
  catch (e) {
682
726
  (0, core_1.LogError)(e);
683
- throw (e);
727
+ throw e;
684
728
  }
685
729
  }
686
730
  async GetRecordChanges(entityName, compositeKey) {
@@ -690,7 +734,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
690
734
  }
691
735
  catch (e) {
692
736
  (0, core_1.LogError)(e);
693
- throw (e);
737
+ throw e;
694
738
  }
695
739
  }
696
740
  /**
@@ -709,24 +753,24 @@ class SQLServerDataProvider extends core_1.ProviderBase {
709
753
  // we do this in SQL by combining the pirmary key name and value for each row using the default separator defined by the CompositeKey class
710
754
  // the output of this should be like the following 'Field1|Value1||Field2|Value2||Field3|Value3' where the || is the CompositeKey.DefaultFieldDelimiter and the | is the CompositeKey.DefaultValueDelimiter
711
755
  const quotes = entity.FirstPrimaryKey.NeedsQuotes ? "'" : '';
712
- const primaryKeySelectString = `CONCAT(${entity.PrimaryKeys.map(pk => `'${pk.Name}|', CAST(${pk.Name} AS NVARCHAR(MAX))`).join(`,'${core_1.CompositeKey.DefaultFieldDelimiter}',`)})`;
756
+ const primaryKeySelectString = `CONCAT(${entity.PrimaryKeys.map((pk) => `'${pk.Name}|', CAST(${pk.Name} AS NVARCHAR(MAX))`).join(`,'${core_1.CompositeKey.DefaultFieldDelimiter}',`)})`;
713
757
  // for this entity, check to see if it has any fields that are soft links, and for each of those, generate the SQL
714
758
  entity.Fields.filter((f) => f.EntityIDFieldName && f.EntityIDFieldName.length > 0).forEach((f) => {
715
759
  // each field in f must be processed
716
760
  if (sSQL.length > 0)
717
761
  sSQL += ' UNION ALL ';
718
- // there is a layer of indirection here because each ROW in each of the entity records for this entity/field combination could point to a DIFFERENT
762
+ // there is a layer of indirection here because each ROW in each of the entity records for this entity/field combination could point to a DIFFERENT
719
763
  // entity. We find out which entity it is pointed to via the EntityIDFieldName in the field definition, so we have to filter the rows in the entity
720
764
  // based on that.
721
- sSQL += `SELECT
722
- '${entityName}' AS EntityName,
723
- '${entity.Name}' AS RelatedEntityName,
724
- ${primaryKeySelectString} AS PrimaryKeyValue,
725
- '${f.Name}' AS FieldName
726
- FROM
765
+ sSQL += `SELECT
766
+ '${entityName}' AS EntityName,
767
+ '${entity.Name}' AS RelatedEntityName,
768
+ ${primaryKeySelectString} AS PrimaryKeyValue,
769
+ '${f.Name}' AS FieldName
770
+ FROM
727
771
  [${entity.SchemaName}].[${entity.BaseView}]
728
- WHERE
729
- [${f.EntityIDFieldName}] = ${quotes}${entity.ID}${quotes} AND
772
+ WHERE
773
+ [${f.EntityIDFieldName}] = ${quotes}${entity.ID}${quotes} AND
730
774
  [${f.Name}] = ${quotes}${compositeKey.GetValueByIndex(0)}${quotes}`; // we only use the first primary key value, this is because we don't yet support composite primary keys
731
775
  });
732
776
  });
@@ -738,17 +782,17 @@ class SQLServerDataProvider extends core_1.ProviderBase {
738
782
  const entityInfo = this.Entities.find((e) => e.Name.trim().toLowerCase() === entityDependency.EntityName?.trim().toLowerCase());
739
783
  const quotes = entityInfo.FirstPrimaryKey.NeedsQuotes ? "'" : '';
740
784
  const relatedEntityInfo = this.Entities.find((e) => e.Name.trim().toLowerCase() === entityDependency.RelatedEntityName?.trim().toLowerCase());
741
- const primaryKeySelectString = `CONCAT(${entityInfo.PrimaryKeys.map(pk => `'${pk.Name}|', CAST(${pk.Name} AS NVARCHAR(MAX))`).join(`,'${core_1.CompositeKey.DefaultFieldDelimiter}',`)})`;
785
+ const primaryKeySelectString = `CONCAT(${entityInfo.PrimaryKeys.map((pk) => `'${pk.Name}|', CAST(${pk.Name} AS NVARCHAR(MAX))`).join(`,'${core_1.CompositeKey.DefaultFieldDelimiter}',`)})`;
742
786
  if (sSQL.length > 0)
743
787
  sSQL += ' UNION ALL ';
744
- sSQL += `SELECT
745
- '${entityDependency.EntityName}' AS EntityName,
746
- '${entityDependency.RelatedEntityName}' AS RelatedEntityName,
747
- ${primaryKeySelectString} AS PrimaryKeyValue,
748
- '${entityDependency.FieldName}' AS FieldName
749
- FROM
788
+ sSQL += `SELECT
789
+ '${entityDependency.EntityName}' AS EntityName,
790
+ '${entityDependency.RelatedEntityName}' AS RelatedEntityName,
791
+ ${primaryKeySelectString} AS PrimaryKeyValue,
792
+ '${entityDependency.FieldName}' AS FieldName
793
+ FROM
750
794
  [${relatedEntityInfo.SchemaName}].[${relatedEntityInfo.BaseView}]
751
- WHERE
795
+ WHERE
752
796
  [${entityDependency.FieldName}] = ${this.GetRecordDependencyLinkSQL(entityDependency, entityInfo, relatedEntityInfo, compositeKey)}`;
753
797
  }
754
798
  return sSQL;
@@ -771,8 +815,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
771
815
  return recordDependencies;
772
816
  }
773
817
  // now, we have to construct a query that will return the dependencies for this record, both hard and soft links
774
- const sSQL = this.GetHardLinkDependencySQL(entityDependencies, compositeKey) + '\n' +
775
- this.GetSoftLinkDependencySQL(entityName, compositeKey);
818
+ const sSQL = this.GetHardLinkDependencySQL(entityDependencies, compositeKey) + '\n' + this.GetSoftLinkDependencySQL(entityName, compositeKey);
776
819
  // now, execute the query
777
820
  const result = await this.ExecuteSQL(sSQL);
778
821
  if (!result || result.length === 0) {
@@ -803,7 +846,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
803
846
  EntityName: r.EntityName,
804
847
  RelatedEntityName: r.RelatedEntityName,
805
848
  FieldName: r.FieldName,
806
- PrimaryKey: compositeKey
849
+ PrimaryKey: compositeKey,
807
850
  };
808
851
  recordDependencies.push(recordDependency);
809
852
  }
@@ -812,7 +855,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
812
855
  catch (e) {
813
856
  // log and throw
814
857
  (0, core_1.LogError)(e);
815
- throw (e);
858
+ throw e;
816
859
  }
817
860
  }
818
861
  GetRecordDependencyLinkSQL(dep, entity, relatedEntity, CompositeKey) {
@@ -834,7 +877,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
834
877
  }
835
878
  async GetRecordDuplicates(params, contextUser) {
836
879
  if (!contextUser) {
837
- throw new Error("User context is required to get record duplicates.");
880
+ throw new Error('User context is required to get record duplicates.');
838
881
  }
839
882
  const listEntity = await this.GetEntityObject('Lists');
840
883
  listEntity.ContextCurrentUser = contextUser;
@@ -857,12 +900,12 @@ class SQLServerDataProvider extends core_1.ProviderBase {
857
900
  }
858
901
  let response = {
859
902
  Status: 'Inprogress',
860
- PotentialDuplicateResult: []
903
+ PotentialDuplicateResult: [],
861
904
  };
862
905
  return response;
863
906
  }
864
907
  async MergeRecords(request, contextUser) {
865
- const e = this.Entities.find(e => e.Name.trim().toLowerCase() === request.EntityName.trim().toLowerCase());
908
+ const e = this.Entities.find((e) => e.Name.trim().toLowerCase() === request.EntityName.trim().toLowerCase());
866
909
  if (!e || !e.AllowRecordMerge)
867
910
  throw new Error(`Entity ${request.EntityName} does not allow record merging, check the AllowRecordMerge property in the entity metadata`);
868
911
  const result = {
@@ -875,13 +918,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
875
918
  const mergeRecordLog = await this.StartMergeLogging(request, result, contextUser);
876
919
  try {
877
920
  /*
878
- we will follow this process...
879
- * 1. Begin Transaction
880
- * 2. The surviving record is loaded and fields are updated from the field map, if provided, and the record is saved. If a FieldMap not provided within the request object, this step is skipped.
881
- * 3. For each of the records that will be merged INTO the surviving record, we call the GetEntityDependencies() method and get a list of all other records in the database are linked to the record to be deleted. We then go through each of those dependencies and update the link to point to the SurvivingRecordID and save the record.
882
- * 4. The record to be deleted is then deleted.
883
- * 5. Commit or Rollback Transaction
884
- */
921
+ we will follow this process...
922
+ * 1. Begin Transaction
923
+ * 2. The surviving record is loaded and fields are updated from the field map, if provided, and the record is saved. If a FieldMap not provided within the request object, this step is skipped.
924
+ * 3. For each of the records that will be merged INTO the surviving record, we call the GetEntityDependencies() method and get a list of all other records in the database are linked to the record to be deleted. We then go through each of those dependencies and update the link to point to the SurvivingRecordID and save the record.
925
+ * 4. The record to be deleted is then deleted.
926
+ * 5. Commit or Rollback Transaction
927
+ */
885
928
  // Step 1 - begin transaction
886
929
  await this.BeginTransaction();
887
930
  // Step 2 - update the surviving record, but only do this if we were provided a field map
@@ -891,7 +934,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
891
934
  for (const fieldMap of request.FieldMap) {
892
935
  survivor.Set(fieldMap.FieldName, fieldMap.Value);
893
936
  }
894
- if (!await survivor.Save()) {
937
+ if (!(await survivor.Save())) {
895
938
  result.OverallStatus = 'Error saving survivor record with values from provided field map.';
896
939
  throw new Error(result.OverallStatus);
897
940
  }
@@ -913,13 +956,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
913
956
  await relatedEntity.InnerLoad(dependency.PrimaryKey);
914
957
  relatedEntity.Set(dependency.FieldName, request.SurvivingRecordCompositeKey.GetValueByIndex(0)); // only support single field foreign keys for now
915
958
  /*
916
- if we later support composite foreign keys, we'll need to do this instead, at the moment this code will break as dependency.KeyValuePair is a single value, not an array
917
-
918
- for (let pkv of dependency.KeyValuePairs) {
919
- relatedEntity.Set(dependency.FieldName, pkv.Value);
920
- }
921
- */
922
- if (!await relatedEntity.Save()) {
959
+ if we later support composite foreign keys, we'll need to do this instead, at the moment this code will break as dependency.KeyValuePair is a single value, not an array
960
+
961
+ for (let pkv of dependency.KeyValuePairs) {
962
+ relatedEntity.Set(dependency.FieldName, pkv.Value);
963
+ }
964
+ */
965
+ if (!(await relatedEntity.Save())) {
923
966
  newRecStatus.Success = false;
924
967
  newRecStatus.Message = `Error updating dependency record ${dependency.PrimaryKey.ToString} for entity ${dependency.RelatedEntityName} to point to surviving record ${request.SurvivingRecordCompositeKey.ToString()}`;
925
968
  throw new Error(newRecStatus.Message);
@@ -928,7 +971,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
928
971
  // if we get here, that means that all of the dependencies were updated successfully, so we can now delete the records to be merged
929
972
  const recordToDelete = await this.GetEntityObject(request.EntityName, contextUser);
930
973
  await recordToDelete.InnerLoad(pksToDelete);
931
- if (!await recordToDelete.Delete()) {
974
+ if (!(await recordToDelete.Delete())) {
932
975
  newRecStatus.Message = `Error deleting record ${pksToDelete.ToString()} for entity ${request.EntityName}`;
933
976
  throw new Error(newRecStatus.Message);
934
977
  }
@@ -947,7 +990,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
947
990
  await this.RollbackTransaction();
948
991
  // attempt to persist the status to the DB, although that might fail
949
992
  await this.CompleteMergeLogging(mergeRecordLog, result, contextUser);
950
- throw (e);
993
+ throw e;
951
994
  }
952
995
  }
953
996
  async StartMergeLogging(request, result, contextUser) {
@@ -961,7 +1004,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
961
1004
  throw new Error(`contextUser is null and no CurrentUser is set`);
962
1005
  recordMergeLog.NewRecord();
963
1006
  recordMergeLog.EntityID = entity.ID;
964
- recordMergeLog.SurvivingRecordID = request.SurvivingRecordCompositeKey.Values(); // this would join together all of the primary key values, which is fine as the primary key is a string
1007
+ recordMergeLog.SurvivingRecordID = request.SurvivingRecordCompositeKey.Values(); // this would join together all of the primary key values, which is fine as the primary key is a string
965
1008
  recordMergeLog.InitiatedByUserID = contextUser ? contextUser.ID : this.CurrentUser?.ID;
966
1009
  recordMergeLog.ApprovalStatus = 'Approved';
967
1010
  recordMergeLog.ApprovedByUserID = contextUser ? contextUser.ID : this.CurrentUser?.ID;
@@ -976,7 +1019,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
976
1019
  }
977
1020
  catch (e) {
978
1021
  (0, core_1.LogError)(e);
979
- throw (e);
1022
+ throw e;
980
1023
  }
981
1024
  }
982
1025
  async CompleteMergeLogging(recordMergeLog, result, contextUser) {
@@ -986,18 +1029,19 @@ class SQLServerDataProvider extends core_1.ProviderBase {
986
1029
  throw new Error(`contextUser is null and no CurrentUser is set`);
987
1030
  recordMergeLog.ProcessingStatus = result.Success ? 'Complete' : 'Error';
988
1031
  recordMergeLog.ProcessingEndedAt = new Date();
989
- if (!result.Success) // only create the log record if the merge failed, otherwise it is wasted space
1032
+ if (!result.Success)
1033
+ // only create the log record if the merge failed, otherwise it is wasted space
990
1034
  recordMergeLog.ProcessingLog = result.OverallStatus;
991
1035
  if (await recordMergeLog.Save()) {
992
1036
  // top level saved, now let's create the deletion detail records for each of the records that were merged
993
1037
  for (const d of result.RecordStatus) {
994
- const recordMergeDeletionLog = await this.GetEntityObject('Record Merge Deletion Logs', contextUser);
1038
+ const recordMergeDeletionLog = (await this.GetEntityObject('Record Merge Deletion Logs', contextUser));
995
1039
  recordMergeDeletionLog.NewRecord();
996
1040
  recordMergeDeletionLog.RecordMergeLogID = recordMergeLog.ID;
997
1041
  recordMergeDeletionLog.DeletedRecordID = d.CompositeKey.Values(); // this would join together all of the primary key values, which is fine as the primary key is a string
998
1042
  recordMergeDeletionLog.Status = d.Success ? 'Complete' : 'Error';
999
1043
  recordMergeDeletionLog.ProcessingLog = d.Success ? null : d.Message; // only save the message if it failed
1000
- if (!await recordMergeDeletionLog.Save())
1044
+ if (!(await recordMergeDeletionLog.Save()))
1001
1045
  throw new Error(`Error saving record merge deletion log`);
1002
1046
  }
1003
1047
  }
@@ -1012,13 +1056,14 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1012
1056
  }
1013
1057
  GetSaveSQL(entity, bNewRecord, spName, user) {
1014
1058
  const sSimpleSQL = `EXEC [${entity.EntityInfo.SchemaName}].${spName} ${this.generateSPParams(entity, !bNewRecord)}`;
1015
- const recordChangesEntityInfo = this.Entities.find(e => e.Name === 'Record Changes');
1059
+ const recordChangesEntityInfo = this.Entities.find((e) => e.Name === 'Record Changes');
1016
1060
  let sSQL = '';
1017
- if (entity.EntityInfo.TrackRecordChanges && entity.EntityInfo.Name.trim().toLowerCase() !== 'record changes') { // don't track changes for the record changes entity
1061
+ if (entity.EntityInfo.TrackRecordChanges && entity.EntityInfo.Name.trim().toLowerCase() !== 'record changes') {
1062
+ // don't track changes for the record changes entity
1018
1063
  let oldData = null;
1019
1064
  // use SQL Server CONCAT function to combine all of the primary key values and then combine them together
1020
1065
  // using the default field delimiter and default value delimiter as defined in the CompositeKey class
1021
- const concatPKIDString = `CONCAT(${entity.EntityInfo.PrimaryKeys.map(pk => `'${pk.CodeName}','${core_1.CompositeKey.DefaultValueDelimiter}',${pk.Name}`).join(`,'${core_1.CompositeKey.DefaultFieldDelimiter}',`)})`;
1066
+ const concatPKIDString = `CONCAT(${entity.EntityInfo.PrimaryKeys.map((pk) => `'${pk.CodeName}','${core_1.CompositeKey.DefaultValueDelimiter}',${pk.Name}`).join(`,'${core_1.CompositeKey.DefaultFieldDelimiter}',`)})`;
1022
1067
  if (!bNewRecord)
1023
1068
  oldData = entity.GetAll(true); // get all the OLD values, only do for existing records, for new records, not relevant
1024
1069
  sSQL = `
@@ -1029,13 +1074,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1029
1074
  INSERT INTO @ResultTable
1030
1075
  ${sSimpleSQL}
1031
1076
 
1032
- DECLARE @ID NVARCHAR(MAX)
1077
+ DECLARE @ID NVARCHAR(MAX)
1033
1078
  SELECT @ID = ${concatPKIDString} FROM @ResultTable
1034
- IF @ID IS NOT NULL
1079
+ IF @ID IS NOT NULL
1035
1080
  BEGIN
1036
1081
  DECLARE @ResultChangesTable TABLE (
1037
- ${this.getAllEntityColumnsSQL(recordChangesEntityInfo)}
1038
- )
1082
+ ${this.getAllEntityColumnsSQL(recordChangesEntityInfo)}
1083
+ )
1039
1084
 
1040
1085
  INSERT INTO @ResultChangesTable
1041
1086
  ${this.GetLogRecordChangeSQL(entity.GetAll(false), oldData, entity.EntityInfo.Name, '@ID', entity.EntityInfo, bNewRecord ? 'Create' : 'Update', user, false)}
@@ -1050,8 +1095,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1050
1095
  return sSQL;
1051
1096
  }
1052
1097
  GetEntityAIActions(entityInfo, before) {
1053
- return aiengine_1.AIEngine.Instance.EntityAIActions.filter((a) => a.EntityID === entityInfo.ID &&
1054
- a.TriggerEvent.toLowerCase().trim() === (before ? 'before save' : 'after save'));
1098
+ return aiengine_1.AIEngine.Instance.EntityAIActions.filter((a) => a.EntityID === entityInfo.ID && a.TriggerEvent.toLowerCase().trim() === (before ? 'before save' : 'after save'));
1055
1099
  }
1056
1100
  async HandleEntityActions(entity, baseType, before, user) {
1057
1101
  // use the EntityActionEngine for this
@@ -1060,7 +1104,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1060
1104
  await engine.Config(false, user);
1061
1105
  const newRecord = entity.IsSaved ? false : true;
1062
1106
  const baseTypeType = baseType === 'save' ? (newRecord ? 'Create' : 'Update') : 'Delete';
1063
- const invocationType = baseType === 'validate' ? 'Validate' : (before ? 'Before' + baseTypeType : 'After' + baseTypeType);
1107
+ const invocationType = baseType === 'validate' ? 'Validate' : before ? 'Before' + baseTypeType : 'After' + baseTypeType;
1064
1108
  const invocationTypeEntity = engine.InvocationTypes.find((i) => i.Name === invocationType);
1065
1109
  if (!invocationTypeEntity) {
1066
1110
  (0, core_1.LogError)(`Invocation Type ${invocationType} not found in metadata`);
@@ -1074,7 +1118,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1074
1118
  EntityAction: a,
1075
1119
  EntityObject: entity,
1076
1120
  InvocationType: invocationTypeEntity,
1077
- ContextUser: user
1121
+ ContextUser: user,
1078
1122
  });
1079
1123
  results.push(result);
1080
1124
  }
@@ -1106,13 +1150,12 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1106
1150
  const ai = aiengine_1.AIEngine.Instance;
1107
1151
  for (let i = 0; i < actions.length; i++) {
1108
1152
  const a = actions[i];
1109
- if (a.TriggerEvent === 'before save' && before ||
1110
- a.TriggerEvent === 'after save' && !before) {
1153
+ if ((a.TriggerEvent === 'before save' && before) || (a.TriggerEvent === 'after save' && !before)) {
1111
1154
  const p = {
1112
1155
  entityAIActionId: a.ID,
1113
1156
  entityRecord: entity,
1114
1157
  actionId: a.AIActionID,
1115
- modelId: a.AIModelID
1158
+ modelId: a.AIModelID,
1116
1159
  };
1117
1160
  if (before) {
1118
1161
  // do it with await so we're blocking, as it needs to complete before the record save continues
@@ -1154,11 +1197,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1154
1197
  else {
1155
1198
  // getting here means we are good to save, now check to see if we're dirty and need to save
1156
1199
  // REMEMBER - this is the provider and the BaseEntity/subclasses handle user-level permission checking already, we just make sure API was turned on for the operation
1157
- if (entity.Dirty || (options.IgnoreDirtyState || options.ReplayOnly)) {
1200
+ if (entity.Dirty || options.IgnoreDirtyState || options.ReplayOnly) {
1158
1201
  entityResult.StartedAt = new Date();
1159
1202
  entityResult.Type = bNewRecord ? 'create' : 'update';
1160
- entityResult.OriginalValues = entity.Fields.map(f => { return { FieldName: f.Name, Value: f.Value }; }); // save the original values before we start the process
1161
- entity.ResultHistory.push(entityResult); // push the new result as we have started a process
1203
+ entityResult.OriginalValues = entity.Fields.map((f) => {
1204
+ return { FieldName: f.Name, Value: f.Value };
1205
+ }); // save the original values before we start the process
1206
+ entity.ResultHistory.push(entityResult); // push the new result as we have started a process
1162
1207
  // The assumption is that Validate() has already been called by the BaseEntity object that is invoking this provider.
1163
1208
  // However, we have an extra responsibility in this situation which is to fire off the EntityActions for the Validate invocation type and
1164
1209
  // make sure they clear. If they don't clear we throw an exception with the message provided.
@@ -1166,7 +1211,10 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1166
1211
  const validationResult = await this.HandleEntityActions(entity, 'validate', false, user);
1167
1212
  if (validationResult && validationResult.length > 0) {
1168
1213
  // one or more actions executed, see the reults and if any failed, concat their messages and return as exception being thrown
1169
- const message = validationResult.filter(v => !v.Success).map(v => v.Message).join('\n\n');
1214
+ const message = validationResult
1215
+ .filter((v) => !v.Success)
1216
+ .map((v) => v.Message)
1217
+ .join('\n\n');
1170
1218
  if (message) {
1171
1219
  entityResult.Success = false;
1172
1220
  entityResult.EndedAt = new Date();
@@ -1178,8 +1226,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1178
1226
  else {
1179
1227
  // we are in replay mode we so do NOT need to do the validation stuff, skipping it...
1180
1228
  }
1181
- const spName = bNewRecord ? (entity.EntityInfo.spCreate && entity.EntityInfo.spCreate.length > 0 ? entity.EntityInfo.spCreate : 'spCreate' + entity.EntityInfo.BaseTable) :
1182
- (entity.EntityInfo.spUpdate && entity.EntityInfo.spUpdate.length > 0 ? entity.EntityInfo.spUpdate : 'spUpdate' + entity.EntityInfo.BaseTable);
1229
+ const spName = bNewRecord
1230
+ ? entity.EntityInfo.spCreate && entity.EntityInfo.spCreate.length > 0
1231
+ ? entity.EntityInfo.spCreate
1232
+ : 'spCreate' + entity.EntityInfo.BaseTable
1233
+ : entity.EntityInfo.spUpdate && entity.EntityInfo.spUpdate.length > 0
1234
+ ? entity.EntityInfo.spUpdate
1235
+ : 'spUpdate' + entity.EntityInfo.BaseTable;
1183
1236
  if (options.SkipEntityActions !== true /*options set, but not set to skip entity actions*/) {
1184
1237
  await this.HandleEntityActions(entity, 'save', true, user);
1185
1238
  }
@@ -1198,7 +1251,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1198
1251
  this._bAllowRefresh = false; // stop refreshes of metadata while we're doing work
1199
1252
  entity.TransactionGroup.AddTransaction(new core_1.TransactionItem(entity, sSQL, null, { dataSource: this._dataSource }, (results, success) => {
1200
1253
  // we get here whenever the transaction group does gets around to committing
1201
- // our query.
1254
+ // our query.
1202
1255
  this._bAllowRefresh = true; // allow refreshes again
1203
1256
  entityResult.EndedAt = new Date();
1204
1257
  if (success && results) {
@@ -1214,7 +1267,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1214
1267
  resolve(results[0]);
1215
1268
  }
1216
1269
  else {
1217
- // the transaction failed, nothing to update, but we need to call Reject so the
1270
+ // the transaction failed, nothing to update, but we need to call Reject so the
1218
1271
  // promise resolves with a rejection so our outer caller knows
1219
1272
  entityResult.Success = false;
1220
1273
  entityResult.Message = 'Transaction Failed';
@@ -1276,18 +1329,18 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1276
1329
  const f = entity.EntityInfo.Fields[i];
1277
1330
  if (f.AllowUpdateAPI) {
1278
1331
  if (!f.SkipValidation) {
1279
- // DO NOT INCLUDE any fields where we skip validation, these are fields that are not editable by the user/object
1332
+ // DO NOT INCLUDE any fields where we skip validation, these are fields that are not editable by the user/object
1280
1333
  // model/api because they're special fields like ID, CreatedAt, etc. or they're virtual or auto-increment, etc.
1281
1334
  let value = entity.Get(f.Name);
1282
1335
  if (f.Type.trim().toLowerCase() === 'datetimeoffset') {
1283
1336
  value = new Date(value).toISOString();
1284
1337
  }
1285
1338
  else if (!isUpdate && f.Type.trim().toLowerCase() === 'uniqueidentifier') {
1286
- // in the case of unique identifiers, for CREATE procs only,
1339
+ // in the case of unique identifiers, for CREATE procs only,
1287
1340
  // we need to check to see if the value we have in the entity object is a function like newid() or newsquentialid()
1288
- // in those cases we should just skip the parameter entirely because that means there is a default value that should be used
1341
+ // in those cases we should just skip the parameter entirely because that means there is a default value that should be used
1289
1342
  // and that will be handled by the database not by us
1290
- // instead of just checking for specific functions like newid(), we can instead check for any string that includes ()
1343
+ // instead of just checking for specific functions like newid(), we can instead check for any string that includes ()
1291
1344
  // this way we can handle any function that the database might support in the future
1292
1345
  if (typeof value === 'string' && value.includes('()')) {
1293
1346
  continue; // skip this field entirely by going to the next iteration of the loop
@@ -1362,7 +1415,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1362
1415
  }
1363
1416
  else
1364
1417
  pVal = paramValue;
1365
- return (paramValue === null || paramValue === undefined) ? "NULL" : quoteString + pVal + quoteString;
1418
+ return paramValue === null || paramValue === undefined ? 'NULL' : quoteString + pVal + quoteString;
1366
1419
  }
1367
1420
  GetLogRecordChangeSQL(newData, oldData, entityName, recordID, entityInfo, type, user, wrapRecordIdInQuotes) {
1368
1421
  const fullRecordJSON = JSON.stringify(this.escapeQuotesInProperties(newData ? newData : oldData, "'")); // stringify old data if we don't have new - means we are DELETING A RECORD
@@ -1371,14 +1424,14 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1371
1424
  if (changesKeys.length > 0 || oldData === null /*new record*/ || newData === null /*deleted record*/) {
1372
1425
  const changesJSON = changes !== null ? JSON.stringify(changes) : '';
1373
1426
  const quotes = wrapRecordIdInQuotes ? "'" : '';
1374
- const sSQL = `EXEC [${this.MJCoreSchemaName}].spCreateRecordChange_Internal @EntityName='${entityName}',
1375
- @RecordID=${quotes}${recordID}${quotes},
1427
+ const sSQL = `EXEC [${this.MJCoreSchemaName}].spCreateRecordChange_Internal @EntityName='${entityName}',
1428
+ @RecordID=${quotes}${recordID}${quotes},
1376
1429
  @UserID='${user.ID}',
1377
1430
  @Type='${type}',
1378
- @ChangesJSON='${changesJSON}',
1379
- @ChangesDescription='${oldData && newData ? this.CreateUserDescriptionOfChanges(changes) : !oldData ? 'Record Created' : 'Record Deleted'}',
1380
- @FullRecordJSON='${fullRecordJSON}',
1381
- @Status='Complete',
1431
+ @ChangesJSON='${changesJSON}',
1432
+ @ChangesDescription='${oldData && newData ? this.CreateUserDescriptionOfChanges(changes) : !oldData ? 'Record Created' : 'Record Deleted'}',
1433
+ @FullRecordJSON='${fullRecordJSON}',
1434
+ @Status='Complete',
1382
1435
  @Comments=null`;
1383
1436
  return sSQL;
1384
1437
  }
@@ -1407,11 +1460,14 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1407
1460
  if (sRet.length > 0) {
1408
1461
  sRet += '\n';
1409
1462
  }
1410
- if (change.oldValue && change.newValue) // both old and new values set, show change
1463
+ if (change.oldValue && change.newValue)
1464
+ // both old and new values set, show change
1411
1465
  sRet += `${change.field} changed from ${this.trimString(change.oldValue, maxValueLength, cutOffText)} to ${this.trimString(change.newValue, maxValueLength, cutOffText)}`;
1412
- else if (change.newValue) // old value was blank, new value isn't
1466
+ else if (change.newValue)
1467
+ // old value was blank, new value isn't
1413
1468
  sRet += `${change.field} set to ${this.trimString(change.newValue, maxValueLength, cutOffText)}`;
1414
- else if (change.oldValue) // new value is blank, old value wasn't
1469
+ else if (change.oldValue)
1470
+ // new value is blank, old value wasn't
1415
1471
  sRet += `${change.field} cleared from ${this.trimString(change.oldValue, maxValueLength, cutOffText)}`;
1416
1472
  }
1417
1473
  return sRet.replace(/'/g, "''");
@@ -1451,12 +1507,11 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1451
1507
  else {
1452
1508
  const changes = {};
1453
1509
  for (const key in newData) {
1454
- const f = entityInfo.Fields.find(f => f.Name.toLowerCase() === key.toLowerCase());
1510
+ const f = entityInfo.Fields.find((f) => f.Name.toLowerCase() === key.toLowerCase());
1455
1511
  let bDiff = false;
1456
1512
  if (f.ReadOnly)
1457
1513
  bDiff = false; // read only fields are never different, they can change in the database, but we don't consider them to be a change for record changes purposes.
1458
- else if ((oldData[key] == undefined || oldData[key] == null) &&
1459
- (newData[key] == undefined || newData[key] == null))
1514
+ else if ((oldData[key] == undefined || oldData[key] == null) && (newData[key] == undefined || newData[key] == null))
1460
1515
  bDiff = false; // this branch of logic ensures that undefined and null are treated the same
1461
1516
  else {
1462
1517
  switch (f.TSType) {
@@ -1464,7 +1519,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1464
1519
  bDiff = oldData[key] !== newData[key];
1465
1520
  break;
1466
1521
  case core_1.EntityFieldTSType.Date:
1467
- bDiff = (new Date(oldData[key]).getTime() !== new Date(newData[key]).getTime());
1522
+ bDiff = new Date(oldData[key]).getTime() !== new Date(newData[key]).getTime();
1468
1523
  break;
1469
1524
  case core_1.EntityFieldTSType.Number:
1470
1525
  case core_1.EntityFieldTSType.Boolean:
@@ -1475,14 +1530,12 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1475
1530
  if (bDiff) {
1476
1531
  // make sure we escape things properly
1477
1532
  const r = new RegExp(quoteToEscape, 'g');
1478
- const o = (oldData[key] && typeof oldData[key] === 'string') ?
1479
- oldData[key].replace(r, quoteToEscape + quoteToEscape) : oldData[key];
1480
- const n = (newData[key] && typeof newData[key] === 'string') ?
1481
- newData[key].replace(r, quoteToEscape + quoteToEscape) : newData[key];
1533
+ const o = oldData[key] && typeof oldData[key] === 'string' ? oldData[key].replace(r, quoteToEscape + quoteToEscape) : oldData[key];
1534
+ const n = newData[key] && typeof newData[key] === 'string' ? newData[key].replace(r, quoteToEscape + quoteToEscape) : newData[key];
1482
1535
  changes[key] = {
1483
1536
  field: key,
1484
1537
  oldValue: o,
1485
- newValue: n
1538
+ newValue: n,
1486
1539
  };
1487
1540
  }
1488
1541
  }
@@ -1505,28 +1558,29 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1505
1558
  if (EntityRelationshipsToLoad && EntityRelationshipsToLoad.length > 0) {
1506
1559
  for (let i = 0; i < EntityRelationshipsToLoad.length; i++) {
1507
1560
  const rel = EntityRelationshipsToLoad[i];
1508
- const relInfo = entity.EntityInfo.RelatedEntities.find(r => r.RelatedEntity == rel);
1561
+ const relInfo = entity.EntityInfo.RelatedEntities.find((r) => r.RelatedEntity == rel);
1509
1562
  if (relInfo) {
1510
1563
  let relSql = '';
1511
- const relEntitySchemaName = this.Entities.find(e => e.Name.trim().toLowerCase() === relInfo.RelatedEntity.trim().toLowerCase())?.SchemaName;
1564
+ const relEntitySchemaName = this.Entities.find((e) => e.Name.trim().toLowerCase() === relInfo.RelatedEntity.trim().toLowerCase())?.SchemaName;
1512
1565
  const quotes = entity.FirstPrimaryKey.NeedsQuotes ? "'" : '';
1513
1566
  if (relInfo.Type.trim().toLowerCase() === 'one to many')
1514
1567
  // one to many - simple query
1515
- relSql = ` SELECT
1516
- *
1517
- FROM
1518
- [${relEntitySchemaName}].[${relInfo.RelatedEntityBaseView}]
1519
- WHERE
1520
- [${relInfo.RelatedEntityJoinField}] = ${quotes}${ret[entity.FirstPrimaryKey.Name]}${quotes}`; // don't yet support composite foreign keys
1568
+ relSql = ` SELECT
1569
+ *
1570
+ FROM
1571
+ [${relEntitySchemaName}].[${relInfo.RelatedEntityBaseView}]
1572
+ WHERE
1573
+ [${relInfo.RelatedEntityJoinField}] = ${quotes}${ret[entity.FirstPrimaryKey.Name]}${quotes}`;
1574
+ // don't yet support composite foreign keys
1575
+ // many to many - need to use join view
1521
1576
  else
1522
- // many to many - need to use join view
1523
- relSql = ` SELECT
1524
- _theview.*
1525
- FROM
1526
- [${relEntitySchemaName}].[${relInfo.RelatedEntityBaseView}] _theview
1527
- INNER JOIN
1528
- [${relEntitySchemaName}].[${relInfo.JoinView}] _jv ON _theview.[${relInfo.RelatedEntityJoinField}] = _jv.[${relInfo.JoinEntityInverseJoinField}]
1529
- WHERE
1577
+ relSql = ` SELECT
1578
+ _theview.*
1579
+ FROM
1580
+ [${relEntitySchemaName}].[${relInfo.RelatedEntityBaseView}] _theview
1581
+ INNER JOIN
1582
+ [${relEntitySchemaName}].[${relInfo.JoinView}] _jv ON _theview.[${relInfo.RelatedEntityJoinField}] = _jv.[${relInfo.JoinEntityInverseJoinField}]
1583
+ WHERE
1530
1584
  _jv.${relInfo.JoinEntityJoinField} = ${quotes}${ret[entity.FirstPrimaryKey.Name]}${quotes}`; // don't yet support composite foreign keys
1531
1585
  const relData = await this.ExecuteSQL(relSql);
1532
1586
  if (relData && relData.length > 0) {
@@ -1549,8 +1603,9 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1549
1603
  return `@${f.CodeName}=${quotes}${kv.Value}${quotes}`;
1550
1604
  }).join(', ');
1551
1605
  const sSimpleSQL = `EXEC [${entity.EntityInfo.SchemaName}].[${spName}] ${sParams}`;
1552
- const recordChangesEntityInfo = this.Entities.find(e => e.Name === 'Record Changes');
1553
- if (entity.EntityInfo.TrackRecordChanges && entity.EntityInfo.Name.trim().toLowerCase() !== 'record changes') { // don't track changes for the record changes entity
1606
+ const recordChangesEntityInfo = this.Entities.find((e) => e.Name === 'Record Changes');
1607
+ if (entity.EntityInfo.TrackRecordChanges && entity.EntityInfo.Name.trim().toLowerCase() !== 'record changes') {
1608
+ // don't track changes for the record changes entity
1554
1609
  const oldData = entity.GetAll(true); // get all the OLD values
1555
1610
  const sTableDeclare = entity.PrimaryKeys.map((pk) => {
1556
1611
  return `${pk.CodeName} ${pk.EntityFieldInfo.SQLFullType}`;
@@ -1581,11 +1636,11 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1581
1636
 
1582
1637
  DECLARE ${sVariableDeclare}
1583
1638
  SELECT ${sSelectDeclare} FROM @ResultTable
1584
- IF ${sIF}
1639
+ IF ${sIF}
1585
1640
  BEGIN
1586
1641
  DECLARE @ResultChangesTable TABLE (
1587
- ${this.getAllEntityColumnsSQL(recordChangesEntityInfo)}
1588
- )
1642
+ ${this.getAllEntityColumnsSQL(recordChangesEntityInfo)}
1643
+ )
1589
1644
 
1590
1645
  INSERT INTO @ResultChangesTable
1591
1646
  ${this.GetLogRecordChangeSQL(null /*pass in null for new data for deleted records*/, oldData, entity.EntityInfo.Name, sCombinedPrimaryKey, entity.EntityInfo, 'Delete', user, true)}
@@ -1615,7 +1670,9 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1615
1670
  throw new Error(`Delete() isn't callable for ${entity.EntityInfo.Name} as AllowDeleteAPI is false`);
1616
1671
  result.StartedAt = new Date();
1617
1672
  result.Type = 'delete';
1618
- result.OriginalValues = entity.Fields.map(f => { return { FieldName: f.Name, Value: f.Value }; }); // save the original values before we start the process
1673
+ result.OriginalValues = entity.Fields.map((f) => {
1674
+ return { FieldName: f.Name, Value: f.Value };
1675
+ }); // save the original values before we start the process
1619
1676
  entity.ResultHistory.push(result); // push the new result as we have started a process
1620
1677
  // REMEMBER - this is the provider and the BaseEntity/subclasses handle user-level permission checking already, we just make sure API was turned on for the operation
1621
1678
  // if we get here we can delete, so build the SQL and then handle appropriately either as part of TransGroup or directly...
@@ -1633,7 +1690,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1633
1690
  // and when the transaction is committed, we will send all the queries at once
1634
1691
  entity.TransactionGroup.AddTransaction(new core_1.TransactionItem(entity, sSQL, null, { dataSource: this._dataSource }, (results, success) => {
1635
1692
  // we get here whenever the transaction group does gets around to committing
1636
- // our query.
1693
+ // our query.
1637
1694
  result.EndedAt = new Date();
1638
1695
  if (success && results) {
1639
1696
  // Entity AI Actions and Actions - fired off async, NO await on purpose
@@ -1652,9 +1709,9 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1652
1709
  result.Success = true;
1653
1710
  resolve(true);
1654
1711
  }
1712
+ // the transaction failed, nothing to update, but we need to call Reject so the
1713
+ // promise resolves with a rejection so our outer caller knows
1655
1714
  else
1656
- // the transaction failed, nothing to update, but we need to call Reject so the
1657
- // promise resolves with a rejection so our outer caller knows
1658
1715
  result.Success = false;
1659
1716
  result.Message = 'Transaction failed to commit';
1660
1717
  reject(results);
@@ -1701,23 +1758,23 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1701
1758
  // START ---- IMetadataProvider
1702
1759
  /**************************************************************************/
1703
1760
  async GetDatasetByName(datasetName, itemFilters) {
1704
- const sSQL = `SELECT
1761
+ const sSQL = `SELECT
1705
1762
  di.*,
1706
1763
  e.BaseView EntityBaseView,
1707
1764
  e.SchemaName EntitySchemaName,
1708
1765
  di.__mj_UpdatedAt AS DatasetItemUpdatedAt,
1709
1766
  d.__mj_UpdatedAt AS DatasetUpdatedAt
1710
- FROM
1711
- [${this.MJCoreSchemaName}].vwDatasets d
1712
- INNER JOIN
1713
- [${this.MJCoreSchemaName}].vwDatasetItems di
1767
+ FROM
1768
+ [${this.MJCoreSchemaName}].vwDatasets d
1769
+ INNER JOIN
1770
+ [${this.MJCoreSchemaName}].vwDatasetItems di
1714
1771
  ON
1715
1772
  d.ID = di.DatasetID
1716
- INNER JOIN
1717
- [${this.MJCoreSchemaName}].vwEntities e
1773
+ INNER JOIN
1774
+ [${this.MJCoreSchemaName}].vwEntities e
1718
1775
  ON
1719
1776
  di.EntityID = e.ID
1720
- WHERE
1777
+ WHERE
1721
1778
  d.Name = @0`;
1722
1779
  const items = await this.ExecuteSQL(sSQL, [datasetName]);
1723
1780
  // now we have the dataset and the items, we need to get the update date from the items underlying entities
@@ -1729,7 +1786,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1729
1786
  // execute all promises in parallel
1730
1787
  const results = await Promise.all(promises);
1731
1788
  // determine overall success
1732
- const bSuccess = results.every(result => result.Success);
1789
+ const bSuccess = results.every((result) => result.Success);
1733
1790
  // get the latest update date from all the results
1734
1791
  const latestUpdateDate = results.reduce((acc, result) => {
1735
1792
  if (result.LatestUpdatedDate && result.LatestUpdatedDate > acc) {
@@ -1743,12 +1800,12 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1743
1800
  Success: bSuccess,
1744
1801
  Status: '',
1745
1802
  LatestUpdateDate: latestUpdateDate,
1746
- Results: results
1803
+ Results: results,
1747
1804
  };
1748
1805
  }
1749
1806
  else {
1750
1807
  return {
1751
- DatasetID: "",
1808
+ DatasetID: '',
1752
1809
  DatasetName: datasetName,
1753
1810
  Success: false,
1754
1811
  Status: 'No Dataset or Items found for DatasetName: ' + datasetName,
@@ -1760,7 +1817,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1760
1817
  async GetDatasetItem(item, itemFilters, datasetName) {
1761
1818
  let filterSQL = '';
1762
1819
  if (itemFilters && itemFilters.length > 0) {
1763
- const filter = itemFilters.find(f => f.ItemCode === item.Code);
1820
+ const filter = itemFilters.find((f) => f.ItemCode === item.Code);
1764
1821
  if (filter)
1765
1822
  filterSQL = (item.WhereClause ? ' AND ' : ' WHERE ') + '(' + filter.Filter + ')';
1766
1823
  }
@@ -1777,7 +1834,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1777
1834
  Results: null,
1778
1835
  LatestUpdateDate: null,
1779
1836
  Status: 'Invalid columns specified for dataset item',
1780
- Success: false
1837
+ Success: false,
1781
1838
  };
1782
1839
  }
1783
1840
  const itemSQL = `SELECT ${columns} FROM [${item.EntitySchemaName}].[${item.EntityBaseView}] ${item.WhereClause ? 'WHERE ' + item.WhereClause : ''}${filterSQL}`;
@@ -1801,7 +1858,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1801
1858
  Code: item.Code,
1802
1859
  Results: itemData,
1803
1860
  LatestUpdateDate: latestUpdateDate,
1804
- Success: itemData !== null && itemData !== undefined
1861
+ Success: itemData !== null && itemData !== undefined,
1805
1862
  };
1806
1863
  }
1807
1864
  /**
@@ -1812,10 +1869,10 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1812
1869
  * @returns
1813
1870
  */
1814
1871
  GetColumnsForDatasetItem(item, datasetName) {
1815
- const specifiedColumns = item.Columns ? item.Columns.split(',').map(col => col.trim()) : [];
1872
+ const specifiedColumns = item.Columns ? item.Columns.split(',').map((col) => col.trim()) : [];
1816
1873
  if (specifiedColumns.length > 0) {
1817
- // validate that the columns specified are valid within the entity metadata
1818
- const entity = this.Entities.find(e => e.ID === item.EntityID);
1874
+ // validate that the columns specified are valid within the entity metadata
1875
+ const entity = this.Entities.find((e) => e.ID === item.EntityID);
1819
1876
  if (!entity && this.Entities.length > 0) {
1820
1877
  // we have loaded entities (e.g. Entites.length > 0) but the entity wasn't found, log an error and return a failed result
1821
1878
  // the reason we continue below if we have NOT loaded Entities is that when the system first bootstraps, DATASET gets loaded
@@ -1826,11 +1883,11 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1826
1883
  }
1827
1884
  else {
1828
1885
  if (entity) {
1829
- // have a valid entity, now make sure that all of the columns specified are valid
1886
+ // have a valid entity, now make sure that all of the columns specified are valid
1830
1887
  // only do the column validity check if we have an entity, we can get here if the entity wasn't found IF we haven't loaded entities yet per above comment
1831
1888
  const invalidColumns = [];
1832
1889
  specifiedColumns.forEach((col) => {
1833
- if (!entity.Fields.find(f => f.Name.trim().toLowerCase() === col.trim().toLowerCase())) {
1890
+ if (!entity.Fields.find((f) => f.Name.trim().toLowerCase() === col.trim().toLowerCase())) {
1834
1891
  invalidColumns.push(col);
1835
1892
  }
1836
1893
  });
@@ -1841,35 +1898,34 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1841
1898
  }
1842
1899
  // check to see if the specified columns include the DateFieldToCheck
1843
1900
  // in the below we only check entity metadata if we have it, if we don't have it, we just add the special fields back in
1844
- if (item.DateFieldToCheck && item.DateFieldToCheck.trim().length > 0 &&
1845
- specifiedColumns.indexOf(item.DateFieldToCheck) === -1) {
1901
+ if (item.DateFieldToCheck && item.DateFieldToCheck.trim().length > 0 && specifiedColumns.indexOf(item.DateFieldToCheck) === -1) {
1846
1902
  // we only check the entity if we have it, otherwise we just add it back in
1847
- if (!entity || entity.Fields.find(f => f.Name.trim().toLowerCase() === item.DateFieldToCheck.trim().toLowerCase()))
1903
+ if (!entity || entity.Fields.find((f) => f.Name.trim().toLowerCase() === item.DateFieldToCheck.trim().toLowerCase()))
1848
1904
  specifiedColumns.push(item.DateFieldToCheck);
1849
1905
  }
1850
1906
  }
1851
1907
  }
1852
- return specifiedColumns.length > 0 ? specifiedColumns.map(colName => `[${colName.trim()}]`).join(',') : '*';
1908
+ return specifiedColumns.length > 0 ? specifiedColumns.map((colName) => `[${colName.trim()}]`).join(',') : '*';
1853
1909
  }
1854
1910
  async GetDatasetStatusByName(datasetName, itemFilters) {
1855
1911
  const sSQL = `
1856
- SELECT
1912
+ SELECT
1857
1913
  di.*,
1858
1914
  e.BaseView EntityBaseView,
1859
1915
  e.SchemaName EntitySchemaName,
1860
1916
  d.__mj_UpdatedAt AS DatasetUpdatedAt,
1861
- di.__mj_UpdatedAt AS DatasetItemUpdatedAt
1862
- FROM
1863
- [${this.MJCoreSchemaName}].vwDatasets d
1864
- INNER JOIN
1865
- [${this.MJCoreSchemaName}].vwDatasetItems di
1917
+ di.__mj_UpdatedAt AS DatasetItemUpdatedAt
1918
+ FROM
1919
+ [${this.MJCoreSchemaName}].vwDatasets d
1920
+ INNER JOIN
1921
+ [${this.MJCoreSchemaName}].vwDatasetItems di
1866
1922
  ON
1867
1923
  d.ID = di.DatasetID
1868
1924
  INNER JOIN
1869
1925
  [${this.MJCoreSchemaName}].vwEntities e
1870
1926
  ON
1871
1927
  di.EntityID = e.ID
1872
- WHERE
1928
+ WHERE
1873
1929
  d.Name = @0`;
1874
1930
  const items = await this.ExecuteSQL(sSQL, [datasetName]);
1875
1931
  // now we have the dataset and the items, we need to get the update date from the items underlying entities
@@ -1880,22 +1936,22 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1880
1936
  items.forEach((item, index) => {
1881
1937
  let filterSQL = '';
1882
1938
  if (itemFilters && itemFilters.length > 0) {
1883
- const filter = itemFilters.find(f => f.ItemCode === item.Code);
1939
+ const filter = itemFilters.find((f) => f.ItemCode === item.Code);
1884
1940
  if (filter)
1885
1941
  filterSQL = ' WHERE ' + filter.Filter;
1886
1942
  }
1887
1943
  const itemUpdatedAt = new Date(item.DatasetItemUpdatedAt);
1888
1944
  const datasetUpdatedAt = new Date(item.DatasetUpdatedAt);
1889
1945
  const datasetMaxUpdatedAt = new Date(Math.max(itemUpdatedAt.getTime(), datasetUpdatedAt.getTime())).toISOString();
1890
- const itemSQL = `SELECT
1891
- CASE
1892
- WHEN MAX(${item.DateFieldToCheck}) > '${datasetMaxUpdatedAt}' THEN MAX(${item.DateFieldToCheck})
1893
- ELSE '${datasetMaxUpdatedAt}'
1946
+ const itemSQL = `SELECT
1947
+ CASE
1948
+ WHEN MAX(${item.DateFieldToCheck}) > '${datasetMaxUpdatedAt}' THEN MAX(${item.DateFieldToCheck})
1949
+ ELSE '${datasetMaxUpdatedAt}'
1894
1950
  END AS UpdateDate,
1895
- COUNT(*) AS TheRowCount,
1896
- '${item.EntityID}' AS EntityID,
1897
- '${item.Entity}' AS EntityName
1898
- FROM
1951
+ COUNT(*) AS TheRowCount,
1952
+ '${item.EntityID}' AS EntityID,
1953
+ '${item.Entity}' AS EntityName
1954
+ FROM
1899
1955
  [${item.EntitySchemaName}].[${item.EntityBaseView}]${filterSQL}`;
1900
1956
  combinedSQL += itemSQL;
1901
1957
  if (index < items.length - 1) {
@@ -1905,13 +1961,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1905
1961
  const itemUpdateDates = await this.ExecuteSQL(combinedSQL);
1906
1962
  if (itemUpdateDates && itemUpdateDates.length > 0) {
1907
1963
  let latestUpdateDate = new Date(1900, 1, 1);
1908
- itemUpdateDates.forEach(itemUpdate => {
1964
+ itemUpdateDates.forEach((itemUpdate) => {
1909
1965
  const updateDate = new Date(itemUpdate.UpdateDate);
1910
1966
  updateDates.push({
1911
1967
  EntityID: itemUpdate.EntityID,
1912
1968
  EntityName: itemUpdate.EntityName,
1913
1969
  RowCount: itemUpdate.TheRowCount,
1914
- UpdateDate: updateDate
1970
+ UpdateDate: updateDate,
1915
1971
  });
1916
1972
  if (updateDate > latestUpdateDate) {
1917
1973
  latestUpdateDate = updateDate;
@@ -1923,7 +1979,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1923
1979
  Success: true,
1924
1980
  Status: '',
1925
1981
  LatestUpdateDate: latestUpdateDate,
1926
- EntityUpdateDates: updateDates
1982
+ EntityUpdateDates: updateDates,
1927
1983
  };
1928
1984
  }
1929
1985
  else {
@@ -1933,18 +1989,18 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1933
1989
  Success: false,
1934
1990
  Status: 'No update dates found for DatasetName: ' + datasetName,
1935
1991
  LatestUpdateDate: null,
1936
- EntityUpdateDates: null
1992
+ EntityUpdateDates: null,
1937
1993
  };
1938
1994
  }
1939
1995
  }
1940
1996
  else {
1941
1997
  return {
1942
- DatasetID: "",
1998
+ DatasetID: '',
1943
1999
  DatasetName: datasetName,
1944
2000
  Success: false,
1945
2001
  Status: 'No Dataset or Items found for DatasetName: ' + datasetName,
1946
2002
  EntityUpdateDates: null,
1947
- LatestUpdateDate: null
2003
+ LatestUpdateDate: null,
1948
2004
  };
1949
2005
  }
1950
2006
  }
@@ -1955,7 +2011,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1955
2011
  for (let i = 0; i < apps.length; i++) {
1956
2012
  ret.push(new core_1.ApplicationInfo(this, {
1957
2013
  ...apps[i],
1958
- ApplicationEntities: appEntities.filter(ae => ae.ApplicationName.trim().toLowerCase() === apps[i].Name.trim().toLowerCase())
2014
+ ApplicationEntities: appEntities.filter((ae) => ae.ApplicationName.trim().toLowerCase() === apps[i].Name.trim().toLowerCase()),
1959
2015
  }));
1960
2016
  }
1961
2017
  return ret;
@@ -1976,7 +2032,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1976
2032
  for (let i = 0; i < users.length; i++) {
1977
2033
  ret.push(new core_1.UserInfo(this, {
1978
2034
  ...users[i],
1979
- UserRoles: userRoles.filter(ur => ur.UserID === users[i].ID)
2035
+ UserRoles: userRoles.filter((ur) => ur.UserID === users[i].ID),
1980
2036
  }));
1981
2037
  }
1982
2038
  return ret;
@@ -1988,7 +2044,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1988
2044
  for (let i = 0; i < auths.length; i++) {
1989
2045
  ret.push(new core_1.AuthorizationInfo(this, {
1990
2046
  ...auths[i],
1991
- AuthorizationRoles: authRoles.filter(ar => ar.AuthorizationName.trim().toLowerCase() === auths[i].Name.trim().toLowerCase())
2047
+ AuthorizationRoles: authRoles.filter((ar) => ar.AuthorizationName.trim().toLowerCase() === auths[i].Name.trim().toLowerCase()),
1992
2048
  }));
1993
2049
  }
1994
2050
  return ret;
@@ -1999,7 +2055,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
1999
2055
  else if (this._currentUserEmail && this._currentUserEmail.length > 0) {
2000
2056
  // attempt to lookup current user from email since this.CurrentUser is null for some reason (unexpected)
2001
2057
  if (UserCache_1.UserCache && UserCache_1.UserCache.Users)
2002
- return UserCache_1.UserCache.Users.find(u => u.Email.trim().toLowerCase() === this._currentUserEmail.trim().toLowerCase());
2058
+ return UserCache_1.UserCache.Users.find((u) => u.Email.trim().toLowerCase() === this._currentUserEmail.trim().toLowerCase());
2003
2059
  }
2004
2060
  // if we get here we can't get the current user
2005
2061
  return null;
@@ -2010,7 +2066,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
2010
2066
  const userRoles = await this.ExecuteSQL(`SELECT * FROM [${this.MJCoreSchemaName}].vwUserRoles WHERE UserID='${user[0].ID}'`);
2011
2067
  return new core_1.UserInfo(this, {
2012
2068
  ...user[0],
2013
- UserRoles: userRoles ? userRoles : []
2069
+ UserRoles: userRoles ? userRoles : [],
2014
2070
  });
2015
2071
  }
2016
2072
  else
@@ -2104,7 +2160,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
2104
2160
  const result = [];
2105
2161
  for (let i = 0; i < info.length; i++) {
2106
2162
  const r = await this.GetEntityRecordName(info[i].EntityName, info[i].CompositeKey);
2107
- result.push({ EntityName: info[i].EntityName, CompositeKey: info[i].CompositeKey, RecordName: r, Success: r ? true : false, Status: r ? 'Success' : 'Error' });
2163
+ result.push({
2164
+ EntityName: info[i].EntityName,
2165
+ CompositeKey: info[i].CompositeKey,
2166
+ RecordName: r,
2167
+ Success: r ? true : false,
2168
+ Status: r ? 'Success' : 'Error',
2169
+ });
2108
2170
  }
2109
2171
  return result;
2110
2172
  }
@@ -2129,13 +2191,13 @@ class SQLServerDataProvider extends core_1.ProviderBase {
2129
2191
  }
2130
2192
  }
2131
2193
  GetEntityRecordNameSQL(entityName, CompositeKey) {
2132
- const e = this.Entities.find(e => e.Name === entityName);
2194
+ const e = this.Entities.find((e) => e.Name === entityName);
2133
2195
  if (!e)
2134
2196
  throw new Error(`Entity ${entityName} not found`);
2135
2197
  else {
2136
- let f = e.Fields.find(f => f.IsNameField);
2198
+ let f = e.Fields.find((f) => f.IsNameField);
2137
2199
  if (!f)
2138
- f = e.Fields.find(f => f.Name === 'Name');
2200
+ f = e.Fields.find((f) => f.Name === 'Name');
2139
2201
  if (!f) {
2140
2202
  (0, core_1.LogError)(`Entity ${entityName} does not have an IsNameField or a field with the column name of Name, returning null, use recordId`);
2141
2203
  return null;
@@ -2145,7 +2207,7 @@ class SQLServerDataProvider extends core_1.ProviderBase {
2145
2207
  let sql = `SELECT [${f.Name}] FROM [${e.SchemaName}].[${e.BaseView}] WHERE `;
2146
2208
  let where = '';
2147
2209
  for (let pkv of CompositeKey.KeyValuePairs) {
2148
- const pk = e.PrimaryKeys.find(pk => pk.Name === pkv.FieldName);
2210
+ const pk = e.PrimaryKeys.find((pk) => pk.Name === pkv.FieldName);
2149
2211
  const quotes = pk.NeedsQuotes ? "'" : '';
2150
2212
  if (where.length > 0)
2151
2213
  where += ' AND ';