@memberjunction/server 1.8.1 → 2.0.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 (191) hide show
  1. package/CHANGELOG.json +1 -1
  2. package/CHANGELOG.md +2 -2
  3. package/package.json +30 -39
  4. package/src/apolloServer/index.ts +0 -1
  5. package/src/auth/newUsers.ts +4 -3
  6. package/src/context.ts +19 -27
  7. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  8. package/src/generated/generated.ts +9030 -8051
  9. package/src/generic/ResolverBase.ts +297 -172
  10. package/src/generic/RunViewResolver.ts +204 -14
  11. package/src/index.ts +18 -19
  12. package/src/resolvers/AskSkipResolver.ts +35 -35
  13. package/src/resolvers/ColorResolver.ts +3 -14
  14. package/src/resolvers/DatasetResolver.ts +4 -4
  15. package/src/resolvers/EntityCommunicationsResolver.ts +37 -15
  16. package/src/resolvers/FileCategoryResolver.ts +2 -2
  17. package/src/resolvers/FileResolver.ts +6 -4
  18. package/src/resolvers/MergeRecordsResolver.ts +3 -2
  19. package/src/resolvers/PotentialDuplicateRecordResolver.ts +48 -40
  20. package/src/resolvers/QueryResolver.ts +2 -2
  21. package/src/resolvers/ReportResolver.ts +6 -6
  22. package/src/resolvers/UserFavoriteResolver.ts +8 -8
  23. package/src/resolvers/UserResolver.ts +3 -1
  24. package/src/resolvers/UserViewResolver.ts +1 -1
  25. package/src/types.ts +21 -0
  26. package/tsconfig.json +3 -4
  27. package/dist/apolloServer/TransactionPlugin.d.ts +0 -4
  28. package/dist/apolloServer/TransactionPlugin.d.ts.map +0 -1
  29. package/dist/apolloServer/TransactionPlugin.js +0 -49
  30. package/dist/apolloServer/TransactionPlugin.js.map +0 -1
  31. package/dist/apolloServer/index.d.ts +0 -11
  32. package/dist/apolloServer/index.d.ts.map +0 -1
  33. package/dist/apolloServer/index.js +0 -27
  34. package/dist/apolloServer/index.js.map +0 -1
  35. package/dist/auth/exampleNewUserSubClass.d.ts +0 -6
  36. package/dist/auth/exampleNewUserSubClass.d.ts.map +0 -1
  37. package/dist/auth/exampleNewUserSubClass.js +0 -54
  38. package/dist/auth/exampleNewUserSubClass.js.map +0 -1
  39. package/dist/auth/index.d.ts +0 -30
  40. package/dist/auth/index.d.ts.map +0 -1
  41. package/dist/auth/index.js +0 -129
  42. package/dist/auth/index.js.map +0 -1
  43. package/dist/auth/newUsers.d.ts +0 -5
  44. package/dist/auth/newUsers.d.ts.map +0 -1
  45. package/dist/auth/newUsers.js +0 -66
  46. package/dist/auth/newUsers.js.map +0 -1
  47. package/dist/auth/tokenExpiredError.d.ts +0 -5
  48. package/dist/auth/tokenExpiredError.d.ts.map +0 -1
  49. package/dist/auth/tokenExpiredError.js +0 -16
  50. package/dist/auth/tokenExpiredError.js.map +0 -1
  51. package/dist/cache.d.ts +0 -3
  52. package/dist/cache.d.ts.map +0 -1
  53. package/dist/cache.js +0 -11
  54. package/dist/cache.js.map +0 -1
  55. package/dist/config.d.ts +0 -196
  56. package/dist/config.d.ts.map +0 -1
  57. package/dist/config.js +0 -72
  58. package/dist/config.js.map +0 -1
  59. package/dist/context.d.ts +0 -17
  60. package/dist/context.d.ts.map +0 -1
  61. package/dist/context.js +0 -114
  62. package/dist/context.js.map +0 -1
  63. package/dist/directives/Public.d.ts +0 -4
  64. package/dist/directives/Public.d.ts.map +0 -1
  65. package/dist/directives/Public.js +0 -34
  66. package/dist/directives/Public.js.map +0 -1
  67. package/dist/directives/index.d.ts +0 -2
  68. package/dist/directives/index.d.ts.map +0 -1
  69. package/dist/directives/index.js +0 -18
  70. package/dist/directives/index.js.map +0 -1
  71. package/dist/entitySubclasses/DuplicateRunEntity.server.d.ts +0 -6
  72. package/dist/entitySubclasses/DuplicateRunEntity.server.d.ts.map +0 -1
  73. package/dist/entitySubclasses/DuplicateRunEntity.server.js +0 -37
  74. package/dist/entitySubclasses/DuplicateRunEntity.server.js.map +0 -1
  75. package/dist/entitySubclasses/EntityBehavior.server.d.ts +0 -29
  76. package/dist/entitySubclasses/EntityBehavior.server.d.ts.map +0 -1
  77. package/dist/entitySubclasses/EntityBehavior.server.js +0 -213
  78. package/dist/entitySubclasses/EntityBehavior.server.js.map +0 -1
  79. package/dist/entitySubclasses/entityPermissions.server.d.ts +0 -23
  80. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +0 -1
  81. package/dist/entitySubclasses/entityPermissions.server.js +0 -99
  82. package/dist/entitySubclasses/entityPermissions.server.js.map +0 -1
  83. package/dist/entitySubclasses/userViewEntity.server.d.ts +0 -13
  84. package/dist/entitySubclasses/userViewEntity.server.d.ts.map +0 -1
  85. package/dist/entitySubclasses/userViewEntity.server.js +0 -164
  86. package/dist/entitySubclasses/userViewEntity.server.js.map +0 -1
  87. package/dist/generated/generated.d.ts +0 -6482
  88. package/dist/generated/generated.d.ts.map +0 -1
  89. package/dist/generated/generated.js +0 -35073
  90. package/dist/generated/generated.js.map +0 -1
  91. package/dist/generic/DeleteOptionsInput.d.ts +0 -5
  92. package/dist/generic/DeleteOptionsInput.d.ts.map +0 -1
  93. package/dist/generic/DeleteOptionsInput.js +0 -28
  94. package/dist/generic/DeleteOptionsInput.js.map +0 -1
  95. package/dist/generic/KeyInputOutputTypes.d.ts +0 -16
  96. package/dist/generic/KeyInputOutputTypes.d.ts.map +0 -1
  97. package/dist/generic/KeyInputOutputTypes.js +0 -62
  98. package/dist/generic/KeyInputOutputTypes.js.map +0 -1
  99. package/dist/generic/KeyValuePairInput.d.ts +0 -5
  100. package/dist/generic/KeyValuePairInput.d.ts.map +0 -1
  101. package/dist/generic/KeyValuePairInput.js +0 -28
  102. package/dist/generic/KeyValuePairInput.js.map +0 -1
  103. package/dist/generic/PushStatusResolver.d.ts +0 -14
  104. package/dist/generic/PushStatusResolver.d.ts.map +0 -1
  105. package/dist/generic/PushStatusResolver.js +0 -58
  106. package/dist/generic/PushStatusResolver.js.map +0 -1
  107. package/dist/generic/ResolverBase.d.ts +0 -37
  108. package/dist/generic/ResolverBase.d.ts.map +0 -1
  109. package/dist/generic/ResolverBase.js +0 -468
  110. package/dist/generic/ResolverBase.js.map +0 -1
  111. package/dist/generic/RunViewResolver.d.ts +0 -88
  112. package/dist/generic/RunViewResolver.d.ts.map +0 -1
  113. package/dist/generic/RunViewResolver.js +0 -443
  114. package/dist/generic/RunViewResolver.js.map +0 -1
  115. package/dist/index.d.ts +0 -28
  116. package/dist/index.d.ts.map +0 -1
  117. package/dist/index.js +0 -145
  118. package/dist/index.js.map +0 -1
  119. package/dist/orm.d.ts +0 -4
  120. package/dist/orm.d.ts.map +0 -1
  121. package/dist/orm.js +0 -34
  122. package/dist/orm.js.map +0 -1
  123. package/dist/resolvers/AskSkipResolver.d.ts +0 -54
  124. package/dist/resolvers/AskSkipResolver.d.ts.map +0 -1
  125. package/dist/resolvers/AskSkipResolver.js +0 -800
  126. package/dist/resolvers/AskSkipResolver.js.map +0 -1
  127. package/dist/resolvers/ColorResolver.d.ts +0 -22
  128. package/dist/resolvers/ColorResolver.d.ts.map +0 -1
  129. package/dist/resolvers/ColorResolver.js +0 -94
  130. package/dist/resolvers/ColorResolver.js.map +0 -1
  131. package/dist/resolvers/DatasetResolver.d.ts +0 -42
  132. package/dist/resolvers/DatasetResolver.d.ts.map +0 -1
  133. package/dist/resolvers/DatasetResolver.js +0 -168
  134. package/dist/resolvers/DatasetResolver.js.map +0 -1
  135. package/dist/resolvers/EntityCommunicationsResolver.d.ts +0 -49
  136. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +0 -1
  137. package/dist/resolvers/EntityCommunicationsResolver.js +0 -228
  138. package/dist/resolvers/EntityCommunicationsResolver.js.map +0 -1
  139. package/dist/resolvers/EntityRecordNameResolver.d.ts +0 -21
  140. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +0 -1
  141. package/dist/resolvers/EntityRecordNameResolver.js +0 -113
  142. package/dist/resolvers/EntityRecordNameResolver.js.map +0 -1
  143. package/dist/resolvers/EntityResolver.d.ts +0 -6
  144. package/dist/resolvers/EntityResolver.d.ts.map +0 -1
  145. package/dist/resolvers/EntityResolver.js +0 -60
  146. package/dist/resolvers/EntityResolver.js.map +0 -1
  147. package/dist/resolvers/FileCategoryResolver.d.ts +0 -6
  148. package/dist/resolvers/FileCategoryResolver.d.ts.map +0 -1
  149. package/dist/resolvers/FileCategoryResolver.js +0 -65
  150. package/dist/resolvers/FileCategoryResolver.js.map +0 -1
  151. package/dist/resolvers/FileResolver.d.ts +0 -24
  152. package/dist/resolvers/FileResolver.d.ts.map +0 -1
  153. package/dist/resolvers/FileResolver.js +0 -162
  154. package/dist/resolvers/FileResolver.js.map +0 -1
  155. package/dist/resolvers/MergeRecordsResolver.d.ts +0 -59
  156. package/dist/resolvers/MergeRecordsResolver.d.ts.map +0 -1
  157. package/dist/resolvers/MergeRecordsResolver.js +0 -256
  158. package/dist/resolvers/MergeRecordsResolver.js.map +0 -1
  159. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +0 -29
  160. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +0 -1
  161. package/dist/resolvers/PotentialDuplicateRecordResolver.js +0 -127
  162. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +0 -1
  163. package/dist/resolvers/QueryResolver.d.ts +0 -13
  164. package/dist/resolvers/QueryResolver.d.ts.map +0 -1
  165. package/dist/resolvers/QueryResolver.js +0 -74
  166. package/dist/resolvers/QueryResolver.js.map +0 -1
  167. package/dist/resolvers/ReportResolver.d.ts +0 -20
  168. package/dist/resolvers/ReportResolver.d.ts.map +0 -1
  169. package/dist/resolvers/ReportResolver.js +0 -175
  170. package/dist/resolvers/ReportResolver.js.map +0 -1
  171. package/dist/resolvers/UserFavoriteResolver.d.ts +0 -42
  172. package/dist/resolvers/UserFavoriteResolver.d.ts.map +0 -1
  173. package/dist/resolvers/UserFavoriteResolver.js +0 -221
  174. package/dist/resolvers/UserFavoriteResolver.js.map +0 -1
  175. package/dist/resolvers/UserResolver.d.ts +0 -10
  176. package/dist/resolvers/UserResolver.d.ts.map +0 -1
  177. package/dist/resolvers/UserResolver.js +0 -72
  178. package/dist/resolvers/UserResolver.js.map +0 -1
  179. package/dist/resolvers/UserViewResolver.d.ts +0 -13
  180. package/dist/resolvers/UserViewResolver.d.ts.map +0 -1
  181. package/dist/resolvers/UserViewResolver.js +0 -102
  182. package/dist/resolvers/UserViewResolver.js.map +0 -1
  183. package/dist/types.d.ts +0 -17
  184. package/dist/types.d.ts.map +0 -1
  185. package/dist/types.js +0 -3
  186. package/dist/types.js.map +0 -1
  187. package/dist/util.d.ts +0 -4
  188. package/dist/util.d.ts.map +0 -1
  189. package/dist/util.js +0 -89
  190. package/dist/util.js.map +0 -1
  191. package/src/entitySubclasses/EntityBehavior.server.ts +0 -241
