@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,18 +1,18 @@
1
- import { BaseEntity, CompositeKey, EntityFieldTSType, EntityPermissionType, Metadata, RunView, UserInfo } from '@memberjunction/core';
1
+ import { BaseEntity, CompositeKey, EntityFieldTSType, EntityPermissionType, LogError, Metadata, RunView, RunViewParams, RunViewResult, UserInfo } from '@memberjunction/core';
2
2
  import { AuditLogEntity, UserViewEntity } from '@memberjunction/core-entities';
3
3
  import { UserCache } from '@memberjunction/sqlserver-dataprovider';
4
4
  import { PubSubEngine } from 'type-graphql';
5
5
  import { GraphQLError } from 'graphql';
6
6
  import { DataSource } from 'typeorm';
7
7
 
8
- import { UserPayload } from '../types';
8
+ import { RunViewGenericParams, UserPayload } from '../types';
9
9
  import { RunDynamicViewInput, RunViewByIDInput, RunViewByNameInput } from './RunViewResolver';
10
10
  import { DeleteOptionsInput } from './DeleteOptionsInput';
11
11
  import { MJGlobal } from '@memberjunction/global';
12
12
  import { PUSH_STATUS_UPDATES_TOPIC } from './PushStatusResolver';
13
-
13
+ import { FieldMapper } from '@memberjunction/graphql-dataprovider';
14
+
14
15
  export class ResolverBase {
15
-
16
16
  protected MapFieldNamesToCodeNames(entityName: string, dataObject: any) {
17
17
  // for the given entity name provided, check to see if there are any fields
18
18
  // where the code name is different from the field name, and for just those
@@ -22,21 +22,19 @@ export class ResolverBase {
22
22
  if (dataObject) {
23
23
  const md = new Metadata();
24
24
  const entityInfo = md.Entities.find((e) => e.Name === entityName);
25
- if (!entityInfo)
26
- throw new Error(`Entity ${entityName} not found in metadata`);
27
- const fields = entityInfo.Fields.filter((f) => f.Name !== f.CodeName || f.Name.startsWith('__mj_'));
28
- fields.forEach((f) => {
25
+ if (!entityInfo) throw new Error(`Entity ${entityName} not found in metadata`);
26
+ // const fields = entityInfo.Fields.filter((f) => f.Name !== f.CodeName || f.Name.startsWith('__mj_'));
27
+ const mapper = new FieldMapper();
28
+ entityInfo.Fields.forEach((f) => {
29
29
  if (dataObject.hasOwnProperty(f.Name)) {
30
- if (f.CodeName.startsWith('__mj_')) { // GraphQL doesn't allow us to pass back fields with __ so we are mapping our special field cases that start with __mj_ to _mj__ for transport - they are converted back on the other side automatically
31
- const newCodeName = `_mj__${f.CodeName.substring(5)}`;
32
- dataObject[newCodeName] = dataObject[f.Name];
33
- }
34
- else {
35
- dataObject[f.CodeName] = dataObject[f.Name];
30
+ // GraphQL doesn't allow us to pass back fields with __ so we are mapping our special field cases that start with __mj_ to _mj__ for transport - they are converted back on the other side automatically
31
+ const mappedFieldName = mapper.MapFieldName(f.CodeName)
32
+ if (mappedFieldName !== f.Name) {
33
+ dataObject[mappedFieldName] = dataObject[f.Name];
34
+ delete dataObject[f.Name];
36
35
  }
37
- delete dataObject[f.Name];
38
36
  }
39
- });
37
+ });
40
38
  }
41
39
  return dataObject;
42
40
  }
@@ -46,7 +44,7 @@ export class ResolverBase {
46
44
  if (dataObjectArray && dataObjectArray.length > 0) {
47
45
  dataObjectArray.forEach((element) => {
48
46
  this.MapFieldNamesToCodeNames(entityName, element);
49
- });
47
+ });
50
48
  }
51
49
  return dataObjectArray;
52
50
  }
@@ -75,7 +73,9 @@ export class ResolverBase {
75
73
 
76
74
  async RunViewByNameGeneric(viewInput: RunViewByNameInput, dataSource: DataSource, userPayload: UserPayload, pubSub: PubSubEngine) {
77
75
  try {
78
- const viewInfo: UserViewEntity = this.safeFirstArrayElement(await this.findBy(dataSource, 'User Views', { Name: viewInput.ViewName }));;
76
+ const viewInfo: UserViewEntity = this.safeFirstArrayElement(
77
+ await this.findBy(dataSource, 'User Views', { Name: viewInput.ViewName })
78
+ );
79
79
  return this.RunViewGenericInternal(
80
80
  viewInfo,
81
81
  dataSource,
@@ -134,9 +134,9 @@ export class ResolverBase {
134
134
  if (!entity) throw new Error(`Entity ${viewInput.EntityName} not found in metadata`);
135
135
 
136
136
  const viewInfo: UserViewEntity = {
137
- ID: -1,
137
+ ID: "",
138
138
  Entity: viewInput.EntityName,
139
- EntityID: entity.ID as number,
139
+ EntityID: entity.ID,
140
140
  EntityBaseView: entity.BaseView as string,
141
141
  } as UserViewEntity; // only providing a few bits of data here, but it's enough to get the view to run
142
142
 
@@ -164,6 +164,71 @@ export class ResolverBase {
164
164
  }
165
165
  }
166
166
 
167
+ async RunViewsGeneric(viewInputs: (RunViewByNameInput & RunViewByIDInput & RunDynamicViewInput)[], dataSource: DataSource, userPayload: UserPayload, pubSub: PubSubEngine) {
168
+ let md: Metadata | null = null;
169
+ let params: RunViewGenericParams[] = [];
170
+ for(const viewInput of viewInputs) {
171
+ try {
172
+ let viewInfo: UserViewEntity | null = null;
173
+
174
+ if(viewInput.ViewName) {
175
+ viewInfo = this.safeFirstArrayElement(
176
+ await this.findBy(dataSource, 'User Views', { Name: viewInput.ViewName })
177
+ );
178
+ }
179
+ else if(viewInput.ViewID) {
180
+ viewInfo = this.safeFirstArrayElement(await this.findBy(dataSource, 'User Views', { ID: viewInput.ViewID }));
181
+ }
182
+ else if(viewInput.EntityName) {
183
+ md = md || new Metadata();
184
+ const entity = md.Entities.find((e) => e.Name === viewInput.EntityName);
185
+ if (!entity) {
186
+ throw new Error(`Entity ${viewInput.EntityName} not found in metadata`);
187
+ }
188
+
189
+ // only providing a few bits of data here, but it's enough to get the view to run
190
+ viewInfo = {
191
+ ID: "",
192
+ Entity: viewInput.EntityName,
193
+ EntityID: entity.ID,
194
+ EntityBaseView: entity.BaseView,
195
+ } as UserViewEntity;
196
+ }
197
+ else{
198
+ throw new Error("Unable to determine input type");
199
+ }
200
+
201
+ params.push({
202
+ viewInfo: viewInfo,
203
+ dataSource: dataSource,
204
+ extraFilter: viewInput.ExtraFilter,
205
+ orderBy: viewInput.OrderBy,
206
+ userSearchString: viewInput.UserSearchString,
207
+ excludeUserViewRunID: viewInput.ExcludeUserViewRunID,
208
+ overrideExcludeFilter: viewInput.OverrideExcludeFilter,
209
+ saveViewResults: viewInput.EntityName ? false : viewInput.SaveViewResults,
210
+ fields: viewInput.Fields,
211
+ ignoreMaxRows: viewInput.IgnoreMaxRows,
212
+ excludeDataFromAllPriorViewRuns: viewInput.EntityName ? false : viewInput.ExcludeDataFromAllPriorViewRuns,
213
+ forceAuditLog: viewInput.ForceAuditLog,
214
+ auditLogDescription: viewInput.AuditLogDescription,
215
+ resultType: viewInput.ResultType,
216
+ userPayload,
217
+ pubSub
218
+ });
219
+
220
+ } catch (err) {
221
+ LogError(err);
222
+ return null;
223
+ }
224
+ }
225
+
226
+ let results: RunViewResult[] = await this.RunViewsGenericInternal(params);
227
+ return results;
228
+ }
229
+
230
+
231
+
167
232
  protected CheckUserReadPermissions(entityName: string, userPayload: UserPayload | null) {
168
233
  const md = new Metadata();
169
234
  const entityInfo = md.Entities.find((e) => e.Name === entityName);
@@ -185,7 +250,7 @@ export class ResolverBase {
185
250
  extraFilter: string,
186
251
  orderBy: string,
187
252
  userSearchString: string,
188
- excludeUserViewRunID: number | undefined,
253
+ excludeUserViewRunID: string | undefined,
189
254
  overrideExcludeFilter: string | undefined,
190
255
  saveViewResults: boolean | undefined,
191
256
  fields: string[] | undefined,
@@ -241,22 +306,90 @@ export class ResolverBase {
241
306
  user
242
307
  );
243
308
  // go through the result and convert all fields that start with __mj_*** to _mj__*** for GraphQL transport
309
+ const mapper = new FieldMapper();
244
310
  if (result && result.Success) {
245
311
  for (const r of result.Results) {
246
- const keys = Object.keys(r);
247
- keys.forEach((k) => {
248
- if (k.trim().toLowerCase().startsWith('__mj_')) {
249
- r[`_mj__${k.substring(5)}`] = r[k];
250
- delete r[k];
251
- }
252
- });
253
-
312
+ mapper.MapFields(r);
254
313
  }
255
314
  }
256
315
  return result;
257
- }
258
- else
259
- return null;
316
+ } else return null;
317
+ } catch (err) {
318
+ console.log(err);
319
+ throw err;
320
+ }
321
+ }
322
+
323
+ protected async RunViewsGenericInternal(params: RunViewGenericParams[]): Promise<RunViewResult[]> {
324
+ try {
325
+ let md: Metadata | null = null;
326
+ const rv = new RunView();
327
+ let RunViewParams: RunViewParams[] = [];
328
+ let contextUser: UserInfo | null = null;
329
+ for(const param of params){
330
+ if (param.viewInfo && param.userPayload) {
331
+ md = md || new Metadata();
332
+ const user: UserInfo = UserCache.Users.find((u) => u.Email.toLowerCase().trim() === param.userPayload?.email.toLowerCase().trim());
333
+ if (!user) {
334
+ throw new Error(`User ${param.userPayload?.email} not found in metadata`);
335
+ }
336
+
337
+ contextUser = contextUser || user;
338
+
339
+ const entityInfo = md.Entities.find((e) => e.Name === param.viewInfo.Entity);
340
+ if (!entityInfo){
341
+ throw new Error(`Entity ${param.viewInfo.Entity} not found in metadata`);
342
+ }
343
+ }
344
+
345
+ // figure out the result type from the input string (if provided)
346
+ let rt: 'simple' | 'entity_object' | 'count_only' = 'simple';
347
+ switch (param.resultType?.trim().toLowerCase()) {
348
+ case 'count_only':
349
+ rt = 'count_only';
350
+ break;
351
+ // use simple as the default AND for entity_object
352
+ // becuase on teh server we don't really pass back
353
+ // a true entity_object anyway, just passing back
354
+ // the simple object anyway
355
+ case 'entity_object':
356
+ default:
357
+ rt = 'simple';
358
+ break;
359
+ }
360
+
361
+ RunViewParams.push({
362
+ ViewID: param.viewInfo.ID,
363
+ ViewName: param.viewInfo.Name,
364
+ EntityName: param.viewInfo.Entity,
365
+ ExtraFilter: param.extraFilter,
366
+ OrderBy: param.orderBy,
367
+ Fields: param.fields,
368
+ UserSearchString: param.userSearchString,
369
+ ExcludeUserViewRunID: param.excludeUserViewRunID,
370
+ OverrideExcludeFilter: param.overrideExcludeFilter,
371
+ SaveViewResults: param.saveViewResults,
372
+ ExcludeDataFromAllPriorViewRuns: param.excludeDataFromAllPriorViewRuns,
373
+ IgnoreMaxRows: param.ignoreMaxRows,
374
+ ForceAuditLog: param.forceAuditLog,
375
+ AuditLogDescription: param.auditLogDescription,
376
+ ResultType: rt,
377
+ });
378
+ }
379
+
380
+ let runViewResults: RunViewResult[] = await rv.RunViews(RunViewParams, contextUser);
381
+
382
+ // go through the result and convert all fields that start with __mj_*** to _mj__*** for GraphQL transport
383
+ const mapper = new FieldMapper();
384
+ for(const runViewResult of runViewResults){
385
+ if (runViewResult && runViewResult.Success) {
386
+ for (const result of runViewResult.Results) {
387
+ mapper.MapFields(result);
388
+ }
389
+ }
390
+ }
391
+
392
+ return runViewResults;
260
393
  }
261
394
  catch (err) {
262
395
  console.log(err);
@@ -301,7 +434,7 @@ export class ResolverBase {
301
434
  auditLogTypeName: string,
302
435
  status: string,
303
436
  details: string | null,
304
- entityId: number,
437
+ entityId: string,
305
438
  recordId: any | null
306
439
  ): Promise<any> {
307
440
  try {
@@ -318,30 +451,23 @@ export class ResolverBase {
318
451
  const auditLog = await md.GetEntityObject<AuditLogEntity>('Audit Logs', userInfo); // must pass user context on back end as we're not authenticated the same way as the front end
319
452
  auditLog.NewRecord();
320
453
  auditLog.UserID = userInfo.ID;
321
- auditLog.AuditLogTypeName = auditLogType.Name;
454
+ auditLog.AuditLogTypeID = auditLogType.ID;
322
455
 
323
456
  if (authorization)
324
- auditLog.AuthorizationName = authorization.Name;
457
+ auditLog.AuthorizationID = authorization.ID;
325
458
 
326
- if (status?.trim().toLowerCase() === 'success')
327
- auditLog.Status = "Success"
328
- else
329
- auditLog.Status = "Failed";
459
+ if (status?.trim().toLowerCase() === 'success') auditLog.Status = 'Success';
460
+ else auditLog.Status = 'Failed';
330
461
 
331
- if (details)
332
- auditLog.Details = details;
462
+ if (details) auditLog.Details = details;
333
463
 
334
464
  auditLog.EntityID = entityId;
335
465
 
336
- if (recordId)
337
- auditLog.RecordID = recordId;
466
+ if (recordId) auditLog.RecordID = recordId;
338
467
 
339
- if (await auditLog.Save())
340
- return auditLog;
341
- else
342
- throw new Error(`Error saving audit log record`);
343
- }
344
- catch (err) {
468
+ if (await auditLog.Save()) return auditLog;
469
+ else throw new Error(`Error saving audit log record`);
470
+ } catch (err) {
345
471
  console.log(err);
346
472
  return null;
347
473
  }
@@ -385,118 +511,114 @@ export class ResolverBase {
385
511
  type: 'EntityObjectStatusMessage',
386
512
  entityName: entityObject.EntityInfo.Name,
387
513
  primaryKey: entityObject.PrimaryKey,
388
- message: event.args.message
514
+ message: event.args.message,
389
515
  }),
390
- sessionId: userPayload.sessionId
391
- });
516
+ sessionId: userPayload.sessionId,
517
+ });
392
518
  }
393
519
  }
