@memberjunction/server 1.8.1 → 2.1.1

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 (192) hide show
  1. package/CHANGELOG.json +130 -1
  2. package/CHANGELOG.md +29 -2
  3. package/package.json +31 -40
  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/entitySubclasses/userViewEntity.server.ts +1 -1
  9. package/src/generated/generated.ts +9482 -8398
  10. package/src/generic/ResolverBase.ts +299 -172
  11. package/src/generic/RunViewResolver.ts +204 -14
  12. package/src/index.ts +18 -19
  13. package/src/resolvers/AskSkipResolver.ts +35 -35
  14. package/src/resolvers/ColorResolver.ts +3 -14
  15. package/src/resolvers/DatasetResolver.ts +4 -4
  16. package/src/resolvers/EntityCommunicationsResolver.ts +37 -15
  17. package/src/resolvers/FileCategoryResolver.ts +2 -2
  18. package/src/resolvers/FileResolver.ts +6 -4
  19. package/src/resolvers/MergeRecordsResolver.ts +3 -2
  20. package/src/resolvers/PotentialDuplicateRecordResolver.ts +48 -40
  21. package/src/resolvers/QueryResolver.ts +2 -2
  22. package/src/resolvers/ReportResolver.ts +6 -6
  23. package/src/resolvers/UserFavoriteResolver.ts +8 -8
  24. package/src/resolvers/UserResolver.ts +3 -1
  25. package/src/resolvers/UserViewResolver.ts +1 -1
  26. package/src/types.ts +21 -0
  27. package/tsconfig.json +3 -4
  28. package/dist/apolloServer/TransactionPlugin.d.ts +0 -4
  29. package/dist/apolloServer/TransactionPlugin.d.ts.map +0 -1
  30. package/dist/apolloServer/TransactionPlugin.js +0 -49
  31. package/dist/apolloServer/TransactionPlugin.js.map +0 -1
  32. package/dist/apolloServer/index.d.ts +0 -11
  33. package/dist/apolloServer/index.d.ts.map +0 -1
  34. package/dist/apolloServer/index.js +0 -27
  35. package/dist/apolloServer/index.js.map +0 -1
  36. package/dist/auth/exampleNewUserSubClass.d.ts +0 -6
  37. package/dist/auth/exampleNewUserSubClass.d.ts.map +0 -1
  38. package/dist/auth/exampleNewUserSubClass.js +0 -54
  39. package/dist/auth/exampleNewUserSubClass.js.map +0 -1
  40. package/dist/auth/index.d.ts +0 -30
  41. package/dist/auth/index.d.ts.map +0 -1
  42. package/dist/auth/index.js +0 -129
  43. package/dist/auth/index.js.map +0 -1
  44. package/dist/auth/newUsers.d.ts +0 -5
  45. package/dist/auth/newUsers.d.ts.map +0 -1
  46. package/dist/auth/newUsers.js +0 -66
  47. package/dist/auth/newUsers.js.map +0 -1
  48. package/dist/auth/tokenExpiredError.d.ts +0 -5
  49. package/dist/auth/tokenExpiredError.d.ts.map +0 -1
  50. package/dist/auth/tokenExpiredError.js +0 -16
  51. package/dist/auth/tokenExpiredError.js.map +0 -1
  52. package/dist/cache.d.ts +0 -3
  53. package/dist/cache.d.ts.map +0 -1
  54. package/dist/cache.js +0 -11
  55. package/dist/cache.js.map +0 -1
  56. package/dist/config.d.ts +0 -196
  57. package/dist/config.d.ts.map +0 -1
  58. package/dist/config.js +0 -72
  59. package/dist/config.js.map +0 -1
  60. package/dist/context.d.ts +0 -17
  61. package/dist/context.d.ts.map +0 -1
  62. package/dist/context.js +0 -114
  63. package/dist/context.js.map +0 -1
  64. package/dist/directives/Public.d.ts +0 -4
  65. package/dist/directives/Public.d.ts.map +0 -1
  66. package/dist/directives/Public.js +0 -34
  67. package/dist/directives/Public.js.map +0 -1
  68. package/dist/directives/index.d.ts +0 -2
  69. package/dist/directives/index.d.ts.map +0 -1
  70. package/dist/directives/index.js +0 -18
  71. package/dist/directives/index.js.map +0 -1
  72. package/dist/entitySubclasses/DuplicateRunEntity.server.d.ts +0 -6
  73. package/dist/entitySubclasses/DuplicateRunEntity.server.d.ts.map +0 -1
  74. package/dist/entitySubclasses/DuplicateRunEntity.server.js +0 -37
  75. package/dist/entitySubclasses/DuplicateRunEntity.server.js.map +0 -1
  76. package/dist/entitySubclasses/EntityBehavior.server.d.ts +0 -29
  77. package/dist/entitySubclasses/EntityBehavior.server.d.ts.map +0 -1
  78. package/dist/entitySubclasses/EntityBehavior.server.js +0 -213
  79. package/dist/entitySubclasses/EntityBehavior.server.js.map +0 -1
  80. package/dist/entitySubclasses/entityPermissions.server.d.ts +0 -23
  81. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +0 -1
  82. package/dist/entitySubclasses/entityPermissions.server.js +0 -99
  83. package/dist/entitySubclasses/entityPermissions.server.js.map +0 -1
  84. package/dist/entitySubclasses/userViewEntity.server.d.ts +0 -13
  85. package/dist/entitySubclasses/userViewEntity.server.d.ts.map +0 -1
  86. package/dist/entitySubclasses/userViewEntity.server.js +0 -164
  87. package/dist/entitySubclasses/userViewEntity.server.js.map +0 -1
  88. package/dist/generated/generated.d.ts +0 -6482
  89. package/dist/generated/generated.d.ts.map +0 -1
  90. package/dist/generated/generated.js +0 -35073
  91. package/dist/generated/generated.js.map +0 -1
  92. package/dist/generic/DeleteOptionsInput.d.ts +0 -5
  93. package/dist/generic/DeleteOptionsInput.d.ts.map +0 -1
  94. package/dist/generic/DeleteOptionsInput.js +0 -28
  95. package/dist/generic/DeleteOptionsInput.js.map +0 -1
  96. package/dist/generic/KeyInputOutputTypes.d.ts +0 -16
  97. package/dist/generic/KeyInputOutputTypes.d.ts.map +0 -1
  98. package/dist/generic/KeyInputOutputTypes.js +0 -62
  99. package/dist/generic/KeyInputOutputTypes.js.map +0 -1
  100. package/dist/generic/KeyValuePairInput.d.ts +0 -5
  101. package/dist/generic/KeyValuePairInput.d.ts.map +0 -1
  102. package/dist/generic/KeyValuePairInput.js +0 -28
  103. package/dist/generic/KeyValuePairInput.js.map +0 -1
  104. package/dist/generic/PushStatusResolver.d.ts +0 -14
  105. package/dist/generic/PushStatusResolver.d.ts.map +0 -1
  106. package/dist/generic/PushStatusResolver.js +0 -58
  107. package/dist/generic/PushStatusResolver.js.map +0 -1
  108. package/dist/generic/ResolverBase.d.ts +0 -37
  109. package/dist/generic/ResolverBase.d.ts.map +0 -1
  110. package/dist/generic/ResolverBase.js +0 -468
  111. package/dist/generic/ResolverBase.js.map +0 -1
  112. package/dist/generic/RunViewResolver.d.ts +0 -88
  113. package/dist/generic/RunViewResolver.d.ts.map +0 -1
  114. package/dist/generic/RunViewResolver.js +0 -443
  115. package/dist/generic/RunViewResolver.js.map +0 -1
  116. package/dist/index.d.ts +0 -28
  117. package/dist/index.d.ts.map +0 -1
  118. package/dist/index.js +0 -145
  119. package/dist/index.js.map +0 -1
  120. package/dist/orm.d.ts +0 -4
  121. package/dist/orm.d.ts.map +0 -1
  122. package/dist/orm.js +0 -34
  123. package/dist/orm.js.map +0 -1
  124. package/dist/resolvers/AskSkipResolver.d.ts +0 -54
  125. package/dist/resolvers/AskSkipResolver.d.ts.map +0 -1
  126. package/dist/resolvers/AskSkipResolver.js +0 -800
  127. package/dist/resolvers/AskSkipResolver.js.map +0 -1
  128. package/dist/resolvers/ColorResolver.d.ts +0 -22
  129. package/dist/resolvers/ColorResolver.d.ts.map +0 -1
  130. package/dist/resolvers/ColorResolver.js +0 -94
  131. package/dist/resolvers/ColorResolver.js.map +0 -1
  132. package/dist/resolvers/DatasetResolver.d.ts +0 -42
  133. package/dist/resolvers/DatasetResolver.d.ts.map +0 -1
  134. package/dist/resolvers/DatasetResolver.js +0 -168
  135. package/dist/resolvers/DatasetResolver.js.map +0 -1
  136. package/dist/resolvers/EntityCommunicationsResolver.d.ts +0 -49
  137. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +0 -1
  138. package/dist/resolvers/EntityCommunicationsResolver.js +0 -228
  139. package/dist/resolvers/EntityCommunicationsResolver.js.map +0 -1
  140. package/dist/resolvers/EntityRecordNameResolver.d.ts +0 -21
  141. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +0 -1
  142. package/dist/resolvers/EntityRecordNameResolver.js +0 -113
  143. package/dist/resolvers/EntityRecordNameResolver.js.map +0 -1
  144. package/dist/resolvers/EntityResolver.d.ts +0 -6
  145. package/dist/resolvers/EntityResolver.d.ts.map +0 -1
  146. package/dist/resolvers/EntityResolver.js +0 -60
  147. package/dist/resolvers/EntityResolver.js.map +0 -1
  148. package/dist/resolvers/FileCategoryResolver.d.ts +0 -6
  149. package/dist/resolvers/FileCategoryResolver.d.ts.map +0 -1
  150. package/dist/resolvers/FileCategoryResolver.js +0 -65
  151. package/dist/resolvers/FileCategoryResolver.js.map +0 -1
  152. package/dist/resolvers/FileResolver.d.ts +0 -24
  153. package/dist/resolvers/FileResolver.d.ts.map +0 -1
  154. package/dist/resolvers/FileResolver.js +0 -162
  155. package/dist/resolvers/FileResolver.js.map +0 -1
  156. package/dist/resolvers/MergeRecordsResolver.d.ts +0 -59
  157. package/dist/resolvers/MergeRecordsResolver.d.ts.map +0 -1
  158. package/dist/resolvers/MergeRecordsResolver.js +0 -256
  159. package/dist/resolvers/MergeRecordsResolver.js.map +0 -1
  160. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +0 -29
  161. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +0 -1
  162. package/dist/resolvers/PotentialDuplicateRecordResolver.js +0 -127
  163. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +0 -1
  164. package/dist/resolvers/QueryResolver.d.ts +0 -13
  165. package/dist/resolvers/QueryResolver.d.ts.map +0 -1
  166. package/dist/resolvers/QueryResolver.js +0 -74
  167. package/dist/resolvers/QueryResolver.js.map +0 -1
  168. package/dist/resolvers/ReportResolver.d.ts +0 -20
  169. package/dist/resolvers/ReportResolver.d.ts.map +0 -1
  170. package/dist/resolvers/ReportResolver.js +0 -175
  171. package/dist/resolvers/ReportResolver.js.map +0 -1
  172. package/dist/resolvers/UserFavoriteResolver.d.ts +0 -42
  173. package/dist/resolvers/UserFavoriteResolver.d.ts.map +0 -1
  174. package/dist/resolvers/UserFavoriteResolver.js +0 -221
  175. package/dist/resolvers/UserFavoriteResolver.js.map +0 -1
  176. package/dist/resolvers/UserResolver.d.ts +0 -10
  177. package/dist/resolvers/UserResolver.d.ts.map +0 -1
  178. package/dist/resolvers/UserResolver.js +0 -72
  179. package/dist/resolvers/UserResolver.js.map +0 -1
  180. package/dist/resolvers/UserViewResolver.d.ts +0 -13
  181. package/dist/resolvers/UserViewResolver.d.ts.map +0 -1
  182. package/dist/resolvers/UserViewResolver.js +0 -102
  183. package/dist/resolvers/UserViewResolver.js.map +0 -1
  184. package/dist/types.d.ts +0 -17
  185. package/dist/types.d.ts.map +0 -1
  186. package/dist/types.js +0 -3
  187. package/dist/types.js.map +0 -1
  188. package/dist/util.d.ts +0 -4
  189. package/dist/util.d.ts.map +0 -1
  190. package/dist/util.js +0 -89
  191. package/dist/util.js.map +0 -1
  192. 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,115 @@ 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___')
