@memberjunction/core 0.9.166 → 0.9.167

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 (66) hide show
  1. package/dist/generic/interfaces.d.ts +0 -2
  2. package/dist/generic/interfaces.js.map +1 -1
  3. package/dist/generic/providerBase.d.ts +0 -7
  4. package/dist/generic/providerBase.js +1 -11
  5. package/dist/generic/providerBase.js.map +1 -1
  6. package/package.json +1 -1
  7. package/dist/MJCore/src/generic/applicationInfo.d.ts +0 -26
  8. package/dist/MJCore/src/generic/applicationInfo.js +0 -57
  9. package/dist/MJCore/src/generic/applicationInfo.js.map +0 -1
  10. package/dist/MJCore/src/generic/baseEntity.d.ts +0 -195
  11. package/dist/MJCore/src/generic/baseEntity.js +0 -674
  12. package/dist/MJCore/src/generic/baseEntity.js.map +0 -1
  13. package/dist/MJCore/src/generic/baseInfo.d.ts +0 -8
  14. package/dist/MJCore/src/generic/baseInfo.js +0 -38
  15. package/dist/MJCore/src/generic/baseInfo.js.map +0 -1
  16. package/dist/MJCore/src/generic/entityInfo.d.ts +0 -520
  17. package/dist/MJCore/src/generic/entityInfo.js +0 -801
  18. package/dist/MJCore/src/generic/entityInfo.js.map +0 -1
  19. package/dist/MJCore/src/generic/interfaces.d.ts +0 -288
  20. package/dist/MJCore/src/generic/interfaces.js +0 -50
  21. package/dist/MJCore/src/generic/interfaces.js.map +0 -1
  22. package/dist/MJCore/src/generic/logging.d.ts +0 -4
  23. package/dist/MJCore/src/generic/logging.js +0 -69
  24. package/dist/MJCore/src/generic/logging.js.map +0 -1
  25. package/dist/MJCore/src/generic/metadata.d.ts +0 -201
  26. package/dist/MJCore/src/generic/metadata.js +0 -312
  27. package/dist/MJCore/src/generic/metadata.js.map +0 -1
  28. package/dist/MJCore/src/generic/providerBase.d.ts +0 -183
  29. package/dist/MJCore/src/generic/providerBase.js +0 -640
  30. package/dist/MJCore/src/generic/providerBase.js.map +0 -1
  31. package/dist/MJCore/src/generic/queryInfo.d.ts +0 -64
  32. package/dist/MJCore/src/generic/queryInfo.js +0 -124
  33. package/dist/MJCore/src/generic/queryInfo.js.map +0 -1
  34. package/dist/MJCore/src/generic/runQuery.d.ts +0 -11
  35. package/dist/MJCore/src/generic/runQuery.js +0 -26
  36. package/dist/MJCore/src/generic/runQuery.js.map +0 -1
  37. package/dist/MJCore/src/generic/runReport.d.ts +0 -11
  38. package/dist/MJCore/src/generic/runReport.js +0 -27
  39. package/dist/MJCore/src/generic/runReport.js.map +0 -1
  40. package/dist/MJCore/src/generic/securityInfo.d.ts +0 -100
  41. package/dist/MJCore/src/generic/securityInfo.js +0 -194
  42. package/dist/MJCore/src/generic/securityInfo.js.map +0 -1
  43. package/dist/MJCore/src/generic/transactionGroup.d.ts +0 -24
  44. package/dist/MJCore/src/generic/transactionGroup.js +0 -79
  45. package/dist/MJCore/src/generic/transactionGroup.js.map +0 -1
  46. package/dist/MJCore/src/generic/util.d.ts +0 -16
  47. package/dist/MJCore/src/generic/util.js +0 -151
  48. package/dist/MJCore/src/generic/util.js.map +0 -1
  49. package/dist/MJCore/src/index.d.ts +0 -16
  50. package/dist/MJCore/src/index.js +0 -44
  51. package/dist/MJCore/src/index.js.map +0 -1
  52. package/dist/MJCore/src/views/runView.d.ts +0 -112
  53. package/dist/MJCore/src/views/runView.js +0 -63
  54. package/dist/MJCore/src/views/runView.js.map +0 -1
  55. package/dist/MJCore/src/views/viewInfo.d.ts +0 -59
  56. package/dist/MJCore/src/views/viewInfo.js +0 -121
  57. package/dist/MJCore/src/views/viewInfo.js.map +0 -1
  58. package/dist/MJGlobal/src/ObjectCache.d.ts +0 -26
  59. package/dist/MJGlobal/src/ObjectCache.js +0 -55
  60. package/dist/MJGlobal/src/ObjectCache.js.map +0 -1
  61. package/dist/generic/objectCache.d.ts +0 -26
  62. package/dist/generic/objectCache.js +0 -55
  63. package/dist/generic/objectCache.js.map +0 -1
  64. package/dist/generic/viewInfo.d.ts +0 -59
  65. package/dist/generic/viewInfo.js +0 -121
  66. package/dist/generic/viewInfo.js.map +0 -1