394
520
  });
395
521
  }
396
522
 
397
523
  protected async CreateRecord(entityName: string, input: any, dataSource: DataSource, userPayload: UserPayload, pubSub: PubSubEngine) {
398
- if (await this.BeforeCreate(dataSource, input)) { // fire event and proceed if it wasn't cancelled
399
- const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
400
- entityObject.NewRecord();
401
- entityObject.SetMany(input);
402
-
403
- this.ListenForEntityMessages(entityObject, pubSub, userPayload);
404
-
405
- if (await entityObject.Save()) {
406
- // save worked, fire the AfterCreate event and then return all the data
407
- await this.AfterCreate(dataSource, input); // fire event
408
- return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
409
- }
410
- else
411
- // save failed, return null
412
- throw entityObject.LatestResult.Message;
413
- }
414
- else
415
- return null;
524
+ if (await this.BeforeCreate(dataSource, input)) {
525
+ // fire event and proceed if it wasn't cancelled
526
+ const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
527
+ entityObject.NewRecord();
528
+ entityObject.SetMany(input);
529
+
530
+ this.ListenForEntityMessages(entityObject, pubSub, userPayload);
531
+
532
+ if (await entityObject.Save()) {
533
+ // save worked, fire the AfterCreate event and then return all the data
534
+ await this.AfterCreate(dataSource, input); // fire event
535
+ return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
536
+ }
537
+ // save failed, return null
538
+ else throw entityObject.LatestResult.Message;
539
+ } else return null;
416
540
  }