558
+ clientNewValues[key] = input[key];
559
+ }); // grab all the props except for the OldValues property
560
+
561
+ if (entityInfo.TrackRecordChanges || !input.OldValues___) {
562
+ // the entity tracks record changes, so we need to load the old values from the DB to make sure they are not inconsistent
563
+ // with the old values from the input.OldValues property. If they are different, but on different fields, we allow it
564
+ // but if they are different on fields that the current UpdateRecord call is trying to update, we throw an error.
565
+ const cKey = new CompositeKey(
566
+ entityInfo.PrimaryKeys.map((pk) => {
567
+ return {
568
+ FieldName: pk.Name,
569
+ Value: input[pk.CodeName],
570
+ };
571
+ })
572
+ );
573
+
574
+ if (await entityObject.InnerLoad(cKey)) {
575
+ // load worked, now, if we HAVE OldValues, we need to check them against the values in the DB we just loaded.
576
+ if (!input.OldValues___) {
577
+ // no OldValues, so we can just set the new values from input
578
+ entityObject.SetMany(input);
579
+ } else {
580
+ // we DO have OldValues, so we need to do a more in depth analysis
581
+ this.TestAndSetClientOldValuesToDBValues(input, clientNewValues, entityObject);
462
582
  }
583
+ } else {
584
+ // save failed, return null
585
+ throw new GraphQLError(`Record not found for ${entityName} with key ${JSON.stringify(cKey)}`, {
586
+ extensions: { code: 'LOAD_ENTITY_ERROR', entityName },
587
+ });
463
588
  }
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);
589
+ } else {
590
+ // not tracking changes and we DO have OldValues, so we can load from them
591
+ const oldValues = {};
592
+ // for each item in the oldValues array, add it to the oldValues object
593
+ input.OldValues___?.forEach((item) => (oldValues[item.Key] = item.Value));
472
594
 
473
- // 2) set the new values from the input, not including the OldValues property
474
- entityObject.SetMany(clientNewValues);
475
- }
595
+ // 1) load the old values, this will be the initial state of the object
596
+ entityObject.LoadFromData(oldValues);
476
597
 
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
- }
598
+ // 2) set the new values from the input, not including the OldValues property
599
+ entityObject.SetMany(clientNewValues);
488
600
  }
