@memberjunction/server 2.75.0 → 2.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +61 -2
  2. package/dist/apolloServer/index.d.ts.map +1 -1
  3. package/dist/apolloServer/index.js +1 -3
  4. package/dist/apolloServer/index.js.map +1 -1
  5. package/dist/auth/exampleNewUserSubClass.d.ts +1 -1
  6. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  7. package/dist/auth/exampleNewUserSubClass.js +1 -1
  8. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  9. package/dist/auth/newUsers.js.map +1 -1
  10. package/dist/context.d.ts +5 -0
  11. package/dist/context.d.ts.map +1 -1
  12. package/dist/context.js +34 -3
  13. package/dist/context.js.map +1 -1
  14. package/dist/entitySubclasses/entityPermissions.server.d.ts +2 -2
  15. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
  16. package/dist/entitySubclasses/entityPermissions.server.js +2 -2
  17. package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
  18. package/dist/generated/generated.d.ts +1787 -1628
  19. package/dist/generated/generated.d.ts.map +1 -1
  20. package/dist/generated/generated.js +5871 -4367
  21. package/dist/generated/generated.js.map +1 -1
  22. package/dist/generic/ResolverBase.d.ts +20 -21
  23. package/dist/generic/ResolverBase.d.ts.map +1 -1
  24. package/dist/generic/ResolverBase.js +75 -59
  25. package/dist/generic/ResolverBase.js.map +1 -1
  26. package/dist/generic/RunViewResolver.d.ts +8 -8
  27. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  28. package/dist/generic/RunViewResolver.js +50 -48
  29. package/dist/generic/RunViewResolver.js.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/resolvers/AskSkipResolver.d.ts +6 -6
  35. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  36. package/dist/resolvers/AskSkipResolver.js +33 -21
  37. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  38. package/dist/resolvers/CreateQueryResolver.d.ts +12 -3
  39. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  40. package/dist/resolvers/CreateQueryResolver.js +121 -65
  41. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  42. package/dist/resolvers/EntityResolver.d.ts +1 -2
  43. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  44. package/dist/resolvers/EntityResolver.js +17 -9
  45. package/dist/resolvers/EntityResolver.js.map +1 -1
  46. package/dist/resolvers/FileCategoryResolver.d.ts +1 -1
  47. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  48. package/dist/resolvers/FileCategoryResolver.js +9 -9
  49. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  50. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  51. package/dist/resolvers/FileResolver.js +3 -1
  52. package/dist/resolvers/FileResolver.js.map +1 -1
  53. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  54. package/dist/resolvers/GetDataResolver.js +3 -0
  55. package/dist/resolvers/GetDataResolver.js.map +1 -1
  56. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  57. package/dist/resolvers/MergeRecordsResolver.js +2 -1
  58. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  59. package/dist/resolvers/QueryResolver.d.ts +4 -4
  60. package/dist/resolvers/QueryResolver.js +18 -18
  61. package/dist/resolvers/ReportResolver.js.map +1 -1
  62. package/dist/resolvers/SyncDataResolver.d.ts +8 -8
  63. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  64. package/dist/resolvers/SyncDataResolver.js +19 -19
  65. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  66. package/dist/resolvers/SyncRolesUsersResolver.d.ts +10 -10
  67. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  68. package/dist/resolvers/SyncRolesUsersResolver.js +19 -19
  69. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  70. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  71. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  72. package/dist/resolvers/UserFavoriteResolver.d.ts +2 -2
  73. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  74. package/dist/resolvers/UserFavoriteResolver.js +7 -4
  75. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  76. package/dist/resolvers/UserResolver.d.ts +3 -3
  77. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  78. package/dist/resolvers/UserResolver.js +10 -6
  79. package/dist/resolvers/UserResolver.js.map +1 -1
  80. package/dist/resolvers/UserViewResolver.d.ts +2 -2
  81. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  82. package/dist/resolvers/UserViewResolver.js +11 -6
  83. package/dist/resolvers/UserViewResolver.js.map +1 -1
  84. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
  85. package/dist/scheduler/LearningCycleScheduler.js +7 -1
  86. package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
  87. package/dist/types.d.ts +7 -4
  88. package/dist/types.d.ts.map +1 -1
  89. package/dist/types.js +4 -0
  90. package/dist/types.js.map +1 -1
  91. package/dist/util.d.ts +8 -1
  92. package/dist/util.d.ts.map +1 -1
  93. package/dist/util.js +28 -0
  94. package/dist/util.js.map +1 -1
  95. package/package.json +34 -34
  96. package/src/apolloServer/index.ts +3 -3
  97. package/src/auth/exampleNewUserSubClass.ts +3 -2
  98. package/src/auth/newUsers.ts +1 -1
  99. package/src/context.ts +49 -9
  100. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  101. package/src/generated/generated.ts +5506 -4368
  102. package/src/generic/ResolverBase.ts +103 -86
  103. package/src/generic/RunViewResolver.ts +55 -54
  104. package/src/index.ts +1 -1
  105. package/src/resolvers/AskSkipResolver.ts +44 -23
  106. package/src/resolvers/CreateQueryResolver.ts +104 -62
  107. package/src/resolvers/EntityResolver.ts +18 -9
  108. package/src/resolvers/FileCategoryResolver.ts +12 -9
  109. package/src/resolvers/FileResolver.ts +4 -2
  110. package/src/resolvers/GetDataResolver.ts +3 -0
  111. package/src/resolvers/MergeRecordsResolver.ts +2 -1
  112. package/src/resolvers/QueryResolver.ts +14 -14
  113. package/src/resolvers/ReportResolver.ts +1 -1
  114. package/src/resolvers/SyncDataResolver.ts +21 -21
  115. package/src/resolvers/SyncRolesUsersResolver.ts +24 -21
  116. package/src/resolvers/TransactionGroupResolver.ts +1 -1
  117. package/src/resolvers/UserFavoriteResolver.ts +7 -5
  118. package/src/resolvers/UserResolver.ts +10 -6
  119. package/src/resolvers/UserViewResolver.ts +13 -7
  120. package/src/scheduler/LearningCycleScheduler.ts +10 -4
  121. package/src/types.ts +14 -4
  122. package/src/util.ts +45 -2
  123. package/dist/apolloServer/TransactionPlugin.d.ts +0 -4
  124. package/dist/apolloServer/TransactionPlugin.d.ts.map +0 -1
  125. package/dist/apolloServer/TransactionPlugin.js +0 -46
  126. package/dist/apolloServer/TransactionPlugin.js.map +0 -1
  127. package/src/apolloServer/TransactionPlugin.ts +0 -53
