objection 3.0.4 → 3.1.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/README.md +4 -4
- package/lib/model/AjvValidator.js +13 -13
- package/lib/model/Model.js +0 -5
- package/lib/model/getModel.js +9 -0
- package/lib/queryBuilder/QueryBuilder.js +1 -1
- package/lib/queryBuilder/QueryBuilderBase.js +4 -0
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +63 -16
- package/lib/queryBuilder/operations/FindOperation.js +0 -2
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +3 -2
- package/lib/relations/RelationOwner.js +4 -1
- package/lib/relations/manyToMany/ManyToManyRelation.js +1 -1
- package/package.json +3 -2
- package/typings/objection/index.d.ts +8 -12
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ What objection.js gives you:
|
|
|
17
17
|
- **Simple and fun way to [fetch, insert, update and delete](https://vincit.github.io/objection.js/guide/query-examples.html) objects using the full power of SQL**
|
|
18
18
|
- **Powerful mechanisms for [eager loading](https://vincit.github.io/objection.js/guide/query-examples.html#eager-loading), [inserting](https://vincit.github.io/objection.js/guide/query-examples.html#graph-inserts) and [upserting](https://vincit.github.io/objection.js/guide/query-examples.html#graph-upserts) object graphs**
|
|
19
19
|
- **Easy to use [transactions](https://vincit.github.io/objection.js/guide/transactions.html)**
|
|
20
|
-
- **Official [TypeScript](https://github.com/Vincit/objection.js/blob/
|
|
20
|
+
- **Official [TypeScript](https://github.com/Vincit/objection.js/blob/main/typings/objection/index.d.ts) support**
|
|
21
21
|
- **Optional [JSON schema](https://vincit.github.io/objection.js/guide/validation.html) validation**
|
|
22
22
|
- **A way to [store complex documents](https://vincit.github.io/objection.js/guide/documents.html) as single rows**
|
|
23
23
|
|
|
@@ -36,9 +36,9 @@ What objection.js **doesn't** give you:
|
|
|
36
36
|
For simple things it is useful that the database schema is automatically generated from the model definitions,
|
|
37
37
|
but usually just gets in your way when doing anything non-trivial. Objection.js leaves the schema related things
|
|
38
38
|
to you. knex has a great [migration tool](https://knexjs.org/guide/migrations.html) that we recommend for this job. Check
|
|
39
|
-
out the [example project](https://github.com/Vincit/objection.js/tree/
|
|
39
|
+
out the [example project](https://github.com/Vincit/objection.js/tree/main/examples/koa-ts).
|
|
40
40
|
|
|
41
|
-
The best way to get started is to clone our [example project](https://github.com/Vincit/objection.js/tree/
|
|
41
|
+
The best way to get started is to clone our [example project](https://github.com/Vincit/objection.js/tree/main/examples/koa) and start playing with it. There's also a [typescript version](https://github.com/Vincit/objection.js/tree/main/examples/koa-ts) available.
|
|
42
42
|
|
|
43
43
|
Check out [this issue](https://github.com/Vincit/objection.js/issues/1069) to see who is using objection and what they think about it.
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ Shortcuts:
|
|
|
46
46
|
|
|
47
47
|
- [Who uses objection.js](https://github.com/Vincit/objection.js/issues/1069)
|
|
48
48
|
- [API reference](https://vincit.github.io/objection.js/api/query-builder/)
|
|
49
|
-
- [Example projects](https://github.com/Vincit/objection.js/tree/
|
|
49
|
+
- [Example projects](https://github.com/Vincit/objection.js/tree/main/examples)
|
|
50
50
|
- [Changelog](https://vincit.github.io/objection.js/release-notes/changelog.html)
|
|
51
51
|
- [v1 -> v2 migration guide](https://vincit.github.io/objection.js/release-notes/migration.html)
|
|
52
52
|
- [Contribution guide](https://vincit.github.io/objection.js/guide/contributing.html)
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const Ajv = require('ajv');
|
|
4
|
+
const addFormats = require('ajv-formats');
|
|
3
5
|
const { Validator } = require('./Validator');
|
|
4
6
|
const { ValidationErrorType } = require('../model/ValidationError');
|
|
5
7
|
const { isObject, once, cloneDeep: lodashCloneDeep, omit } = require('../utils/objectUtils');
|
|
6
8
|
|
|
7
|
-
const getAjv = once(() => {
|
|
8
|
-
try {
|
|
9
|
-
return require('ajv');
|
|
10
|
-
} catch (err) {
|
|
11
|
-
throw new Error('Optional ajv dependency not installed. Please run `npm install ajv --save`');
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
|
|
15
9
|
class AjvValidator extends Validator {
|
|
16
10
|
static init(self, conf) {
|
|
17
11
|
super.init(self, conf);
|
|
@@ -20,8 +14,6 @@ class AjvValidator extends Validator {
|
|
|
20
14
|
allErrors: true,
|
|
21
15
|
});
|
|
22
16
|
|
|
23
|
-
const Ajv = getAjv();
|
|
24
|
-
|
|
25
17
|
// Create a normal Ajv instance.
|
|
26
18
|
self.ajv = new Ajv(
|
|
27
19
|
Object.assign(
|
|
@@ -43,8 +35,13 @@ class AjvValidator extends Validator {
|
|
|
43
35
|
// A cache for the compiled validator functions.
|
|
44
36
|
self.cache = new Map();
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
const setupAjv = (ajv) => {
|
|
39
|
+
addFormats(ajv);
|
|
40
|
+
conf.onCreateAjv(ajv);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
setupAjv(self.ajv);
|
|
44
|
+
setupAjv(self.ajvNoDefaults);
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
beforeValidate({ json, model, options, ctx }) {
|
|
@@ -256,7 +253,7 @@ function jsonSchemaWithoutRequired(jsonSchema) {
|
|
|
256
253
|
return Object.assign(
|
|
257
254
|
omit(jsonSchema, ['required', ...subSchemaProps]),
|
|
258
255
|
...subSchemaProps.map((prop) => subSchemaWithoutRequired(jsonSchema, prop)),
|
|
259
|
-
jsonSchema && jsonSchema.definitions
|
|
256
|
+
jsonSchema && jsonSchema.definitions && Object.keys(jsonSchema.definitions).length > 0
|
|
260
257
|
? {
|
|
261
258
|
definitions: Object.assign(
|
|
262
259
|
...Object.keys(jsonSchema.definitions).map((prop) => ({
|
|
@@ -264,6 +261,9 @@ function jsonSchemaWithoutRequired(jsonSchema) {
|
|
|
264
261
|
}))
|
|
265
262
|
),
|
|
266
263
|
}
|
|
264
|
+
: {},
|
|
265
|
+
jsonSchema.discriminator && jsonSchema.discriminator.propertyName
|
|
266
|
+
? { required: [jsonSchema.discriminator.propertyName] }
|
|
267
267
|
: {}
|
|
268
268
|
);
|
|
269
269
|
}
|
package/lib/model/Model.js
CHANGED
|
@@ -1070,11 +1070,11 @@ function beforeExecute(builder) {
|
|
|
1070
1070
|
// Resolve all before hooks before building and executing the query
|
|
1071
1071
|
// and the rest of the hooks.
|
|
1072
1072
|
promise = chainOperationHooks(promise, builder, 'onBefore1');
|
|
1073
|
+
promise = chainOperationHooks(promise, builder, 'onBefore2');
|
|
1073
1074
|
|
|
1074
1075
|
promise = chainHooks(promise, builder, builder.context().runBefore);
|
|
1075
1076
|
promise = chainHooks(promise, builder, builder.internalContext().runBefore);
|
|
1076
1077
|
|
|
1077
|
-
promise = chainOperationHooks(promise, builder, 'onBefore2');
|
|
1078
1078
|
promise = chainOperationHooks(promise, builder, 'onBefore3');
|
|
1079
1079
|
|
|
1080
1080
|
return promise;
|
|
@@ -345,6 +345,10 @@ class QueryBuilderBase extends QueryBuilderOperationSupport {
|
|
|
345
345
|
return this.addOperation(new KnexOperation('intersect'), args);
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
except(...args) {
|
|
349
|
+
return this.addOperation(new KnexOperation('except'), args);
|
|
350
|
+
}
|
|
351
|
+
|
|
348
352
|
having(...args) {
|
|
349
353
|
return this.addOperation(new KnexOperation('having'), args);
|
|
350
354
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { getModel } = require('../../../model/getModel');
|
|
3
4
|
const { GraphAction } = require('../GraphAction');
|
|
4
5
|
const { isInternalProp } = require('../../../utils/internalPropUtils');
|
|
5
6
|
const { union, difference, isObject, jsonEquals } = require('../../../utils/objectUtils');
|
|
@@ -30,20 +31,40 @@ class GraphPatchAction extends GraphAction {
|
|
|
30
31
|
// properties. That's why we handle them here.
|
|
31
32
|
const changePropsBecauseOfBelongsToOneDelete = this._handleBelongsToOneDeletes(node);
|
|
32
33
|
|
|
33
|
-
const
|
|
34
|
-
|
|
34
|
+
const handleUpdate = () => {
|
|
35
|
+
const { changedProps, unchangedProps } = this._findChanges(node);
|
|
36
|
+
const allProps = union(changedProps, unchangedProps);
|
|
37
|
+
|
|
38
|
+
const propsToUpdate = difference(
|
|
39
|
+
shouldPatch || shouldUpdate
|
|
40
|
+
? changedProps
|
|
41
|
+
: [...changedPropsBecauseOfBelongsToOneInsert, ...changePropsBecauseOfBelongsToOneDelete],
|
|
42
|
+
|
|
43
|
+
// Remove id properties from the props to update. With upsertGraph
|
|
44
|
+
// it never makes sense to change the id.
|
|
45
|
+
node.modelClass.getIdPropertyArray()
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const update = propsToUpdate.length > 0;
|
|
49
|
+
if (update) {
|
|
50
|
+
// Don't update the fields that we know not to change.
|
|
51
|
+
node.obj.$omitFromDatabaseJson(difference(allProps, propsToUpdate));
|
|
52
|
+
node.userData.updated = true;
|
|
53
|
+
}
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
? changedProps
|
|
39
|
-
: [...changedPropsBecauseOfBelongsToOneInsert, ...changePropsBecauseOfBelongsToOneDelete],
|
|
55
|
+
return update;
|
|
56
|
+
};
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
const Model = getModel();
|
|
59
|
+
// See if the model defines a beforeUpdate or $beforeUpdate hook. If it does
|
|
60
|
+
// not, we can check for updated properties now and drop out immediately if
|
|
61
|
+
// there is nothing to update. Otherwise, we need to wait for the hook to be
|
|
62
|
+
// called before calling handleUpdate. See issue #2233.
|
|
63
|
+
const hasBeforeUpdate =
|
|
64
|
+
node.obj.constructor.beforeUpdate !== Model.beforeUpdate ||
|
|
65
|
+
node.obj.$beforeUpdate !== Model.prototype.$beforeUpdate;
|
|
45
66
|
|
|
46
|
-
if (
|
|
67
|
+
if (!hasBeforeUpdate && !handleUpdate()) {
|
|
47
68
|
return null;
|
|
48
69
|
}
|
|
49
70
|
|
|
@@ -56,14 +77,31 @@ class GraphPatchAction extends GraphAction {
|
|
|
56
77
|
patch: shouldPatch || (!shouldPatch && !shouldUpdate),
|
|
57
78
|
});
|
|
58
79
|
|
|
59
|
-
// Don't update the fields that we know not to change.
|
|
60
|
-
node.obj.$omitFromDatabaseJson(difference(allProps, propsToUpdate));
|
|
61
|
-
node.userData.updated = true;
|
|
62
|
-
|
|
63
80
|
const updateBuilder = this._createBuilder(node)
|
|
64
|
-
.childQueryOf(builder)
|
|
81
|
+
.childQueryOf(builder, childQueryOptions())
|
|
65
82
|
.copyFrom(builder, GraphAction.ReturningAllSelector);
|
|
66
83
|
|
|
84
|
+
if (hasBeforeUpdate) {
|
|
85
|
+
updateBuilder
|
|
86
|
+
.context({
|
|
87
|
+
runBefore: () => {
|
|
88
|
+
// Call handleUpdate in the runBefore hook which runs after the
|
|
89
|
+
// $beforeUpdate hook, allowing it to modify the object before the
|
|
90
|
+
// updated properties are determined. See issue #2233.
|
|
91
|
+
if (hasBeforeUpdate && !handleUpdate()) {
|
|
92
|
+
throw new ReturnNullException();
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
.onError((error) => {
|
|
97
|
+
if (error instanceof ReturnNullException) {
|
|
98
|
+
return null;
|
|
99
|
+
} else {
|
|
100
|
+
return Promise.reject(error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
67
105
|
if (shouldPatch) {
|
|
68
106
|
updateBuilder.patch(node.obj);
|
|
69
107
|
} else {
|
|
@@ -197,6 +235,15 @@ class GraphPatchAction extends GraphAction {
|
|
|
197
235
|
}
|
|
198
236
|
}
|
|
199
237
|
|
|
238
|
+
class ReturnNullException {}
|
|
239
|
+
|
|
240
|
+
function childQueryOptions() {
|
|
241
|
+
return {
|
|
242
|
+
fork: true,
|
|
243
|
+
isInternalQuery: true,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
200
247
|
function nonStrictEquals(val1, val2) {
|
|
201
248
|
if (val1 == val2) {
|
|
202
249
|
return true;
|
|
@@ -154,8 +154,6 @@ function doCallAfterFind(ctx, model, result) {
|
|
|
154
154
|
function getAfterFindHook(model) {
|
|
155
155
|
if (model.$afterFind !== model.$objectionModelClass.prototype.$afterFind) {
|
|
156
156
|
return model.$afterFind;
|
|
157
|
-
} else if (model.$afterGet !== model.$objectionModelClass.prototype.$afterGet) {
|
|
158
|
-
return model.$afterGet;
|
|
159
157
|
} else {
|
|
160
158
|
return null;
|
|
161
159
|
}
|
|
@@ -4,7 +4,7 @@ const promiseUtils = require('../../../utils/promiseUtils');
|
|
|
4
4
|
|
|
5
5
|
const { EagerOperation } = require('./EagerOperation');
|
|
6
6
|
const { isMsSql, isOracle, isSqlite } = require('../../../utils/knexUtils');
|
|
7
|
-
const { asArray, flatten, chunk } = require('../../../utils/objectUtils');
|
|
7
|
+
const { isObject, asArray, flatten, chunk } = require('../../../utils/objectUtils');
|
|
8
8
|
const { ValidationErrorType } = require('../../../model/ValidationError');
|
|
9
9
|
const { createModifier } = require('../../../utils/createModifier');
|
|
10
10
|
const { RelationDoesNotExistError } = require('../../../model/RelationDoesNotExistError');
|
|
@@ -63,7 +63,8 @@ class WhereInEagerOperation extends EagerOperation {
|
|
|
63
63
|
|
|
64
64
|
const models = asArray(result);
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
// Check models to be actual objects, to filter out `count` results (#2397).
|
|
67
|
+
if (!models.length || !isObject(models[0])) {
|
|
67
68
|
return result;
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -187,7 +187,10 @@ function isQueryBuilder(item) {
|
|
|
187
187
|
|
|
188
188
|
function findFirstNonPartialAncestorQuery(builder) {
|
|
189
189
|
builder = builder.parentQuery();
|
|
190
|
-
|
|
190
|
+
if (!builder)
|
|
191
|
+
throw Error(
|
|
192
|
+
'query method `for` ommitted outside a subquery, can not figure out relation target'
|
|
193
|
+
);
|
|
191
194
|
while (builder.isPartial()) {
|
|
192
195
|
if (!builder.parentQuery()) {
|
|
193
196
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objection",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "An SQL-friendly ORM for Node.js",
|
|
5
5
|
"main": "lib/objection.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"url": "git://github.com/vincit/objection.js.git"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": ">=
|
|
36
|
+
"node": ">=14.0.0"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|
|
39
39
|
"orm",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"types": "./typings/objection/index.d.ts",
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"ajv": "^8.6.2",
|
|
57
|
+
"ajv-formats": "^2.1.1",
|
|
57
58
|
"db-errors": "^0.2.3"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// * Joseph T Lapp <https://github.com/jtlapp>
|
|
11
11
|
// * Drew R. <https://github.com/drew-r>
|
|
12
12
|
// * Karl Blomster <https://github.com/kblomster>
|
|
13
|
-
// * And many others: See <https://github.com/Vincit/objection.js/blob/
|
|
13
|
+
// * And many others: See <https://github.com/Vincit/objection.js/blob/main/typings/objection/index.d.ts>
|
|
14
14
|
|
|
15
15
|
import Ajv, { Options as AjvOptions } from 'ajv';
|
|
16
16
|
import * as dbErrors from 'db-errors';
|
|
@@ -194,9 +194,7 @@ declare namespace Objection {
|
|
|
194
194
|
/**
|
|
195
195
|
* A Pojo version of model.
|
|
196
196
|
*/
|
|
197
|
-
type ModelObject<T extends Model> =
|
|
198
|
-
[K in DataPropertyNames<T>]: T[K];
|
|
199
|
-
};
|
|
197
|
+
type ModelObject<T extends Model> = Pick<T, DataPropertyNames<T>>;
|
|
200
198
|
|
|
201
199
|
/**
|
|
202
200
|
* Any object that has some of the properties of model class T match this type.
|
|
@@ -234,10 +232,10 @@ declare namespace Objection {
|
|
|
234
232
|
type PartialModelGraphField<F> = Defined<F> extends Model
|
|
235
233
|
? PartialModelGraph<Defined<F>>
|
|
236
234
|
: Defined<F> extends Array<infer I>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
235
|
+
? I extends Model
|
|
236
|
+
? PartialModelGraph<I>[]
|
|
237
|
+
: Expression<F>
|
|
238
|
+
: Expression<F>;
|
|
241
239
|
|
|
242
240
|
/**
|
|
243
241
|
* Extracts the property names (excluding relations) of a model class.
|
|
@@ -709,9 +707,7 @@ declare namespace Objection {
|
|
|
709
707
|
<QB extends AnyQueryBuilder>(
|
|
710
708
|
this: QB,
|
|
711
709
|
column: string | Raw | (string | Raw)[]
|
|
712
|
-
): QB extends NumberQueryBuilder<QB>
|
|
713
|
-
? ArrayQueryBuilder<QB>
|
|
714
|
-
: QB;
|
|
710
|
+
): QB extends NumberQueryBuilder<QB> ? ArrayQueryBuilder<QB> : QB;
|
|
715
711
|
}
|
|
716
712
|
|
|
717
713
|
interface TimeoutOptions {
|
|
@@ -995,6 +991,7 @@ declare namespace Objection {
|
|
|
995
991
|
union: UnionMethod<this>;
|
|
996
992
|
unionAll: UnionMethod<this>;
|
|
997
993
|
intersect: SetOperationsMethod<this>;
|
|
994
|
+
except: SetOperationsMethod<this>;
|
|
998
995
|
|
|
999
996
|
with: WithMethod<this>;
|
|
1000
997
|
withRecursive: WithMethod<this>;
|
|
@@ -1680,7 +1677,6 @@ declare namespace Objection {
|
|
|
1680
1677
|
$afterInsert(queryContext: QueryContext): Promise<any> | void;
|
|
1681
1678
|
$afterUpdate(opt: ModelOptions, queryContext: QueryContext): Promise<any> | void;
|
|
1682
1679
|
$beforeUpdate(opt: ModelOptions, queryContext: QueryContext): Promise<any> | void;
|
|
1683
|
-
$afterGet(queryContext: QueryContext): Promise<any> | void;
|
|
1684
1680
|
$afterFind(queryContext: QueryContext): Promise<any> | void;
|
|
1685
1681
|
$beforeDelete(queryContext: QueryContext): Promise<any> | void;
|
|
1686
1682
|
$afterDelete(queryContext: QueryContext): Promise<any> | void;
|