417
541
 
418
542
  // Before/After CREATE Event Hooks for Sub-Classes to Override
419
543
  protected async BeforeCreate(dataSource: DataSource, input: any): Promise<boolean> {
420
- return true;
544
+ return true;
421
545
  }
422
- protected async AfterCreate(dataSource: DataSource, input: any) {
423
- }
424
-
546
+ protected async AfterCreate(dataSource: DataSource, input: any) {}
425
547
 
426
548
  protected async UpdateRecord(entityName: string, input: any, dataSource: DataSource, userPayload: UserPayload, pubSub: PubSubEngine) {
427
- if (await this.BeforeUpdate(dataSource, input)) { // fire event and proceed if it wasn't cancelled
428
- const md = new Metadata();
429
- const userInfo = this.GetUserFromPayload(userPayload)
430
- const entityObject = await md.GetEntityObject(entityName, userInfo);
431
- const entityInfo = entityObject.EntityInfo;
432
- const clientNewValues = {};
433
- Object.keys(input).forEach((key) => { if (key !== 'OldValues___') clientNewValues[key] = input[key]; }); // grab all the props except for the OldValues property
434
-
435
- if (entityInfo.TrackRecordChanges || !input.OldValues___) {
436
- // the entity tracks record changes, so we need to load the old values from the DB to make sure they are not inconsistent
437
- // with the old values from the input.OldValues property. If they are different, but on different fields, we allow it
438
- // but if they are different on fields that the current UpdateRecord call is trying to update, we throw an error.
439
- const cKey = new CompositeKey(entityInfo.PrimaryKeys.map((pk) => {
440
- return {
441
- FieldName: pk.Name,
442
- Value: input[pk.CodeName]
443
- }
444
- }));
445
-
446
- if (await entityObject.InnerLoad(cKey)) {
447
- // load worked, now, if we HAVE OldValues, we need to check them against the values in the DB we just loaded.
448
- if (!input.OldValues___) {
449
- // no OldValues, so we can just set the new values from input
450
- entityObject.SetMany(input);
451
- }
452
- else {
453
- // we DO have OldValues, so we need to do a more in depth analysis
454
- this.TestAndSetClientOldValuesToDBValues(input, clientNewValues, entityObject);
455
- }
456
- }
457
- else {
458
- // save failed, return null
459
- throw new GraphQLError(`Record not found for ${entityName} with key ${JSON.stringify(cKey)}`, {
460
- extensions: { code: 'LOAD_ENTITY_ERROR', entityName },
461
- });
549
+ if (await this.BeforeUpdate(dataSource, input)) {
550
+ // fire event and proceed if it wasn't cancelled
551
+ const md = new Metadata();
552
+ const userInfo = this.GetUserFromPayload(userPayload);
553
+ const entityObject = await md.GetEntityObject(entityName, userInfo);
554
+ const entityInfo = entityObject.EntityInfo;
555
+ const clientNewValues = {};
556
+ Object.keys(input).forEach((key) => {
557
+ if (key !== 'OldValues___') clientNewValues[key] = input[key];
558
+ }); // grab all the props except for the OldValues property
559
+
560
+ if (entityInfo.TrackRecordChanges || !input.OldValues___) {
561
+ // the entity tracks record changes, so we need to load the old values from the DB to make sure they are not inconsistent
562
+ // with the old values from the input.OldValues property. If they are different, but on different fields, we allow it
563
+ // but if they are different on fields that the current UpdateRecord call is trying to update, we throw an error.
564
+ const cKey = new CompositeKey(
565
+ entityInfo.PrimaryKeys.map((pk) => {
566
+ return {
567
+ FieldName: pk.Name,
568
+ Value: input[pk.CodeName],
569
+ };
570
+ })
571
+ );
572
+
573
+ if (await entityObject.InnerLoad(cKey)) {
574
+ // load worked, now, if we HAVE OldValues, we need to check them against the values in the DB we just loaded.
575
+ if (!input.OldValues___) {
576
+ // no OldValues, so we can just set the new values from input
577
+ entityObject.SetMany(input);
578
+ } else {
579
+ // we DO have OldValues, so we need to do a more in depth analysis
580
+ this.TestAndSetClientOldValuesToDBValues(input, clientNewValues, entityObject);
462
581
  }
582
+ } else {
583
+ // save failed, return null
584
+ throw new GraphQLError(`Record not found for ${entityName} with key ${JSON.stringify(cKey)}`, {
585
+ extensions: { code: 'LOAD_ENTITY_ERROR', entityName },
586
+ });
463
587
  }
464
- else {
465
- // not tracking changes and we DO have OldValues, so we can load from them
466
- const oldValues = {};
467
- // for each item in the oldValues array, add it to the oldValues object
468
- input.OldValues___?.forEach((item) => oldValues[item.Key] = item.Value);
469
-
470
- // 1) load the old values, this will be the initial state of the object
471
- entityObject.LoadFromData(oldValues);
588
+ } else {
589
+ // not tracking changes and we DO have OldValues, so we can load from them
590
+ const oldValues = {};
591
+ // for each item in the oldValues array, add it to the oldValues object
592
+ input.OldValues___?.forEach((item) => (oldValues[item.Key] = item.Value));
472
593
 
473
- // 2) set the new values from the input, not including the OldValues property
474
- entityObject.SetMany(clientNewValues);
475
- }
594
+ // 1) load the old values, this will be the initial state of the object
595
+ entityObject.LoadFromData(oldValues);
476
596
 
477
- this.ListenForEntityMessages(entityObject, pubSub, userPayload);
478
- if (await entityObject.Save()) {
479
- // save worked, fire afterevent and return all the data
480
- await this.AfterUpdate(dataSource, input); // fire event
481
- return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
482
- }
483
- else {
484
- throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
485
- extensions: { code: 'SAVE_ENTITY_ERROR', entityName },
486
- });
487
- }
597
+ // 2) set the new values from the input, not including the OldValues property
598
+ entityObject.SetMany(clientNewValues);
488
599
  }
489
- else
490
- throw new GraphQLError('Save Canceled by BeforeSave() handler in ResolverBase', {
600
+
601
+ this.ListenForEntityMessages(entityObject, pubSub, userPayload);
602
+ if (await entityObject.Save()) {
603
+ // save worked, fire afterevent and return all the data
604
+ await this.AfterUpdate(dataSource, input); // fire event
605
+ return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
606
+ } else {
607
+ throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
491
608
  extensions: { code: 'SAVE_ENTITY_ERROR', entityName },
492
609
  });
493
- }
610
+ }
611
+ } else
612
+ throw new GraphQLError('Save Canceled by BeforeSave() handler in ResolverBase', {
613
+ extensions: { code: 'SAVE_ENTITY_ERROR', entityName },
614
+ });
615
+ }
494
616
 