@@ -1,6 +1,7 @@
1
1
  import { Arg, Ctx, Field, InputType, Int, ObjectType, PubSubEngine, Query, Resolver } from 'type-graphql';
2
2
  import { AppContext } from '../types';
3
3
  import { ResolverBase } from './ResolverBase';
4
+ import { LogError, LogStatus } from '@memberjunction/core';
4
5
 
5
6
  /********************************************************************************
6
7
  * The PURPOSE of this resolver is to provide a generic way to run a view and return the results.
@@ -14,8 +15,8 @@ import { ResolverBase } from './ResolverBase';
14
15
  //****************************************************************************
15
16
  @InputType()
16
17
  export class RunViewByIDInput {
17
- @Field(() => Int)
18
- ViewID: number;
18
+ @Field(() => String)
19
+ ViewID: string;
19
20
 
20
21
  @Field(() => String, {
21
22
  nullable: true,
@@ -41,8 +42,8 @@ export class RunViewByIDInput {
41
42
  @Field(() => String, { nullable: true })
42
43
  UserSearchString: string;
43
44
 
44
- @Field(() => Int, { nullable: true, description: 'Pass in a UserViewRun ID value to exclude all records from that run from results' })
45
- ExcludeUserViewRunID?: number;
45
+ @Field(() => String, { nullable: true, description: 'Pass in a UserViewRun ID value to exclude all records from that run from results' })
46
+ ExcludeUserViewRunID?: string;
46
47
 
47
48
  @Field(() => String, {
48
49
  nullable: true,
@@ -98,7 +99,14 @@ export class RunViewByIDInput {
98
99
  description:
99
100
  'Optional, pass in entity_object, simple, or count_only as options to specify the type of result you want back. Defaults to simple if not provided',
100
101
  })
101
- ResultType?: string;
102
+ ResultType?: string;
103
+
104
+ @Field(() => Int, {
105
+ nullable: true,
106
+ description:
107
+ 'If a value > 0 is provided, this value will be used to offset the rows returned.',
108
+ })
109
+ StartRow?: number
102
110
  }
103
111
 
104
112
  @InputType()
@@ -130,8 +138,8 @@ export class RunViewByNameInput {
130
138
  @Field(() => String, { nullable: true })
131
139
  UserSearchString: string;
132
140
 
133
- @Field(() => Int, { nullable: true, description: 'Pass in a UserViewRun ID value to exclude all records from that run from results' })
134
- ExcludeUserViewRunID?: number;
141
+ @Field(() => String, { nullable: true, description: 'Pass in a UserViewRun ID value to exclude all records from that run from results' })
142
+ ExcludeUserViewRunID?: string;
135
143
 
136
144
  @Field(() => String, {
137
145
  nullable: true,
@@ -188,7 +196,15 @@ export class RunViewByNameInput {
188
196
  'Optional, pass in entity_object, simple, or count_only as options to specify the type of result you want back. Defaults to simple if not provided',
189
197
  })
190
198
  ResultType?: string;
199
+
200
+ @Field(() => Int, {
201
+ nullable: true,
202
+ description:
203
+ 'If a value > 0 is provided, this value will be used to offset the rows returned.',
204
+ })
205
+ StartRow?: number
191
206
  }
207
+
192
208
  @InputType()
193
209
  export class RunDynamicViewInput {
194
210
  @Field(() => String)
@@ -218,8 +234,8 @@ export class RunDynamicViewInput {
218
234
  @Field(() => String, { nullable: true })
219
235
  UserSearchString: string;
220
236
 
221
- @Field(() => Int, { nullable: true, description: 'Pass in a UserViewRun ID value to exclude all records from that run from results' })
222
- ExcludeUserViewRunID?: number;
237
+ @Field(() => String, { nullable: true, description: 'Pass in a UserViewRun ID value to exclude all records from that run from results' })
238
+ ExcludeUserViewRunID?: string;
223
239
 
224
240
  @Field(() => String, {
225
241
  nullable: true,
@@ -262,6 +278,109 @@ export class RunDynamicViewInput {
262
278
  'Optional, pass in entity_object, simple, or count_only as options to specify the type of result you want back. Defaults to simple if not provided',
263
279
  })
264
280
  ResultType?: string;
281
+
282
+ @Field(() => Int, {
283
+ nullable: true,
284
+ description:
285
+ 'If a value > 0 is provided, this value will be used to offset the rows returned.',
286
+ })
287
+ StartRow?: number
288
+ }
289
+
290
+ @InputType()
291
+ export class RunViewGenericInput {
292
+ @Field(() => String)
293
+ EntityName: string;
294
+
295
+ @Field(() => String, {
296
+ nullable: true,
297
+ description:
298
+ 'Optional, pass in a valid condition to use as the view WHERE clause. For example, UpdatedAt >= Some Date - if not provided, no filter is applied',
299
+ })
300
+ ExtraFilter: string;
301
+
302
+ @Field(() => String, {
303
+ nullable: true,
304
+ description:
305
+ 'Optional, pass in a valid order by clause sort the results on the server. For example, CreatedAt DESC to order by row creation date in reverse order. Any Valid SQL Order By clause is okay - if not provided, no server-side sorting is applied',
306
+ })
307
+ OrderBy: string;
308
+
309
+ @Field(() => [String], {
310
+ nullable: true,
311
+ description:
312
+ 'Optional, array of entity field names, if not provided, all columns are returned. If provided, only the fields in the array are returned.',
313
+ })
314
+ Fields?: string[];
315
+
316
+ @Field(() => String, { nullable: true })
317
+ UserSearchString: string;
318
+
319
+ @Field(() => String, { nullable: true, description: 'Pass in a UserViewRun ID value to exclude all records from that run from results' })
320
+ ExcludeUserViewRunID?: string;
321
+
322
+ @Field(() => String, {
323
+ nullable: true,
324
+ description:
325
+ 'Pass in a valid condition to append to the view WHERE clause to override the Exclude List. For example, UpdatedAt >= Some Date',
326
+ })
327
+ OverrideExcludeFilter?: string;
328
+
329
+ @Field(() => Boolean, {
330
+ nullable: true,
331
+ description:
332
+ 'If set to True, the results of this view are saved into a new UserViewRun record and the UserViewRun.ID is passed back in the results.',
333
+ })
334
+ SaveViewResults?: boolean;
335
+
336
+ @Field(() => Boolean, {
337
+ nullable: true,
338
+ description:
339
+ 'if set to true, the resulting data will filter out ANY records that were ever returned by this view, when the SaveViewResults property was set to true. This is useful if you want to run a particular view over time and make sure the results returned each time are new to the view.',
340
+ })
341
+ ExcludeDataFromAllPriorViewRuns?: boolean;
342
+
343
+ @Field(() => Boolean, {
344
+ nullable: true,
345
+ description:
346
+ 'if set to true, if there IS any UserViewMaxRows property set for the entity in question, it will be IGNORED. This is useful in scenarios where you want to programmatically run a view and get ALL the data back, regardless of the MaxRows setting on the entity.',
347
+ })
348
+ IgnoreMaxRows?: boolean;
349
+
350
+ @Field(() => Int, {
351
+ nullable: true,
352
+ description:
353
+ 'if a value > 0 is provided, and IgnoreMaxRows is set to false, this value is used for the max rows to be returned by the view.',
354
+ })
355
+ MaxRows?: number
356
+
357
+ @Field(() => Boolean, {
358
+ nullable: true,
359
+ description:
360
+ 'If set to true, an Audit Log record will be created for the view run, regardless of the property settings in the entity for auditing view runs',
361
+ })
362
+ ForceAuditLog?: boolean;
363
+
364
+ @Field(() => String, {
365
+ nullable: true,
366
+ description:
367
+ "if provided and either ForceAuditLog is set, or the entity's property settings for logging view runs are set to true, this will be used as the Audit Log Description.",
368
+ })
369
+ AuditLogDescription?: string;
370
+
371
+ @Field(() => String, {
372
+ nullable: true,
373
+ description:
374
+ 'Optional, pass in entity_object, simple, or count_only as options to specify the type of result you want back. Defaults to simple if not provided',
375
+ })
376
+ ResultType?: string;
377
+
378
+ @Field(() => Int, {
379
+ nullable: true,
380
+ description:
381
+ 'If a value > 0 is provided, this value will be used to offset the rows returned.',
382
+ })
383
+ StartRow?: number
265
384
  }
266
385
 
267
386
  @ObjectType()
@@ -269,8 +388,20 @@ export class RunViewResultRow {
269
388
  @Field(() => Int)
270
389
  ID: number;
271
390
 
272
- @Field(() => Int)
273
- EntityID: number;
391
+ @Field(() => String)
392
+ EntityID: string;
393
+
394
+ @Field(() => String)
395
+ Data: string;
396
+ }
397
+
398
+ @ObjectType()
399
+ export class RunViewGenericResultRow {
400
+ @Field(() => String)
401
+ ID: string;
402
+
403
+ @Field(() => String)
404
+ EntityID: string;
274
405
 
275
406
  @Field(() => String)
276
407
  Data: string;
@@ -281,8 +412,32 @@ export class RunViewResult {
281
412
  @Field(() => [RunViewResultRow])
282
413
  Results: RunViewResultRow[];
283
414
 
415
+ @Field(() => String, { nullable: true })
416
+ UserViewRunID?: string;
417
+
284
418
  @Field(() => Int, { nullable: true })
285
- UserViewRunID?: number;
419
+ RowCount: number;
420
+
421
+ @Field(() => Int, { nullable: true })
422
+ TotalRowCount: number;
423
+
424
+ @Field(() => Int, { nullable: true })
425
+ ExecutionTime: number;
426
+
427
+ @Field(() => String, { nullable: true })
428
+ ErrorMessage?: string;
429
+
430
+ @Field(() => Boolean, { nullable: false })
431
+ Success: boolean;
432
+ }
433
+
434
+ @ObjectType()
435
+ export class RunViewGenericResult {
436
+ @Field(() => [RunViewGenericResultRow])
437
+ Results: RunViewGenericResultRow[];
438
+
439
+ @Field(() => String, { nullable: true })
440
+ UserViewRunID?: string;
286
441
 
287
442
  @Field(() => Int, { nullable: true })
288
443
  RowCount: number;
@@ -377,12 +532,47 @@ export class RunViewResolver extends ResolverBase {
377
532
  }
378
533
  }
379
534
 
380
- protected processRawData(rawData: any[], entityId: number): RunViewResultRow[] {
535
+ @Query(() => [RunViewGenericResult])
536
+ async RunViews(
537
+ @Arg('input', () => [RunViewGenericInput]) input: (RunViewByNameInput & RunViewByIDInput & RunDynamicViewInput)[],
538
+ @Ctx() { dataSource, userPayload }: AppContext,
539
+ pubSub: PubSubEngine
540
+ ) {
541
+ try {
542
+ const rawData: RunViewGenericResult[] = await super.RunViewsGeneric(input, dataSource, userPayload, pubSub);
543
+ if (!rawData) {
544
+ return null;
545
+ }
546
+
547
+ let results: RunViewGenericResult[] = [];
548
+ for(const [index, data] of rawData.entries()){
549
+ const entityId = await dataSource.query(`SELECT TOP 1 ID from [${this.MJCoreSchema}].vwEntities WHERE Name='${input[index].EntityName}'`);
550
+ const returnData: any[] = this.processRawData(data.Results, entityId[0].ID);
551
+
552
+ results.push({
553
+ Results: returnData,
554
+ UserViewRunID: data?.UserViewRunID,
555
+ RowCount: data?.RowCount,
556
+ TotalRowCount: data?.TotalRowCount,
557
+ ExecutionTime: data?.ExecutionTime,
558
+ Success: data?.Success,
559
+ });
560
+ }
561
+
562
+ return results;
563
+ }
564
+ catch (err) {
565
+ LogError(err);
566
+ return null;
567
+ }
568
+ }
569
+
570
+ protected processRawData(rawData: any[], entityId: string): RunViewResultRow[] {
381
571
  const returnResult = [];
382
572
  for (let i = 0; i < rawData.length; i++) {
383
573
  const row = rawData[i];
384
574
  returnResult.push({
385
- ID: row.ID,
575
+ ID: row.ID.toString(),
386
576
  EntityID: entityId,
387
577
  Data: JSON.stringify(row),
388
578
  });
package/src/index.ts CHANGED
@@ -6,12 +6,13 @@ import { expressMiddleware } from '@apollo/server/express4';
6
6
  import { mergeSchemas } from '@graphql-tools/schema';
7
7
  import { Metadata } from '@memberjunction/core';
8
8
  import { setupSQLServerClient, SQLServerProviderConfigData, UserCache } from '@memberjunction/sqlserver-dataprovider';
9
- import { json } from 'body-parser';
9
+ import { default as BodyParser } from 'body-parser';
10
10
  import cors from 'cors';
11
11
  import express from 'express';
12
- import { globSync } from 'fast-glob';
12
+ import { default as fg } from 'fast-glob';
13
13
  import { useServer } from 'graphql-ws/lib/use/ws';
14
14
  import { createServer } from 'node:http';
15
+ import { fileURLToPath } from 'node:url';
15
16
  import { sep } from 'node:path';
16
17
  import 'reflect-metadata';
17
18
  import { ReplaySubject } from 'rxjs';
@@ -42,7 +43,6 @@ export * from './directives';
42
43
  export * from './entitySubclasses/userViewEntity.server';
43
44
  export * from './entitySubclasses/entityPermissions.server';
44
45
  export * from './entitySubclasses/DuplicateRunEntity.server';
45
- export * from './entitySubclasses/EntityBehavior.server';
46
46
  export * from './types';
47
47
  export { TokenExpiredError } from './auth';
48
48
 
@@ -50,7 +50,7 @@ export * from './generic/PushStatusResolver';
50
50
  export * from './generic/ResolverBase';
51
51
  export * from './generic/RunViewResolver';
52
52
  export * from './generic/KeyValuePairInput';
53
- export * from './generic/KeyInputOutputTypes'
53
+ export * from './generic/KeyInputOutputTypes';
54
54
  export * from './generic/DeleteOptionsInput';
55
55
 
56
56
  export * from './resolvers/AskSkipResolver';
@@ -60,23 +60,26 @@ export * from './resolvers/EntityRecordNameResolver';
60
60
  export * from './resolvers/MergeRecordsResolver';
61
61
  export * from './resolvers/ReportResolver';
62
62
 
63
- export * from './generated/generated'
63
+ export * from './generated/generated';
64
64
 
65
65
  import { resolve } from 'node:path';
66
66
 
67
- const localPath = (p: string) => resolve(__dirname, p);
67
+ const localPath = (p: string) => {
68
+ // Convert import.meta.url to a local directory path
69
+ const dirname = fileURLToPath(new URL('.', import.meta.url));
70
+ // Resolve the provided path relative to the derived directory path
71
+ const resolvedPath = resolve(dirname, p);
72
+ return resolvedPath;
73
+ };
68
74
 
69
75
  export const serve = async (resolverPaths: Array<string>) => {
70
- const localResolverPaths = [
71
- 'resolvers/**/*Resolver.{js,ts}',
72
- 'generic/*Resolver.{js,ts}',
73
- 'generated/generated.{js,ts}'
74
- ].map(localPath);
76
+ const localResolverPaths = ['resolvers/**/*Resolver.{js,ts}', 'generic/*Resolver.{js,ts}', 'generated/generated.{js,ts}'].map(localPath);
75
77
 