489
- else
490
- throw new GraphQLError('Save Canceled by BeforeSave() handler in ResolverBase', {
601
+
602
+ this.ListenForEntityMessages(entityObject, pubSub, userPayload);
603
+ if (await entityObject.Save()) {
604
+ // save worked, fire afterevent and return all the data
605
+ await this.AfterUpdate(dataSource, input); // fire event
606
+ return this.MapFieldNamesToCodeNames(entityName, entityObject.GetAll());
607
+ } else {
608
+ throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
491
609
  extensions: { code: 'SAVE_ENTITY_ERROR', entityName },
492
610
  });
493
- }
611
+ }
612
+ } else
613
+ throw new GraphQLError('Save Canceled by BeforeSave() handler in ResolverBase', {
614
+ extensions: { code: 'SAVE_ENTITY_ERROR', entityName },
615
+ });
616
+ }
494
617
 
495
618
  /**
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
619
+ * 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
620
  * 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
621
  * and the ClientOldValues are not 100% the same, so long as there is no overlap in the specific FIELDS that are different.
499
- *
622
+ *
500
623
  * 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
624
  */
502
625
  protected TestAndSetClientOldValuesToDBValues(input: any, clientNewValues: any, entityObject: BaseEntity) {
@@ -507,8 +630,8 @@ export class ResolverBase {
507
630
  // 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
631
  const field = entityObject.EntityInfo.Fields.find((f) => f.CodeName === item.Key);
509
632
  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
633
+ if ((val === null || val === undefined) && field.DefaultValue !== null && field.DefaultValue !== undefined && !field.AllowsNull)
634
+ val = field.DefaultValue; // set default value as the field was never set and it does NOT allow nulls
512
635
 
513
636
  if (field) {
514
637
  switch (field.TSType) {
@@ -516,12 +639,11 @@ export class ResolverBase {
516
639
  val = val !== null && val !== undefined ? parseInt(val) : null;
517
640
  break;
518
641
  case EntityFieldTSType.Boolean:
519
- val = (val === null || val === undefined || val === 'false' || val === '0' || parseInt(val) === 0) ? false : true;
642
+ val = val === null || val === undefined || val === 'false' || val === '0' || parseInt(val) === 0 ? false : true;
520
643
  break;
521
644
  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);
645
+ // first, if val is a string and it is actually a number (milliseconds since epoch), convert it to a number.
646
+ if (val !== null && val !== undefined && val.toString().trim() !== '' && !isNaN(val)) val = parseInt(val);
525
647
 
526
648
  val = val !== null && val !== undefined ? new Date(val) : null;
527
649
  break;
@@ -529,7 +651,7 @@ export class ResolverBase {
529
651
  break; // already a string
530
652
  }
531
653
  }
532
- clientOldValues[item.Key] = val
654
+ clientOldValues[item.Key] = val;
533
655
  });
534
656
 
535
657
  // clientOldValues now has all of the oldValues the CLIENT passed us. Now we need to build the same kind of object
@@ -557,12 +679,12 @@ export class ResolverBase {
557
679
  different = clientOldValues[key] !== dbValues[key];
558
680
  break;
559
681
  }
560
- if (different && f && !f.ReadOnly ) {
682
+ if (different && f && !f.ReadOnly) {
561
683
  // only include updateable fields
562
684
  dbDifferences.push({
563
685
  FieldName: key,
564
686
  ClientOldValue: clientOldValues[key],
565
- DBValue: dbValues[key]
687
+ DBValue: dbValues[key],
566
688
  });
567
689
  }
568
690
  });