@@ -2,8 +2,11 @@ import {
2
2
  BaseEntity,
3
3
  BaseEntityEvent,
4
4
  CompositeKey,
5
+ DatabaseProviderBase,
5
6
  EntityFieldTSType,
6
7
  EntityPermissionType,
8
+ EntitySaveOptions,
9
+ IRunViewProvider,
7
10
  LogDebug,
8
11
  LogError,
9
12
  LogStatus,
@@ -80,65 +83,82 @@ export class ResolverBase {
80
83
  return dataObjectArray;
81
84
  }
82
85
 
83
- protected async findBy(dataSource: sql.ConnectionPool, entity: string, params: any, contextUser: UserInfo) {
86
+ protected async findBy<T = any>(provider: DatabaseProviderBase, entity: string, params: any, contextUser: UserInfo): Promise<Array<T>> {
84
87
  // build the SQL query based on the params passed in
85
- const md = new Metadata();
86
- const e = md.Entities.find((e) => e.Name === entity);
88
+ const rv = provider as any as IRunViewProvider;
89
+ const e = provider.Entities.find((e) => e.Name === entity);
87
90
  if (!e) throw new Error(`Entity ${entity} not found in metadata`);
88
91
  // now build a SQL string using the entityInfo and using the properties in the params object
89
- let sqlQuery = `SELECT * FROM ${e.SchemaName}.${e.BaseView} WHERE `;
92
+ let extraFilter = "";
90
93
  const keys = Object.keys(params);
91
94
  keys.forEach((k, i) => {
92
- if (i > 0) sqlQuery += ' AND ';
95
+ if (i > 0) extraFilter += ' AND ';
93
96
  // look up the field in the entityInfo to see if it needs quotes
94
97
  const field = e.Fields.find((f) => f.Name === k);
95
98
  if (!field) throw new Error(`Field ${k} not found in entity ${entity}`);
96
99
  const quotes = field.NeedsQuotes ? "'" : '';
97
- sqlQuery += `${k} = ${quotes}${params[k]}${quotes}`;
100
+ extraFilter += `${k} = ${quotes}${params[k]}${quotes}`;
98
101
  });
99
102
 
100
103
  // ok, now we have a SQL string, run it and return the results
101
104
  // use the SQLServerDataProvider
102
- const result = await SQLServerDataProvider.ExecuteSQLWithPool(dataSource, sqlQuery, undefined, contextUser);
103
- return result;
105
+ const result = await rv.RunView({
106
+ EntityName: entity,
107
+ ExtraFilter: extraFilter,
108
+ }, contextUser)
109
+ if (result && result.Success && result.Results.length > 0) {
110
+ return result.Results;
111
+ }
112
+ else {
113
+ return [];
114
+ }
104
115
  }
105
116
 
106
- async RunViewByNameGeneric(viewInput: RunViewByNameInput, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
117
+ async RunViewByNameGeneric(viewInput: RunViewByNameInput, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
107
118
  try {
108
- const viewInfo: UserViewEntity = this.safeFirstArrayElement(
109
- await this.findBy(dataSource, 'User Views', { Name: viewInput.ViewName }, userPayload.userRecord)
110
- );
111
- return this.RunViewGenericInternal(
112
- viewInfo,
113
- dataSource,
114
- viewInput.ExtraFilter,
115
- viewInput.OrderBy,
116
- viewInput.UserSearchString,
117
- viewInput.ExcludeUserViewRunID,
118
- viewInput.OverrideExcludeFilter,
119
- viewInput.SaveViewResults,
120
- viewInput.Fields,
121
- viewInput.IgnoreMaxRows,
122
- viewInput.ExcludeDataFromAllPriorViewRuns,
123
- viewInput.ForceAuditLog,
124
- viewInput.AuditLogDescription,
125
- viewInput.ResultType,
126
- userPayload,
127
- pubSub,
128
- viewInput.MaxRows
129
- );
119
+ const rv = provider as any as IRunViewProvider;
120
+ const result = await rv.RunView<UserViewEntity>({
121
+ EntityName: 'User Views',
122
+ ExtraFilter: "Name='" + viewInput.ViewName + "'",
123
+ }, userPayload.userRecord);
124
+ if (result && result.Success && result.Results.length > 0) {
125
+ const viewInfo = result.Results[0];
126
+ return this.RunViewGenericInternal(
127
+ provider,
128
+ viewInfo,
129
+ viewInput.ExtraFilter,
130
+ viewInput.OrderBy,
131
+ viewInput.UserSearchString,
132
+ viewInput.ExcludeUserViewRunID,
133
+ viewInput.OverrideExcludeFilter,
134
+ viewInput.SaveViewResults,
135
+ viewInput.Fields,
136
+ viewInput.IgnoreMaxRows,
137
+ viewInput.ExcludeDataFromAllPriorViewRuns,
138
+ viewInput.ForceAuditLog,
139
+ viewInput.AuditLogDescription,
140
+ viewInput.ResultType,
141
+ userPayload,
142
+ viewInput.MaxRows
143
+ );
144
+ }
145
+ else {
146
+ LogError(`RunViewByNameGeneric: View ${viewInput.ViewName} not found or no results returned`);
147
+ return null;
148
+ }
130
149
  } catch (err) {
131
150
  console.log(err);
132
151
  return null;
133
152
  }
134
153
  }
135
154
 
136
- async RunViewByIDGeneric(viewInput: RunViewByIDInput, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
155
+ async RunViewByIDGeneric(viewInput: RunViewByIDInput, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
137
156
  try {
138
- const viewInfo: UserViewEntity = this.safeFirstArrayElement(await this.findBy(dataSource, 'User Views', { ID: viewInput.ViewID }, userPayload.userRecord));
157
+ const viewInfo = await provider.GetEntityObject<UserViewEntity>('User Views');
158
+ await viewInfo.Load(viewInput.ViewID);
139
159
  return this.RunViewGenericInternal(
160
+ provider,
140
161
  viewInfo,
141
- dataSource,
142
162
  viewInput.ExtraFilter,
143
163
  viewInput.OrderBy,
144
164
  viewInput.UserSearchString,
@@ -152,7 +172,6 @@ export class ResolverBase {
152
172
  viewInput.AuditLogDescription,
153
173
  viewInput.ResultType,
154
174
  userPayload,
155
- pubSub,
156
175
  viewInput.MaxRows
157
176
  );
158
177
  } catch (err) {
@@ -161,9 +180,9 @@ export class ResolverBase {
161
180
  }
162
181
  }
163
182
 
164
- async RunDynamicViewGeneric(viewInput: RunDynamicViewInput, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
183
+ async RunDynamicViewGeneric(viewInput: RunDynamicViewInput, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
165
184
  try {
166
- const md = new Metadata();
185
+ const md = provider;
167
186
  const entity = md.Entities.find((e) => e.Name === viewInput.EntityName);
168
187
  if (!entity) throw new Error(`Entity ${viewInput.EntityName} not found in metadata`);
169
188
 
@@ -175,8 +194,8 @@ export class ResolverBase {
175
194
  } as UserViewEntity; // only providing a few bits of data here, but it's enough to get the view to run
176
195
 
177
196
  return this.RunViewGenericInternal(
197
+ provider,
178
198
  viewInfo,
179
- dataSource,
180
199
  viewInput.ExtraFilter,
181
200
  viewInput.OrderBy,
182
201
  viewInput.UserSearchString,
@@ -190,7 +209,6 @@ export class ResolverBase {
190
209
  viewInput.AuditLogDescription,
191
210
  viewInput.ResultType,
192
211
  userPayload,
193
- pubSub,
194
212
  viewInput.MaxRows
195
213
  );
196
214
  } catch (err) {
@@ -201,22 +219,21 @@ export class ResolverBase {
201
219
 
202
220
  async RunViewsGeneric(
203
221
  viewInputs: (RunViewByNameInput & RunViewByIDInput & RunDynamicViewInput)[],
204
- dataSource: sql.ConnectionPool,
205
- userPayload: UserPayload,
206
- pubSub: PubSubEngine
222
+ provider: DatabaseProviderBase,
223
+ userPayload: UserPayload
207
224
  ) {
208
- let md: Metadata | null = null;
225
+ const md = provider;
209
226
  let params: RunViewGenericParams[] = [];
210
227
  for (const viewInput of viewInputs) {
211
228
  try {
212
229
  let viewInfo: UserViewEntity | null = null;
213
230
 
214
231
  if (viewInput.ViewName) {
215
- viewInfo = this.safeFirstArrayElement(await this.findBy(dataSource, 'User Views', { Name: viewInput.ViewName }, userPayload.userRecord));
232
+ viewInfo = this.safeFirstArrayElement(await this.findBy(provider, 'User Views', { Name: viewInput.ViewName }, userPayload.userRecord));
216
233
  } else if (viewInput.ViewID) {
217
- viewInfo = this.safeFirstArrayElement(await this.findBy(dataSource, 'User Views', { ID: viewInput.ViewID }, userPayload.userRecord));
234
+ viewInfo = await provider.GetEntityObject<UserViewEntity>('User Views');
235
+ await viewInfo.Load(viewInput.ViewID);
218
236
  } else if (viewInput.EntityName) {
219
- md = md || new Metadata();
220
237
  const entity = md.Entities.find((e) => e.Name === viewInput.EntityName);
221
238
  if (!entity) {
222
239
  throw new Error(`Entity ${viewInput.EntityName} not found in metadata`);
@@ -234,8 +251,8 @@ export class ResolverBase {
234
251
  }
235
252
 
236
253
  params.push({
237
- viewInfo: viewInfo,
238
- dataSource: dataSource,
254
+ viewInfo,
255
+ provider,
239
256
  extraFilter: viewInput.ExtraFilter,
240
257
  orderBy: viewInput.OrderBy,
241
258
  userSearchString: viewInput.UserSearchString,
@@ -248,8 +265,7 @@ export class ResolverBase {
248
265
  forceAuditLog: viewInput.ForceAuditLog,
249
266
  auditLogDescription: viewInput.AuditLogDescription,
250
267
  resultType: viewInput.ResultType,
251
- userPayload,
252
- pubSub,
268
+ userPayload,
253
269
  });
254
270
  } catch (err) {
255
271
  LogError(err);
@@ -335,8 +351,8 @@ export class ResolverBase {
335
351
  * - Improved error handling (Fix #9)
336
352
  */
337
353
  protected async RunViewGenericInternal(
354
+ provider: DatabaseProviderBase,
338
355
  viewInfo: UserViewEntity,
339
- dataSource: sql.ConnectionPool,
340
356
  extraFilter: string,
341
357
  orderBy: string,
342
358
  userSearchString: string,
@@ -350,20 +366,19 @@ export class ResolverBase {
350
366
  auditLogDescription: string | undefined,
351
367
  resultType: string | undefined,
352
368
  userPayload: UserPayload | null,
353
- pubSub: PubSubEngine,
354
369
  maxRows: number | undefined
355
370
  ) {
356
371
  try {
357
372
  if (!viewInfo || !userPayload) return null;
358
373
 
359
- const md = new Metadata();
374
+ const md = provider
360
375
  const user = UserCache.Users.find((u) => u.Email.toLowerCase().trim() === userPayload?.email.toLowerCase().trim());
361
376
  if (!user) throw new Error(`User ${userPayload?.email} not found in metadata`);
362
377
 
363
378
  const entityInfo = md.Entities.find((e) => e.Name === viewInfo.Entity);
364
379
  if (!entityInfo) throw new Error(`Entity ${viewInfo.Entity} not found in metadata`);
365
380
 
366
- const rv = new RunView();
381
+ const rv = md as any as IRunViewProvider;
367
382
 
368
383
  // Determine result type
369
384
  let rt: 'simple' | 'entity_object' | 'count_only' = 'simple';
@@ -445,7 +460,7 @@ export class ResolverBase {
445
460
  if (!params.length) return [];
446
461
 
447
462
  let md: Metadata | null = null;
448
- const rv = new RunView();
463
+ const rv = params[0].provider as any as IRunViewProvider;
449
464
  let runViewParams: RunViewParams[] = [];
450
465
 
451
466
  // Fix #1: Get user info only once for all queries
@@ -524,9 +539,9 @@ export class ResolverBase {
524
539
  }
525
540
  }
526
541
 
527
- protected async createRecordAccessAuditLogRecord(userPayload: UserPayload, entityName: string, recordId: any): Promise<any> {
542
+ protected async createRecordAccessAuditLogRecord(provider: DatabaseProviderBase, userPayload: UserPayload, entityName: string, recordId: any): Promise<any> {
528
543
  try {
529
- const md = new Metadata();
544
+ const md = provider;
530
545
  const entityInfo = md.Entities.find((e) => e.Name.trim().toLowerCase() === entityName.trim().toLowerCase());
531
546
  if (!entityInfo) throw new Error(`Entity ${entityName} not found in metadata`);
532
547
 
@@ -538,15 +553,15 @@ export class ResolverBase {
538
553
  if (!userInfo) throw new Error(`User ${userPayload?.email} not found in metadata`);
539
554
  if (!auditLogType) throw new Error(`Audit Log Type ${auditLogTypeName} not found in metadata`);
540
555
 
541
- return await this.createAuditLogRecord(userPayload, null, auditLogTypeName, 'Success', null, entityInfo.ID, recordId);
556
+ return await this.createAuditLogRecord(provider, userPayload, null, auditLogTypeName, 'Success', null, entityInfo.ID, recordId);
542
557
  }
543
558
  } catch (e) {
544
559
  console.log(e);
545
560
  }
546
561
  }
547
562
 
548
- protected getRowLevelSecurityWhereClause(entityName: string, userPayload: UserPayload, type: EntityPermissionType, returnPrefix: string) {
549
- const md = new Metadata();
563
+ protected getRowLevelSecurityWhereClause(provider: DatabaseProviderBase, entityName: string, userPayload: UserPayload, type: EntityPermissionType, returnPrefix: string) {
564
+ const md = provider;
550
565
  const entityInfo = md.Entities.find((e) => e.Name.trim().toLowerCase() === entityName.trim().toLowerCase());
551
566
  if (!entityInfo) throw new Error(`Entity ${entityName} not found in metadata`);
552
567
  const user = UserCache.Users.find((u) => u.Email.toLowerCase().trim() === userPayload?.email.toLowerCase().trim());
@@ -556,6 +571,7 @@ export class ResolverBase {
556
571
  }
557
572
 
558
573
  protected async createAuditLogRecord(
574
+ provider: DatabaseProviderBase,
559
575
  userPayload: UserPayload,
560
576
  authorizationName: string | null,
561
577
  auditLogTypeName: string,
@@ -565,7 +581,7 @@ export class ResolverBase {
565
581
  recordId: any | null
566
582
  ): Promise<any> {
567
583
  try {
568
- const md = new Metadata();
584
+ const md = provider;
569
585
  const userInfo = UserCache.Users.find((u) => u.Email.toLowerCase().trim() === userPayload?.email.toLowerCase().trim());
570
586
  const authorization = authorizationName
571
587
  ? md.Authorizations.find((a) => a.Name.trim().toLowerCase() === authorizationName.trim().toLowerCase())
@@ -593,15 +609,17 @@ export class ResolverBase {
593
609
 
594
610
  if (recordId) auditLog.RecordID = recordId;
595
611
 
596
- if (await auditLog.Save()) return auditLog;
597
- else throw new Error(`Error saving audit log record`);
612
+ if (await auditLog.Save())
613
+ return auditLog;
614
+ else
615
+ throw new Error(`Error saving audit log record`);
598
616
  } catch (err) {
599
617
  console.log(err);
600
618
  return null;
601
619
  }
602
620
  }
603
621
 
604
- protected safeFirstArrayElement(arr: any[]) {
622
+ protected safeFirstArrayElement<T = any>(arr: T[]) {
605
623
  if (arr && arr.length > 0) {
606
624
  return arr[0];
607
625
  }
@@ -613,7 +631,6 @@ export class ResolverBase {
613
631
  }
614
632
 
615
633
  protected GetUserFromEmail(email: string): UserInfo | undefined {
616
- const md = new Metadata();
617
634
  return UserCache.Users.find((u) => u.Email.toLowerCase().trim() === email.toLowerCase().trim());
618
635
  }
619
636
  protected GetUserFromPayload(userPayload: UserPayload): UserInfo | undefined {
@@ -626,7 +643,6 @@ export class ResolverBase {
626
643
  if (!userPayload.email)
627
644
  return undefined;
628
645
 
629
- const md = new Metadata();
630
646
  return UserCache.Users.find((u) => u.Email.toLowerCase().trim() === userPayload.email.toLowerCase().trim());
631
647
  }
632
648
 
@@ -673,18 +689,19 @@ export class ResolverBase {
673
689
  }
674
690
  }
675
691
 
676
- protected async CreateRecord(entityName: string, input: any, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
677
- if (await this.BeforeCreate(dataSource, input)) {
692
+ protected async CreateRecord(entityName: string, input: any, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
693
+ if (await this.BeforeCreate(provider, input)) {
678
694
  // fire event and proceed if it wasn't cancelled
679
- const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
695
+ const entityObject = await provider.GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
680
696
  entityObject.NewRecord();
681
697
  entityObject.SetMany(input);
682
698
 
683
699
  this.ListenForEntityMessages(entityObject, pubSub, userPayload);
684
700
 
701
+ // Pass the transactionScopeId from the user payload to the save operation
685
702
  if (await entityObject.Save()) {
686
703
  // save worked, fire the AfterCreate event and then return all the data
687
- await this.AfterCreate(dataSource, input); // fire event
704
+ await this.AfterCreate(provider, input); // fire event
688
705
  return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
689
706
  }
690
707
  // save failed, return null
@@ -693,17 +710,16 @@ export class ResolverBase {
693
710
  }
694
711
 
695
712
  // Before/After CREATE Event Hooks for Sub-Classes to Override
696
- protected async BeforeCreate(dataSource: sql.ConnectionPool, input: any): Promise<boolean> {
713
+ protected async BeforeCreate(provider: DatabaseProviderBase, input: any): Promise<boolean> {
697
714
  return true;
698
715
  }
699
- protected async AfterCreate(dataSource: sql.ConnectionPool, input: any) {}
716
+ protected async AfterCreate(provider: DatabaseProviderBase, input: any) {}
700
717
 
701
- protected async UpdateRecord(entityName: string, input: any, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
702
- if (await this.BeforeUpdate(dataSource, input)) {
718
+ protected async UpdateRecord(entityName: string, input: any, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
719
+ if (await this.BeforeUpdate(provider, input)) {
703
720
  // fire event and proceed if it wasn't cancelled
704
- const md = new Metadata();
705
721
  const userInfo = this.GetUserFromPayload(userPayload);
706
- const entityObject = await md.GetEntityObject(entityName, userInfo);
722
+ const entityObject = await provider.GetEntityObject(entityName, userInfo);
707
723
  const entityInfo = entityObject.EntityInfo;
708
724
  const clientNewValues = {};
709
725
  Object.keys(input).forEach((key) => {
@@ -752,9 +768,10 @@ export class ResolverBase {
752
768
  }
753
769
 
754
770
  this.ListenForEntityMessages(entityObject, pubSub, userPayload);
771
+
755
772
  if (await entityObject.Save()) {
756
773
  // save worked, fire afterevent and return all the data
757
- await this.AfterUpdate(dataSource, input); // fire event
774
+ await this.AfterUpdate(provider, input); // fire event
758
775
 
759
776
  return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
760
777
  } else {
@@ -937,20 +954,20 @@ export class ResolverBase {
937
954
  entityName: string,
938
955
  key: CompositeKey,
939
956
  options: DeleteOptionsInput,
940
- dataSource: sql.ConnectionPool,
957
+ provider: DatabaseProviderBase,
941
958
  userPayload: UserPayload,
942
959
  pubSub: PubSubEngine
943
960
  ) {
944
- if (await this.BeforeDelete(dataSource, key)) {
961
+ if (await this.BeforeDelete(provider, key)) {
945
962
  // fire event and proceed if it wasn't cancelled
946
- const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
963
+ const entityObject = await provider.GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
947
964
  await entityObject.InnerLoad(key);
948
965
  const returnValue = entityObject.GetAll(); // grab the values before we delete so we can return last state before delete if we are successful.
949
966
 
950
967
  this.ListenForEntityMessages(entityObject, pubSub, userPayload);
951
-
968
+
952
969
  if (await entityObject.Delete(options)) {
953
- await this.AfterDelete(dataSource, key); // fire event
970
+ await this.AfterDelete(provider, key); // fire event
954
971
  return returnValue;
955
972
  } else {
956
973
  throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
@@ -965,14 +982,14 @@ export class ResolverBase {
965
982
  }
966
983
 
967
984
  // Before/After DELETE Event Hooks for Sub-Classes to Override
968
- protected async BeforeDelete(dataSource: sql.ConnectionPool, key: CompositeKey): Promise<boolean> {
985
+ protected async BeforeDelete(provider: DatabaseProviderBase, key: CompositeKey): Promise<boolean> {
969
986
  return true;
970
987
  }
971
- protected async AfterDelete(dataSource: sql.ConnectionPool, key: CompositeKey) {}
988
+ protected async AfterDelete(provider: DatabaseProviderBase, key: CompositeKey) {}
972
989
 
973
990
  // Before/After UPDATE Event Hooks for Sub-Classes to Override
974
- protected async BeforeUpdate(dataSource: sql.ConnectionPool, input: any): Promise<boolean> {
991
+ protected async BeforeUpdate(provider: DatabaseProviderBase, input: any): Promise<boolean> {
975
992
  return true;
976
993
  }
977
- protected async AfterUpdate(dataSource: sql.ConnectionPool, input: any) {}
994
+ protected async AfterUpdate(provider: DatabaseProviderBase, input: any) {}
978
995
  }
@@ -3,6 +3,8 @@ import { AppContext } from '../types.js';
3
3
  import { ResolverBase } from './ResolverBase.js';
4
4
  import { LogError, LogStatus } from '@memberjunction/core';
5
5
  import { RequireSystemUser } from '../directives/RequireSystemUser.js';
6
+ import { GetReadOnlyProvider } from '../util.js';
7
+ import { UserViewEntity } from '@memberjunction/core-entities';
6
8
 
7
9
  /********************************************************************************
8
10
  * The PURPOSE of this resolver is to provide a generic way to run a view and return the results.
@@ -457,17 +459,17 @@ export class RunViewResolver extends ResolverBase {
457
459
  @Query(() => RunViewResult)
458
460
  async RunViewByName(
459
461
  @Arg('input', () => RunViewByNameInput) input: RunViewByNameInput,
460
- @Ctx() { dataSource, userPayload }: AppContext,
462
+ @Ctx() { providers, userPayload }: AppContext,
461
463
  pubSub: PubSubEngine
462
464
  ) {
463
465
  try {
464
- const rawData = await super.RunViewByNameGeneric(input, dataSource, userPayload, pubSub);
465
- if (rawData === null) return null;
466
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
467
+ const rawData = await super.RunViewByNameGeneric(input, provider, userPayload, pubSub);
468
+ if (rawData === null)
469
+ return null;
466
470
 
467
- const request = dataSource.request();
468
- const entityIdResult = await request.query(`SELECT EntityID from [${this.MJCoreSchema}].vwUserViews WHERE Name='${input.ViewName}'`);
469
- const entityId = entityIdResult.recordset;
470
- const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
471
+ const viewInfo = super.safeFirstArrayElement<UserViewEntity>(await super.findBy<UserViewEntity>(provider, "User Views", { Name: input.ViewName }, userPayload.userRecord));
472
+ const returnData = this.processRawData(rawData.Results, viewInfo.EntityID);
471
473
  return {
472
474
  Results: returnData,
473
475
  UserViewRunID: rawData?.UserViewRunID,
@@ -484,17 +486,17 @@ export class RunViewResolver extends ResolverBase {
484
486
  @Query(() => RunViewResult)
485
487
  async RunViewByID(
486
488
  @Arg('input', () => RunViewByIDInput) input: RunViewByIDInput,
487
- @Ctx() { dataSource, userPayload }: AppContext,
489
+ @Ctx() { providers, userPayload }: AppContext,
488
490
  pubSub: PubSubEngine
489
491
  ) {
490
492
  try {
491
- const rawData = await super.RunViewByIDGeneric(input, dataSource, userPayload, pubSub);
492
- if (rawData === null) return null;
493
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
494
+ const rawData = await super.RunViewByIDGeneric(input, provider, userPayload, pubSub);
495
+ if (rawData === null)
496
+ return null;
493
497
 
494
- const request = dataSource.request();
495
- const entityIdResult = await request.query(`SELECT EntityID from [${this.MJCoreSchema}].vwUserViews WHERE ID=${input.ViewID}`);
496
- const entityId = entityIdResult.recordset;
497
- const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
498
+ const viewInfo = super.safeFirstArrayElement<UserViewEntity>(await super.findBy<UserViewEntity>(provider, "User Views", { ID: input.ViewID }, userPayload.userRecord));
499
+ const returnData = this.processRawData(rawData.Results, viewInfo.EntityID);
498
500
  return {
499
501
  Results: returnData,
500
502
  UserViewRunID: rawData?.UserViewRunID,
@@ -511,17 +513,16 @@ export class RunViewResolver extends ResolverBase {
511
513
  @Query(() => RunViewResult)
512
514
  async RunDynamicView(
513
515
  @Arg('input', () => RunDynamicViewInput) input: RunDynamicViewInput,
514
- @Ctx() { dataSource, userPayload }: AppContext,
516
+ @Ctx() { providers, userPayload }: AppContext,
515
517
  pubSub: PubSubEngine
516
518
  ) {
517
519
  try {
518
- const rawData = await super.RunDynamicViewGeneric(input, dataSource, userPayload, pubSub);
520
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
521
+ const rawData = await super.RunDynamicViewGeneric(input, provider, userPayload, pubSub);
519
522
  if (rawData === null) return null;
520
523
 
521
- const request = dataSource.request();
522
- const entityIdResult = await request.query(`SELECT ID from [${this.MJCoreSchema}].vwEntities WHERE Name='${input.EntityName}'`);
523
- const entityId = entityIdResult.recordset;
524
- const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
524
+ const entity = provider.Entities.find((e) => e.Name === input.EntityName);
525
+ const returnData = this.processRawData(rawData.Results, entity.ID);
525
526
  return {
526
527
  Results: returnData,
527
528
  UserViewRunID: rawData?.UserViewRunID,
@@ -538,23 +539,20 @@ export class RunViewResolver extends ResolverBase {
538
539
  @Query(() => [RunViewGenericResult])
539
540
  async RunViews(
540
541
  @Arg('input', () => [RunViewGenericInput]) input: (RunViewByNameInput & RunViewByIDInput & RunDynamicViewInput)[],
541
- @Ctx() { dataSource, userPayload }: AppContext,
542
+ @Ctx() { providers, userPayload }: AppContext,
542
543
  pubSub: PubSubEngine
543
544
  ) {
544
545
  try {
545
- const rawData: RunViewGenericResult[] = await super.RunViewsGeneric(input, dataSource, userPayload, pubSub);
546
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
547
+ const rawData: RunViewGenericResult[] = await super.RunViewsGeneric(input, provider, userPayload);
546
548
  if (!rawData) {
547
549
  return null;
548
550
  }
549
551
 
550
552
  let results: RunViewGenericResult[] = [];
551
553
  for (const [index, data] of rawData.entries()) {
552
- const request = dataSource.request();
553
- const entityIdResult = await request.query(
554
- `SELECT TOP 1 ID from [${this.MJCoreSchema}].vwEntities WHERE Name='${input[index].EntityName}'`
555
- );
556
- const entityId = entityIdResult.recordset;
557
- const returnData: any[] = this.processRawData(data.Results, entityId[0].ID);
554
+ const entity = provider.Entities.find((e) => e.Name === input[index].EntityName);
555
+ const returnData: any[] = this.processRawData(data.Results, entity.ID);
558
556
 
559
557
  results.push({
560
558
  Results: returnData,
@@ -577,17 +575,17 @@ export class RunViewResolver extends ResolverBase {
577
575
  @Query(() => RunViewResult)
578
576
  async RunViewByNameSystemUser(
579
577
  @Arg('input', () => RunViewByNameInput) input: RunViewByNameInput,
580
- @Ctx() { dataSource, userPayload }: AppContext,
578
+ @Ctx() { providers, userPayload }: AppContext,
581
579
  pubSub: PubSubEngine
582
580
  ) {
583
581
  try {
584
- const rawData = await super.RunViewByNameGeneric(input, dataSource, userPayload, pubSub);
582
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
583
+ const rawData = await super.RunViewByNameGeneric(input, provider, userPayload, pubSub);
585
584
  if (rawData === null) return null;
586
585
 
587
- const request = dataSource.request();
588
- const entityIdResult = await request.query(`SELECT EntityID from [${this.MJCoreSchema}].vwUserViews WHERE Name='${input.ViewName}'`);
589
- const entityId = entityIdResult.recordset;
590
- const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
586
+ const entity = provider.Entities.find((e) => e.Name === input.ViewName);
587
+ const entityId = entity ? entity.ID : null;
588
+ const returnData = this.processRawData(rawData.Results, entityId);
591
589
  return {
592
590
  Results: returnData,
593
591
  UserViewRunID: rawData?.UserViewRunID,
@@ -607,17 +605,16 @@ export class RunViewResolver extends ResolverBase {
607
605
  @Query(() => RunViewResult)
608
606
  async RunViewByIDSystemUser(
609
607
  @Arg('input', () => RunViewByIDInput) input: RunViewByIDInput,
610
- @Ctx() { dataSource, userPayload }: AppContext,
608
+ @Ctx() { providers, userPayload }: AppContext,
611
609
  pubSub: PubSubEngine
612
610
  ) {
613
611
  try {
614
- const rawData = await super.RunViewByIDGeneric(input, dataSource, userPayload, pubSub);
612
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
613
+ const rawData = await super.RunViewByIDGeneric(input, provider, userPayload, pubSub);
615
614
  if (rawData === null) return null;
616
615
 
617
- const request = dataSource.request();
618
- const entityIdResult = await request.query(`SELECT EntityID from [${this.MJCoreSchema}].vwUserViews WHERE ID=${input.ViewID}`);
619
- const entityId = entityIdResult.recordset;
620
- const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
616
+ const viewInfo = super.safeFirstArrayElement<UserViewEntity>(await super.findBy<UserViewEntity>(provider, "User Views", { ID: input.ViewID }, userPayload.userRecord));
617
+ const returnData = this.processRawData(rawData.Results, viewInfo.EntityID);
621
618
  return {
622
619
  Results: returnData,
623
620
  UserViewRunID: rawData?.UserViewRunID,
@@ -637,17 +634,20 @@ export class RunViewResolver extends ResolverBase {
637
634
  @Query(() => RunViewResult)
638
635
  async RunDynamicViewSystemUser(
639
636
  @Arg('input', () => RunDynamicViewInput) input: RunDynamicViewInput,
640
- @Ctx() { dataSource, userPayload }: AppContext,
637
+ @Ctx() { providers, userPayload }: AppContext,
641
638
  pubSub: PubSubEngine
642
639
  ) {
643
640
  try {
644
- const rawData = await super.RunDynamicViewGeneric(input, dataSource, userPayload, pubSub);
641
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
642
+ const rawData = await super.RunDynamicViewGeneric(input, provider, userPayload, pubSub);
645
643
  if (rawData === null) return null;
646
644
 
647
- const request = dataSource.request();
648
- const entityIdResult = await request.query(`SELECT ID from [${this.MJCoreSchema}].vwEntities WHERE Name='${input.EntityName}'`);
649
- const entityId = entityIdResult.recordset;
650
- const returnData = this.processRawData(rawData.Results, entityId[0].ID);
645
+ const entity = provider.Entities.find((e) => e.Name === input.EntityName);
646
+ if (!entity) {
647
+ LogError(new Error(`Entity with name ${input.EntityName} not found`));
648
+ return null;
649
+ }
650
+ const returnData = this.processRawData(rawData.Results, entity.ID);
651
651
  return {
652
652
  Results: returnData,
653
653
  UserViewRunID: rawData?.UserViewRunID,
@@ -667,23 +667,24 @@ export class RunViewResolver extends ResolverBase {
667
667
  @Query(() => [RunViewGenericResult])
668
668
  async RunViewsSystemUser(
669
669
  @Arg('input', () => [RunViewGenericInput]) input: (RunViewByNameInput & RunViewByIDInput & RunDynamicViewInput)[],
670
- @Ctx() { dataSource, userPayload }: AppContext,
670
+ @Ctx() { providers, userPayload }: AppContext,
671
671
  pubSub: PubSubEngine
672
672
  ) {
673
673
  try {
674
- const rawData: RunViewGenericResult[] = await super.RunViewsGeneric(input, dataSource, userPayload, pubSub);
674
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
675
+ const rawData: RunViewGenericResult[] = await super.RunViewsGeneric(input, provider, userPayload);
675
676
  if (!rawData) {
676
677
  return null;
677
678
  }
678
679
 
679
680
  let results: RunViewGenericResult[] = [];
680
681
  for (const [index, data] of rawData.entries()) {
681
- const request = dataSource.request();
682
- const entityIdResult = await request.query(
683
- `SELECT TOP 1 ID from [${this.MJCoreSchema}].vwEntities WHERE Name='${input[index].EntityName}'`
684
- );
685
- const entityId = entityIdResult.recordset;
686
- const returnData: any[] = this.processRawData(data.Results, entityId[0].ID);
682
+ const entity = provider.Entities.find((e) => e.Name === input[index].EntityName);
683
+ if (!entity) {
684
+ LogError(new Error(`Entity with name ${input[index].EntityName} not found`));
685
+ continue;
686
+ }
687
+ const returnData: any[] = this.processRawData(data.Results, entity.ID);
687
688
 
688
689
  results.push({
689
690
  Results: returnData,