objection 3.0.5 → 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 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/master/typings/objection/index.d.ts) support**
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/master/examples/koa-ts).
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/master/examples/koa) and start playing with it. There's also a [typescript version](https://github.com/Vincit/objection.js/tree/master/examples/koa-ts) available.
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/master/examples)
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)
@@ -172,11 +172,6 @@ class Model {
172
172
  // Do nothing by default.
173
173
  }
174
174
 
175
- // TODO: Deprecate & remove
176
- $afterGet(queryContext) {
177
- // Do nothing by default.
178
- }
179
-
180
175
  $afterFind(queryContext) {
181
176
  // Do nothing by default.
182
177
  }
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ // A small helper method for cached lazy-importing of the Model class.
4
+ let Model;
5
+ const getModel = () => Model || (Model = require('./Model').Model);
6
+
7
+ module.exports = {
8
+ getModel,
9
+ };
@@ -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;
@@ -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 { changedProps, unchangedProps } = this._findChanges(node);
34
- const allProps = union(changedProps, unchangedProps);
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
- const propsToUpdate = difference(
37
- shouldPatch || shouldUpdate
38
- ? changedProps
39
- : [...changedPropsBecauseOfBelongsToOneInsert, ...changePropsBecauseOfBelongsToOneDelete],
55
+ return update;
56
+ };
40
57
 
41
- // Remove id properties from the props to update. With upsertGraph
42
- // it never makes sense to change the id.
43
- node.modelClass.getIdPropertyArray()
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 (propsToUpdate.length === 0) {
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
  }
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const getModel = () => require('../../model/Model').Model;
3
+ const { getModel } = require('../../model/getModel');
4
4
  const { Relation } = require('../Relation');
5
5
  const { RelationProperty } = require('../RelationProperty');
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "objection",
3
- "version": "3.0.5",
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": ">=12.0.0"
36
+ "node": ">=14.0.0"
37
37
  },
38
38
  "keywords": [
39
39
  "orm",
@@ -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/master/typings/objection/index.d.ts>
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';
@@ -1677,7 +1677,6 @@ declare namespace Objection {
1677
1677
  $afterInsert(queryContext: QueryContext): Promise<any> | void;
1678
1678
  $afterUpdate(opt: ModelOptions, queryContext: QueryContext): Promise<any> | void;
1679
1679
  $beforeUpdate(opt: ModelOptions, queryContext: QueryContext): Promise<any> | void;
1680
- $afterGet(queryContext: QueryContext): Promise<any> | void;
1681
1680
  $afterFind(queryContext: QueryContext): Promise<any> | void;
1682
1681
  $beforeDelete(queryContext: QueryContext): Promise<any> | void;
1683
1682
  $afterDelete(queryContext: QueryContext): Promise<any> | void;