495
617
  /**
496
- * This routine compares the OldValues property in the input object to the values in the DB that we just loaded. If there are differences, we need to check to see if the client
618
+ * This routine compares the OldValues property in the input object to the values in the DB that we just loaded. If there are differences, we need to check to see if the client
497
619
  * is trying to update any of those fields (e.g. overlap). If there is overlap, we throw an error. If there is no overlap, we can proceed with the update even if the DB Values
498
620
  * and the ClientOldValues are not 100% the same, so long as there is no overlap in the specific FIELDS that are different.
499
- *
621
+ *
500
622
  * ASSUMES: input object has an OldValues___ property that is an array of Key/Value pairs that represent the old values of the record that the client is trying to update.
501
623
  */
502
624
  protected TestAndSetClientOldValuesToDBValues(input: any, clientNewValues: any, entityObject: BaseEntity) {
@@ -507,8 +629,7 @@ export class ResolverBase {
507
629
  // we need to do a quick transform on the values to make sure they match the TS Type for the given field because item.Value will always be a string
508
630
  const field = entityObject.EntityInfo.Fields.find((f) => f.CodeName === item.Key);
509
631
  let val = item.Value;
510
- if ( (val === null || val === undefined) && field.DefaultValue !== null && field.DefaultValue !== undefined)
511
- val = field.DefaultValue; // set default value as the field was never set
632
+ if ((val === null || val === undefined) && field.DefaultValue !== null && field.DefaultValue !== undefined) val = field.DefaultValue; // set default value as the field was never set
512
633
 
513
634
  if (field) {
514
635
  switch (field.TSType) {
@@ -516,12 +637,11 @@ export class ResolverBase {
516
637
  val = val !== null && val !== undefined ? parseInt(val) : null;
517
638
  break;
518
639
  case EntityFieldTSType.Boolean:
519
- val = (val === null || val === undefined || val === 'false' || val === '0' || parseInt(val) === 0) ? false : true;
640
+ val = val === null || val === undefined || val === 'false' || val === '0' || parseInt(val) === 0 ? false : true;
520
641
  break;
521
642
  case EntityFieldTSType.Date:
522
- // first, if val is a string and it is actually a number (milliseconds since epoch), convert it to a number.
523
- if (val !== null && val !== undefined && val.toString().trim() !== '' && !isNaN(val))
524
- val = parseInt(val);
643
+ // first, if val is a string and it is actually a number (milliseconds since epoch), convert it to a number.
644
+ if (val !== null && val !== undefined && val.toString().trim() !== '' && !isNaN(val)) val = parseInt(val);
525
645
 
526
646
  val = val !== null && val !== undefined ? new Date(val) : null;
527
647
  break;
@@ -529,7 +649,7 @@ export class ResolverBase {
529
649
  break; // already a string
530
650
  }
531
651
  }
532
- clientOldValues[item.Key] = val
652
+ clientOldValues[item.Key] = val;
533
653
  });
534
654
 
535
655
  // clientOldValues now has all of the oldValues the CLIENT passed us. Now we need to build the same kind of object
@@ -557,12 +677,12 @@ export class ResolverBase {
557
677
  different = clientOldValues[key] !== dbValues[key];
558
678
  break;
559
679
  }
560
- if (different && f && !f.ReadOnly ) {
680
+ if (different && f && !f.ReadOnly) {
561
681
  // only include updateable fields
562
682
  dbDifferences.push({
563
683
  FieldName: key,
564
684
  ClientOldValue: clientOldValues[key],
565
- DBValue: dbValues[key]
685
+ DBValue: dbValues[key],
566
686
  });
567
687
  }
568
688
  });
@@ -578,7 +698,7 @@ export class ResolverBase {
578
698
  clientDifferences.push({
579
699
  FieldName: key,
580
700
  ClientOldValue: clientOldValues[key],
581
- ClientNewValue: clientNewValues[key]
701
+ ClientNewValue: clientNewValues[key],
582
702
  });
583
703
  }
584
704
  });
