@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.
- package/dist/generic/interfaces.d.ts +0 -2
- package/dist/generic/interfaces.js.map +1 -1
- package/dist/generic/providerBase.d.ts +0 -7
- package/dist/generic/providerBase.js +1 -11
- package/dist/generic/providerBase.js.map +1 -1
- package/package.json +1 -1
- package/dist/MJCore/src/generic/applicationInfo.d.ts +0 -26
- package/dist/MJCore/src/generic/applicationInfo.js +0 -57
- package/dist/MJCore/src/generic/applicationInfo.js.map +0 -1
- package/dist/MJCore/src/generic/baseEntity.d.ts +0 -195
- package/dist/MJCore/src/generic/baseEntity.js +0 -674
- package/dist/MJCore/src/generic/baseEntity.js.map +0 -1
- package/dist/MJCore/src/generic/baseInfo.d.ts +0 -8
- package/dist/MJCore/src/generic/baseInfo.js +0 -38
- package/dist/MJCore/src/generic/baseInfo.js.map +0 -1
- package/dist/MJCore/src/generic/entityInfo.d.ts +0 -520
- package/dist/MJCore/src/generic/entityInfo.js +0 -801
- package/dist/MJCore/src/generic/entityInfo.js.map +0 -1
- package/dist/MJCore/src/generic/interfaces.d.ts +0 -288
- package/dist/MJCore/src/generic/interfaces.js +0 -50
- package/dist/MJCore/src/generic/interfaces.js.map +0 -1
- package/dist/MJCore/src/generic/logging.d.ts +0 -4
- package/dist/MJCore/src/generic/logging.js +0 -69
- package/dist/MJCore/src/generic/logging.js.map +0 -1
- package/dist/MJCore/src/generic/metadata.d.ts +0 -201
- package/dist/MJCore/src/generic/metadata.js +0 -312
- package/dist/MJCore/src/generic/metadata.js.map +0 -1
- package/dist/MJCore/src/generic/providerBase.d.ts +0 -183
- package/dist/MJCore/src/generic/providerBase.js +0 -640
- package/dist/MJCore/src/generic/providerBase.js.map +0 -1
- package/dist/MJCore/src/generic/queryInfo.d.ts +0 -64
- package/dist/MJCore/src/generic/queryInfo.js +0 -124
- package/dist/MJCore/src/generic/queryInfo.js.map +0 -1
- package/dist/MJCore/src/generic/runQuery.d.ts +0 -11
- package/dist/MJCore/src/generic/runQuery.js +0 -26
- package/dist/MJCore/src/generic/runQuery.js.map +0 -1
- package/dist/MJCore/src/generic/runReport.d.ts +0 -11
- package/dist/MJCore/src/generic/runReport.js +0 -27
- package/dist/MJCore/src/generic/runReport.js.map +0 -1
- package/dist/MJCore/src/generic/securityInfo.d.ts +0 -100
- package/dist/MJCore/src/generic/securityInfo.js +0 -194
- package/dist/MJCore/src/generic/securityInfo.js.map +0 -1
- package/dist/MJCore/src/generic/transactionGroup.d.ts +0 -24
- package/dist/MJCore/src/generic/transactionGroup.js +0 -79
- package/dist/MJCore/src/generic/transactionGroup.js.map +0 -1
- package/dist/MJCore/src/generic/util.d.ts +0 -16
- package/dist/MJCore/src/generic/util.js +0 -151
- package/dist/MJCore/src/generic/util.js.map +0 -1
- package/dist/MJCore/src/index.d.ts +0 -16
- package/dist/MJCore/src/index.js +0 -44
- package/dist/MJCore/src/index.js.map +0 -1
- package/dist/MJCore/src/views/runView.d.ts +0 -112
- package/dist/MJCore/src/views/runView.js +0 -63
- package/dist/MJCore/src/views/runView.js.map +0 -1
- package/dist/MJCore/src/views/viewInfo.d.ts +0 -59
- package/dist/MJCore/src/views/viewInfo.js +0 -121
- package/dist/MJCore/src/views/viewInfo.js.map +0 -1
- package/dist/MJGlobal/src/ObjectCache.d.ts +0 -26
- package/dist/MJGlobal/src/ObjectCache.js +0 -55
- package/dist/MJGlobal/src/ObjectCache.js.map +0 -1
- package/dist/generic/objectCache.d.ts +0 -26
- package/dist/generic/objectCache.js +0 -55
- package/dist/generic/objectCache.js.map +0 -1
- package/dist/generic/viewInfo.d.ts +0 -59
- package/dist/generic/viewInfo.js +0 -121
- 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
|