@@ -1,674 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BaseEntity = exports.BaseEntityAIActionParams = exports.DataObjectParams = exports.DataObjectRelatedEntityParam = exports.EntityField = void 0;
4
- const global_1 = require("@memberjunction/global");
5
- const entityInfo_1 = require("./entityInfo");
6
- const interfaces_1 = require("./interfaces");
7
- const metadata_1 = require("./metadata");
8
- const runView_1 = require("../views/runView");
9
- const logging_1 = require("./logging");
10
- class EntityField {
11
- get Name() {
12
- return this._entityFieldInfo.Name;
13
- }
14
- get FieldType() {
15
- return this._entityFieldInfo.TSType;
16
- }
17
- get SQLType() {
18
- return this._entityFieldInfo.Type;
19
- }
20
- get IsPrimaryKey() {
21
- return this._entityFieldInfo.IsPrimaryKey;
22
- }
23
- get NeedsQuotes() {
24
- return this._entityFieldInfo.NeedsQuotes;
25
- }
26
- /**
27
- * Removes spaces from the field name and returns the result.
28
- */
29
- get CodeName() {
30
- return this._entityFieldInfo.CodeName;
31
- }
32
- get IsUnique() {
33
- return this._entityFieldInfo.IsUnique;
34
- }
35
- get Value() {
36
- return this._Value;
37
- }
38
- get ReadOnly() {
39
- return this._entityFieldInfo.ReadOnly;
40
- }
41
- get EntityFieldInfo() {
42
- return this._entityFieldInfo;
43
- }
44
- set Value(value) {
45
- if (!this.ReadOnly ||
46
- this._NeverSet /* Allow one time set of any field because BaseEntity Object passes in ReadOnly fields when we load,
47
- after that load for a given INSTANCE of an EntityField object we never set a ReadOnly Field*/) {
48
- this._Value = value;
49
- if (this._NeverSet &&
50
- //this._OldValue == null && ---- we used to check _OldValue == null, but actually that doesn't make any sense because we don't care about the old value here, we just care that _NeverSet === true
51
- value != null) {
52
- // initial value set
53
- this._OldValue = value;
54
- }
55
- this._NeverSet = false;
56
- }
57
- }
58
- get Dirty() {
59
- if (this.ReadOnly)
60
- return false;
61
- else {
62
- const oldNull = this._OldValue === null || this.OldValue === undefined;
63
- const curNull = this.Value === null || this.Value === undefined;
64
- if (oldNull && curNull)
65
- return false; // BOTH are null or undefined, not dirty
66
- else {
67
- let oldCompare = this._OldValue;
68
- let newCompare = this.Value;
69
- if (this._OldValue instanceof Date) {
70
- // sometimes we have old values that are date objects, convert to UTC timestamp for comparison
71
- oldCompare = this._OldValue.getTime();
72
- }
73
- if (this.Value instanceof Date) {
74
- // and sometimes the new values are date objects, convert to UTC timestamp for comparison
75
- newCompare = this.Value.getTime();
76
- }
77
- return oldCompare !== newCompare;
78
- }
79
- }
80
- }
81
- FormatValue(decimals = 2, currency = 'USD') {
82
- return this.EntityFieldInfo.FormatValue(this.Value, decimals, currency);
83
- }
84
- Validate() {
85
- const ef = this._entityFieldInfo;
86
- const result = new entityInfo_1.ValidationResult();
87
- result.Success = true; // assume success
88
- if (!ef.ReadOnly && !ef.SkipValidation) {
89
- // only do validation on updatable fields and skip the special case fields defined inside the SkipValidation property (like ID/CreatedAt/UpdatedAt)
90
- if (!ef.AllowsNull && (this.Value === null || this.Value === undefined)) {
91
- // make sure this isn't a field that has a default value and we are inside a new record
92
- if (ef.DefaultValue === null || ef.DefaultValue === undefined || ef.DefaultValue.trim().length === 0) {
93
- // we have no default value, so this is an error
94
- result.Success = false;
95
- result.Errors.push(new entityInfo_1.ValidationErrorInfo(ef.Name, `${ef.DisplayNameOrName} cannot be null`, null));
96
- }
97
- else {
98
- // we do have a default value, but our current value is null. If we are in an EXISTING record, this is an error, check the OldValue to determine this
99
- if (this._OldValue !== null && this._OldValue !== undefined) {
100
- result.Success = false;
101
- result.Errors.push(new entityInfo_1.ValidationErrorInfo(ef.Name, `${ef.DisplayNameOrName} cannot be null`, null));
102
- }
103
- }
104
- }
105
- if (ef.TSType == entityInfo_1.EntityFieldTSType.String && ef.MaxLength > 0 && this.Value && this.Value.length > ef.MaxLength) {
106
- result.Success = false;
107
- result.Errors.push(new entityInfo_1.ValidationErrorInfo(ef.Name, `${ef.DisplayNameOrName} cannot be longer than ${ef.MaxLength} characters. Current value is ${this.Value.length} characters`, this.Value));
108
- }
109
- if (ef.TSType == entityInfo_1.EntityFieldTSType.Date && (this.Value !== null && this.Value !== undefined && !(this.Value instanceof Date))) {
110
- // invalid non-null date, but that is okay if we are a new record and we have a default value
111
- result.Success = false;
112
- result.Errors.push(new entityInfo_1.ValidationErrorInfo(ef.Name, `${this.Value} is not a valid date for ${ef.DisplayNameOrName}`, this.Value));
113
- }
114
- }
115
- return result;
116
- }
117
- constructor(fieldInfo, Value) {
118
- this._NeverSet = true;
119
- this._entityFieldInfo = fieldInfo;
120
- if (Value)
121
- this.Value = Value;
122
- else if (fieldInfo.DefaultValue) {
123
- if (fieldInfo.TSType === entityInfo_1.EntityFieldTSType.Boolean) {
124
- // special handling for booleans as we don't want a string passed into a boolean field, we want a true boolean
125
- if (typeof fieldInfo.DefaultValue === "string" && fieldInfo.DefaultValue.trim() === "1" || fieldInfo.DefaultValue.trim().toLowerCase() === "true")
126
- this.Value = true;
127
- else
128
- this.Value = false;
129
- }
130
- else if (fieldInfo.TSType === entityInfo_1.EntityFieldTSType.Number) {
131
- // special handling for numbers as we don't want a string passed into a value for a numeric field
132
- this.Value = Number(fieldInfo.DefaultValue);
133
- }
134
- else if (fieldInfo.Type.trim().toLowerCase() === "uniqueidentifier") {
135
- // special handling for GUIDs, we don't want to populate anything here because the server always sets the value, leave blank
136
- this.Value = null;
137
- }
138
- else if (fieldInfo.TSType === entityInfo_1.EntityFieldTSType.Date) {
139
- if (fieldInfo.DefaultValue.trim().length > 0) {
140
- // special handling for dates as we don't want to use getdate() type defaults as is, we want to convert them to a JS Date object
141
- try {
142
- // attempt to convert the default value to a date
143
- const temp = new Date(fieldInfo.DefaultValue);
144
- // if we get here, that means it worked, but we could have an invalid date, so check for that
145
- if (isNaN(temp.getTime())) {
146
- // this is an invalid date, so throw an error
147
- throw new Error(); // blank error because we just end up going to the catch block anyway and in there we have logic to handle this properly
148
- }
149
- else
150
- this.Value = temp;
151
- }
152
- catch (e) {
153
- // if we get here, that means the default value is not a valid date, so we need to check to see if the date is a getdate() type default
154
- // use includes() below because it is possible that the value is wrapped in parenthesis, like (getdate()) and that is still valid.
155
- if (fieldInfo.DefaultValue.trim().toLowerCase().includes("getdate()") || fieldInfo.DefaultValue.trim().toLowerCase().includes("getutcdate()")) {
156
- // we have a getdate() type default, this is always populated by the server, so we should set this to a blank value
157
- this.Value = null;
158
- }
159
- else {
160
- // we have a default value that is not a valid date and not a getdate() type default, so we need to throw an error
161
- throw new Error(`Invalid default value for ${fieldInfo.Entity}.${fieldInfo.Name} of ${fieldInfo.DefaultValue}. Default values for date fields must be a valid date or a getdate() type default`);
162
- }
163
- }
164
- }
165
- }
166
- else {
167
- // for strings we're good to just set the value
168
- this.Value = fieldInfo.DefaultValue;
169
- }
170
- this._NeverSet = true; // set this back to true because we are setting the default value and we want to be able to set this ONCE from BaseEntity when we load
171
- }
172
- else {
173
- this.Value = null; // we need to set the value to null instead of being undefined as the value is defined, it is NULL
174
- this._NeverSet = true; // set this back to true because we are setting the value to null;
175
- }
176
- }
177
- ResetOldValue() {
178
- this._OldValue = this.Value;
179
- }
180
- get OldValue() {
181
- return this._OldValue;
182
- }
183
- }
184
- exports.EntityField = EntityField;
185
- class DataObjectRelatedEntityParam {
186
- }
187
- exports.DataObjectRelatedEntityParam = DataObjectRelatedEntityParam;
188
- class DataObjectParams {
189
- constructor() {
190
- this.oldValues = false;
191
- this.omitNullValues = true;
192
- this.omitEmptyStrings = true;
193
- this.excludeFields = null;
194
- this.includeRelatedEntityData = true;
195
- this.relatedEntityList = null;
196
- }
197
- }
198
- exports.DataObjectParams = DataObjectParams;
199
- class BaseEntityAIActionParams {
200
- }
201
- exports.BaseEntityAIActionParams = BaseEntityAIActionParams;
202
- class BaseEntity {
203
- constructor(Entity) {
204
- this._Fields = [];
205
- this._recordLoaded = false;
206
- this._contextCurrentUser = null;
207
- this._transactionGroup = null;
208
- this._EntityInfo = Entity;
209
- this.init();
210
- }
211
- /**
212
- * Returns true if the record has been saved to the database, false otherwise. This is a useful property to check to determine if the record is a "New Record" or an existing one.
213
- */
214
- get IsSaved() {
215
- const v = this.PrimaryKey?.Value;
216
- return v !== null && v !== undefined; // if the primary key (or first primary key) value is null/undefined, we haven't saved yet
217
- }
218
- get TransactionGroup() {
219
- return this._transactionGroup;
220
- }
221
- set TransactionGroup(group) {
222
- this._transactionGroup = group;
223
- }
224
- get EntityInfo() {
225
- return this._EntityInfo;
226
- }
227
- get Fields() {
228
- return this._Fields;
229
- }
230
- GetFieldByName(fieldName) {
231
- return this.Fields.find(f => f.Name.trim().toLowerCase() == fieldName.trim().toLowerCase());
232
- }
233
- get Dirty() {
234
- return !this.IsSaved || this.Fields.some(f => f.Dirty);
235
- }
236
- get PrimaryKey() {
237
- const fieldInfo = this.EntityInfo.PrimaryKey;
238
- if (fieldInfo) {
239
- return this.GetFieldByName(fieldInfo.Name);
240
- }
241
- else
242
- return null;
243
- }
244
- get PrimaryKeys() {
245
- return this.EntityInfo.PrimaryKeys.map(pk => this.GetFieldByName(pk.Name));
246
- }
247
- get RecordLoaded() {
248
- return this._recordLoaded;
249
- }
250
- /**
251
- * Sets the value of a given field. If the field doesn't exist, nothing happens.
252
- * @param FieldName
253
- * @param Value
254
- */
255
- Set(FieldName, Value) {
256
- let field = this.Fields.find(f => f.Name.trim().toLowerCase() === FieldName.trim().toLowerCase());
257
- if (field != null) {
258
- field.Value = Value;
259
- }
260
- }
261
- /**
262
- * Returns the value of the field with the given name. If the field is a date, and the value is a string, it will be converted to a date object.
263
- * @param FieldName
264
- * @returns
265
- */
266
- Get(FieldName) {
267
- let field = this.Fields.find(f => f.Name.trim().toLowerCase() === FieldName.trim().toLowerCase());
268
- if (field != null) {
269
- // if the field is a date and the value is a string, convert it to a date
270
- if (field.EntityFieldInfo.TSType == entityInfo_1.EntityFieldTSType.Date && (typeof field.Value === 'string' || typeof field.Value === 'number')) {
271
- field.Value = new Date(field.Value);
272
- }
273
- return field.Value;
274
- }
275
- return null;
276
- }
277
- /**
278
- * Sets any number of values on the entity object from the object passed in. The properties of the object being passed in must either match the field name (in most cases) or the CodeName (which is only different from field name if field name has spaces in it)
279
- * @param object
280
- * @param ignoreNonExistentFields
281
- */
282
- SetMany(object, ignoreNonExistentFields = false) {
283
- if (!object)
284
- throw new Error('calling BaseEntity.SetMany(), object cannot be null or undefined');
285
- for (let key in object) {
286
- if (this.Fields.some(f => f.Name.trim().toLowerCase() == key.trim().toLowerCase())) {
287
- // check to see if key matches a field name, if so, set it
288
- this.Set(key, object[key]);
289
- }
290
- else {
291
- // if we don't find a match for the field name, check to see if we have a match for the code name
292
- // because some objects passed in will use the code name
293
- const field = this.Fields.find(f => f.CodeName.trim().toLowerCase() == key.trim().toLowerCase());
294
- if (field) {
295
- this.Set(field.Name, object[key]);
296
- }
297
- else {
298
- // if we get here, we have a field that doesn't match either the field name or the code name, so throw an error
299
- if (!ignoreNonExistentFields)
300
- throw new Error(`Field ${key} does not exist on ${this.EntityInfo.Name}`);
301
- else
302
- console.warn(`Field ${key} does not exist on ${this.EntityInfo.Name}, ignoring because ignoreNonExistentFields was set to true`);
303
- }
304
- }
305
- }
306
- }
307
- GetAll(oldValues = false) {
308
- let obj = {};
309
- for (let field of this.Fields) {
310
- obj[field.Name] = oldValues ? field.OldValue : field.Value;
311
- if (field.EntityFieldInfo.TSType == entityInfo_1.EntityFieldTSType.Date && obj[field.Name] && !(obj[field.Name] instanceof Date)) {
312
- obj[field.Name] = new Date(obj[field.Name]); // a timestamp, convert to JS Date Object
313
- }
314
- }
315
- return obj;
316
- }
317
- /**
318
- * This utility method calls GetDataObject() internally and formats the result as a JSON string. If you want to get the data as an object instead of a string, call GetDataObject() directly.
319
- * @param params
320
- * @param minifyJSON
321
- * @returns
322
- */
323
- async GetDataObjectJSON(params, minifyJSON = true) {
324
- const obj = await this.GetDataObject(params);
325
- if (minifyJSON)
326
- return JSON.stringify(obj);
327
- else
328
- return JSON.stringify(obj, null, 2);
329
- }
330
- /**
331
- * This utility method generates a completely new object that has properties that map to the fields and values in the entity at the time it is called. It is a copy, NOT a link, so any changes
332
- * made to the object after calling this method will NOT be reflected in the object that is returned. This is useful for things like sending data to a client, or for use in a view model.
333
- * @param params
334
- * @returns
335
- */
336
- async GetDataObject(params) {
337
- // first, get the object from GetAll
338
- const obj = this.GetAll(params.oldValues);
339
- // next, check each value and if it is null or empty string, remove it if those options were set
340
- for (let key in obj) {
341
- const v = obj[key];
342
- if (params.omitNullValues && v === null)
343
- delete obj[key]; // null value, and caller didn't want null values
344
- else if (params.omitEmptyStrings && (v !== null && typeof v === 'string' && v.trim().length === 0))
345
- delete obj[key]; // blank string, and caller didn't want blank strings
346
- else if (params.excludeFields && params.excludeFields.indexOf(key) >= 0)
347
- delete obj[key]; // caller asked to not get this field
348
- }
349
- if (params.includeRelatedEntityData) {
350
- // now, get the related entity data
351
- for (let i = 0; i < this._EntityInfo.RelatedEntities.length; i++) {
352
- const re = this._EntityInfo.RelatedEntities[i];
353
- const pre = params.relatedEntityList ? params.relatedEntityList.find(r => r.relatedEntityName === re.RelatedEntity) : null;
354
- if (pre) {
355
- // pre now has the param that matches the related entity (re) that we are looking at
356
- // we are now here because either the caller didn't provide a list of entities to include
357
- // (which means to include all of 'em), or they did and this entity is in the list
358
- const reData = await this.GetRelatedEntityData(re, pre.filter);
359
- if (reData)
360
- obj[re.RelatedEntity] = reData; // got some data (or an empty array) back, add it to the object
361
- }
362
- }
363
- }
364
- return obj;
365
- }
366
- async GetRelatedEntityData(re, filter = null) {
367
- // we need to query the database to get related entity info
368
- const params = entityInfo_1.EntityInfo.BuildRelationshipViewParams(this, re, filter);
369
- const rv = new runView_1.RunView();
370
- const result = await rv.RunView(params, this._contextCurrentUser);
371
- if (result && result.Success)
372
- return result.Results;
373
- }
374
- init() {
375
- this._recordLoaded = false;
376
- this._Fields = [];
377
- if (this.EntityInfo)
378
- for (let field of this.EntityInfo.Fields) {
379
- this.Fields.push(new EntityField(field));
380
- }
381
- }
382
- /**
383
- * This method will copy the values from the other entity object into the current one. This is useful for things like cloning a record.
384
- * This method will ONLY copy values for fields that exist in the current entity object. If the other object has fields that don't exist in the current object, they will be ignored.
385
- * @param other
386
- * @param includePrimaryKeys - if true, the primary keys will be copied as well, if false, they will be ignored, defaults to false and generally you want to leave it that way
387
- */
388
- CopyFrom(other, includePrimaryKeys = false) {
389
- try {
390
- // iterate through all of OUR fields and set them to the value of the other object, if they exist in the other object
391
- for (let field of this.Fields) {
392
- if (!field.IsPrimaryKey || includePrimaryKeys) {
393
- const otherField = other.GetFieldByName(field.Name);
394
- if (otherField) {
395
- this.Set(field.Name, otherField.Value);
396
- }
397
- }
398
- }
399
- return true;
400
- }
401
- catch (e) {
402
- (0, logging_1.LogError)(`Error in BaseEntity.CopyFrom: ${e}`);
403
- return false;
404
- }
405
- }
406
- set ContextCurrentUser(user) {
407
- this._contextCurrentUser = user;
408
- }
409
- get ContextCurrentUser() {
410
- return this._contextCurrentUser;
411
- }
412
- /**
413
- * This method will create a new state for the object that is equivalent to a new record including default values.
414
- * @returns
415
- */
416
- NewRecord() {
417
- this.init();
418
- return true;
419
- }
420
- /**
421
- * Saves the current state of the object to the database. Uses the active provider to handle the actual saving of the record. If the record is new, it will be created, if it already exists, it will be updated.
422
- * @param options
423
- * @returns
424
- */
425
- async Save(options) {
426
- const _options = options ? options : new interfaces_1.EntitySaveOptions();
427
- const type = this.IsSaved ? entityInfo_1.EntityPermissionType.Update : entityInfo_1.EntityPermissionType.Create;
428
- this.CheckPermissions(type, true); // this will throw an error and exit out if we don't have permission
429
- if (_options.IgnoreDirtyState || this.Dirty) {
430
- if (BaseEntity.Provider == null) {
431
- throw new Error('No provider set');
432
- }
433
- else {
434
- const valResult = this.Validate();
435
- if (valResult.Success) {
436
- const data = await BaseEntity.Provider.Save(this, this.ActiveUser, _options);
437
- if (data) {
438
- this.init(); // wipe out the current data to flush out the DIRTY flags, load the ID as part of this too
439
- this.SetMany(data);
440
- return true;
441
- }
442
- else
443
- return false;
444
- }
445
- else {
446
- throw valResult; // pass this along to the caller
447
- }
448
- }
449
- }
450
- else
451
- return true; // nothing to save since we're not dirty
452
- }
453
- get ActiveUser() {
454
- return this.ContextCurrentUser || metadata_1.Metadata.Provider.CurrentUser; // use the context user ahead of the Provider.Current User - this is for SERVER side ops where the user changes per request
455
- }
456
- /**
457
- * Utility method that returns true if the given permission being checked is enabled for the current user, and false if not.
458
- * @param type
459
- * @param throwError
460
- * @returns
461
- */
462
- CheckPermissions(type, throwError) {
463
- const u = this.ActiveUser;
464
- if (!u)
465
- throw new Error('No user set - either the context user for the entity object must be set, or the Metadata.Provider.CurrentUser must be set');
466
- const permissions = this.EntityInfo.GetUserPermisions(u);
467
- let bAllowed = false;
468
- switch (type) {
469
- case entityInfo_1.EntityPermissionType.Create:
470
- bAllowed = permissions.CanCreate;
471
- break;
472
- case entityInfo_1.EntityPermissionType.Read:
473
- bAllowed = permissions.CanRead;
474
- break;
475
- case entityInfo_1.EntityPermissionType.Update:
476
- bAllowed = permissions.CanUpdate;
477
- break;
478
- case entityInfo_1.EntityPermissionType.Delete:
479
- bAllowed = permissions.CanDelete;
480
- break;
481
- }
482
- if (!bAllowed && throwError) {
483
- this.ThrowPermissionError(u, type, null);
484
- return false; // this never happens due to the thrown error but have it anyway to avoid strict compile errors
485
- }
486
- else
487
- return bAllowed;
488
- }
489
- ThrowPermissionError(u, type, additionalInfoMessage) {
490
- throw new Error(`User: ${u.Name} (ID: ${u.ID}, Email: ${u.Email})
491
- Does NOT have permission to ${entityInfo_1.EntityPermissionType[type]} ${this.EntityInfo.Name} records.
492
- If you believe this is an error, please contact your system administrator.${additionalInfoMessage ? '\nAdditional Information: ' + additionalInfoMessage : ''}}`);
493
- }
494
- /**
495
- * This method will revert the internal state of the object back to what it was when it was last saved, or if never saved, from when it was intially loaded from the database. This is useful if you want to offer a user an "undo" type of feature in a UI.
496
- * @returns
497
- */
498
- Revert() {
499
- if (this.Dirty) {
500
- for (let field of this.Fields) {
501
- field.Value = field.OldValue;
502
- }
503
- }
504
- return true;
505
- }
506
- /**
507
- * * This method loads a single record from the database. Make sure you first get the correct BaseEntity sub-class for your entity by calling Metadata.GetEntityObject() first. From there, you can
508
- * call this method to load your records.
509
- * * NOTE: You should not be calling this method directly from outside of a sub-class in most cases. You will use the auto-generated sub-classes that have overriden versions of this method that blow out the primary keys into individual parameters. This is much easier to program against.
510
- * @param PrimaryKeyValues An array of objects that contain the field name and value for the primary key of the record you want to load. For example, if you have a table called "Customers" with a primary key of "ID", you would pass in an array with a single object like this: {FieldName: "ID", Value: 1234}.
511
- * *If you had a composite primary key, you would pass in an array with multiple objects, one for each field in the primary key. You may ONLY pass in the primary key fields, no other fields are allowed.
512
- * @param EntityRelationshipsToLoad Optional, you can specify the names of the relationships to load up. This is an expensive operation as it loads up an array of the related entity objects for the main record, so use it sparingly.
513
- * @returns true if success, false otherwise
514
- */
515
- async InnerLoad(PrimaryKeyValues, EntityRelationshipsToLoad = null) {
516
- if (BaseEntity.Provider == null) {
517
- throw new Error('No provider set');
518
- }
519
- else {
520
- const start = new Date().getTime();
521
- this.ValidatePrimaryKeyArray(PrimaryKeyValues);
522
- this.CheckPermissions(entityInfo_1.EntityPermissionType.Read, true); // this will throw an error and exit out if we don't have permission
523
- if (!this.IsSaved)
524
- this.init(); // wipe out current data if we're loading on top of existing record
525
- const data = await BaseEntity.Provider.Load(this, PrimaryKeyValues, EntityRelationshipsToLoad, this.ActiveUser);
526
- this.SetMany(data);
527
- if (EntityRelationshipsToLoad) {
528
- for (let relationship of EntityRelationshipsToLoad) {
529
- if (data[relationship]) {
530
- // we have some data, put into an array for ease of access
531
- this[relationship] = data[relationship];
532
- }
533
- }
534
- }
535
- this._recordLoaded = true;
536
- // const end = new Date().getTime();
537
- // const time = end - start;
538
- // LogStatus(`BaseEntity.Load(${this.EntityInfo.Name}, ID: ${ID}, EntityRelationshipsToLoad.length: ${EntityRelationshipsToLoad ? EntityRelationshipsToLoad.length : 0 }), took ${time}ms`);
539
- return true;
540
- }
541
- }
542
- ValidatePrimaryKeyArray(PrimaryKeyValues) {
543
- // make sure that PrimaryKeyValues is an array of 1+ objects, and that each object has a FieldName and Value property and that the FieldName is a valid field on the entity that has IsPrimaryKey set to true
544
- if (!PrimaryKeyValues || PrimaryKeyValues.length === 0)
545
- throw new Error('PrimaryKeyValues cannot be null or empty');
546
- else {
547
- // now loop through the array and make sure each object has a FieldName and Value property
548
- // and that the field name is a valid field on the entity that has IsPrimaryKey set to true
549
- for (let i = 0; i < PrimaryKeyValues.length; i++) {
550
- const pk = PrimaryKeyValues[i];
551
- if (!pk.FieldName || pk.FieldName.trim().length === 0)
552
- throw new Error(`PrimaryKeyValues[${i}].FieldName cannot be null, empty, or whitespace`);
553
- if (pk.Value === null || pk.Value === undefined)
554
- throw new Error(`PrimaryKeyValues[${i}].Value cannot be null or undefined`);
555
- const field = this.Fields.find(f => f.Name.trim().toLowerCase() === pk.FieldName.trim().toLowerCase());
556
- if (!field)
557
- throw new Error(`PrimaryKeyValues[${i}].FieldName of ${pk.FieldName} does not exist on ${this.EntityInfo.Name}`);
558
- if (!field.IsPrimaryKey)
559
- throw new Error(`PrimaryKeyValues[${i}].FieldName of ${pk.FieldName} is not a primary key field on ${this.EntityInfo.Name}`);
560
- }
561
- }
562
- }
563
- /**
564
- * This method is meant to be used only in situations where you are sure that the data you are loading is current in the database. MAKE SURE YOU ARE PASSING IN ALL FIELDS.
565
- * The Dirty flags and other internal state will assume what is loading from the data parameter you pass in is equivalent to what is in the database. Generally speaking, you should use Load() instead of this method. The main use case(s) where this makes sense are:
566
- * (1) On the server if you are pulling data you know is fresh from say the result of another DB operation
567
- * (2) If on any tier you run a fresh RunView result, that gives you data from the database, you can then instantiate objects via Metadata.GetEntityObject() and then use this with the result from the RunView call
568
- * *** Note: for the #2 use case, when you call the RunView Object RunView() method with the ResultType='entity_object', you'll get an array of BaseEntity-derived objects instead of simple objects, that functionality utilizes this method
569
- * @param data
570
- * @returns
571
- */
572
- LoadFromData(data) {
573
- this.SetMany(data, true);
574
- return true;
575
- }
576
- /**
577
- * This method is used automatically within Save() and is used to determine if the state of the object is valid relative to the validation rules that are defined in metadata. In addition, sub-classes can
578
- * override or wrap this base class method to add other logic for validation.
579
- * @returns
580
- */
581
- Validate() {
582
- const result = new entityInfo_1.ValidationResult();
583
- result.Success = true; // start off with assumption of success, if any field fails, we'll set this to false
584
- for (let field of this.Fields) {
585
- const err = field.Validate();
586
- err.Errors.forEach(element => {
587
- result.Errors.push(element);
588
- });
589
- result.Success = result.Success && err.Success; // if any field fails, we fail, but keep going to get all of the validation messages
590
- }
591
- return result;
592
- }
593
- /**
594
- * This method deletes a record from the database. You must call Load() first in order to load the context of the record you are deleting.
595
- * @returns
596
- */
597
- async Delete() {
598
- if (BaseEntity.Provider == null) {
599
- throw new Error('No provider set');
600
- }
601
- else {
602
- this.CheckPermissions(entityInfo_1.EntityPermissionType.Delete, true); // this will throw an error and exit out if we don't have permission
603
- if (await BaseEntity.Provider.Delete(this, this.ActiveUser)) {
604
- // record deleted correctly
605
- // wipe out the current data to flush out the DIRTY flags by calling NewRecord()
606
- this.NewRecord();
607
- return true;
608
- }
609
- else // record didn't save, return false, but also don't wipe out the entity like we do if the Delete() worked
610
- return false;
611
- }
612
- }
613
- /**
614
- * Called before an Action is executed by the AI Engine
615
- * This is intended to be overriden by subclass as needed, these methods called at the right time by the execution context
616
- */
617
- async BeforeEntityAIAction(params) {
618
- return true; // default implementation does nothing
619
- }
620
- /**
621
- * Called after an Action is executed by the AI Engine
622
- */
623
- async AfterEntityAIAction(params) {
624
- return true; // default implementation does nothing
625
- }
626
- static get Provider() {
627
- const g = global_1.MJGlobal.Instance.GetGlobalObjectStore();
628
- if (g)
629
- return g[BaseEntity._globalProviderKey];
630
- else
631
- throw new Error('No global object store, so we cant get the static provider');
632
- }
633
- static set Provider(value) {
634
- const g = global_1.MJGlobal.Instance.GetGlobalObjectStore();
635
- if (g)
636
- g[BaseEntity._globalProviderKey] = value;
637
- else
638
- throw new Error('No global object store, so we cant set the static provider');
639
- }
640
- /**
641
- * Returns a list of changes made to this record, over time. Keep in mind this is only going to return valid data if you have the TrackRecordChanges bit set to 1 on the entity you're working with.
642
- */
643
- get RecordChanges() {
644
- if (this.IsSaved)
645
- return BaseEntity.GetRecordChanges(this.EntityInfo.Name, this.PrimaryKey.Value);
646
- else
647
- throw new Error('Cannot get record changes for a record that has not been saved yet');
648
- }
649
- /**
650
- * Static Utility method to get RecordChanges for a given entityName/PrimaryKeyValue combination
651
- * @param entityName
652
- * @param PrimaryKeyValue
653
- * @returns
654
- */
655
- static async GetRecordChanges(entityName, PrimaryKeyValue) {
656
- if (BaseEntity.Provider === null) {
657
- throw new Error('No provider set');
658
- }
659
- else {
660
- const results = await BaseEntity.Provider.GetRecordChanges(entityName, PrimaryKeyValue);
661
- if (results) {
662
- const changes = [];
663
- for (let result of results)
664
- changes.push(new entityInfo_1.RecordChange(result));
665
- return changes;
666
- }
667
- else
668
- return [];
669
- }
670
- }
671
- }
672
- exports.BaseEntity = BaseEntity;
673
- BaseEntity._globalProviderKey = 'MJ_BaseEntityProvider';
674
- //# sourceMappingURL=baseEntity.js.map