76
78
  const combinedResolverPaths = [...resolverPaths, ...localResolverPaths];
77
79
 
78
- const replaceBackslashes = sep === '\\';
79
- const paths = combinedResolverPaths.flatMap((path) => globSync(replaceBackslashes ? path.replace(/\\/g, '/') : path));
80
+ const isWindows = sep === '\\';
81
+ const globs = combinedResolverPaths.flatMap((path) => (isWindows ? path.replace(/\\/g, '/') : path));
82
+ const paths = fg.globSync(globs);
80
83
  if (paths.length === 0) {
81
84
  console.warn(`No resolvers found in ${combinedResolverPaths.join(', ')}`);
82
85
  console.log({ combinedResolverPaths, paths, cwd: process.cwd() });
@@ -107,9 +110,7 @@ export const serve = async (resolverPaths: Array<string>) => {
107
110
  /******TEST HARNESS FOR CHANGE DETECTION */
108
111
  /******TEST HARNESS FOR CHANGE DETECTION */
109
112
 
110
-
111
-
112
- const dynamicModules = await Promise.all(paths.map((modulePath) => import(modulePath.replace(/\.[jt]s$/, ''))));
113
+ const dynamicModules = await Promise.all(paths.map((modulePath) => import(isWindows ? `file://${modulePath}` : modulePath)));
113
114
  const resolvers = dynamicModules.flatMap((module) =>
114
115
  Object.values(module).filter((value) => typeof value === 'function')
115
116
  ) as BuildSchemaOptions['resolvers'];
@@ -149,7 +150,7 @@ export const serve = async (resolverPaths: Array<string>) => {
149
150
  app.use(
150
151
  graphqlRootPath,
151
152
  cors<cors.CorsRequest>(),
152
- json({limit: '50mb'}),
153
+ BodyParser.json({ limit: '50mb' }),
153
154
  expressMiddleware(apolloServer, {
154
155
  context: contextFunction({ setupComplete$, dataSource }),
155
156
  })
@@ -158,5 +159,3 @@ export const serve = async (resolverPaths: Array<string>) => {
158
159
  await new Promise<void>((resolve) => httpServer.listen({ port: graphqlPort }, resolve));
159
160
  console.log(`🚀 Server ready at http://localhost:${graphqlPort}/`);
160
161
  };
161
-
162
- export default serve;
@@ -48,13 +48,13 @@ export class AskSkipResultType {
48
48
  Result: string;
49
49
 
50
50
  @Field(() => Int)
51
- ConversationId: number;
51
+ ConversationId: string;
52
52
 
53
53
  @Field(() => Int)
54
- UserMessageConversationDetailId: number;
54
+ UserMessageConversationDetailId: string;
55
55
 
56
56
  @Field(() => Int)
57
- AIMessageConversationDetailId: number;
57
+ AIMessageConversationDetailId: string;
58
58
  }
59
59
 
60
60
 
@@ -71,7 +71,7 @@ export class AskSkipResolver {
71
71
  */
72
72
  @Query(() => AskSkipResultType)
73
73
  async ExecuteAskSkipRecordChat(@Arg('UserQuestion', () => String) UserQuestion: string,
74
- @Arg('ConversationId', () => Int) ConversationId: number,
74
+ @Arg('ConversationId', () => String) ConversationId: string,
75
75
  @Arg('EntityName', () => String) EntityName: string,
76
76
  @Arg('CompositeKey', () => CompositeKeyInputType) compositeKey: CompositeKeyInputType,
77
77
  @Ctx() { dataSource, userPayload }: AppContext,
@@ -91,7 +91,7 @@ export class AskSkipResolver {
91
91
  await this.HandleSkipInitialObjectLoading(dataSource, ConversationId, UserQuestion, user, userPayload, md, null);
92
92
 
93
93
  // if we have a new conversation, update the data context to have an item for this record
94
- if (!ConversationId || ConversationId <= 0) {
94
+ if (!ConversationId || ConversationId.length === 0) {
95
95
  const dci = await md.GetEntityObject<DataContextItemEntity>('Data Context Items', user);
96
96
  dci.DataContextID = dataContext.ID;
97
97
  dci.Type = 'single_record';
@@ -126,7 +126,7 @@ export class AskSkipResolver {
126
126
  return this.handleSimpleSkipPostRequest(input, convoEntity.ID, convoDetailEntity.ID, true, user);
127
127
  }
128
128
 
129
- protected async handleSimpleSkipPostRequest(input: SkipAPIRequest, conversationID: number = 0, UserMessageConversationDetailId: number = 0, createAIMessageConversationDetail: boolean = false, user: UserInfo = null): Promise<AskSkipResultType> {
129
+ protected async handleSimpleSkipPostRequest(input: SkipAPIRequest, conversationID: string = "", UserMessageConversationDetailId: string = "", createAIMessageConversationDetail: boolean = false, user: UserInfo = null): Promise<AskSkipResultType> {
130
130
  LogStatus(` >>> HandleSimpleSkipPostRequest Sending request to Skip API: ${___skipAPIurl}`);
131
131
 
132
132
  const response = await sendPostRequest(___skipAPIurl, input, true, null);
@@ -134,7 +134,7 @@ export class AskSkipResolver {
134
134
  if (response && response.length > 0) {
135
135
  // the last object in the response array is the final response from the Skip API
136
136
  const apiResponse = <SkipAPIResponse>response[response.length - 1].value;
137
- const AIMessageConversationDetailID = createAIMessageConversationDetail ? await this.CreateAIMessageConversationDetail(apiResponse, conversationID, user) : 0;
137
+ const AIMessageConversationDetailID = createAIMessageConversationDetail ? await this.CreateAIMessageConversationDetail(apiResponse, conversationID, user) : "";
138
138
  // const apiResponse = <SkipAPIResponse>response.data;
139
139
  LogStatus(` Skip API response: ${apiResponse.responsePhase}`)
140
140
  return {
@@ -155,12 +155,12 @@ export class AskSkipResolver {
155
155
  ResponsePhase: SkipResponsePhase.AnalysisComplete,
156
156
  ConversationId: conversationID,
157
157
  UserMessageConversationDetailId: UserMessageConversationDetailId,
158
- AIMessageConversationDetailId: 0,
158
+ AIMessageConversationDetailId: "",
159
159
  };
160
160
  }
161
161
  }
162
162
 
163
- protected async CreateAIMessageConversationDetail(apiResponse: SkipAPIResponse, conversationID: number, user: UserInfo): Promise<number> {
163
+ protected async CreateAIMessageConversationDetail(apiResponse: SkipAPIResponse, conversationID: string, user: UserInfo): Promise<string> {
164
164
  const md = new Metadata();
165
165
  const convoDetailEntityAI = <ConversationDetailEntity>await md.GetEntityObject('Conversation Details', user);
166
166
  convoDetailEntityAI.NewRecord();
@@ -175,11 +175,11 @@ export class AskSkipResolver {
175
175
  }
176
176
  else{
177
177
  LogError(`Error saving conversation detail entity for AI message: ${lastSystemMessage?.content}`, undefined, convoDetailEntityAI.LatestResult);
178
- return 0;
178
+ return "";
179
179
  }
180
180
  }
181
181
 
182
- protected buildSkipAPIRequest(messages: SkipMessage[], conversationId: number, dataContext: DataContext, requestPhase: SkipRequestPhase, includeEntities: boolean, includeQueries: boolean): SkipAPIRequest {
182
+ protected buildSkipAPIRequest(messages: SkipMessage[], conversationId: string, dataContext: DataContext, requestPhase: SkipRequestPhase, includeEntities: boolean, includeQueries: boolean): SkipAPIRequest {
183
183
  const entities = includeEntities ? this.BuildSkipEntities() : [];
184
184
  const queries = includeQueries ? this.BuildSkipQueries() : [];
185
185
  const input: SkipAPIRequest = {
@@ -188,7 +188,7 @@ export class AskSkipResolver {
188
188
  messages: messages,
189
189
  conversationID: conversationId.toString(),
190
190
  dataContext: <DataContext>CopyScalarsAndArrays(dataContext), // we are casting this to DataContext as we're pushing this to the Skip API, and we don't want to send the real DataContext object, just a copy of the scalar and array properties
191
- organizationID: !isNaN(parseInt(___skipAPIOrgId)) ? parseInt(___skipAPIOrgId) : 0,
191
+ organizationID: ___skipAPIOrgId,
192
192
  requestPhase: requestPhase,
193
193
  entities: entities,
194
194
  queries: queries
@@ -205,13 +205,13 @@ export class AskSkipResolver {
205
205
  @Query(() => AskSkipResultType)
206
206
  async ExecuteAskSkipRunScript(@Ctx() { dataSource, userPayload }: AppContext,
207
207
  @PubSub() pubSub: PubSubEngine,
208
- @Arg('DataContextId', () => Int) DataContextId: number,
208
+ @Arg('DataContextId', () => String) DataContextId: string,
209
209
  @Arg('ScriptText', () => String) ScriptText: string) {
210
210
  const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
211
211
  if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
212
212
  const dataContext: DataContext = new DataContext();
213
213
  await dataContext.Load(DataContextId, dataSource, true, false, 0, user);
214
- const input = this.buildSkipAPIRequest([], 0, dataContext, 'run_existing_script', false, false);
214
+ const input = this.buildSkipAPIRequest([], "", dataContext, 'run_existing_script', false, false);
215
215
  return this.handleSimpleSkipPostRequest(input);
216
216
  }
217
217
 
@@ -243,10 +243,10 @@ export class AskSkipResolver {
243
243
  @Query(() => AskSkipResultType)
244
244
  async ExecuteAskSkipAnalysisQuery(
245
245
  @Arg('UserQuestion', () => String) UserQuestion: string,
246
- @Arg('ConversationId', () => Int) ConversationId: number,
246
+ @Arg('ConversationId', () => String) ConversationId: string,
247
247
  @Ctx() { dataSource, userPayload }: AppContext,
248
248
  @PubSub() pubSub: PubSubEngine,
249
- @Arg('DataContextId', () => Int, { nullable: true }) DataContextId?: number
249
+ @Arg('DataContextId', () => String, { nullable: true }) DataContextId?: string
250
250
  ) {
251
251
  const md = new Metadata();
252
252
  const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
@@ -370,19 +370,19 @@ export class AskSkipResolver {
370
370
  }
371
371
 
372
372
  protected async HandleSkipInitialObjectLoading(dataSource: DataSource,
373
- ConversationId: number,
373
+ ConversationId: string,
374
374
  UserQuestion: string,
375
375
  user: UserInfo,
376
376
  userPayload: UserPayload,
377
377
  md: Metadata,
378
- DataContextId: number): Promise<{convoEntity: ConversationEntity,
378
+ DataContextId: string): Promise<{convoEntity: ConversationEntity,
379
379
  dataContextEntity: DataContextEntity,
380
380
  convoDetailEntity: ConversationDetailEntity,
381
381
  dataContext: DataContext}> {
382
382
  const convoEntity = <ConversationEntity>await md.GetEntityObject('Conversations', user);
383
383
  let dataContextEntity: DataContextEntity;
384
384
 
385
- if (!ConversationId || ConversationId <= 0) {
385
+ if (!ConversationId || ConversationId.length === 0) {
386
386
  // create a new conversation id
387
387
  convoEntity.NewRecord();
388
388
  if (user) {
@@ -390,7 +390,7 @@ export class AskSkipResolver {
390
390
  convoEntity.Name = AskSkipResolver._defaultNewChatName;
391
391
 
392
392
  dataContextEntity = await md.GetEntityObject<DataContextEntity>('Data Contexts', user);
393
- if (!DataContextId || DataContextId <= 0) {
393
+ if (!DataContextId || DataContextId.length === 0) {
394
394
  dataContextEntity.NewRecord();
395
395
  dataContextEntity.UserID = user.ID;
396
396
  dataContextEntity.Name = 'Data Context for Skip Conversation';
@@ -408,7 +408,7 @@ export class AskSkipResolver {
408
408
  convoEntity.DataContextID = dataContextEntity.ID;
409
409
  if (await convoEntity.Save()) {
410
410
  ConversationId = convoEntity.ID;
411
- if (!DataContextId || dataContextEntity.ID <= 0) {
411
+ if (!DataContextId || dataContextEntity.ID.length === 0) {
412
412
  // only do this if we created a new data context for this conversation
413
413
  dataContextEntity.Name += ` ${ConversationId}`;
414
414
  const dciSaveResult: boolean = await dataContextEntity.Save();
@@ -430,7 +430,7 @@ export class AskSkipResolver {
430
430
  dataContextEntity = await md.GetEntityObject<DataContextEntity>('Data Contexts', user);
431
431
 
432
432
  // note - we ignore the parameter DataContextId if it is passed in, we will use the data context from the conversation that is saved. If a user wants to change the data context for a convo, they can do that elsewhere
433
- if (DataContextId && DataContextId > 0 && DataContextId !== convoEntity.DataContextID) {
433
+ if (DataContextId && DataContextId.length > 0 && DataContextId !== convoEntity.DataContextID) {
434
434
  if (convoEntity.DataContextID === null) {
435
435
  convoEntity.DataContextID = DataContextId;
436
436
  const convoEntitySaveResult: boolean = await convoEntity.Save();
@@ -464,7 +464,7 @@ export class AskSkipResolver {
464
464
  return {dataContext, convoEntity, dataContextEntity, convoDetailEntity};
465
465
  }
466
466
 
467
- protected async LoadConversationDetailsIntoSkipMessages(dataSource: DataSource, ConversationId: number, maxHistoricalMessages?: number): Promise<SkipMessage[]> {
467
+ protected async LoadConversationDetailsIntoSkipMessages(dataSource: DataSource, ConversationId: string, maxHistoricalMessages?: number): Promise<SkipMessage[]> {
468
468
  try {
469
469
  // load up all the conversation details from the database server
470
470
  const md = new Metadata();
@@ -474,7 +474,7 @@ export class AskSkipResolver {
474
474
  FROM
475
475
  ${e.SchemaName}.${e.BaseView}
476
476
  WHERE
477
- ConversationID = ${ConversationId}
477
+ ConversationID = '${ConversationId}'
478
478
  ORDER
479
479
  BY __mj_CreatedAt DESC`;
480
480
  const result = await dataSource.query(sql);
@@ -555,7 +555,7 @@ export class AskSkipResolver {
555
555
  }
556
556
 
557
557
  protected async HandleSkipRequest(input: SkipAPIRequest, UserQuestion: string, user: UserInfo, dataSource: DataSource,
558
- ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, md: Metadata,
558
+ ConversationId: string, userPayload: UserPayload, pubSub: PubSubEngine, md: Metadata,
559
559
  convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
560
560
  dataContext: DataContext, dataContextEntity: DataContextEntity): Promise<AskSkipResultType> {
561
561
  LogStatus(` >>> HandleSkipRequest: Sending request to Skip API: ${___skipAPIurl}`);
@@ -628,13 +628,13 @@ export class AskSkipResolver {
628
628
  Result: `User Question ${UserQuestion} didn't work!`,
629
629
  ResponsePhase: SkipResponsePhase.AnalysisComplete,
630
630
  ConversationId: ConversationId,
631
- UserMessageConversationDetailId: 0,
632
- AIMessageConversationDetailId: 0,
631
+ UserMessageConversationDetailId: "",
632
+ AIMessageConversationDetailId: "",
633
633
  };
634
634
  }
635
635
  }
636
636
 
637
- protected async PublishApiResponseUserUpdateMessage(apiResponse: SkipAPIResponse, userPayload: UserPayload, conversationID: number, pubSub: PubSubEngine) {
637
+ protected async PublishApiResponseUserUpdateMessage(apiResponse: SkipAPIResponse, userPayload: UserPayload, conversationID: string, pubSub: PubSubEngine) {
638
638
  let sUserMessage: string = '';
639
639
  switch (apiResponse.responsePhase) {
640
640
  case 'data_request':
@@ -662,7 +662,7 @@ export class AskSkipResolver {
662
662
  }
663
663
 
664
664
  protected async HandleAnalysisComplete(apiRequest: SkipAPIRequest, apiResponse: SkipAPIAnalysisCompleteResponse, UserQuestion: string, user: UserInfo, dataSource: DataSource,
665
- ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
665
+ ConversationId: string, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
666
666
  dataContext: DataContext, dataContextEntity: DataContextEntity): Promise<AskSkipResultType> {
667
667
  // analysis is complete
668
668
  // all done, wrap things up
@@ -681,7 +681,7 @@ export class AskSkipResolver {
681
681
  }
682
682
 
683
683
  protected async HandleClarifyingQuestionPhase(apiRequest: SkipAPIRequest, apiResponse: SkipAPIClarifyingQuestionResponse, UserQuestion: string, user: UserInfo, dataSource: DataSource,
684
- ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity): Promise<AskSkipResultType> {
684
+ ConversationId: string, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity): Promise<AskSkipResultType> {
685
685
  // need to create a message here in the COnversation and then pass that id below
686
686
  const md = new Metadata();
687
687
  const convoDetailEntityAI = <ConversationDetailEntity>await md.GetEntityObject('Conversation Details', user);
@@ -716,7 +716,7 @@ export class AskSkipResolver {
716
716
  }
717
717
 
718
718
  protected async HandleDataRequestPhase(apiRequest: SkipAPIRequest, apiResponse: SkipAPIDataRequestResponse, UserQuestion: string, user: UserInfo, dataSource: DataSource,
719
- ConversationId: number, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
719
+ ConversationId: string, userPayload: UserPayload, pubSub: PubSubEngine, convoEntity: ConversationEntity, convoDetailEntity: ConversationDetailEntity,
720
720
  dataContext: DataContext, dataContextEntity: DataContextEntity): Promise<AskSkipResultType> {
721
721
  // our job in this method is to go through each of the data requests from the Skip API, get the data, and then go back to the Skip API again and to the next phase
722
722
  try {
@@ -728,7 +728,7 @@ export class AskSkipResolver {
728
728
  ResponsePhase: SkipResponsePhase.DataRequest,
729
729
  ConversationId: ConversationId,
730
730
  UserMessageConversationDetailId: convoDetailEntity.ID,
731
- AIMessageConversationDetailId: 0,
731
+ AIMessageConversationDetailId: "",
732
732
  Result: JSON.stringify(apiResponse)
733
733
  };
734
734
  }
@@ -806,7 +806,7 @@ export class AskSkipResolver {
806
806
  ResponsePhase: SkipResponsePhase.DataRequest,
807
807
  ConversationId: ConversationId,
808
808
  UserMessageConversationDetailId: convoDetailEntity.ID,
809
- AIMessageConversationDetailId: 0,
809
+ AIMessageConversationDetailId: "",
810
810
  Result: JSON.stringify(apiResponse)
811
811
  };
812
812
  }
@@ -846,7 +846,7 @@ export class AskSkipResolver {
846
846
  * @param userPayload
847
847
  * @returns
848
848
  */
849
- protected async FinishConversationAndNotifyUser(apiResponse: SkipAPIAnalysisCompleteResponse, dataContext: DataContext, dataContextEntity: DataContextEntity, md: Metadata, user: UserInfo, convoEntity: ConversationEntity, pubSub: PubSubEngine, userPayload: UserPayload): Promise<{AIMessageConversationDetailID: number}> {
849
+ protected async FinishConversationAndNotifyUser(apiResponse: SkipAPIAnalysisCompleteResponse, dataContext: DataContext, dataContextEntity: DataContextEntity, md: Metadata, user: UserInfo, convoEntity: ConversationEntity, pubSub: PubSubEngine, userPayload: UserPayload): Promise<{AIMessageConversationDetailID: string}> {
850
850
  const sTitle = apiResponse.reportTitle;
851
851
  const sResult = JSON.stringify(apiResponse);
852
852
 
@@ -913,7 +913,7 @@ export class AskSkipResolver {
913
913
  };
914
914
  }
915
915
 
916
- protected async getViewData(ViewId: number, user: UserInfo): Promise<any> {
916
+ protected async getViewData(ViewId: string, user: UserInfo): Promise<any> {
917
917
  const rv = new RunView();
918
918
  const result = await rv.RunView({ViewID: ViewId, IgnoreMaxRows: true}, user);
919
919
  if (result && result.Success)