@vsaas/loopback-datasource-juggler 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +25 -0
- package/NOTICE +23 -0
- package/README.md +74 -0
- package/dist/_virtual/_rolldown/runtime.js +4 -0
- package/dist/index.js +26 -0
- package/dist/lib/browser.depd.js +13 -0
- package/dist/lib/case-utils.js +21 -0
- package/dist/lib/connectors/kv-memory.js +158 -0
- package/dist/lib/connectors/memory.js +810 -0
- package/dist/lib/connectors/transient.js +126 -0
- package/dist/lib/dao.js +2445 -0
- package/dist/lib/datasource.js +2215 -0
- package/dist/lib/date-string.js +87 -0
- package/dist/lib/geo.js +244 -0
- package/dist/lib/globalize.js +33 -0
- package/dist/lib/hooks.js +79 -0
- package/dist/lib/id-utils.js +66 -0
- package/dist/lib/include.js +795 -0
- package/dist/lib/include_utils.js +104 -0
- package/dist/lib/introspection.js +37 -0
- package/dist/lib/jutil.js +65 -0
- package/dist/lib/kvao/delete-all.js +57 -0
- package/dist/lib/kvao/delete.js +43 -0
- package/dist/lib/kvao/expire.js +35 -0
- package/dist/lib/kvao/get.js +34 -0
- package/dist/lib/kvao/index.js +28 -0
- package/dist/lib/kvao/iterate-keys.js +38 -0
- package/dist/lib/kvao/keys.js +55 -0
- package/dist/lib/kvao/set.js +39 -0
- package/dist/lib/kvao/ttl.js +35 -0
- package/dist/lib/list.js +101 -0
- package/dist/lib/mixins.js +58 -0
- package/dist/lib/model-builder.js +608 -0
- package/dist/lib/model-definition.js +231 -0
- package/dist/lib/model-utils.js +368 -0
- package/dist/lib/model.js +586 -0
- package/dist/lib/observer.js +235 -0
- package/dist/lib/relation-definition.js +2604 -0
- package/dist/lib/relations.js +587 -0
- package/dist/lib/scope.js +392 -0
- package/dist/lib/transaction.js +183 -0
- package/dist/lib/types.js +58 -0
- package/dist/lib/utils.js +625 -0
- package/dist/lib/validations.js +742 -0
- package/dist/package.js +93 -0
- package/package.json +85 -0
- package/types/common.d.ts +28 -0
- package/types/connector.d.ts +52 -0
- package/types/datasource.d.ts +324 -0
- package/types/date-string.d.ts +21 -0
- package/types/inclusion-mixin.d.ts +44 -0
- package/types/index.d.ts +36 -0
- package/types/kv-model.d.ts +201 -0
- package/types/model.d.ts +368 -0
- package/types/observer-mixin.d.ts +174 -0
- package/types/persisted-model.d.ts +505 -0
- package/types/query.d.ts +108 -0
- package/types/relation-mixin.d.ts +577 -0
- package/types/relation.d.ts +301 -0
- package/types/scope.d.ts +92 -0
- package/types/transaction-mixin.d.ts +47 -0
- package/types/types.d.ts +65 -0
- package/types/validation-mixin.d.ts +287 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.js");
|
|
2
|
+
const require_lib_globalize = require("./globalize.js");
|
|
3
|
+
const require_lib_jutil = require("./jutil.js");
|
|
4
|
+
const require_lib_utils = require("./utils.js");
|
|
5
|
+
const require_lib_list = require("./list.js");
|
|
6
|
+
const require_lib_model_utils = require("./model-utils.js");
|
|
7
|
+
const require_lib_observer = require("./observer.js");
|
|
8
|
+
const require_lib_hooks = require("./hooks.js");
|
|
9
|
+
const require_lib_validations = require("./validations.js");
|
|
10
|
+
const require_lib_id_utils = require("./id-utils.js");
|
|
11
|
+
//#region src/lib/model.ts
|
|
12
|
+
var require_model = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
|
|
13
|
+
/*!
|
|
14
|
+
* Module exports class Model
|
|
15
|
+
*/
|
|
16
|
+
module.exports = ModelBaseClass;
|
|
17
|
+
/*!
|
|
18
|
+
* Module dependencies
|
|
19
|
+
*/
|
|
20
|
+
const g = require_lib_globalize();
|
|
21
|
+
const util = require("util");
|
|
22
|
+
const jutil = require_lib_jutil;
|
|
23
|
+
const List = require_lib_list;
|
|
24
|
+
const DataAccessUtils = require_lib_model_utils;
|
|
25
|
+
const Observer = require_lib_observer;
|
|
26
|
+
const Hookable = require_lib_hooks;
|
|
27
|
+
const validations = require_lib_validations;
|
|
28
|
+
const utils = require_lib_utils;
|
|
29
|
+
const fieldsToArray = utils.fieldsToArray;
|
|
30
|
+
const { uuidV1, uuidV4, nanoid } = require_lib_id_utils;
|
|
31
|
+
const BASE_TYPES = {
|
|
32
|
+
String: true,
|
|
33
|
+
Boolean: true,
|
|
34
|
+
Number: true,
|
|
35
|
+
Date: true,
|
|
36
|
+
Text: true,
|
|
37
|
+
ObjectID: true
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Model class: base class for all persistent objects.
|
|
41
|
+
*
|
|
42
|
+
* `ModelBaseClass` mixes `Validatable` and `Hookable` classes methods
|
|
43
|
+
*
|
|
44
|
+
* @class
|
|
45
|
+
* @param {Object} data Initial object data
|
|
46
|
+
* @param {Object} options An object to control the instantiation
|
|
47
|
+
* @returns {ModelBaseClass} an instance of the ModelBaseClass
|
|
48
|
+
*/
|
|
49
|
+
function ModelBaseClass(data, options) {
|
|
50
|
+
options = options || {};
|
|
51
|
+
if (!("applySetters" in options)) options.applySetters = true;
|
|
52
|
+
if (!("applyDefaultValues" in options)) options.applyDefaultValues = true;
|
|
53
|
+
this._initProperties(data, options);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Initialize the model instance with a list of properties
|
|
57
|
+
* @param {Object} data The data object
|
|
58
|
+
* @param {Object} options An object to control the instantiation
|
|
59
|
+
* @property {Boolean} applySetters Controls if the setters will be applied
|
|
60
|
+
* @property {Boolean} applyDefaultValues Default attributes and values will be applied
|
|
61
|
+
* @property {Boolean} strict Set the instance level strict mode
|
|
62
|
+
* @property {Boolean} persisted Whether the instance has been persisted
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
ModelBaseClass.prototype._initProperties = function(data, options) {
|
|
66
|
+
const self = this;
|
|
67
|
+
const ctor = this.constructor;
|
|
68
|
+
if (typeof data !== "undefined" && data !== null && data.constructor && typeof data.constructor !== "function") throw new Error(g.f("Property name \"{{constructor}}\" is not allowed in %s data", ctor.modelName));
|
|
69
|
+
if (data instanceof ctor) data = data.toObject(false);
|
|
70
|
+
const properties = Object.assign({}, ctor.definition.properties);
|
|
71
|
+
data = data || {};
|
|
72
|
+
if (typeof ctor.applyProperties === "function") ctor.applyProperties(data);
|
|
73
|
+
options = options || {};
|
|
74
|
+
const applySetters = options.applySetters;
|
|
75
|
+
const applyDefaultValues = options.applyDefaultValues;
|
|
76
|
+
let strict = options.strict;
|
|
77
|
+
if (strict === void 0) strict = ctor.definition.settings.strict;
|
|
78
|
+
else if (strict === "throw") g.warn("Warning: Model %s, {{strict mode: `throw`}} has been removed, please use {{`strict: true`}} instead, which returns{{`Validation Error`}} for unknown properties,", ctor.modelName);
|
|
79
|
+
const persistUndefinedAsNull = ctor.definition.settings.persistUndefinedAsNull;
|
|
80
|
+
if (ctor.hideInternalProperties) {
|
|
81
|
+
Object.defineProperties(this, {
|
|
82
|
+
__cachedRelations: {
|
|
83
|
+
writable: true,
|
|
84
|
+
enumerable: false,
|
|
85
|
+
configurable: true,
|
|
86
|
+
value: {}
|
|
87
|
+
},
|
|
88
|
+
__data: {
|
|
89
|
+
writable: true,
|
|
90
|
+
enumerable: false,
|
|
91
|
+
configurable: true,
|
|
92
|
+
value: {}
|
|
93
|
+
},
|
|
94
|
+
__dataSource: {
|
|
95
|
+
writable: true,
|
|
96
|
+
enumerable: false,
|
|
97
|
+
configurable: true,
|
|
98
|
+
value: options.dataSource
|
|
99
|
+
},
|
|
100
|
+
__strict: {
|
|
101
|
+
writable: true,
|
|
102
|
+
enumerable: false,
|
|
103
|
+
configurable: true,
|
|
104
|
+
value: strict
|
|
105
|
+
},
|
|
106
|
+
__persisted: {
|
|
107
|
+
writable: true,
|
|
108
|
+
enumerable: false,
|
|
109
|
+
configurable: true,
|
|
110
|
+
value: false
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
if (strict) Object.defineProperty(this, "__unknownProperties", {
|
|
114
|
+
writable: true,
|
|
115
|
+
enumerable: false,
|
|
116
|
+
configrable: true,
|
|
117
|
+
value: []
|
|
118
|
+
});
|
|
119
|
+
} else {
|
|
120
|
+
this.__cachedRelations = {};
|
|
121
|
+
this.__data = {};
|
|
122
|
+
this.__dataSource = options.dataSource;
|
|
123
|
+
this.__strict = strict;
|
|
124
|
+
this.__persisted = false;
|
|
125
|
+
if (strict) this.__unknownProperties = [];
|
|
126
|
+
}
|
|
127
|
+
if (options.persisted !== void 0) this.__persisted = options.persisted === true;
|
|
128
|
+
if (data.__cachedRelations) this.__cachedRelations = data.__cachedRelations;
|
|
129
|
+
let keys = Object.keys(data);
|
|
130
|
+
if (Array.isArray(options.fields)) keys = keys.filter(function(k) {
|
|
131
|
+
return options.fields.indexOf(k) != -1;
|
|
132
|
+
});
|
|
133
|
+
let size = keys.length;
|
|
134
|
+
let p, propVal;
|
|
135
|
+
for (let k = 0; k < size; k++) {
|
|
136
|
+
p = keys[k];
|
|
137
|
+
propVal = data[p];
|
|
138
|
+
if (typeof propVal === "function") continue;
|
|
139
|
+
if (propVal === void 0 && persistUndefinedAsNull) propVal = null;
|
|
140
|
+
if (properties[p]) if (applySetters || properties[p].id) self[p] = propVal;
|
|
141
|
+
else self.__data[p] = propVal;
|
|
142
|
+
else if (ctor.relations[p]) {
|
|
143
|
+
const relationType = ctor.relations[p].type;
|
|
144
|
+
let modelTo;
|
|
145
|
+
if (!properties[p]) {
|
|
146
|
+
modelTo = ctor.relations[p].modelTo || ModelBaseClass;
|
|
147
|
+
const multiple = ctor.relations[p].multiple;
|
|
148
|
+
properties[p] = {
|
|
149
|
+
name: multiple ? "Array" : modelTo.modelName,
|
|
150
|
+
type: multiple ? [modelTo] : modelTo
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (relationType === "belongsTo" && propVal != null) {
|
|
154
|
+
self.__data[ctor.relations[p].keyFrom] = propVal[ctor.relations[p].keyTo];
|
|
155
|
+
if (ctor.relations[p].options.embedsProperties) {
|
|
156
|
+
const fields = fieldsToArray(ctor.relations[p].properties, modelTo.definition.properties, modelTo.settings.strict);
|
|
157
|
+
if (!~fields.indexOf(ctor.relations[p].keyTo)) fields.push(ctor.relations[p].keyTo);
|
|
158
|
+
self.__data[p] = new modelTo(propVal, {
|
|
159
|
+
fields,
|
|
160
|
+
applySetters: false,
|
|
161
|
+
persisted: options.persisted
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
self.__cachedRelations[p] = propVal;
|
|
166
|
+
} else if (strict === false || self.__cachedRelations[p]) {
|
|
167
|
+
self[p] = self.__data[p] = propVal !== void 0 ? propVal : self.__cachedRelations[p];
|
|
168
|
+
if (/\./.test(p)) throw new Error(g.f("Property names containing dot(s) are not supported. Model: %s, dynamic property: %s", this.constructor.modelName, p));
|
|
169
|
+
} else if (strict !== "filter") this.__unknownProperties.push(p);
|
|
170
|
+
}
|
|
171
|
+
keys = Object.keys(properties);
|
|
172
|
+
if (Array.isArray(options.fields)) keys = keys.filter(function(k) {
|
|
173
|
+
return options.fields.indexOf(k) != -1;
|
|
174
|
+
});
|
|
175
|
+
size = keys.length;
|
|
176
|
+
for (let k = 0; k < size; k++) {
|
|
177
|
+
p = keys[k];
|
|
178
|
+
propVal = self.__data[p];
|
|
179
|
+
const type = properties[p].type;
|
|
180
|
+
if (applyDefaultValues && propVal === void 0 && appliesDefaultsOnWrites(properties[p])) {
|
|
181
|
+
let def = properties[p]["default"];
|
|
182
|
+
if (def !== void 0) {
|
|
183
|
+
if (typeof def === "function") if (def === Date) def = /* @__PURE__ */ new Date();
|
|
184
|
+
else def = def();
|
|
185
|
+
else if (type.name === "Date" && def === "$now") def = /* @__PURE__ */ new Date();
|
|
186
|
+
self.__data[p] = propVal = def;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (ignoresMatchedDefault(properties[p]) && properties[p].default === propVal) delete self.__data[p];
|
|
190
|
+
if (applyDefaultValues && propVal === void 0) {
|
|
191
|
+
const defn = properties[p].defaultFn;
|
|
192
|
+
switch (defn) {
|
|
193
|
+
case void 0: break;
|
|
194
|
+
case "guid":
|
|
195
|
+
case "uuid":
|
|
196
|
+
propVal = uuidV1();
|
|
197
|
+
break;
|
|
198
|
+
case "uuidv4":
|
|
199
|
+
propVal = uuidV4();
|
|
200
|
+
break;
|
|
201
|
+
case "now":
|
|
202
|
+
propVal = /* @__PURE__ */ new Date();
|
|
203
|
+
break;
|
|
204
|
+
case "shortid":
|
|
205
|
+
case "nanoid":
|
|
206
|
+
propVal = nanoid(9);
|
|
207
|
+
break;
|
|
208
|
+
default: g.warn("Unknown default value provider %s", defn);
|
|
209
|
+
}
|
|
210
|
+
if (propVal !== void 0) self.__data[p] = propVal;
|
|
211
|
+
}
|
|
212
|
+
if (propVal === void 0 && persistUndefinedAsNull) self.__data[p] = propVal = null;
|
|
213
|
+
if (!BASE_TYPES[type.name]) {
|
|
214
|
+
if (typeof self.__data[p] !== "object" && self.__data[p]) try {
|
|
215
|
+
self.__data[p] = JSON.parse(self.__data[p] + "");
|
|
216
|
+
} catch {
|
|
217
|
+
self.__data[p] = String(self.__data[p]);
|
|
218
|
+
}
|
|
219
|
+
if (type.prototype instanceof ModelBaseClass) {
|
|
220
|
+
if (!(self.__data[p] instanceof type) && typeof self.__data[p] === "object" && self.__data[p] !== null) {
|
|
221
|
+
self.__data[p] = new type(self.__data[p]);
|
|
222
|
+
utils.applyParentProperty(self.__data[p], this);
|
|
223
|
+
}
|
|
224
|
+
} else if (type.name === "Array" || Array.isArray(type)) {
|
|
225
|
+
if (!(self.__data[p] instanceof List) && self.__data[p] !== void 0 && self.__data[p] !== null) self.__data[p] = List(self.__data[p], type, self);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
this.trigger("initialize");
|
|
230
|
+
};
|
|
231
|
+
function ignoresMatchedDefault(property) {
|
|
232
|
+
if (property && property.persistDefaultValues === false) return true;
|
|
233
|
+
}
|
|
234
|
+
function appliesDefaultsOnWrites(property) {
|
|
235
|
+
if (property && "applyDefaultOnWrites" in property) return property.applyDefaultOnWrites;
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Define a property on the model.
|
|
240
|
+
* @param {String} prop Property name
|
|
241
|
+
* @param {Object} params Various property configuration
|
|
242
|
+
*/
|
|
243
|
+
ModelBaseClass.defineProperty = function(prop, params) {
|
|
244
|
+
if (this.dataSource) this.dataSource.defineProperty(this.modelName, prop, params);
|
|
245
|
+
else this.modelBuilder.defineProperty(this.modelName, prop, params);
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Get model property type.
|
|
249
|
+
* @param {String} propName Property name
|
|
250
|
+
* @returns {String} Name of property type
|
|
251
|
+
*/
|
|
252
|
+
ModelBaseClass.getPropertyType = function(propName) {
|
|
253
|
+
const prop = this.definition.properties[propName];
|
|
254
|
+
if (!prop) return null;
|
|
255
|
+
if (!prop.type) throw new Error(g.f("Type not defined for property %s.%s", this.modelName, propName));
|
|
256
|
+
return prop.type.name;
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Get model property type.
|
|
260
|
+
* @param {String} propName Property name
|
|
261
|
+
* @returns {String} Name of property type
|
|
262
|
+
*/
|
|
263
|
+
ModelBaseClass.prototype.getPropertyType = function(propName) {
|
|
264
|
+
return this.constructor.getPropertyType(propName);
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Return string representation of class
|
|
268
|
+
* This overrides the default `toString()` method
|
|
269
|
+
*/
|
|
270
|
+
ModelBaseClass.toString = function() {
|
|
271
|
+
return "[Model " + this.modelName + "]";
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* Convert model instance to a plain JSON object.
|
|
275
|
+
* Returns a canonical object representation (no getters and setters).
|
|
276
|
+
*
|
|
277
|
+
* @param {Boolean} onlySchema Restrict properties to dataSource only. Default is false. If true, the function returns only properties defined in the schema; Otherwise it returns all enumerable properties.
|
|
278
|
+
* @param {Boolean} removeHidden Boolean flag as part of the transformation. If true, then hidden properties should not be brought out.
|
|
279
|
+
* @param {Boolean} removeProtected Boolean flag as part of the transformation. If true, then protected properties should not be brought out.
|
|
280
|
+
* @returns {Object} returns Plain JSON object
|
|
281
|
+
*/
|
|
282
|
+
ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
|
283
|
+
if (typeof onlySchema === "object" && onlySchema != null) {
|
|
284
|
+
const options = onlySchema;
|
|
285
|
+
onlySchema = options.onlySchema;
|
|
286
|
+
removeHidden = options.removeHidden;
|
|
287
|
+
removeProtected = options.removeProtected;
|
|
288
|
+
}
|
|
289
|
+
if (onlySchema === void 0) onlySchema = true;
|
|
290
|
+
const data = {};
|
|
291
|
+
const self = this;
|
|
292
|
+
const Model = this.constructor;
|
|
293
|
+
if (Model === Object) return self;
|
|
294
|
+
const schemaLess = this.__strict === false || !onlySchema;
|
|
295
|
+
const persistUndefinedAsNull = Model.definition.settings.persistUndefinedAsNull;
|
|
296
|
+
const props = Model.definition.properties;
|
|
297
|
+
let keys = Object.keys(props);
|
|
298
|
+
let propertyName, val;
|
|
299
|
+
for (let i = 0; i < keys.length; i++) {
|
|
300
|
+
propertyName = keys[i];
|
|
301
|
+
val = self[propertyName];
|
|
302
|
+
if (typeof val === "function") continue;
|
|
303
|
+
if (removeHidden && Model.isHiddenProperty(propertyName)) continue;
|
|
304
|
+
if (removeProtected && Model.isProtectedProperty(propertyName)) continue;
|
|
305
|
+
if (val instanceof List) data[propertyName] = val.toObject(!schemaLess, removeHidden, true);
|
|
306
|
+
else if (val !== void 0 && val !== null && val.toObject) data[propertyName] = val.toObject(!schemaLess, removeHidden, true);
|
|
307
|
+
else {
|
|
308
|
+
if (val === void 0 && persistUndefinedAsNull) val = null;
|
|
309
|
+
data[propertyName] = val;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (schemaLess) {
|
|
313
|
+
keys = Object.keys(self);
|
|
314
|
+
let size = keys.length;
|
|
315
|
+
for (let i = 0; i < size; i++) {
|
|
316
|
+
propertyName = keys[i];
|
|
317
|
+
if (props[propertyName]) continue;
|
|
318
|
+
if (propertyName.indexOf("__") === 0) continue;
|
|
319
|
+
if (removeHidden && Model.isHiddenProperty(propertyName)) continue;
|
|
320
|
+
if (removeProtected && Model.isProtectedProperty(propertyName)) continue;
|
|
321
|
+
if (data[propertyName] !== void 0) continue;
|
|
322
|
+
val = self[propertyName];
|
|
323
|
+
if (val !== void 0) {
|
|
324
|
+
if (typeof val === "function") continue;
|
|
325
|
+
if (val !== null && val.toObject) data[propertyName] = val.toObject(!schemaLess, removeHidden, true);
|
|
326
|
+
else data[propertyName] = val;
|
|
327
|
+
} else if (persistUndefinedAsNull) data[propertyName] = null;
|
|
328
|
+
}
|
|
329
|
+
keys = Object.keys(self.__data);
|
|
330
|
+
size = keys.length;
|
|
331
|
+
for (let i = 0; i < size; i++) {
|
|
332
|
+
propertyName = keys[i];
|
|
333
|
+
if (propertyName.indexOf("__") === 0) continue;
|
|
334
|
+
if (data[propertyName] === void 0) {
|
|
335
|
+
if (removeHidden && Model.isHiddenProperty(propertyName)) continue;
|
|
336
|
+
if (removeProtected && Model.isProtectedProperty(propertyName)) continue;
|
|
337
|
+
const ownVal = self[propertyName];
|
|
338
|
+
val = ownVal !== void 0 && typeof ownVal !== "function" ? ownVal : self.__data[propertyName];
|
|
339
|
+
if (typeof val === "function") continue;
|
|
340
|
+
if (val !== void 0 && val !== null && val.toObject) data[propertyName] = val.toObject(!schemaLess, removeHidden, true);
|
|
341
|
+
else if (val === void 0 && persistUndefinedAsNull) data[propertyName] = null;
|
|
342
|
+
else data[propertyName] = val;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return data;
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* Convert an array of strings into an object as the map
|
|
350
|
+
* @param {string[]} arr An array of strings
|
|
351
|
+
*/
|
|
352
|
+
function asObjectMap(arr) {
|
|
353
|
+
const obj = {};
|
|
354
|
+
if (Array.isArray(arr)) {
|
|
355
|
+
for (let i = 0; i < arr.length; i++) obj[arr[i]] = true;
|
|
356
|
+
return obj;
|
|
357
|
+
}
|
|
358
|
+
return arr || obj;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Checks if property is protected.
|
|
362
|
+
* @param {String} propertyName Property name
|
|
363
|
+
* @returns {Boolean} true or false if protected or not.
|
|
364
|
+
*/
|
|
365
|
+
ModelBaseClass.isProtectedProperty = function(propertyName) {
|
|
366
|
+
const settings = this.definition && this.definition.settings || {};
|
|
367
|
+
settings.protectedProperties = asObjectMap(settings.protectedProperties || settings.protected);
|
|
368
|
+
return settings.protectedProperties[propertyName];
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Checks if property is hidden.
|
|
372
|
+
* @param {String} propertyName Property name
|
|
373
|
+
* @returns {Boolean} true or false if hidden or not.
|
|
374
|
+
*/
|
|
375
|
+
ModelBaseClass.isHiddenProperty = function(propertyName) {
|
|
376
|
+
const settings = this.definition && this.definition.settings || {};
|
|
377
|
+
settings.hiddenProperties = asObjectMap(settings.hiddenProperties || settings.hidden);
|
|
378
|
+
return settings.hiddenProperties[propertyName];
|
|
379
|
+
};
|
|
380
|
+
ModelBaseClass.prototype.toJSON = function() {
|
|
381
|
+
return this.toObject(false, true, false);
|
|
382
|
+
};
|
|
383
|
+
ModelBaseClass.prototype.fromObject = function(obj) {
|
|
384
|
+
for (const key in obj) this[key] = obj[key];
|
|
385
|
+
};
|
|
386
|
+
/**
|
|
387
|
+
* Reset dirty attributes.
|
|
388
|
+
* This method does not perform any database operations; it just resets the object to its
|
|
389
|
+
* initial state.
|
|
390
|
+
*/
|
|
391
|
+
ModelBaseClass.prototype.reset = function() {
|
|
392
|
+
const obj = this;
|
|
393
|
+
for (const k in obj) if (k !== "id" && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) delete obj[k];
|
|
394
|
+
};
|
|
395
|
+
const versionParts = process.versions && process.versions.node ? process.versions.node.split(/\./g).map(function(v) {
|
|
396
|
+
return +v;
|
|
397
|
+
}) : [
|
|
398
|
+
1,
|
|
399
|
+
0,
|
|
400
|
+
0
|
|
401
|
+
];
|
|
402
|
+
const INSPECT_SUPPORTS_OBJECT_RETVAL = versionParts[0] > 0 || versionParts[1] > 11 || versionParts[0] === 11 && versionParts[1] >= 14;
|
|
403
|
+
ModelBaseClass.prototype.inspect = function(depth) {
|
|
404
|
+
if (INSPECT_SUPPORTS_OBJECT_RETVAL) return this.__data;
|
|
405
|
+
return util.inspect(this.__data, {
|
|
406
|
+
showHidden: false,
|
|
407
|
+
depth,
|
|
408
|
+
colors: false
|
|
409
|
+
});
|
|
410
|
+
};
|
|
411
|
+
if (util.inspect.custom) ModelBaseClass.prototype[util.inspect.custom] = ModelBaseClass.prototype.inspect;
|
|
412
|
+
/**
|
|
413
|
+
*
|
|
414
|
+
* @param {String} anotherClass could be string or class. Name of the class or the class itself
|
|
415
|
+
* @param {Object} options An object to control the instantiation
|
|
416
|
+
* @returns {ModelClass}
|
|
417
|
+
*/
|
|
418
|
+
ModelBaseClass.mixin = function(anotherClass, options) {
|
|
419
|
+
if (typeof anotherClass === "string") this.modelBuilder.mixins.applyMixin(this, anotherClass, options);
|
|
420
|
+
else {
|
|
421
|
+
if (anotherClass.prototype instanceof ModelBaseClass) {
|
|
422
|
+
const props = anotherClass.definition.properties;
|
|
423
|
+
for (const i in props) {
|
|
424
|
+
if (this.definition.properties[i]) continue;
|
|
425
|
+
this.defineProperty(i, props[i]);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return jutil.mixin(this, anotherClass, options);
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
ModelBaseClass.prototype.getDataSource = function() {
|
|
432
|
+
return this.__dataSource || this.constructor.dataSource;
|
|
433
|
+
};
|
|
434
|
+
ModelBaseClass.getDataSource = function() {
|
|
435
|
+
return this.dataSource;
|
|
436
|
+
};
|
|
437
|
+
ModelBaseClass.prototype.setStrict = function(strict) {
|
|
438
|
+
this.__strict = strict;
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
*
|
|
442
|
+
* `getMergePolicy()` provides model merge policies to apply when extending
|
|
443
|
+
* a child model from a base model. Such a policy drives the way parent/child model
|
|
444
|
+
* properties/settings are merged/mixed-in together.
|
|
445
|
+
*
|
|
446
|
+
* Below is presented the expected merge behaviour for each option.
|
|
447
|
+
* NOTE: This applies to top-level settings properties
|
|
448
|
+
*
|
|
449
|
+
*
|
|
450
|
+
* - Any
|
|
451
|
+
* - `{replace: true}` (default): child replaces the value from parent
|
|
452
|
+
* - assignin `null` on child setting deletes the inherited setting
|
|
453
|
+
*
|
|
454
|
+
* - Arrays:
|
|
455
|
+
* - `{replace: false}`: unique elements of parent and child cumulate
|
|
456
|
+
* - `{rank: true}` adds the model inheritance rank to array
|
|
457
|
+
* elements of type Object {} as internal property `__rank`
|
|
458
|
+
*
|
|
459
|
+
* - Object {}:
|
|
460
|
+
* - `{replace: false}`: deep merges parent and child objects
|
|
461
|
+
* - `{patch: true}`: child replaces inner properties from parent
|
|
462
|
+
*
|
|
463
|
+
*
|
|
464
|
+
* The recommended built-in merge policy is as follows. It is returned by getMergePolicy()
|
|
465
|
+
* when calling the method with option `{configureModelMerge: true}`.
|
|
466
|
+
*
|
|
467
|
+
* ```
|
|
468
|
+
* {
|
|
469
|
+
* description: {replace: true}, // string or array
|
|
470
|
+
* options: {patch: true}, // object
|
|
471
|
+
* hidden: {replace: false}, // array
|
|
472
|
+
* protected: {replace: false}, // array
|
|
473
|
+
* indexes: {patch: true}, // object
|
|
474
|
+
* methods: {patch: true}, // object
|
|
475
|
+
* mixins: {patch: true}, // object
|
|
476
|
+
* relations: {patch: true}, // object
|
|
477
|
+
* scope: {replace: true}, // object
|
|
478
|
+
* scopes: {patch: true}, // object
|
|
479
|
+
* acls: {rank: true}, // array
|
|
480
|
+
* // this setting controls which child model property's value allows deleting
|
|
481
|
+
* // a base model's property
|
|
482
|
+
* __delete: null,
|
|
483
|
+
* // this setting controls the default merge behaviour for settings not defined
|
|
484
|
+
* // in the mergePolicy specification
|
|
485
|
+
* __default: {replace: true},
|
|
486
|
+
* }
|
|
487
|
+
* ```
|
|
488
|
+
*
|
|
489
|
+
* The legacy built-in merge policy is as follows, it is retuned by `getMergePolicy()`
|
|
490
|
+
* when avoiding option `configureModelMerge`.
|
|
491
|
+
* NOTE: it also provides the ACLs ranking in addition to the legacy behaviour, as well
|
|
492
|
+
* as fixes for settings 'description' and 'relations': matching relations from child
|
|
493
|
+
* replace relations from parents.
|
|
494
|
+
*
|
|
495
|
+
* ```
|
|
496
|
+
* {
|
|
497
|
+
* description: {replace: true}, // string or array
|
|
498
|
+
* properties: {patch: true}, // object
|
|
499
|
+
* hidden: {replace: false}, // array
|
|
500
|
+
* protected: {replace: false}, // array
|
|
501
|
+
* relations: {acls: true}, // object
|
|
502
|
+
* acls: {rank: true}, // array
|
|
503
|
+
* }
|
|
504
|
+
* ```
|
|
505
|
+
*
|
|
506
|
+
*
|
|
507
|
+
* `getMergePolicy()` can be customized using model's setting `configureModelMerge` as follows:
|
|
508
|
+
*
|
|
509
|
+
* ``` json
|
|
510
|
+
* {
|
|
511
|
+
* // ..
|
|
512
|
+
* options: {
|
|
513
|
+
* configureModelMerge: {
|
|
514
|
+
* // merge options
|
|
515
|
+
* }
|
|
516
|
+
* }
|
|
517
|
+
* // ..
|
|
518
|
+
* }
|
|
519
|
+
* ```
|
|
520
|
+
*
|
|
521
|
+
* NOTE: mergePolicy parameter can also defined at JSON model definition root
|
|
522
|
+
*
|
|
523
|
+
* `getMergePolicy()` method can also be extended programmatically as follows:
|
|
524
|
+
*
|
|
525
|
+
* ``` js
|
|
526
|
+
* myModel.getMergePolicy = function(options) {
|
|
527
|
+
* const origin = myModel.base.getMergePolicy(options);
|
|
528
|
+
* return Object.assign({}, origin, {
|
|
529
|
+
* // new/overriding options
|
|
530
|
+
* });
|
|
531
|
+
* };
|
|
532
|
+
* ```
|
|
533
|
+
*
|
|
534
|
+
* @param {Object} options option `configureModelMerge` can be used to alter the
|
|
535
|
+
* returned merge policy:
|
|
536
|
+
* - `configureModelMerge: true` will have the method return the recommended merge policy.
|
|
537
|
+
* - `configureModelMerge: {..}` will actually have the method return the provided object.
|
|
538
|
+
* - not providing this options will have the method return a merge policy emulating the
|
|
539
|
+
* the model merge behaviour up to datasource-juggler v3.6.1, as well as the ACLs ranking.
|
|
540
|
+
* @returns {Object} mergePolicy The model merge policy to apply when using the
|
|
541
|
+
* current model as base class for a child model
|
|
542
|
+
*/
|
|
543
|
+
ModelBaseClass.getMergePolicy = function(options) {
|
|
544
|
+
let mergePolicy = {
|
|
545
|
+
description: { replace: true },
|
|
546
|
+
properties: { patch: true },
|
|
547
|
+
hidden: { replace: false },
|
|
548
|
+
protected: { replace: false },
|
|
549
|
+
relations: { patch: true },
|
|
550
|
+
acls: { rank: true }
|
|
551
|
+
};
|
|
552
|
+
const config = (options || {}).configureModelMerge;
|
|
553
|
+
if (config === true) mergePolicy = {
|
|
554
|
+
description: { replace: true },
|
|
555
|
+
options: { patch: true },
|
|
556
|
+
hidden: { replace: false },
|
|
557
|
+
protected: { replace: false },
|
|
558
|
+
indexes: { patch: true },
|
|
559
|
+
methods: { patch: true },
|
|
560
|
+
mixins: { patch: true },
|
|
561
|
+
relations: { patch: true },
|
|
562
|
+
scope: { replace: true },
|
|
563
|
+
scopes: { patch: true },
|
|
564
|
+
acls: { rank: true },
|
|
565
|
+
__delete: null,
|
|
566
|
+
__default: { replace: true }
|
|
567
|
+
};
|
|
568
|
+
if (config && typeof config === "object" && !Array.isArray(config)) mergePolicy = config;
|
|
569
|
+
return mergePolicy;
|
|
570
|
+
};
|
|
571
|
+
/**
|
|
572
|
+
* Gets properties defined with 'updateOnly' flag set to true from the model. This flag is also set to true
|
|
573
|
+
* internally for the id property, if this property is generated and IdInjection is true.
|
|
574
|
+
* @returns {updateOnlyProps} List of properties with updateOnly set to true.
|
|
575
|
+
*/
|
|
576
|
+
ModelBaseClass.getUpdateOnlyProperties = function() {
|
|
577
|
+
const props = this.definition.properties;
|
|
578
|
+
return Object.keys(props).filter((key) => props[key].updateOnly);
|
|
579
|
+
};
|
|
580
|
+
jutil.mixin(ModelBaseClass, DataAccessUtils);
|
|
581
|
+
jutil.mixin(ModelBaseClass, Observer);
|
|
582
|
+
jutil.mixin(ModelBaseClass, Hookable);
|
|
583
|
+
jutil.mixin(ModelBaseClass, validations.Validatable);
|
|
584
|
+
}));
|
|
585
|
+
//#endregion
|
|
586
|
+
module.exports = require_model();
|