@@ -578,7 +700,7 @@ export class ResolverBase {
578
700
  clientDifferences.push({
579
701
  FieldName: key,
580
702
  ClientOldValue: clientOldValues[key],
581
- ClientNewValue: clientNewValues[key]
703
+ ClientNewValue: clientNewValues[key],
582
704
  });
583
705
  }
584
706
  });
@@ -588,36 +710,43 @@ export class ResolverBase {
588
710
  const overlap = clientDifferences.filter((cd) => dbDifferences.find((dd) => dd.FieldName === cd.FieldName));
589
711
  if (overlap.length > 0) {
590
712
  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.',
713
+ Message:
714
+ '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
715
  ClientDifferences: clientDifferences,
593
716
  DBDifferences: dbDifferences,
594
- Overlap: overlap
717
+ Overlap: overlap,
595
718
  };
596
719
  throw new Error(JSON.stringify(msg));
597
720
  }
598
721
  }
599
722
 
600
- // If we get here that means we've not thrown an exception, so there is
723
+ // If we get here that means we've not thrown an exception, so there is
601
724
  // NO OVERLAP, so we can set the new values from the data provided from the client now...
602
725
  entityObject.SetMany(clientNewValues);
603
726
  }
604
727
 
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 {
728
+ protected async DeleteRecord(
729
+ entityName: string,
730
+ key: CompositeKey,
731
+ options: DeleteOptionsInput,
732
+ dataSource: DataSource,
733
+ userPayload: UserPayload,
734
+ pubSub: PubSubEngine
735
+ ) {
736
+ if (await this.BeforeDelete(dataSource, key)) {
737
+ // fire event and proceed if it wasn't cancelled
738
+ const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
739
+ await entityObject.InnerLoad(key);
740
+ const returnValue = entityObject.GetAll(); // grab the values before we delete so we can return last state before delete if we are successful.
741
+ if (await entityObject.Delete(options)) {
742
+ await this.AfterDelete(dataSource, key); // fire event
743
+ return returnValue;
744
+ } else {
745
+ throw new GraphQLError(entityObject.LatestResult?.Message ?? 'Unknown error', {
746
+ extensions: { code: 'DELETE_ENTITY_ERROR', entityName },
747
+ });
748
+ }
749
+ } else {
621
750
  throw new GraphQLError('Delete operation canceled by BeforeDelete() handler in ResolverBase', {
622
751
  extensions: { code: 'DELETE_ENTITY_ERROR', entityName },
623
752
  });
@@ -625,16 +754,14 @@ export class ResolverBase {
625
754
  }
626
755
 
627
756
  // 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,) {
757
+ protected async BeforeDelete(dataSource: DataSource, key: CompositeKey): Promise<boolean> {
758
+ return true;
632
759
  }
760
+ protected async AfterDelete(dataSource: DataSource, key: CompositeKey) {}
633
761
 
634
762
  // Before/After UPDATE Event Hooks for Sub-Classes to Override
635
763
  protected async BeforeUpdate(dataSource: DataSource, input: any): Promise<boolean> {
636
- return true;
764
+ return true;
637
765
  }
638
- protected async AfterUpdate(dataSource: DataSource, input: any) {
639
- }
766
+ protected async AfterUpdate(dataSource: DataSource, input: any) {}
640
767
  }