@abedmurrar/objection 0.0.0-stage → 4.0.0-alpha.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 +69 -2
- package/lib/.eslintrc.json +8 -0
- package/lib/initialize.js +14 -0
- package/lib/model/AjvValidator.js +325 -0
- package/lib/model/Model.js +942 -0
- package/lib/model/ModifierNotFoundError.js +12 -0
- package/lib/model/NotFoundError.js +23 -0
- package/lib/model/RelationDoesNotExistError.js +14 -0
- package/lib/model/ValidationError.js +54 -0
- package/lib/model/Validator.js +26 -0
- package/lib/model/getModel.js +9 -0
- package/lib/model/graph/ModelGraph.js +80 -0
- package/lib/model/graph/ModelGraphBuilder.js +241 -0
- package/lib/model/graph/ModelGraphEdge.js +52 -0
- package/lib/model/graph/ModelGraphNode.js +240 -0
- package/lib/model/inheritModel.js +28 -0
- package/lib/model/modelBindKnex.js +83 -0
- package/lib/model/modelClone.js +121 -0
- package/lib/model/modelColPropMap.js +34 -0
- package/lib/model/modelId.js +62 -0
- package/lib/model/modelJsonAttributes.js +92 -0
- package/lib/model/modelParseRelations.js +82 -0
- package/lib/model/modelQueryProps.js +147 -0
- package/lib/model/modelSet.js +136 -0
- package/lib/model/modelTableMetadata.js +83 -0
- package/lib/model/modelToJson.js +187 -0
- package/lib/model/modelUtils.js +47 -0
- package/lib/model/modelValidate.js +48 -0
- package/lib/model/modelValues.js +126 -0
- package/lib/model/modelVisitor.js +41 -0
- package/lib/objection.js +114 -0
- package/lib/queryBuilder/FunctionBuilder.js +339 -0
- package/lib/queryBuilder/InternalOptions.js +29 -0
- package/lib/queryBuilder/JoinBuilder.js +157 -0
- package/lib/queryBuilder/QueryBuilder.js +1398 -0
- package/lib/queryBuilder/QueryBuilderBase.js +884 -0
- package/lib/queryBuilder/QueryBuilderContext.js +27 -0
- package/lib/queryBuilder/QueryBuilderContextBase.js +35 -0
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +558 -0
- package/lib/queryBuilder/QueryBuilderUserContext.js +31 -0
- package/lib/queryBuilder/RawBuilder.js +93 -0
- package/lib/queryBuilder/ReferenceBuilder.js +284 -0
- package/lib/queryBuilder/RelationExpression.js +431 -0
- package/lib/queryBuilder/StaticHookArguments.js +134 -0
- package/lib/queryBuilder/ValueBuilder.js +127 -0
- package/lib/queryBuilder/expressions.js +151 -0
- package/lib/queryBuilder/functions/mysql.js +817 -0
- package/lib/queryBuilder/graph/GraphAction.js +69 -0
- package/lib/queryBuilder/graph/GraphData.js +14 -0
- package/lib/queryBuilder/graph/GraphFetcher.js +191 -0
- package/lib/queryBuilder/graph/GraphNodeDbExistence.js +184 -0
- package/lib/queryBuilder/graph/GraphOperation.js +27 -0
- package/lib/queryBuilder/graph/GraphOptions.js +207 -0
- package/lib/queryBuilder/graph/GraphUpsert.js +305 -0
- package/lib/queryBuilder/graph/delete/GraphDelete.js +20 -0
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +74 -0
- package/lib/queryBuilder/graph/insert/GraphInsert.js +145 -0
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +225 -0
- package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +92 -0
- package/lib/queryBuilder/graph/patch/GraphPatch.js +20 -0
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +258 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +31 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +126 -0
- package/lib/queryBuilder/join/JoinResultColumn.js +23 -0
- package/lib/queryBuilder/join/JoinResultParser.js +119 -0
- package/lib/queryBuilder/join/RelationJoiner.js +341 -0
- package/lib/queryBuilder/join/TableNode.js +163 -0
- package/lib/queryBuilder/join/TableTree.js +85 -0
- package/lib/queryBuilder/join/utils.js +25 -0
- package/lib/queryBuilder/operations/DelegateOperation.js +141 -0
- package/lib/queryBuilder/operations/DeleteOperation.js +51 -0
- package/lib/queryBuilder/operations/FindByIdOperation.js +37 -0
- package/lib/queryBuilder/operations/FindByIdsOperation.js +29 -0
- package/lib/queryBuilder/operations/FindOperation.js +164 -0
- package/lib/queryBuilder/operations/FirstOperation.js +27 -0
- package/lib/queryBuilder/operations/FromOperation.js +110 -0
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +49 -0
- package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +45 -0
- package/lib/queryBuilder/operations/InsertGraphOperation.js +108 -0
- package/lib/queryBuilder/operations/InsertOperation.js +206 -0
- package/lib/queryBuilder/operations/InstanceDeleteOperation.js +46 -0
- package/lib/queryBuilder/operations/InstanceFindOperation.js +21 -0
- package/lib/queryBuilder/operations/InstanceInsertOperation.js +24 -0
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +59 -0
- package/lib/queryBuilder/operations/JoinRelatedOperation.js +103 -0
- package/lib/queryBuilder/operations/KnexOperation.js +14 -0
- package/lib/queryBuilder/operations/MergeOperation.js +56 -0
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +192 -0
- package/lib/queryBuilder/operations/OnBuildKnexOperation.js +29 -0
- package/lib/queryBuilder/operations/OnBuildOperation.js +29 -0
- package/lib/queryBuilder/operations/OnErrorOperation.js +29 -0
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +297 -0
- package/lib/queryBuilder/operations/RangeOperation.js +47 -0
- package/lib/queryBuilder/operations/RelateOperation.js +27 -0
- package/lib/queryBuilder/operations/ReturningOperation.js +30 -0
- package/lib/queryBuilder/operations/RunAfterOperation.js +29 -0
- package/lib/queryBuilder/operations/RunBeforeOperation.js +29 -0
- package/lib/queryBuilder/operations/UnrelateOperation.js +23 -0
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +80 -0
- package/lib/queryBuilder/operations/UpdateOperation.js +155 -0
- package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +45 -0
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +66 -0
- package/lib/queryBuilder/operations/WhereCompositeOperation.js +57 -0
- package/lib/queryBuilder/operations/WhereInOperation.js +48 -0
- package/lib/queryBuilder/operations/eager/EagerOperation.js +61 -0
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +51 -0
- package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +13 -0
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +285 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +36 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +53 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +31 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +31 -0
- package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +167 -0
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +164 -0
- package/lib/queryBuilder/operations/select/SelectOperation.js +66 -0
- package/lib/queryBuilder/operations/select/Selection.js +123 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +117 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +100 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +104 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +886 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +97 -0
- package/lib/queryBuilder/parsers/relationExpressionParser.js +1042 -0
- package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +141 -0
- package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +22 -0
- package/lib/queryBuilder/transformations/QueryTransformation.js +11 -0
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +71 -0
- package/lib/queryBuilder/transformations/index.js +15 -0
- package/lib/queryBuilder/whereScopes.js +227 -0
- package/lib/relations/Relation.js +401 -0
- package/lib/relations/RelationDeleteOperation.js +29 -0
- package/lib/relations/RelationFindOperation.js +149 -0
- package/lib/relations/RelationInsertOperation.js +33 -0
- package/lib/relations/RelationOwner.js +236 -0
- package/lib/relations/RelationProperty.js +247 -0
- package/lib/relations/RelationUpdateOperation.js +30 -0
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +23 -0
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +44 -0
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +50 -0
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +53 -0
- package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +61 -0
- package/lib/relations/hasMany/HasManyInsertOperation.js +61 -0
- package/lib/relations/hasMany/HasManyRelateOperation.js +64 -0
- package/lib/relations/hasMany/HasManyRelation.js +49 -0
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +33 -0
- package/lib/relations/hasOne/HasOneRelation.js +13 -0
- package/lib/relations/hasOneThrough/HasOneThroughRelation.js +13 -0
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +105 -0
- package/lib/relations/manyToMany/ManyToManyRelation.js +507 -0
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +36 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +10 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +26 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +12 -0
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +105 -0
- package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +52 -0
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +81 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +14 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +34 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +16 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +10 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +82 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +12 -0
- package/lib/transaction.js +96 -0
- package/lib/utils/assert.js +25 -0
- package/lib/utils/buildUtils.js +21 -0
- package/lib/utils/classUtils.js +13 -0
- package/lib/utils/clone.js +2329 -0
- package/lib/utils/createModifier.js +42 -0
- package/lib/utils/deprecate.js +15 -0
- package/lib/utils/identifierMapping.js +227 -0
- package/lib/utils/internalPropUtils.js +11 -0
- package/lib/utils/knexUtils.js +76 -0
- package/lib/utils/mixin.js +25 -0
- package/lib/utils/mysqlJsonPath.js +55 -0
- package/lib/utils/normalizeIds.js +144 -0
- package/lib/utils/objectUtils.js +416 -0
- package/lib/utils/parseFieldExpression.js +51 -0
- package/lib/utils/postgresJsonPath.js +110 -0
- package/lib/utils/promiseUtils/after.js +17 -0
- package/lib/utils/promiseUtils/afterReturn.js +17 -0
- package/lib/utils/promiseUtils/index.js +17 -0
- package/lib/utils/promiseUtils/isPromise.js +11 -0
- package/lib/utils/promiseUtils/map.js +110 -0
- package/lib/utils/promiseUtils/mapAfterAllReturn.js +29 -0
- package/lib/utils/promiseUtils/try.js +22 -0
- package/lib/utils/resolveModel.js +107 -0
- package/lib/utils/tmpColumnUtils.js +16 -0
- package/package.json +86 -4
- package/typings/objection/index.d.ts +4104 -0
- package/typings/objection/relation-expression.d.ts +237 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Sami Koskimäki
|
|
4
|
+
Copyright (c) 2026 Abed Murrar
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
|
package/README.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://github.com/abedmurrar/objection.js/actions/workflows/test.yml)
|
|
2
|
+
[](https://www.npmjs.com/package/@abedmurrar/objection)
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
> **`@abedmurrar/objection`** is a fork of [objection.js](https://github.com/Vincit/objection.js) 3.1.5. It adds:
|
|
5
|
+
>
|
|
6
|
+
> - **Strict TypeScript types.** Query results contain exactly the relations that were loaded, relation expressions and modifier names are checked, and `Model.ref()` only accepts real columns.
|
|
7
|
+
> - **MySQL support** for JSON field expressions and the `whereJson*` methods, `fn.*` helpers for every MySQL 8.4 function, and batch inserts.
|
|
8
|
+
> - **Expressions without raw SQL**, like `Pet.ref('age').gte(3)` and `fn.count().filter(...)`, plus window functions.
|
|
9
|
+
> - **Fixes** for JSON key escaping (SQL injection), `orWhere` escaping relation conditions, and `for()`.
|
|
10
|
+
>
|
|
11
|
+
> See the [changelog](doc/release-notes/changelog.md) and the [migration guide](doc/release-notes/migration.md).
|
|
12
|
+
>
|
|
13
|
+
> ```sh
|
|
14
|
+
> npm install @abedmurrar/objection@next knex
|
|
15
|
+
> ```
|
|
16
|
+
>
|
|
17
|
+
> To keep `import { Model } from 'objection'` working in existing code, install it under the name `objection`:
|
|
18
|
+
>
|
|
19
|
+
> ```sh
|
|
20
|
+
> npm install objection@npm:@abedmurrar/objection@next
|
|
21
|
+
> ```
|
|
22
|
+
|
|
23
|
+
# [Objection.js](https://vincit.github.io/objection.js)
|
|
24
|
+
|
|
25
|
+
Objection.js is an [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Node.js](https://nodejs.org/) that aims to stay out of your way and make it as easy as possible to use the full power of SQL and the underlying database engine while still making the common stuff easy and enjoyable.
|
|
26
|
+
|
|
27
|
+
Even though ORM is the best commonly known acronym to describe objection, a more accurate description is to call it **a relational query builder**. You get all the benefits of an SQL query builder but also a powerful set of tools for working with relations.
|
|
28
|
+
|
|
29
|
+
Objection.js is built on an SQL query builder called [knex](http://knexjs.org). All databases supported by knex are supported by objection.js. **SQLite3**, **Postgres** and **MySQL** are [thoroughly tested](https://github.com/Vincit/objection.js/actions).
|
|
30
|
+
|
|
31
|
+
What objection.js gives you:
|
|
32
|
+
|
|
33
|
+
- **An easy declarative way of [defining models](https://vincit.github.io/objection.js/guide/models.html) and relationships between them**
|
|
34
|
+
- **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**
|
|
35
|
+
- **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**
|
|
36
|
+
- **Easy to use [transactions](https://vincit.github.io/objection.js/guide/transactions.html)**
|
|
37
|
+
- **Official [TypeScript](https://github.com/Vincit/objection.js/blob/main/typings/objection/index.d.ts) support**
|
|
38
|
+
- **Optional [JSON schema](https://vincit.github.io/objection.js/guide/validation.html) validation**
|
|
39
|
+
- **A way to [store complex documents](https://vincit.github.io/objection.js/guide/documents.html) as single rows**
|
|
40
|
+
|
|
41
|
+
What objection.js **doesn't** give you:
|
|
42
|
+
|
|
43
|
+
- **A fully object oriented view of your database**
|
|
44
|
+
With objection you don't work with entities. You work with queries. Objection doesn't try to wrap every concept with an
|
|
45
|
+
object oriented equivalent. The best attempt to do that (IMO) is Hibernate, which is excellent, but it has 800k lines
|
|
46
|
+
of code and a lot more concepts to learn than SQL itself. The point is, writing a good traditional ORM is borderline
|
|
47
|
+
impossible. Objection attempts to provide a completely different way of working with SQL.
|
|
48
|
+
- **A custom query DSL. SQL is used as a query language.**
|
|
49
|
+
This doesn't mean you have to write SQL strings though. A query builder based on [knex](http://knexjs.org) is
|
|
50
|
+
used to build the SQL. However, if the query builder fails you for some reason, raw SQL strings can be easily
|
|
51
|
+
written using the [raw](https://vincit.github.io/objection.js/api/objection/#raw) helper function.
|
|
52
|
+
- **Automatic database schema creation and migration from model definitions.**
|
|
53
|
+
For simple things it is useful that the database schema is automatically generated from the model definitions,
|
|
54
|
+
but usually just gets in your way when doing anything non-trivial. Objection.js leaves the schema related things
|
|
55
|
+
to you. knex has a great [migration tool](https://knexjs.org/guide/migrations.html) that we recommend for this job. Check
|
|
56
|
+
out the [example project](https://github.com/Vincit/objection.js/tree/main/examples/koa-ts).
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
|
|
60
|
+
Check out [this issue](https://github.com/Vincit/objection.js/issues/1069) to see who is using objection and what they think about it.
|
|
61
|
+
|
|
62
|
+
Shortcuts:
|
|
63
|
+
|
|
64
|
+
- [Who uses objection.js](https://github.com/Vincit/objection.js/discussions/2464)
|
|
65
|
+
- [API reference](https://vincit.github.io/objection.js/api/query-builder/)
|
|
66
|
+
- [Example projects](https://github.com/Vincit/objection.js/tree/main/examples)
|
|
67
|
+
- [Changelog](https://vincit.github.io/objection.js/release-notes/changelog.html)
|
|
68
|
+
- [v1 -> v2 -> v3 migration guide](https://vincit.github.io/objection.js/release-notes/migration.html)
|
|
69
|
+
- [Contribution guide](https://vincit.github.io/objection.js/guide/contributing.html)
|
|
70
|
+
- [Plugins](https://vincit.github.io/objection.js/guide/plugins.html)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function initialize(knex, modelClasses) {
|
|
4
|
+
if (!modelClasses) {
|
|
5
|
+
modelClasses = knex;
|
|
6
|
+
knex = modelClasses[0].knex();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
await Promise.all(modelClasses.map((modelClass) => modelClass.fetchTableMetadata({ knex })));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
initialize,
|
|
14
|
+
};
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Ajv = require('ajv');
|
|
4
|
+
const addFormats = require('ajv-formats');
|
|
5
|
+
const { Validator } = require('./Validator');
|
|
6
|
+
const { ValidationErrorType } = require('../model/ValidationError');
|
|
7
|
+
const { isObject, once, cloneDeep: lodashCloneDeep, omit } = require('../utils/objectUtils');
|
|
8
|
+
|
|
9
|
+
class AjvValidator extends Validator {
|
|
10
|
+
static init(self, conf) {
|
|
11
|
+
super.init(self, conf);
|
|
12
|
+
|
|
13
|
+
self.ajvOptions = Object.assign({}, conf.options, {
|
|
14
|
+
allErrors: true,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Create a normal Ajv instance.
|
|
18
|
+
self.ajv = new Ajv(
|
|
19
|
+
Object.assign(
|
|
20
|
+
{
|
|
21
|
+
useDefaults: true,
|
|
22
|
+
},
|
|
23
|
+
self.ajvOptions,
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Create an instance that doesn't set default values. We need this one
|
|
28
|
+
// to validate `patch` objects (objects that have a subset of properties).
|
|
29
|
+
self.ajvNoDefaults = new Ajv(
|
|
30
|
+
Object.assign({}, self.ajvOptions, {
|
|
31
|
+
useDefaults: false,
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// A cache for the compiled validator functions.
|
|
36
|
+
self.cache = new Map();
|
|
37
|
+
|
|
38
|
+
const setupAjv = (ajv) => {
|
|
39
|
+
if (conf.onCreateAjv) {
|
|
40
|
+
conf.onCreateAjv(ajv);
|
|
41
|
+
}
|
|
42
|
+
// Only add Ajv formats if they weren't added in user-space already
|
|
43
|
+
if (!ajv.formats['date-time']) {
|
|
44
|
+
addFormats(ajv);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
setupAjv(self.ajv);
|
|
49
|
+
setupAjv(self.ajvNoDefaults);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
beforeValidate({ json, model, options, ctx }) {
|
|
53
|
+
ctx.jsonSchema = model.constructor.getJsonSchema();
|
|
54
|
+
|
|
55
|
+
// Objection model's have a `$beforeValidate` hook that is allowed to modify the schema.
|
|
56
|
+
// We need to clone the schema in case the function modifies it. We only do this in the
|
|
57
|
+
// rare case that the given model has implemented the hook.
|
|
58
|
+
if (model.$beforeValidate !== model.$objectionModelClass.prototype.$beforeValidate) {
|
|
59
|
+
ctx.jsonSchema = cloneDeep(ctx.jsonSchema);
|
|
60
|
+
const ret = model.$beforeValidate(ctx.jsonSchema, json, options);
|
|
61
|
+
|
|
62
|
+
if (ret !== undefined) {
|
|
63
|
+
ctx.jsonSchema = ret;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
validate({ json, model, options, ctx }) {
|
|
69
|
+
if (!ctx.jsonSchema) {
|
|
70
|
+
return json;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const modelClass = model.constructor;
|
|
74
|
+
const validator = this.getValidator(modelClass, ctx.jsonSchema, !!options.patch);
|
|
75
|
+
|
|
76
|
+
// We need to clone the input json if we are about to set default values.
|
|
77
|
+
if (!options.mutable && !options.patch && setsDefaultValues(ctx.jsonSchema)) {
|
|
78
|
+
json = cloneDeep(json);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
validator.call(model, json);
|
|
82
|
+
const error = parseValidationError(validator.errors, modelClass, options, this.ajvOptions);
|
|
83
|
+
|
|
84
|
+
if (error) {
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return json;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getValidator(modelClass, jsonSchema, isPatchObject) {
|
|
92
|
+
// Use the Ajv custom serializer if provided.
|
|
93
|
+
const createCacheKey = this.ajvOptions.serialize || JSON.stringify;
|
|
94
|
+
|
|
95
|
+
// Optimization for the common case where jsonSchema is never modified.
|
|
96
|
+
// In that case we don't need to call the costly createCacheKey function.
|
|
97
|
+
const cacheKey =
|
|
98
|
+
jsonSchema === modelClass.getJsonSchema()
|
|
99
|
+
? modelClass.uniqueTag()
|
|
100
|
+
: createCacheKey(jsonSchema);
|
|
101
|
+
|
|
102
|
+
let validators = this.cache.get(cacheKey);
|
|
103
|
+
let validator = null;
|
|
104
|
+
|
|
105
|
+
if (!validators) {
|
|
106
|
+
validators = {
|
|
107
|
+
// Validator created for the schema object without `required` properties
|
|
108
|
+
// using the Ajv instance that doesn't set default values.
|
|
109
|
+
patchValidator: null,
|
|
110
|
+
|
|
111
|
+
// Validator created for the unmodified schema.
|
|
112
|
+
normalValidator: null,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
this.cache.set(cacheKey, validators);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (isPatchObject) {
|
|
119
|
+
validator = validators.patchValidator;
|
|
120
|
+
|
|
121
|
+
if (!validator) {
|
|
122
|
+
validator = this.compilePatchValidator(jsonSchema);
|
|
123
|
+
validators.patchValidator = validator;
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
validator = validators.normalValidator;
|
|
127
|
+
|
|
128
|
+
if (!validator) {
|
|
129
|
+
validator = this.compileNormalValidator(jsonSchema);
|
|
130
|
+
validators.normalValidator = validator;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return validator;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
compilePatchValidator(jsonSchema) {
|
|
138
|
+
jsonSchema = jsonSchemaWithoutRequired(jsonSchema);
|
|
139
|
+
// We need to use the ajv instance that doesn't set the default values.
|
|
140
|
+
return this.ajvNoDefaults.compile(jsonSchema);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
compileNormalValidator(jsonSchema) {
|
|
144
|
+
return this.ajv.compile(jsonSchema);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function parseValidationError(errors, modelClass, options, ajvOptions) {
|
|
149
|
+
if (!errors) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let relationNames = modelClass.getRelationNames();
|
|
154
|
+
let errorHash = {};
|
|
155
|
+
let numErrors = 0;
|
|
156
|
+
|
|
157
|
+
for (const error of errors) {
|
|
158
|
+
// If additionalProperties = false, relations can pop up as additionalProperty
|
|
159
|
+
// errors. Skip those.
|
|
160
|
+
if (
|
|
161
|
+
error.params &&
|
|
162
|
+
error.params.additionalProperty &&
|
|
163
|
+
relationNames.includes(error.params.additionalProperty)
|
|
164
|
+
) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let path = error.instancePath.replace(/\//g, '.');
|
|
169
|
+
|
|
170
|
+
if (error.params) {
|
|
171
|
+
if (error.params.missingProperty) {
|
|
172
|
+
path += `.${error.params.missingProperty}`;
|
|
173
|
+
} else if (error.params.additionalProperty) {
|
|
174
|
+
path += `.${error.params.additionalProperty}`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const key = `${options.dataPath || ''}${path}`.substring(1);
|
|
179
|
+
|
|
180
|
+
// More than one error can occur for the same key in Ajv, merge them in the array:
|
|
181
|
+
const array = errorHash[key] || (errorHash[key] = []);
|
|
182
|
+
|
|
183
|
+
// Prepare error object
|
|
184
|
+
const errorObj = {
|
|
185
|
+
message: error.message,
|
|
186
|
+
keyword: error.keyword,
|
|
187
|
+
params: error.params,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Add data if verbose enabled
|
|
191
|
+
if (ajvOptions.verbose) {
|
|
192
|
+
errorObj.data = error.data;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Use unshift instead of push so that the last error ends up at [0],
|
|
196
|
+
// preserving previous behavior where only the last error was stored.
|
|
197
|
+
array.unshift(errorObj);
|
|
198
|
+
|
|
199
|
+
++numErrors;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (numErrors === 0) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return modelClass.createValidationError({
|
|
207
|
+
type: ValidationErrorType.ModelValidation,
|
|
208
|
+
data: errorHash,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function cloneDeep(obj) {
|
|
213
|
+
if (isObject(obj) && obj.$isObjectionModel) {
|
|
214
|
+
return obj.$clone();
|
|
215
|
+
} else {
|
|
216
|
+
return lodashCloneDeep(obj);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function setsDefaultValues(jsonSchema) {
|
|
221
|
+
return jsonSchema && jsonSchema.properties && hasDefaults(jsonSchema.properties);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function hasDefaults(obj) {
|
|
225
|
+
if (Array.isArray(obj)) {
|
|
226
|
+
return arrayHasDefaults(obj);
|
|
227
|
+
} else {
|
|
228
|
+
return objectHasDefaults(obj);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function arrayHasDefaults(arr) {
|
|
233
|
+
for (let i = 0, l = arr.length; i < l; ++i) {
|
|
234
|
+
const val = arr[i];
|
|
235
|
+
|
|
236
|
+
if (isObject(val) && hasDefaults(val)) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function objectHasDefaults(obj) {
|
|
245
|
+
const keys = Object.keys(obj);
|
|
246
|
+
|
|
247
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
248
|
+
const key = keys[i];
|
|
249
|
+
|
|
250
|
+
if (key === 'default') {
|
|
251
|
+
return true;
|
|
252
|
+
} else {
|
|
253
|
+
const val = obj[key];
|
|
254
|
+
|
|
255
|
+
if (isObject(val) && hasDefaults(val)) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function jsonSchemaWithoutRequired(jsonSchema) {
|
|
265
|
+
const subSchemaProps = ['anyOf', 'oneOf', 'allOf', 'not', 'then', 'else', 'properties'];
|
|
266
|
+
return Object.assign(
|
|
267
|
+
omit(jsonSchema, ['required', ...subSchemaProps]),
|
|
268
|
+
...subSchemaProps.map((prop) => subSchemaWithoutRequired(jsonSchema, prop)),
|
|
269
|
+
jsonSchema && jsonSchema.definitions && Object.keys(jsonSchema.definitions).length > 0
|
|
270
|
+
? {
|
|
271
|
+
definitions: Object.assign(
|
|
272
|
+
...Object.keys(jsonSchema.definitions).map((prop) => ({
|
|
273
|
+
[prop]: jsonSchemaWithoutRequired(jsonSchema.definitions[prop]),
|
|
274
|
+
})),
|
|
275
|
+
),
|
|
276
|
+
}
|
|
277
|
+
: {},
|
|
278
|
+
jsonSchema.discriminator && jsonSchema.discriminator.propertyName
|
|
279
|
+
? { required: [jsonSchema.discriminator.propertyName] }
|
|
280
|
+
: {},
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function subSchemaWithoutRequired(jsonSchema, prop) {
|
|
285
|
+
if (jsonSchema[prop]) {
|
|
286
|
+
if (Array.isArray(jsonSchema[prop])) {
|
|
287
|
+
const schemaArray = jsonSchemaArrayWithoutRequired(jsonSchema[prop]);
|
|
288
|
+
|
|
289
|
+
if (schemaArray.length !== 0) {
|
|
290
|
+
return {
|
|
291
|
+
[prop]: schemaArray,
|
|
292
|
+
};
|
|
293
|
+
} else {
|
|
294
|
+
return {};
|
|
295
|
+
}
|
|
296
|
+
} else if (jsonSchema.type === 'object' && prop === 'properties') {
|
|
297
|
+
return {
|
|
298
|
+
[prop]: Object.fromEntries(
|
|
299
|
+
Object.entries(jsonSchema[prop]).map(([key, schema]) => [
|
|
300
|
+
key,
|
|
301
|
+
jsonSchemaWithoutRequired(schema),
|
|
302
|
+
]),
|
|
303
|
+
),
|
|
304
|
+
};
|
|
305
|
+
} else {
|
|
306
|
+
return {
|
|
307
|
+
[prop]: jsonSchemaWithoutRequired(jsonSchema[prop]),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
return {};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function jsonSchemaArrayWithoutRequired(jsonSchemaArray) {
|
|
316
|
+
return jsonSchemaArray.map(jsonSchemaWithoutRequired).filter(isNotEmptyObject);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function isNotEmptyObject(obj) {
|
|
320
|
+
return Object.keys(obj).length !== 0;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
module.exports = {
|
|
324
|
+
AjvValidator,
|
|
325
|
+
};
|