@@ -588,36 +708,43 @@ export class ResolverBase {
588
708
  const overlap = clientDifferences.filter((cd) => dbDifferences.find((dd) => dd.FieldName === cd.FieldName));
589
709
  if (overlap.length > 0) {
590
710
  const msg = {
591
- Message: 'Inconsistency between old values provided for changed fields, and the values of one or more of those fields in the database. Update operation cancelled.',
711
+ Message:
712
+ 'Inconsistency between old values provided for changed fields, and the values of one or more of those fields in the database. Update operation cancelled.',
592
713
  ClientDifferences: clientDifferences,
593
714
  DBDifferences: dbDifferences,
594
- Overlap: overlap
715
+ Overlap: overlap,
595
716
  };
596
717
  throw new Error(JSON.stringify(msg));
597
718
  }
598
719
  }
599
720
 
600
- // If we get here that means we've not thrown an exception, so there is
721
+ // If we get here that means we've not thrown an exception, so there is
601
722
  // NO OVERLAP, so we can set the new values from the data provided from the client now...
602
723
  entityObject.SetMany(clientNewValues);
603
724
  }
604
725
 
605
- protected async DeleteRecord(entityName: string, key: CompositeKey, options: DeleteOptionsInput, dataSource: DataSource, userPayload: UserPayload, pubSub: PubSubEngine) {
606
- if (await this.BeforeDelete(dataSource, key)) { // fire event and proceed if it wasn't cancelled
607
- const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
608
- await entityObject.InnerLoad(key);
609
- const returnValue = entityObject.GetAll(); // grab the values before we delete so we can return last state before delete if we are successful.
610
- if (await entityObject.Delete(options)) {
611
- await this.AfterDelete(dataSource, key); // fire event
612
- return returnValue;
613
- }
614
- else {
615
- throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
616
- extensions: { code: 'DELETE_ENTITY_ERROR', entityName },
617
- });
618
- }
619
- }
620
- else {
726
+ protected async DeleteRecord(
727
+ entityName: string,
728
+ key: CompositeKey,
729
+ options: DeleteOptionsInput,
730
+ dataSource: DataSource,
731
+ userPayload: UserPayload,
732
+ pubSub: PubSubEngine
733
+ ) {
734
+ if (await this.BeforeDelete(dataSource, key)) {
735
+ // fire event and proceed if it wasn't cancelled
736
+ const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
737
+ await entityObject.InnerLoad(key);
738
+ const returnValue = entityObject.GetAll(); // grab the values before we delete so we can return last state before delete if we are successful.
739
+ if (await entityObject.Delete(options)) {
740
+ await this.AfterDelete(dataSource, key); // fire event
741
+ return returnValue;
742
+ } else {
743
+ throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
744
+ extensions: { code: 'DELETE_ENTITY_ERROR', entityName },
745
+ });
746
+ }
747
+ } else {
621
748
  throw new GraphQLError('Delete operation canceled by BeforeDelete() handler in ResolverBase', {
622
749
  extensions: { code: 'DELETE_ENTITY_ERROR', entityName },
623
750
  });
@@ -625,16 +752,14 @@ export class ResolverBase {
625
752
  }
626
753
 
627
754
  // Before/After DELETE Event Hooks for Sub-Classes to Override
628
- protected async BeforeDelete(dataSource: DataSource, key: CompositeKey,): Promise<boolean> {
629
- return true;
630
- }
631
- protected async AfterDelete(dataSource: DataSource, key: CompositeKey,) {
755
+ protected async BeforeDelete(dataSource: DataSource, key: CompositeKey): Promise<boolean> {
756
+ return true;
632
757
  }
758
+ protected async AfterDelete(dataSource: DataSource, key: CompositeKey) {}
633
759
 
634
760
  // Before/After UPDATE Event Hooks for Sub-Classes to Override
635
761
  protected async BeforeUpdate(dataSource: DataSource, input: any): Promise<boolean> {
636
- return true;
762
+ return true;
637
763
  }
638
- protected async AfterUpdate(dataSource: DataSource, input: any) {
639
- }
764
+ protected async AfterUpdate(dataSource: DataSource, input: any) {}
640
765
  }