@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,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.js");
|
|
3
|
+
const require_lib_types = require("./types.js");
|
|
4
|
+
const require_lib_model = require("./model.js");
|
|
5
|
+
const require_lib_model_builder = require("./model-builder.js");
|
|
6
|
+
//#region src/lib/model-definition.ts
|
|
7
|
+
var require_model_definition = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
|
|
8
|
+
const assert = require("assert");
|
|
9
|
+
const util = require("util");
|
|
10
|
+
const EventEmitter = require("events").EventEmitter;
|
|
11
|
+
const traverse = require("neotraverse/legacy");
|
|
12
|
+
const ModelBaseClass = require_lib_model;
|
|
13
|
+
const ModelBuilder = require_lib_model_builder;
|
|
14
|
+
/**
|
|
15
|
+
* Model definition
|
|
16
|
+
*/
|
|
17
|
+
module.exports = ModelDefinition;
|
|
18
|
+
/**
|
|
19
|
+
* Constructor for ModelDefinition
|
|
20
|
+
* @param {ModelBuilder} modelBuilder A model builder instance
|
|
21
|
+
* @param {String|Object} name The model name or the schema object
|
|
22
|
+
* @param {Object} properties The model properties, optional
|
|
23
|
+
* @param {Object} settings The model settings, optional
|
|
24
|
+
* @returns {ModelDefinition}
|
|
25
|
+
* @constructor
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
function ModelDefinition(modelBuilder, name, properties, settings) {
|
|
29
|
+
if (!(this instanceof ModelDefinition)) return new ModelDefinition(modelBuilder, name, properties, settings);
|
|
30
|
+
this.modelBuilder = modelBuilder || ModelBuilder.defaultInstance;
|
|
31
|
+
assert(name, "name is missing");
|
|
32
|
+
if (arguments.length === 2 && typeof name === "object") {
|
|
33
|
+
const schema = name;
|
|
34
|
+
this.name = schema.name;
|
|
35
|
+
this.rawProperties = schema.properties || {};
|
|
36
|
+
this.settings = schema.settings || {};
|
|
37
|
+
} else {
|
|
38
|
+
assert(typeof name === "string", "name must be a string");
|
|
39
|
+
this.name = name;
|
|
40
|
+
this.rawProperties = properties || {};
|
|
41
|
+
this.settings = settings || {};
|
|
42
|
+
}
|
|
43
|
+
this.relations = [];
|
|
44
|
+
this.properties = null;
|
|
45
|
+
this.build();
|
|
46
|
+
}
|
|
47
|
+
util.inherits(ModelDefinition, EventEmitter);
|
|
48
|
+
require_lib_types(ModelDefinition);
|
|
49
|
+
/**
|
|
50
|
+
* Return table name for specified `modelName`
|
|
51
|
+
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
|
52
|
+
*/
|
|
53
|
+
ModelDefinition.prototype.tableName = function(connectorType) {
|
|
54
|
+
const settings = this.settings;
|
|
55
|
+
if (settings[connectorType]) return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
|
56
|
+
else return this.name;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Return column name for specified modelName and propertyName
|
|
60
|
+
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
|
61
|
+
* @param propertyName The property name
|
|
62
|
+
* @returns {String} columnName
|
|
63
|
+
*/
|
|
64
|
+
ModelDefinition.prototype.columnName = function(connectorType, propertyName) {
|
|
65
|
+
if (!propertyName) return propertyName;
|
|
66
|
+
this.build();
|
|
67
|
+
const property = this.properties[propertyName];
|
|
68
|
+
if (property && property[connectorType]) return property[connectorType].column || property[connectorType].columnName || propertyName;
|
|
69
|
+
else return propertyName;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Return column metadata for specified modelName and propertyName
|
|
73
|
+
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
|
74
|
+
* @param propertyName The property name
|
|
75
|
+
* @returns {Object} column metadata
|
|
76
|
+
*/
|
|
77
|
+
ModelDefinition.prototype.columnMetadata = function(connectorType, propertyName) {
|
|
78
|
+
if (!propertyName) return propertyName;
|
|
79
|
+
this.build();
|
|
80
|
+
const property = this.properties[propertyName];
|
|
81
|
+
if (property && property[connectorType]) return property[connectorType];
|
|
82
|
+
else return null;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Return column names for specified modelName
|
|
86
|
+
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
|
87
|
+
* @returns {String[]} column names
|
|
88
|
+
*/
|
|
89
|
+
ModelDefinition.prototype.columnNames = function(connectorType) {
|
|
90
|
+
this.build();
|
|
91
|
+
const props = this.properties;
|
|
92
|
+
const cols = [];
|
|
93
|
+
for (const p in props) if (props[p][connectorType]) cols.push(props[p][connectorType].column || props[p][connectorType].columnName || p);
|
|
94
|
+
else cols.push(p);
|
|
95
|
+
return cols;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Find the ID properties sorted by the index
|
|
99
|
+
* @returns {Object[]} property name/index for IDs
|
|
100
|
+
*/
|
|
101
|
+
ModelDefinition.prototype.ids = function() {
|
|
102
|
+
if (this._ids) return this._ids;
|
|
103
|
+
const ids = [];
|
|
104
|
+
this.build();
|
|
105
|
+
const props = this.properties;
|
|
106
|
+
for (const key in props) {
|
|
107
|
+
let id = props[key].id;
|
|
108
|
+
if (!id) continue;
|
|
109
|
+
if (typeof id !== "number") id = 1;
|
|
110
|
+
ids.push({
|
|
111
|
+
name: key,
|
|
112
|
+
id,
|
|
113
|
+
property: props[key]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
ids.sort(function(a, b) {
|
|
117
|
+
return a.id - b.id;
|
|
118
|
+
});
|
|
119
|
+
this._ids = ids;
|
|
120
|
+
return ids;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Find the ID column name
|
|
124
|
+
* @param {String} modelName The model name
|
|
125
|
+
* @returns {String} columnName for ID
|
|
126
|
+
*/
|
|
127
|
+
ModelDefinition.prototype.idColumnName = function(connectorType) {
|
|
128
|
+
return this.columnName(connectorType, this.idName());
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Find the ID property name
|
|
132
|
+
* @returns {String} property name for ID
|
|
133
|
+
*/
|
|
134
|
+
ModelDefinition.prototype.idName = function() {
|
|
135
|
+
const id = this.ids()[0];
|
|
136
|
+
if (this.properties.id && this.properties.id.id) return "id";
|
|
137
|
+
else return id && id.name;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Find the ID property names sorted by the index
|
|
141
|
+
* @returns {String[]} property names for IDs
|
|
142
|
+
*/
|
|
143
|
+
ModelDefinition.prototype.idNames = function() {
|
|
144
|
+
return this.ids().map(function(id) {
|
|
145
|
+
return id.name;
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
*
|
|
150
|
+
* @returns {{}}
|
|
151
|
+
*/
|
|
152
|
+
ModelDefinition.prototype.indexes = function() {
|
|
153
|
+
this.build();
|
|
154
|
+
const indexes = {};
|
|
155
|
+
if (this.settings.indexes) for (const i in this.settings.indexes) indexes[i] = this.settings.indexes[i];
|
|
156
|
+
for (const p in this.properties) if (this.properties[p].index) indexes[p + "_index"] = this.properties[p].index;
|
|
157
|
+
return indexes;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Build a model definition
|
|
161
|
+
* @param {Boolean} force Forcing rebuild
|
|
162
|
+
*/
|
|
163
|
+
ModelDefinition.prototype.build = function(forceRebuild) {
|
|
164
|
+
if (forceRebuild) {
|
|
165
|
+
this.properties = null;
|
|
166
|
+
this.relations = [];
|
|
167
|
+
this._ids = null;
|
|
168
|
+
this.json = null;
|
|
169
|
+
}
|
|
170
|
+
if (this.properties) return this.properties;
|
|
171
|
+
this.properties = {};
|
|
172
|
+
for (const p in this.rawProperties) {
|
|
173
|
+
const prop = this.rawProperties[p];
|
|
174
|
+
const type = this.modelBuilder.resolveType(prop);
|
|
175
|
+
if (typeof type === "string") this.relations.push({
|
|
176
|
+
source: this.name,
|
|
177
|
+
target: type,
|
|
178
|
+
type: Array.isArray(prop) ? "hasMany" : "belongsTo",
|
|
179
|
+
as: p
|
|
180
|
+
});
|
|
181
|
+
else {
|
|
182
|
+
const typeDef = { type };
|
|
183
|
+
if (typeof prop === "object" && prop !== null) {
|
|
184
|
+
for (const a in prop) if (a !== "type") typeDef[a] = prop[a];
|
|
185
|
+
}
|
|
186
|
+
this.properties[p] = typeDef;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return this.properties;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Define a property
|
|
193
|
+
* @param {String} propertyName The property name
|
|
194
|
+
* @param {Object} propertyDefinition The property definition
|
|
195
|
+
*/
|
|
196
|
+
ModelDefinition.prototype.defineProperty = function(propertyName, propertyDefinition) {
|
|
197
|
+
this.rawProperties[propertyName] = propertyDefinition;
|
|
198
|
+
this.build(true);
|
|
199
|
+
};
|
|
200
|
+
function isModelClass(cls) {
|
|
201
|
+
if (!cls) return false;
|
|
202
|
+
return cls.prototype instanceof ModelBaseClass;
|
|
203
|
+
}
|
|
204
|
+
ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
|
205
|
+
if (forceRebuild) this.json = null;
|
|
206
|
+
if (this.json) return this.json;
|
|
207
|
+
const json = {
|
|
208
|
+
name: this.name,
|
|
209
|
+
properties: {},
|
|
210
|
+
settings: this.settings
|
|
211
|
+
};
|
|
212
|
+
this.build(forceRebuild);
|
|
213
|
+
const mapper = function(val) {
|
|
214
|
+
if (val === void 0 || val === null) return val;
|
|
215
|
+
if ("function" === typeof val.toJSON) return val.toJSON();
|
|
216
|
+
if ("function" === typeof val) {
|
|
217
|
+
if (isModelClass(val)) if (val.settings && val.settings.anonymous) return val.definition && val.definition.toJSON().properties;
|
|
218
|
+
else return val.modelName;
|
|
219
|
+
return val.name;
|
|
220
|
+
} else return val;
|
|
221
|
+
};
|
|
222
|
+
for (const p in this.properties) json.properties[p] = traverse(this.properties[p]).map(mapper);
|
|
223
|
+
this.json = json;
|
|
224
|
+
return json;
|
|
225
|
+
};
|
|
226
|
+
ModelDefinition.prototype.hasPK = function() {
|
|
227
|
+
return this.ids().length > 0;
|
|
228
|
+
};
|
|
229
|
+
}));
|
|
230
|
+
//#endregion
|
|
231
|
+
module.exports = require_model_definition();
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.js");
|
|
2
|
+
const require_lib_globalize = require("./globalize.js");
|
|
3
|
+
const require_lib_geo = require("./geo.js");
|
|
4
|
+
const require_lib_utils = require("./utils.js");
|
|
5
|
+
require("./model.js");
|
|
6
|
+
//#region src/lib/model-utils.ts
|
|
7
|
+
var require_model_utils = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
|
|
8
|
+
module.exports = ModelUtils;
|
|
9
|
+
/*!
|
|
10
|
+
* Module dependencies
|
|
11
|
+
*/
|
|
12
|
+
const g = require_lib_globalize();
|
|
13
|
+
const geo = require_lib_geo;
|
|
14
|
+
const { fieldsToArray, sanitizeQuery: sanitizeQueryOrData, isPlainObject, isClass, toRegExp } = require_lib_utils;
|
|
15
|
+
/**
|
|
16
|
+
* A mixin to contain utility methods for DataAccessObject
|
|
17
|
+
*/
|
|
18
|
+
function ModelUtils() {}
|
|
19
|
+
/**
|
|
20
|
+
* Verify if allowExtendedOperators is enabled
|
|
21
|
+
* @options {Object} [options] Optional options to use.
|
|
22
|
+
* @property {Boolean} allowExtendedOperators.
|
|
23
|
+
* @returns {Boolean} Returns `true` if allowExtendedOperators is enabled, else `false`.
|
|
24
|
+
*/
|
|
25
|
+
ModelUtils._allowExtendedOperators = function(options) {
|
|
26
|
+
const flag = this._getSetting("allowExtendedOperators", options);
|
|
27
|
+
if (flag != null) return !!flag;
|
|
28
|
+
return false;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Get settings via hierarchical determination
|
|
32
|
+
* - method level options
|
|
33
|
+
* - model level settings
|
|
34
|
+
* - data source level settings
|
|
35
|
+
*
|
|
36
|
+
* @param {String} key The setting key
|
|
37
|
+
*/
|
|
38
|
+
ModelUtils._getSetting = function(key, options) {
|
|
39
|
+
let val = options && options[key];
|
|
40
|
+
if (val !== void 0) return val;
|
|
41
|
+
const m = this.definition;
|
|
42
|
+
if (m && m.settings) {
|
|
43
|
+
val = m.settings[key];
|
|
44
|
+
if (val !== void 0) return m.settings[key];
|
|
45
|
+
}
|
|
46
|
+
const ds = this.getDataSource();
|
|
47
|
+
if (ds && ds.settings) return ds.settings[key];
|
|
48
|
+
};
|
|
49
|
+
const operators = {
|
|
50
|
+
eq: "=",
|
|
51
|
+
gt: ">",
|
|
52
|
+
gte: ">=",
|
|
53
|
+
lt: "<",
|
|
54
|
+
lte: "<=",
|
|
55
|
+
between: "BETWEEN",
|
|
56
|
+
inq: "IN",
|
|
57
|
+
nin: "NOT IN",
|
|
58
|
+
neq: "!=",
|
|
59
|
+
like: "LIKE",
|
|
60
|
+
nlike: "NOT LIKE",
|
|
61
|
+
ilike: "ILIKE",
|
|
62
|
+
nilike: "NOT ILIKE",
|
|
63
|
+
regexp: "REGEXP"
|
|
64
|
+
};
|
|
65
|
+
ModelUtils._normalize = function(filter, options) {
|
|
66
|
+
if (!filter) return;
|
|
67
|
+
let err = null;
|
|
68
|
+
if (typeof filter !== "object" || Array.isArray(filter)) {
|
|
69
|
+
err = new Error(g.f("The query filter %j is not an {{object}}", filter));
|
|
70
|
+
err.statusCode = 400;
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
if (filter.limit || filter.skip || filter.offset) {
|
|
74
|
+
const limit = Number(filter.limit || 100);
|
|
75
|
+
const offset = Number(filter.skip || filter.offset || 0);
|
|
76
|
+
if (isNaN(limit) || limit <= 0 || Math.ceil(limit) !== limit) {
|
|
77
|
+
err = new Error(g.f("The {{limit}} parameter %j is not valid", filter.limit));
|
|
78
|
+
err.statusCode = 400;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
if (isNaN(offset) || offset < 0 || Math.ceil(offset) !== offset) {
|
|
82
|
+
err = new Error(g.f("The {{offset/skip}} parameter %j is not valid", filter.skip || filter.offset));
|
|
83
|
+
err.statusCode = 400;
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
filter.limit = limit;
|
|
87
|
+
filter.offset = offset;
|
|
88
|
+
filter.skip = offset;
|
|
89
|
+
}
|
|
90
|
+
if (filter.order) {
|
|
91
|
+
let order = filter.order;
|
|
92
|
+
if (!Array.isArray(order)) order = [order];
|
|
93
|
+
const fields = [];
|
|
94
|
+
for (let i = 0, m = order.length; i < m; i++) if (typeof order[i] === "string") {
|
|
95
|
+
const tokens = order[i].split(/(?:\s*,\s*)+/);
|
|
96
|
+
for (let t = 0, n = tokens.length; t < n; t++) {
|
|
97
|
+
let token = tokens[t];
|
|
98
|
+
if (token.length === 0) continue;
|
|
99
|
+
const parts = token.split(/\s+/);
|
|
100
|
+
if (parts.length >= 2) {
|
|
101
|
+
const dir = parts[1].toUpperCase();
|
|
102
|
+
if (dir === "ASC" || dir === "DESC") token = parts[0] + " " + dir;
|
|
103
|
+
else {
|
|
104
|
+
err = new Error(g.f("The {{order}} %j has invalid direction", token));
|
|
105
|
+
err.statusCode = 400;
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
fields.push(token);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
err = new Error(g.f("The order %j is not valid", order[i]));
|
|
113
|
+
err.statusCode = 400;
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
if (fields.length === 1 && typeof filter.order === "string") filter.order = fields[0];
|
|
117
|
+
else filter.order = fields;
|
|
118
|
+
}
|
|
119
|
+
if (filter.fields) filter.fields = fieldsToArray(filter.fields, Object.keys(this.definition.properties), this.settings.strict);
|
|
120
|
+
filter = this._sanitizeQuery(filter, options);
|
|
121
|
+
this._coerce(filter.where, options);
|
|
122
|
+
return filter;
|
|
123
|
+
};
|
|
124
|
+
function DateType(arg) {
|
|
125
|
+
const d = new Date(arg);
|
|
126
|
+
if (isNaN(d.getTime())) throw new Error(g.f("Invalid date: %s", arg));
|
|
127
|
+
return d;
|
|
128
|
+
}
|
|
129
|
+
function BooleanType(arg) {
|
|
130
|
+
if (typeof arg === "string") switch (arg) {
|
|
131
|
+
case "true":
|
|
132
|
+
case "1": return true;
|
|
133
|
+
case "false":
|
|
134
|
+
case "0": return false;
|
|
135
|
+
}
|
|
136
|
+
if (arg == null) return null;
|
|
137
|
+
return Boolean(arg);
|
|
138
|
+
}
|
|
139
|
+
function NumberType(val) {
|
|
140
|
+
const num = Number(val);
|
|
141
|
+
return !isNaN(num) ? num : val;
|
|
142
|
+
}
|
|
143
|
+
function coerceArray(val) {
|
|
144
|
+
if (Array.isArray(val)) return val;
|
|
145
|
+
if (!isPlainObject(val)) throw new Error(g.f("Value is not an {{array}} or {{object}} with sequential numeric indices"));
|
|
146
|
+
const props = Object.keys(val);
|
|
147
|
+
if (props.length === 0) throw new Error(g.f("Value is an empty {{object}}"));
|
|
148
|
+
const arrayVal = Array.from({ length: props.length });
|
|
149
|
+
for (let i = 0; i < arrayVal.length; ++i) {
|
|
150
|
+
if (!val.hasOwnProperty(i)) throw new Error(g.f("Value is not an {{array}} or {{object}} with sequential numeric indices"));
|
|
151
|
+
arrayVal[i] = val[i];
|
|
152
|
+
}
|
|
153
|
+
return arrayVal;
|
|
154
|
+
}
|
|
155
|
+
function _normalizeAsArray(result) {
|
|
156
|
+
if (typeof result === "string") result = [result];
|
|
157
|
+
if (Array.isArray(result)) return result;
|
|
158
|
+
else {
|
|
159
|
+
const keys = [];
|
|
160
|
+
for (const k in result) if (result[k]) keys.push(k);
|
|
161
|
+
return keys;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get an array of hidden property names
|
|
166
|
+
*/
|
|
167
|
+
ModelUtils._getHiddenProperties = function() {
|
|
168
|
+
const settings = this.definition.settings || {};
|
|
169
|
+
return _normalizeAsArray(settings.hiddenProperties || settings.hidden || []);
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Get an array of protected property names
|
|
173
|
+
*/
|
|
174
|
+
ModelUtils._getProtectedProperties = function() {
|
|
175
|
+
const settings = this.definition.settings || {};
|
|
176
|
+
return _normalizeAsArray(settings.protectedProperties || settings.protected || []);
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Get the maximum depth of a query object
|
|
180
|
+
*/
|
|
181
|
+
ModelUtils._getMaxDepthOfQuery = function(options, defaultValue) {
|
|
182
|
+
options = options || {};
|
|
183
|
+
let maxDepth = this._getSetting("maxDepthOfQuery", options);
|
|
184
|
+
if (maxDepth == null) maxDepth = defaultValue || 32;
|
|
185
|
+
return +maxDepth;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Get the maximum depth of a data object
|
|
189
|
+
*/
|
|
190
|
+
ModelUtils._getMaxDepthOfData = function(options, defaultValue) {
|
|
191
|
+
options = options || {};
|
|
192
|
+
let maxDepth = this._getSetting("maxDepthOfData", options);
|
|
193
|
+
if (maxDepth == null) maxDepth = defaultValue || 64;
|
|
194
|
+
return +maxDepth;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Get the prohibitHiddenPropertiesInQuery flag
|
|
198
|
+
*/
|
|
199
|
+
ModelUtils._getProhibitHiddenPropertiesInQuery = function(options, defaultValue) {
|
|
200
|
+
const flag = this._getSetting("prohibitHiddenPropertiesInQuery", options);
|
|
201
|
+
if (flag == null) return !!defaultValue;
|
|
202
|
+
return !!flag;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Sanitize the query object
|
|
206
|
+
*/
|
|
207
|
+
ModelUtils._sanitizeQuery = function(query, options) {
|
|
208
|
+
options = options || {};
|
|
209
|
+
const normalizeUndefinedInQuery = this._getSetting("normalizeUndefinedInQuery", options);
|
|
210
|
+
const prohibitHiddenPropertiesInQuery = this._getProhibitHiddenPropertiesInQuery(options);
|
|
211
|
+
const maxDepthOfQuery = this._getMaxDepthOfQuery(options);
|
|
212
|
+
let prohibitedKeys = [];
|
|
213
|
+
if (prohibitHiddenPropertiesInQuery) {
|
|
214
|
+
prohibitedKeys = this._getHiddenProperties();
|
|
215
|
+
if (options.prohibitProtectedPropertiesInQuery) prohibitedKeys = prohibitedKeys.concat(this._getProtectedProperties());
|
|
216
|
+
}
|
|
217
|
+
return sanitizeQueryOrData(query, Object.assign({
|
|
218
|
+
maxDepth: maxDepthOfQuery,
|
|
219
|
+
prohibitedKeys,
|
|
220
|
+
normalizeUndefinedInQuery
|
|
221
|
+
}, options));
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Sanitize the data object
|
|
225
|
+
*/
|
|
226
|
+
ModelUtils._sanitizeData = function(data, options) {
|
|
227
|
+
options = options || {};
|
|
228
|
+
return sanitizeQueryOrData(data, Object.assign({ maxDepth: this._getMaxDepthOfData(options) }, options));
|
|
229
|
+
};
|
|
230
|
+
ModelUtils._coerce = function(where, options, modelDef) {
|
|
231
|
+
const self = this;
|
|
232
|
+
if (where == null) return where;
|
|
233
|
+
options = options || {};
|
|
234
|
+
let err;
|
|
235
|
+
if (typeof where !== "object" || Array.isArray(where)) {
|
|
236
|
+
err = new Error(g.f("The where clause %j is not an {{object}}", where));
|
|
237
|
+
err.statusCode = 400;
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
240
|
+
let props;
|
|
241
|
+
if (modelDef && modelDef.properties) props = modelDef.properties;
|
|
242
|
+
else props = self.definition.properties;
|
|
243
|
+
for (const p in where) {
|
|
244
|
+
if (p === "and" || p === "or" || p === "nor") {
|
|
245
|
+
let clauses = where[p];
|
|
246
|
+
try {
|
|
247
|
+
clauses = coerceArray(clauses);
|
|
248
|
+
} catch (e) {
|
|
249
|
+
err = new Error(g.f("The %s operator has invalid clauses %j: %s", p, clauses, e.message));
|
|
250
|
+
err.statusCode = 400;
|
|
251
|
+
throw err;
|
|
252
|
+
}
|
|
253
|
+
for (let k = 0; k < clauses.length; k++) self._coerce(clauses[k], options);
|
|
254
|
+
where[p] = clauses;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
let DataType = props[p] && props[p].type;
|
|
258
|
+
if (!DataType) continue;
|
|
259
|
+
if ((Array.isArray(DataType) || DataType === Array) && !isNestedModel(DataType)) DataType = DataType[0];
|
|
260
|
+
if (DataType === Date) DataType = DateType;
|
|
261
|
+
else if (DataType === Boolean) DataType = BooleanType;
|
|
262
|
+
else if (DataType === Number) DataType = NumberType;
|
|
263
|
+
if (!DataType) continue;
|
|
264
|
+
if (DataType === geo.GeoPoint) continue;
|
|
265
|
+
let val = where[p];
|
|
266
|
+
if (val === null || val === void 0) continue;
|
|
267
|
+
let operator = null;
|
|
268
|
+
const exp = val;
|
|
269
|
+
if (val.constructor === Object) {
|
|
270
|
+
for (const op in operators) if (op in val) {
|
|
271
|
+
val = val[op];
|
|
272
|
+
operator = op;
|
|
273
|
+
switch (operator) {
|
|
274
|
+
case "inq":
|
|
275
|
+
case "nin":
|
|
276
|
+
case "between":
|
|
277
|
+
try {
|
|
278
|
+
val = coerceArray(val);
|
|
279
|
+
} catch (e) {
|
|
280
|
+
err = new Error(g.f("The %s property has invalid clause %j: %s", p, where[p], e));
|
|
281
|
+
err.statusCode = 400;
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
284
|
+
if (operator === "between" && val.length !== 2) {
|
|
285
|
+
err = new Error(g.f("The %s property has invalid clause %j: Expected precisely 2 values, received %d", p, where[p], val.length));
|
|
286
|
+
err.statusCode = 400;
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
case "like":
|
|
291
|
+
case "nlike":
|
|
292
|
+
case "ilike":
|
|
293
|
+
case "nilike":
|
|
294
|
+
if (!(typeof val === "string" || val instanceof RegExp)) {
|
|
295
|
+
err = new Error(g.f("The %s property has invalid clause %j: Expected a string or RegExp", p, where[p]));
|
|
296
|
+
err.statusCode = 400;
|
|
297
|
+
throw err;
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
case "regexp":
|
|
301
|
+
val = toRegExp(val);
|
|
302
|
+
if (val instanceof Error) {
|
|
303
|
+
val.statusCode = 400;
|
|
304
|
+
throw val;
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
val = coerceArray(val);
|
|
313
|
+
} catch {}
|
|
314
|
+
const allowExtendedOperators = self._allowExtendedOperators(options);
|
|
315
|
+
if (Array.isArray(val) && !isNestedModel(DataType)) {
|
|
316
|
+
for (let i = 0; i < val.length; i++) if (val[i] !== null && val[i] !== void 0) {
|
|
317
|
+
if (!(val[i] instanceof RegExp)) val[i] = isClass(DataType) ? new DataType(val[i]) : DataType(val[i]);
|
|
318
|
+
}
|
|
319
|
+
} else if (val != null) if (operator === null && val instanceof RegExp) operator = "regexp";
|
|
320
|
+
else if (operator === "regexp" && val instanceof RegExp) {} else if ((operator === "like" || operator === "nlike" || operator === "ilike" || operator === "nilike") && val instanceof RegExp) {} else if (allowExtendedOperators && typeof val === "object") {} else {
|
|
321
|
+
if (!allowExtendedOperators) {
|
|
322
|
+
const extendedOperators = Object.keys(val).filter(function(k) {
|
|
323
|
+
return k[0] === "$";
|
|
324
|
+
});
|
|
325
|
+
if (extendedOperators.length) {
|
|
326
|
+
const msg = g.f("Operators \"" + extendedOperators.join(", ") + "\" are not allowed in query");
|
|
327
|
+
const err = new Error(msg);
|
|
328
|
+
err.code = "OPERATOR_NOT_ALLOWED_IN_QUERY";
|
|
329
|
+
err.statusCode = 400;
|
|
330
|
+
err.details = {
|
|
331
|
+
operators: extendedOperators,
|
|
332
|
+
where
|
|
333
|
+
};
|
|
334
|
+
throw err;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (isNestedModel(DataType)) {
|
|
338
|
+
if (Array.isArray(DataType) && Array.isArray(val)) {
|
|
339
|
+
if (val === null || val === void 0) continue;
|
|
340
|
+
for (const it of val) self._coerce(it, options, DataType[0].definition);
|
|
341
|
+
} else self._coerce(val, options, DataType.definition);
|
|
342
|
+
continue;
|
|
343
|
+
} else val = isClass(DataType) ? new DataType(val) : DataType(val);
|
|
344
|
+
}
|
|
345
|
+
if (operator && operator !== "eq") {
|
|
346
|
+
const value = {};
|
|
347
|
+
value[operator] = val;
|
|
348
|
+
if (exp.options) value.options = exp.options;
|
|
349
|
+
val = value;
|
|
350
|
+
}
|
|
351
|
+
where[p] = val;
|
|
352
|
+
}
|
|
353
|
+
return where;
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* A utility function which checks for nested property definitions
|
|
357
|
+
*
|
|
358
|
+
* @param {*} propType Property type metadata
|
|
359
|
+
*
|
|
360
|
+
*/
|
|
361
|
+
function isNestedModel(propType) {
|
|
362
|
+
if (!propType) return false;
|
|
363
|
+
if (Array.isArray(propType)) return isNestedModel(propType[0]);
|
|
364
|
+
return propType.hasOwnProperty("definition") && propType.definition.hasOwnProperty("properties");
|
|
365
|
+
}
|
|
366
|
+
}));
|
|
367
|
+
//#endregion
|
|
368
|
+
module.exports = require_model_utils();
|