@abedmurrar/objection 0.0.0-stage → 4.0.0-beta.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 +23 -0
- package/README.md +105 -2
- package/lib/index.mjs +6 -0
- package/lib/initialize.js +11 -0
- package/lib/model/AjvValidator.js +278 -0
- package/lib/model/Model.js +772 -0
- package/lib/model/ModifierNotFoundError.js +10 -0
- package/lib/model/NotFoundError.js +19 -0
- package/lib/model/RelationDoesNotExistError.js +11 -0
- package/lib/model/ValidationError.js +46 -0
- package/lib/model/Validator.js +20 -0
- package/lib/model/getModel.js +7 -0
- package/lib/model/graph/ModelGraph.js +60 -0
- package/lib/model/graph/ModelGraphBuilder.js +188 -0
- package/lib/model/graph/ModelGraphEdge.js +40 -0
- package/lib/model/graph/ModelGraphNode.js +201 -0
- package/lib/model/inheritModel.js +19 -0
- package/lib/model/modelBindKnex.js +69 -0
- package/lib/model/modelClone.js +102 -0
- package/lib/model/modelColPropMap.js +24 -0
- package/lib/model/modelConcurrency.js +24 -0
- package/lib/model/modelId.js +57 -0
- package/lib/model/modelJsonAttributes.js +72 -0
- package/lib/model/modelParseRelations.js +83 -0
- package/lib/model/modelQueryProps.js +116 -0
- package/lib/model/modelSet.js +116 -0
- package/lib/model/modelTableMetadata.js +71 -0
- package/lib/model/modelToJson.js +157 -0
- package/lib/model/modelUtils.js +40 -0
- package/lib/model/modelValidate.js +40 -0
- package/lib/model/modelValues.js +113 -0
- package/lib/model/modelVisitor.js +34 -0
- package/lib/objection.js +89 -0
- package/lib/queryBuilder/FunctionBuilder.js +291 -0
- package/lib/queryBuilder/InternalOptions.js +24 -0
- package/lib/queryBuilder/JoinBuilder.js +124 -0
- package/lib/queryBuilder/QueryBuilder.js +1136 -0
- package/lib/queryBuilder/QueryBuilderBase.js +666 -0
- package/lib/queryBuilder/QueryBuilderContext.js +20 -0
- package/lib/queryBuilder/QueryBuilderContextBase.js +26 -0
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +484 -0
- package/lib/queryBuilder/QueryBuilderUserContext.js +25 -0
- package/lib/queryBuilder/RawBuilder.js +78 -0
- package/lib/queryBuilder/ReferenceBuilder.js +246 -0
- package/lib/queryBuilder/RelationExpression.js +373 -0
- package/lib/queryBuilder/StaticHookArguments.js +122 -0
- package/lib/queryBuilder/ValueBuilder.js +115 -0
- package/lib/queryBuilder/expressions.js +121 -0
- package/lib/queryBuilder/functions/mysql.js +762 -0
- package/lib/queryBuilder/graph/GraphAction.js +54 -0
- package/lib/queryBuilder/graph/GraphData.js +12 -0
- package/lib/queryBuilder/graph/GraphFetcher.js +155 -0
- package/lib/queryBuilder/graph/GraphNodeDbExistence.js +146 -0
- package/lib/queryBuilder/graph/GraphOperation.js +21 -0
- package/lib/queryBuilder/graph/GraphOptions.js +165 -0
- package/lib/queryBuilder/graph/GraphUpsert.js +231 -0
- package/lib/queryBuilder/graph/delete/GraphDelete.js +15 -0
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +56 -0
- package/lib/queryBuilder/graph/insert/GraphInsert.js +116 -0
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +183 -0
- package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +70 -0
- package/lib/queryBuilder/graph/patch/GraphPatch.js +15 -0
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +205 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +26 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +96 -0
- package/lib/queryBuilder/join/JoinResultColumn.js +19 -0
- package/lib/queryBuilder/join/JoinResultParser.js +106 -0
- package/lib/queryBuilder/join/RelationJoiner.js +258 -0
- package/lib/queryBuilder/join/TableNode.js +136 -0
- package/lib/queryBuilder/join/TableTree.js +69 -0
- package/lib/queryBuilder/join/utils.js +19 -0
- package/lib/queryBuilder/operations/DelegateOperation.js +101 -0
- package/lib/queryBuilder/operations/DeleteOperation.js +47 -0
- package/lib/queryBuilder/operations/FindByIdOperation.js +29 -0
- package/lib/queryBuilder/operations/FindByIdsOperation.js +23 -0
- package/lib/queryBuilder/operations/FindOperation.js +147 -0
- package/lib/queryBuilder/operations/FirstOperation.js +22 -0
- package/lib/queryBuilder/operations/FromOperation.js +91 -0
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +38 -0
- package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +35 -0
- package/lib/queryBuilder/operations/InsertGraphOperation.js +87 -0
- package/lib/queryBuilder/operations/InsertOperation.js +173 -0
- package/lib/queryBuilder/operations/InstanceDeleteOperation.js +37 -0
- package/lib/queryBuilder/operations/InstanceFindOperation.js +17 -0
- package/lib/queryBuilder/operations/InstanceInsertOperation.js +20 -0
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +45 -0
- package/lib/queryBuilder/operations/JoinRelatedOperation.js +83 -0
- package/lib/queryBuilder/operations/KnexOperation.js +11 -0
- package/lib/queryBuilder/operations/MergeOperation.js +43 -0
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +173 -0
- package/lib/queryBuilder/operations/OnBuildKnexOperation.js +23 -0
- package/lib/queryBuilder/operations/OnBuildOperation.js +23 -0
- package/lib/queryBuilder/operations/OnErrorOperation.js +23 -0
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +258 -0
- package/lib/queryBuilder/operations/RangeOperation.js +37 -0
- package/lib/queryBuilder/operations/RelateOperation.js +20 -0
- package/lib/queryBuilder/operations/ReturningOperation.js +24 -0
- package/lib/queryBuilder/operations/RunAfterOperation.js +23 -0
- package/lib/queryBuilder/operations/RunBeforeOperation.js +23 -0
- package/lib/queryBuilder/operations/UnrelateOperation.js +18 -0
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +63 -0
- package/lib/queryBuilder/operations/UpdateOperation.js +130 -0
- package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +35 -0
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +47 -0
- package/lib/queryBuilder/operations/WhereCompositeOperation.js +48 -0
- package/lib/queryBuilder/operations/WhereInOperation.js +46 -0
- package/lib/queryBuilder/operations/eager/EagerOperation.js +46 -0
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +41 -0
- package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +10 -0
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +227 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +29 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +37 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +30 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +33 -0
- package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +155 -0
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +135 -0
- package/lib/queryBuilder/operations/select/SelectOperation.js +50 -0
- package/lib/queryBuilder/operations/select/Selection.js +112 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +84 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +90 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +95 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +787 -0
- package/lib/queryBuilder/parsers/relationExpressionParser.js +930 -0
- package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +17 -0
- package/lib/queryBuilder/transformations/QueryTransformation.js +9 -0
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +57 -0
- package/lib/queryBuilder/transformations/index.js +9 -0
- package/lib/queryBuilder/whereScopes.js +199 -0
- package/lib/relations/Relation.js +317 -0
- package/lib/relations/RelationDeleteOperation.js +23 -0
- package/lib/relations/RelationFindOperation.js +116 -0
- package/lib/relations/RelationInsertOperation.js +25 -0
- package/lib/relations/RelationOwner.js +199 -0
- package/lib/relations/RelationProperty.js +207 -0
- package/lib/relations/RelationUpdateOperation.js +23 -0
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +18 -0
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +33 -0
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +39 -0
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +45 -0
- package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +46 -0
- package/lib/relations/hasMany/HasManyInsertOperation.js +45 -0
- package/lib/relations/hasMany/HasManyRelateOperation.js +48 -0
- package/lib/relations/hasMany/HasManyRelation.js +41 -0
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +26 -0
- package/lib/relations/hasOne/HasOneRelation.js +10 -0
- package/lib/relations/hasOneThrough/HasOneThroughRelation.js +10 -0
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +82 -0
- package/lib/relations/manyToMany/ManyToManyRelation.js +405 -0
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +29 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +8 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +20 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +8 -0
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +82 -0
- package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +40 -0
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +64 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +11 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +27 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +11 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +8 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +65 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +8 -0
- package/lib/transaction.js +66 -0
- package/lib/utils/assert.js +19 -0
- package/lib/utils/buildUtils.js +19 -0
- package/lib/utils/classUtils.js +9 -0
- package/lib/utils/cloneDeep.js +365 -0
- package/lib/utils/createModifier.js +37 -0
- package/lib/utils/deprecate.js +11 -0
- package/lib/utils/identifierMapping.js +188 -0
- package/lib/utils/internalPropUtils.js +7 -0
- package/lib/utils/knexUtils.js +81 -0
- package/lib/utils/mixin.js +18 -0
- package/lib/utils/mysqlJsonPath.js +70 -0
- package/lib/utils/normalizeIds.js +127 -0
- package/lib/utils/objectUtils.js +314 -0
- package/lib/utils/parseFieldExpression.js +73 -0
- package/lib/utils/postgresJsonPath.js +96 -0
- package/lib/utils/promiseUtils/after.js +14 -0
- package/lib/utils/promiseUtils/afterReturn.js +14 -0
- package/lib/utils/promiseUtils/index.js +15 -0
- package/lib/utils/promiseUtils/isPromise.js +8 -0
- package/lib/utils/promiseUtils/map.js +96 -0
- package/lib/utils/promiseUtils/mapAfterAllReturn.js +23 -0
- package/lib/utils/promiseUtils/try.js +19 -0
- package/lib/utils/resolveModel.js +104 -0
- package/lib/utils/tmpColumnUtils.js +7 -0
- package/package.json +113 -4
- package/typings/objection/index.d.mts +362 -0
- package/typings/objection/index.d.ts +4131 -0
- package/typings/objection/relation-expression.d.ts +237 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { isObject, cloneDeep } = require('../utils/objectUtils');
|
|
3
|
+
const { hiddenProps } = require('./modelUtils');
|
|
4
|
+
const { defineNonEnumerableProperty } = require('./modelUtils');
|
|
5
|
+
const { isInternalProp } = require('../utils/internalPropUtils');
|
|
6
|
+
function clone(model, shallow, stripInternal) {
|
|
7
|
+
let clone = null;
|
|
8
|
+
const omitFromJson = model.$omitFromJson();
|
|
9
|
+
const omitFromDatabaseJson = model.$omitFromDatabaseJson();
|
|
10
|
+
if (!shallow && !stripInternal) {
|
|
11
|
+
clone = cloneSimple(model);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
clone = cloneWithOpt(model, shallow, stripInternal);
|
|
15
|
+
}
|
|
16
|
+
if (omitFromJson) {
|
|
17
|
+
clone.$omitFromJson(omitFromJson);
|
|
18
|
+
}
|
|
19
|
+
if (omitFromDatabaseJson) {
|
|
20
|
+
clone.$omitFromDatabaseJson(omitFromDatabaseJson);
|
|
21
|
+
}
|
|
22
|
+
clone = copyHiddenProps(model, clone);
|
|
23
|
+
return clone;
|
|
24
|
+
}
|
|
25
|
+
function cloneSimple(model) {
|
|
26
|
+
const clone = new model.constructor();
|
|
27
|
+
const keys = Object.keys(model);
|
|
28
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
29
|
+
const key = keys[i];
|
|
30
|
+
const value = model[key];
|
|
31
|
+
if (isObject(value)) {
|
|
32
|
+
clone[key] = cloneObject(value);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
clone[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return clone;
|
|
39
|
+
}
|
|
40
|
+
function cloneWithOpt(model, shallow, stripInternal) {
|
|
41
|
+
const clone = new model.constructor();
|
|
42
|
+
const keys = Object.keys(model);
|
|
43
|
+
const relationNames = model.constructor.getRelationNames();
|
|
44
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
45
|
+
const key = keys[i];
|
|
46
|
+
const value = model[key];
|
|
47
|
+
if (shallow && relationNames.includes(key)) {
|
|
48
|
+
// The constructor may have given default values for relations.
|
|
49
|
+
delete clone[key];
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (stripInternal && isInternalProp(key)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (isObject(value)) {
|
|
56
|
+
clone[key] = cloneObject(value);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
clone[key] = value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return clone;
|
|
63
|
+
}
|
|
64
|
+
function cloneObject(value) {
|
|
65
|
+
if (Array.isArray(value)) {
|
|
66
|
+
return cloneArray(value);
|
|
67
|
+
}
|
|
68
|
+
else if (value.$isObjectionModel) {
|
|
69
|
+
return clone(value, false, false);
|
|
70
|
+
}
|
|
71
|
+
else if (Buffer.isBuffer(value)) {
|
|
72
|
+
return Buffer.from(value);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return cloneDeep(value);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function cloneArray(value) {
|
|
79
|
+
const ret = new Array(value.length);
|
|
80
|
+
for (let i = 0, l = ret.length; i < l; ++i) {
|
|
81
|
+
const item = value[i];
|
|
82
|
+
if (isObject(item)) {
|
|
83
|
+
ret[i] = cloneObject(item);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
ret[i] = item;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return ret;
|
|
90
|
+
}
|
|
91
|
+
function copyHiddenProps(model, clone) {
|
|
92
|
+
for (let i = 0, l = hiddenProps.length; i < l; ++i) {
|
|
93
|
+
const prop = hiddenProps[i];
|
|
94
|
+
if (Object.hasOwn(model, prop)) {
|
|
95
|
+
defineNonEnumerableProperty(clone, prop, model[prop]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return clone;
|
|
99
|
+
}
|
|
100
|
+
module.exports = {
|
|
101
|
+
clone,
|
|
102
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { difference } = require('../utils/objectUtils');
|
|
3
|
+
function columnNameToPropertyName(modelClass, columnName) {
|
|
4
|
+
const model = new modelClass();
|
|
5
|
+
const addedProps = Object.keys(model.$parseDatabaseJson({}));
|
|
6
|
+
const row = {};
|
|
7
|
+
row[columnName] = null;
|
|
8
|
+
const props = Object.keys(model.$parseDatabaseJson(row));
|
|
9
|
+
const propertyName = difference(props, addedProps)[0];
|
|
10
|
+
return propertyName || columnName;
|
|
11
|
+
}
|
|
12
|
+
function propertyNameToColumnName(modelClass, propertyName) {
|
|
13
|
+
const model = new modelClass();
|
|
14
|
+
const addedCols = Object.keys(model.$formatDatabaseJson({}));
|
|
15
|
+
const obj = {};
|
|
16
|
+
obj[propertyName] = null;
|
|
17
|
+
const cols = Object.keys(model.$formatDatabaseJson(obj));
|
|
18
|
+
const columnName = difference(cols, addedCols)[0];
|
|
19
|
+
return columnName || propertyName;
|
|
20
|
+
}
|
|
21
|
+
module.exports = {
|
|
22
|
+
columnNameToPropertyName,
|
|
23
|
+
propertyNameToColumnName,
|
|
24
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { isMsSql } = require('../utils/knexUtils');
|
|
3
|
+
const DEFAULT_CONCURRENCY = 4;
|
|
4
|
+
let defaultGetConcurrency = null;
|
|
5
|
+
// Called by Model.js with Model.getConcurrency.
|
|
6
|
+
function setDefaultGetConcurrency(getConcurrency) {
|
|
7
|
+
defaultGetConcurrency = getConcurrency;
|
|
8
|
+
}
|
|
9
|
+
// The concurrency of the queries that only read, like the queries of
|
|
10
|
+
// `withGraphFetched()`. By default `getConcurrency()` is 1 on MySQL, because
|
|
11
|
+
// concurrent graph updates and deletes deadlock there, but reads don't. The
|
|
12
|
+
// concurrency a model sets with `concurrency` or `getConcurrency()` is used as
|
|
13
|
+
// is.
|
|
14
|
+
function getReadConcurrency(modelClass, knex) {
|
|
15
|
+
if (modelClass.concurrency !== null || modelClass.getConcurrency !== defaultGetConcurrency) {
|
|
16
|
+
return modelClass.getConcurrency(knex);
|
|
17
|
+
}
|
|
18
|
+
// The mssql driver can't run concurrent queries.
|
|
19
|
+
return knex && isMsSql(knex) ? 1 : DEFAULT_CONCURRENCY;
|
|
20
|
+
}
|
|
21
|
+
module.exports = {
|
|
22
|
+
setDefaultGetConcurrency,
|
|
23
|
+
getReadConcurrency,
|
|
24
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
function getSetId(model, maybeId) {
|
|
3
|
+
if (maybeId !== undefined) {
|
|
4
|
+
return setId(model, maybeId);
|
|
5
|
+
}
|
|
6
|
+
else {
|
|
7
|
+
return getId(model);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function hasId(model) {
|
|
11
|
+
return model.$hasProps(model.constructor.getIdPropertyArray());
|
|
12
|
+
}
|
|
13
|
+
function setId(model, id) {
|
|
14
|
+
const idProp = model.constructor.getIdProperty();
|
|
15
|
+
const isCompositeId = Array.isArray(idProp);
|
|
16
|
+
if (Array.isArray(id)) {
|
|
17
|
+
if (isCompositeId) {
|
|
18
|
+
if (id.length !== idProp.length) {
|
|
19
|
+
throw new Error('trying to set an invalid identifier for a model');
|
|
20
|
+
}
|
|
21
|
+
for (let i = 0; i < id.length; ++i) {
|
|
22
|
+
model[idProp[i]] = id[i];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
if (id.length !== 1) {
|
|
27
|
+
throw new Error('trying to set an invalid identifier for a model');
|
|
28
|
+
}
|
|
29
|
+
model[idProp] = id[0];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (isCompositeId) {
|
|
34
|
+
if (idProp.length > 1) {
|
|
35
|
+
throw new Error('trying to set an invalid identifier for a model');
|
|
36
|
+
}
|
|
37
|
+
model[idProp[0]] = id;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
model[idProp] = id;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function getId(model) {
|
|
45
|
+
const idProp = model.constructor.getIdProperty();
|
|
46
|
+
const isCompositeId = Array.isArray(idProp);
|
|
47
|
+
if (isCompositeId) {
|
|
48
|
+
return model.$values(idProp);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
return model[idProp];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
module.exports = {
|
|
55
|
+
getSetId,
|
|
56
|
+
hasId,
|
|
57
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { asArray, isObject, flatten, isString } = require('../utils/objectUtils');
|
|
3
|
+
function parseJsonAttributes(json, modelClass) {
|
|
4
|
+
const jsonAttr = modelClass.getJsonAttributes();
|
|
5
|
+
if (jsonAttr.length) {
|
|
6
|
+
// JSON attributes may be returned as strings depending on the database and
|
|
7
|
+
// the database client. Convert them to objects here.
|
|
8
|
+
for (let i = 0, l = jsonAttr.length; i < l; ++i) {
|
|
9
|
+
const attr = jsonAttr[i];
|
|
10
|
+
const value = json[attr];
|
|
11
|
+
if (isString(value)) {
|
|
12
|
+
const parsed = tryParseJson(value);
|
|
13
|
+
// tryParseJson returns undefined if parsing failed.
|
|
14
|
+
if (parsed !== undefined) {
|
|
15
|
+
json[attr] = parsed;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return json;
|
|
21
|
+
}
|
|
22
|
+
function formatJsonAttributes(json, modelClass) {
|
|
23
|
+
const jsonAttr = modelClass.getJsonAttributes();
|
|
24
|
+
if (jsonAttr.length) {
|
|
25
|
+
// All database clients want JSON columns as strings. Do the conversion here.
|
|
26
|
+
for (let i = 0, l = jsonAttr.length; i < l; ++i) {
|
|
27
|
+
const attr = jsonAttr[i];
|
|
28
|
+
const value = json[attr];
|
|
29
|
+
if (value != null) {
|
|
30
|
+
json[attr] = JSON.stringify(value);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return json;
|
|
35
|
+
}
|
|
36
|
+
function getJsonAttributes(modelClass) {
|
|
37
|
+
let jsonAttributes = modelClass.jsonAttributes;
|
|
38
|
+
if (Array.isArray(jsonAttributes)) {
|
|
39
|
+
return jsonAttributes;
|
|
40
|
+
}
|
|
41
|
+
jsonAttributes = [];
|
|
42
|
+
if (modelClass.getJsonSchema()) {
|
|
43
|
+
const props = modelClass.getJsonSchema().properties || {};
|
|
44
|
+
for (const propName of Object.keys(props)) {
|
|
45
|
+
const prop = props[propName];
|
|
46
|
+
let types = asArray(prop.type).filter((it) => !!it);
|
|
47
|
+
if (types.length === 0 && Array.isArray(prop.anyOf)) {
|
|
48
|
+
types = flatten(prop.anyOf.map((it) => it.type));
|
|
49
|
+
}
|
|
50
|
+
if (types.length === 0 && Array.isArray(prop.oneOf)) {
|
|
51
|
+
types = flatten(prop.oneOf.map((it) => it.type));
|
|
52
|
+
}
|
|
53
|
+
if (types.indexOf('object') !== -1 || types.indexOf('array') !== -1) {
|
|
54
|
+
jsonAttributes.push(propName);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return jsonAttributes;
|
|
59
|
+
}
|
|
60
|
+
function tryParseJson(maybeJsonStr) {
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(maybeJsonStr);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
module.exports = {
|
|
69
|
+
parseJsonAttributes,
|
|
70
|
+
formatJsonAttributes,
|
|
71
|
+
getJsonAttributes,
|
|
72
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { isObject } = require('../utils/objectUtils');
|
|
3
|
+
function parseRelationsIntoModelInstances(model, json, options = {}) {
|
|
4
|
+
const relationNames = model.constructor.getRelationNames();
|
|
5
|
+
if (!options.cache && !hasRelation(json, relationNames)) {
|
|
6
|
+
// Nothing to parse. The cache would only be used for the relations.
|
|
7
|
+
return model;
|
|
8
|
+
}
|
|
9
|
+
if (!options.cache) {
|
|
10
|
+
options = Object.assign({}, options, {
|
|
11
|
+
cache: new Map(),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
options.cache.set(json, model);
|
|
15
|
+
for (const relationName of relationNames) {
|
|
16
|
+
const relationJson = json[relationName];
|
|
17
|
+
if (relationJson !== undefined) {
|
|
18
|
+
const relation = model.constructor.getRelation(relationName);
|
|
19
|
+
const relationModel = parseRelation(relationJson, relation, options);
|
|
20
|
+
if (relationModel !== relationJson) {
|
|
21
|
+
model[relation.name] = relationModel;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return model;
|
|
26
|
+
}
|
|
27
|
+
function hasRelation(json, relationNames) {
|
|
28
|
+
for (let i = 0, l = relationNames.length; i < l; ++i) {
|
|
29
|
+
if (json[relationNames[i]] !== undefined) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function parseRelation(json, relation, options) {
|
|
36
|
+
if (Array.isArray(json)) {
|
|
37
|
+
return parseRelationArray(json, relation, options);
|
|
38
|
+
}
|
|
39
|
+
else if (json) {
|
|
40
|
+
return parseRelationObject(json, relation, options);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function parseRelationArray(json, relation, options) {
|
|
47
|
+
const models = new Array(json.length);
|
|
48
|
+
let didChange = false;
|
|
49
|
+
for (let i = 0, l = json.length; i < l; ++i) {
|
|
50
|
+
const model = parseRelationObject(json[i], relation, options);
|
|
51
|
+
if (model !== json[i]) {
|
|
52
|
+
didChange = true;
|
|
53
|
+
}
|
|
54
|
+
models[i] = model;
|
|
55
|
+
}
|
|
56
|
+
if (didChange) {
|
|
57
|
+
return models;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return json;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function parseRelationObject(json, relation, options) {
|
|
64
|
+
if (isObject(json)) {
|
|
65
|
+
const modelClass = relation.relatedModelClass;
|
|
66
|
+
let model = options.cache.get(json);
|
|
67
|
+
if (model === undefined) {
|
|
68
|
+
if (json instanceof modelClass) {
|
|
69
|
+
model = parseRelationsIntoModelInstances(json, json, options);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
model = modelClass.fromJson(json, options);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return model;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return json;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
module.exports = {
|
|
82
|
+
parseRelationsIntoModelInstances,
|
|
83
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { isObject, isFunction } = require('../utils/objectUtils');
|
|
3
|
+
const { defineNonEnumerableProperty } = require('./modelUtils');
|
|
4
|
+
const { isKnexRaw, isKnexQueryBuilder } = require('../utils/knexUtils');
|
|
5
|
+
const QUERY_PROPS_PROPERTY = '$$queryProps';
|
|
6
|
+
// Removes query properties from `json` and stores them into a hidden property
|
|
7
|
+
// inside `model` so that they can be later merged back to `json`.
|
|
8
|
+
function splitQueryProps(model, json) {
|
|
9
|
+
const keys = Object.keys(json);
|
|
10
|
+
if (hasQueryProps(json, keys)) {
|
|
11
|
+
const queryProps = {};
|
|
12
|
+
const modelProps = {};
|
|
13
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
14
|
+
const key = keys[i];
|
|
15
|
+
const value = json[key];
|
|
16
|
+
if (isQueryProp(value)) {
|
|
17
|
+
queryProps[key] = value;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
modelProps[key] = value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
defineNonEnumerableProperty(model, QUERY_PROPS_PROPERTY, queryProps);
|
|
24
|
+
return modelProps;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return json;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function hasQueryProps(json, keys) {
|
|
31
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
32
|
+
if (isQueryProp(json[keys[i]])) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
function isQueryProp(value) {
|
|
39
|
+
if (!isObject(value)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return (isKnexQueryBuilder(value) ||
|
|
43
|
+
isKnexRaw(value) ||
|
|
44
|
+
isKnexRawConvertable(value) ||
|
|
45
|
+
value.isObjectionQueryBuilderBase);
|
|
46
|
+
}
|
|
47
|
+
// Merges and converts `model`'s query properties into `json`.
|
|
48
|
+
function mergeQueryProps(model, json, omitProps, builder) {
|
|
49
|
+
json = convertExistingQueryProps(json, builder);
|
|
50
|
+
json = convertAndMergeHiddenQueryProps(model, json, omitProps, builder);
|
|
51
|
+
return json;
|
|
52
|
+
}
|
|
53
|
+
// Converts the query properties in `json` to knex raw instances.
|
|
54
|
+
// `json` may have query properties even though we removed them.
|
|
55
|
+
// For example they may have been added in lifecycle hooks.
|
|
56
|
+
function convertExistingQueryProps(json, builder) {
|
|
57
|
+
const keys = Object.keys(json);
|
|
58
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
59
|
+
const key = keys[i];
|
|
60
|
+
const value = json[key];
|
|
61
|
+
if (isQueryProp(value)) {
|
|
62
|
+
json[key] = queryPropToKnexRaw(value, builder);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return json;
|
|
66
|
+
}
|
|
67
|
+
// Converts and merges the query props that were split from the model
|
|
68
|
+
// and stored into QUERY_PROPS_PROPERTY.
|
|
69
|
+
function convertAndMergeHiddenQueryProps(model, json, omitProps, builder) {
|
|
70
|
+
const queryProps = model[QUERY_PROPS_PROPERTY];
|
|
71
|
+
if (!queryProps) {
|
|
72
|
+
// The model has no query properties.
|
|
73
|
+
return json;
|
|
74
|
+
}
|
|
75
|
+
const modelClass = model.constructor;
|
|
76
|
+
const keys = Object.keys(queryProps);
|
|
77
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
78
|
+
const key = keys[i];
|
|
79
|
+
if (!omitProps || !omitProps.includes(key)) {
|
|
80
|
+
const queryProp = queryPropToKnexRaw(queryProps[key], builder);
|
|
81
|
+
json[modelClass.propertyNameToColumnName(key)] = queryProp;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return json;
|
|
85
|
+
}
|
|
86
|
+
// Converts a query property into a knex `raw` instance.
|
|
87
|
+
function queryPropToKnexRaw(queryProp, builder) {
|
|
88
|
+
if (!queryProp) {
|
|
89
|
+
return queryProp;
|
|
90
|
+
}
|
|
91
|
+
if (queryProp.isObjectionQueryBuilderBase) {
|
|
92
|
+
return buildObjectionQueryBuilder(queryProp, builder);
|
|
93
|
+
}
|
|
94
|
+
else if (isKnexRawConvertable(queryProp)) {
|
|
95
|
+
return buildKnexRawConvertable(queryProp, builder);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return queryProp;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function buildObjectionQueryBuilder(builder, parentBuilder) {
|
|
102
|
+
return builder.subqueryOf(parentBuilder).toKnexQuery();
|
|
103
|
+
}
|
|
104
|
+
function buildKnexRawConvertable(convertable, builder) {
|
|
105
|
+
if (!builder) {
|
|
106
|
+
throw new Error('toDatabaseJson called without a query builder instance for a model with query properties');
|
|
107
|
+
}
|
|
108
|
+
return convertable.toKnexRaw(builder);
|
|
109
|
+
}
|
|
110
|
+
function isKnexRawConvertable(queryProp) {
|
|
111
|
+
return isFunction(queryProp.toKnexRaw);
|
|
112
|
+
}
|
|
113
|
+
module.exports = {
|
|
114
|
+
splitQueryProps,
|
|
115
|
+
mergeQueryProps,
|
|
116
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { isInternalProp } = require('../utils/internalPropUtils');
|
|
3
|
+
const { splitQueryProps } = require('./modelQueryProps');
|
|
4
|
+
const { isFunction, isString, isPlainObject } = require('../utils/objectUtils');
|
|
5
|
+
const { parseRelationsIntoModelInstances } = require('./modelParseRelations');
|
|
6
|
+
function setJson(model, json, options) {
|
|
7
|
+
json = json || {};
|
|
8
|
+
options = options || {};
|
|
9
|
+
if (Object.prototype.toString.call(json) !== '[object Object]') {
|
|
10
|
+
throw new Error('You should only pass objects to $setJson method. ' +
|
|
11
|
+
'$setJson method was given an invalid value ' +
|
|
12
|
+
json);
|
|
13
|
+
}
|
|
14
|
+
if (!json.$isObjectionModel) {
|
|
15
|
+
// json can contain "query properties" like `raw` instances, query builders etc.
|
|
16
|
+
// We take them out of `json` and store them to a hidden property $$queryProps
|
|
17
|
+
// in the model instance for later use.
|
|
18
|
+
json = splitQueryProps(model, json);
|
|
19
|
+
}
|
|
20
|
+
json = model.$parseJson(json, options);
|
|
21
|
+
json = model.$validate(json, options);
|
|
22
|
+
model.$set(json);
|
|
23
|
+
if (!options.skipParseRelations) {
|
|
24
|
+
parseRelationsIntoModelInstances(model, json, options);
|
|
25
|
+
}
|
|
26
|
+
return model;
|
|
27
|
+
}
|
|
28
|
+
function setDatabaseJson(model, json) {
|
|
29
|
+
json = model.$parseDatabaseJson(json);
|
|
30
|
+
if (json) {
|
|
31
|
+
const keys = Object.keys(json);
|
|
32
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
33
|
+
const key = keys[i];
|
|
34
|
+
model[key] = json[key];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return model;
|
|
38
|
+
}
|
|
39
|
+
function setFast(model, obj) {
|
|
40
|
+
if (obj) {
|
|
41
|
+
// Don't try to set read-only properties. They can easily get here
|
|
42
|
+
// through `fromJson` when parsing an object that was previously
|
|
43
|
+
// serialized from a model instance.
|
|
44
|
+
const readOnlyAttr = model.constructor.getReadOnlyAttributes();
|
|
45
|
+
const keys = Object.keys(obj);
|
|
46
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
47
|
+
const key = keys[i];
|
|
48
|
+
const value = obj[key];
|
|
49
|
+
if ((value !== undefined || isPlainObject(obj)) &&
|
|
50
|
+
!isInternalProp(key) &&
|
|
51
|
+
!isFunction(value) &&
|
|
52
|
+
!readOnlyAttr.includes(key)) {
|
|
53
|
+
model[key] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return model;
|
|
58
|
+
}
|
|
59
|
+
function setRelated(model, relation, models) {
|
|
60
|
+
relation = ensureRelation(model, relation);
|
|
61
|
+
if (relation.isOneToOne()) {
|
|
62
|
+
if (Array.isArray(models)) {
|
|
63
|
+
if (models.length === 0) {
|
|
64
|
+
model[relation.name] = null;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
model[relation.name] = models[0] || null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
model[relation.name] = models || null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
if (!models) {
|
|
76
|
+
model[relation.name] = [];
|
|
77
|
+
}
|
|
78
|
+
else if (Array.isArray(models)) {
|
|
79
|
+
model[relation.name] = models;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
model[relation.name] = [models];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return model;
|
|
86
|
+
}
|
|
87
|
+
function appendRelated(model, relation, models) {
|
|
88
|
+
relation = ensureRelation(model, relation);
|
|
89
|
+
if (!model[relation.name] || relation.isOneToOne()) {
|
|
90
|
+
return model.$setRelated(relation, models);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
if (Array.isArray(models)) {
|
|
94
|
+
models.forEach((it) => model[relation.name].push(it));
|
|
95
|
+
}
|
|
96
|
+
else if (models) {
|
|
97
|
+
model[relation.name].push(models);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return model;
|
|
101
|
+
}
|
|
102
|
+
function ensureRelation(model, relation) {
|
|
103
|
+
if (isString(relation)) {
|
|
104
|
+
return model.constructor.getRelation(relation);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return relation;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
module.exports = {
|
|
111
|
+
setFast,
|
|
112
|
+
setJson,
|
|
113
|
+
setDatabaseJson,
|
|
114
|
+
setRelated,
|
|
115
|
+
appendRelated,
|
|
116
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { defineNonEnumerableProperty } = require('./modelUtils');
|
|
3
|
+
const { isPromise } = require('../utils/promiseUtils');
|
|
4
|
+
const TABLE_METADATA = '$$tableMetadata';
|
|
5
|
+
function fetchTableMetadata(modelClass, { parentBuilder = null, knex = null, force = false, table = null } = {}) {
|
|
6
|
+
// The table isn't necessarily same as `modelClass.getTableName()` for example if
|
|
7
|
+
// a view is queried instead.
|
|
8
|
+
if (!table) {
|
|
9
|
+
if (parentBuilder) {
|
|
10
|
+
table = parentBuilder.tableNameFor(modelClass);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
table = modelClass.getTableName();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Call tableMetadata first instead of accessing the cache directly beause
|
|
17
|
+
// tableMetadata may have been overriden.
|
|
18
|
+
let metadata = modelClass.tableMetadata({ table });
|
|
19
|
+
if (!force && metadata) {
|
|
20
|
+
return Promise.resolve(metadata);
|
|
21
|
+
}
|
|
22
|
+
// Memoize metadata but only for modelClass. The hasOwnProperty check
|
|
23
|
+
// will fail for subclasses and the value gets recreated.
|
|
24
|
+
if (!Object.hasOwn(modelClass, TABLE_METADATA)) {
|
|
25
|
+
defineNonEnumerableProperty(modelClass, TABLE_METADATA, new Map());
|
|
26
|
+
}
|
|
27
|
+
// The cache needs to be checked in addition to calling tableMetadata
|
|
28
|
+
// because the cache may contain a temporary promise in which case
|
|
29
|
+
// tableMetadata returns null.
|
|
30
|
+
metadata = modelClass[TABLE_METADATA].get(table);
|
|
31
|
+
if (!force && metadata) {
|
|
32
|
+
return Promise.resolve(metadata);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const promise = modelClass
|
|
36
|
+
.query(knex)
|
|
37
|
+
.childQueryOf(parentBuilder)
|
|
38
|
+
.columnInfo({ table })
|
|
39
|
+
.then((columnInfo) => {
|
|
40
|
+
const metadata = {
|
|
41
|
+
columns: Object.keys(columnInfo),
|
|
42
|
+
};
|
|
43
|
+
modelClass[TABLE_METADATA].set(table, metadata);
|
|
44
|
+
return metadata;
|
|
45
|
+
})
|
|
46
|
+
.catch((err) => {
|
|
47
|
+
modelClass[TABLE_METADATA].delete(table);
|
|
48
|
+
throw err;
|
|
49
|
+
});
|
|
50
|
+
modelClass[TABLE_METADATA].set(table, promise);
|
|
51
|
+
return promise;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function tableMetadata(modelClass, { table } = {}) {
|
|
55
|
+
if (Object.hasOwn(modelClass, TABLE_METADATA)) {
|
|
56
|
+
const metadata = modelClass[TABLE_METADATA].get(table || modelClass.getTableName());
|
|
57
|
+
if (isPromise(metadata)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return metadata;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
module.exports = {
|
|
69
|
+
fetchTableMetadata,
|
|
70
|
+
tableMetadata,
|
|
71
|
+
};
|