@e22m4u/js-repository 0.0.31
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/.c8rc +9 -0
- package/.commitlintrc +5 -0
- package/.editorconfig +13 -0
- package/.eslintignore +1 -0
- package/.eslintrc.cjs +27 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +9 -0
- package/.mocharc.cjs +7 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +523 -0
- package/mocha.setup.js +10 -0
- package/package.json +57 -0
- package/src/adapter/adapter-loader.d.ts +16 -0
- package/src/adapter/adapter-loader.js +63 -0
- package/src/adapter/adapter-loader.spec.js +31 -0
- package/src/adapter/adapter-registry.d.ts +14 -0
- package/src/adapter/adapter-registry.js +36 -0
- package/src/adapter/adapter-registry.spec.js +36 -0
- package/src/adapter/adapter.d.ts +118 -0
- package/src/adapter/adapter.js +181 -0
- package/src/adapter/adapter.spec.js +144 -0
- package/src/adapter/builtin/memory-adapter.d.ts +118 -0
- package/src/adapter/builtin/memory-adapter.js +342 -0
- package/src/adapter/builtin/memory-adapter.spec.js +2925 -0
- package/src/adapter/decorator/data-sanitizing-decorator.d.ts +13 -0
- package/src/adapter/decorator/data-sanitizing-decorator.js +44 -0
- package/src/adapter/decorator/data-sanitizing-decorator.spec.js +59 -0
- package/src/adapter/decorator/data-validation-decorator.d.ts +13 -0
- package/src/adapter/decorator/data-validation-decorator.js +41 -0
- package/src/adapter/decorator/data-validation-decorator.spec.js +59 -0
- package/src/adapter/decorator/default-values-decorator.d.ts +13 -0
- package/src/adapter/decorator/default-values-decorator.js +57 -0
- package/src/adapter/decorator/default-values-decorator.spec.js +141 -0
- package/src/adapter/decorator/fields-filtering-decorator.d.ts +13 -0
- package/src/adapter/decorator/fields-filtering-decorator.js +72 -0
- package/src/adapter/decorator/fields-filtering-decorator.spec.js +119 -0
- package/src/adapter/decorator/inclusion-decorator.d.ts +13 -0
- package/src/adapter/decorator/inclusion-decorator.js +78 -0
- package/src/adapter/decorator/inclusion-decorator.spec.js +117 -0
- package/src/adapter/decorator/index.d.ts +5 -0
- package/src/adapter/decorator/index.js +5 -0
- package/src/adapter/index.d.ts +3 -0
- package/src/adapter/index.js +3 -0
- package/src/definition/datasource/datasource-definition-validator.d.ts +14 -0
- package/src/definition/datasource/datasource-definition-validator.js +33 -0
- package/src/definition/datasource/datasource-definition-validator.spec.js +63 -0
- package/src/definition/datasource/datasource-definition.d.ts +7 -0
- package/src/definition/datasource/index.d.ts +2 -0
- package/src/definition/datasource/index.js +1 -0
- package/src/definition/definition-registry.d.ts +50 -0
- package/src/definition/definition-registry.js +98 -0
- package/src/definition/definition-registry.spec.js +78 -0
- package/src/definition/index.d.ts +3 -0
- package/src/definition/index.js +3 -0
- package/src/definition/model/index.d.ts +7 -0
- package/src/definition/model/index.js +6 -0
- package/src/definition/model/model-data-sanitizer.d.ts +15 -0
- package/src/definition/model/model-data-sanitizer.js +33 -0
- package/src/definition/model/model-data-validator.d.ts +32 -0
- package/src/definition/model/model-data-validator.js +144 -0
- package/src/definition/model/model-data-validator.spec.js +1889 -0
- package/src/definition/model/model-definition-utils.d.ts +161 -0
- package/src/definition/model/model-definition-utils.js +371 -0
- package/src/definition/model/model-definition-utils.spec.js +1474 -0
- package/src/definition/model/model-definition-validator.d.ts +14 -0
- package/src/definition/model/model-definition-validator.js +83 -0
- package/src/definition/model/model-definition-validator.spec.js +143 -0
- package/src/definition/model/model-definition.d.ts +28 -0
- package/src/definition/model/properties/data-type.d.ts +11 -0
- package/src/definition/model/properties/data-type.js +11 -0
- package/src/definition/model/properties/default-values-definition-validator.d.ts +15 -0
- package/src/definition/model/properties/default-values-definition-validator.js +53 -0
- package/src/definition/model/properties/default-values-definition-validator.spec.js +136 -0
- package/src/definition/model/properties/index.d.ts +5 -0
- package/src/definition/model/properties/index.js +4 -0
- package/src/definition/model/properties/primary-keys-definition-validator.d.ts +15 -0
- package/src/definition/model/properties/primary-keys-definition-validator.js +55 -0
- package/src/definition/model/properties/primary-keys-definition-validator.spec.js +145 -0
- package/src/definition/model/properties/properties-definition-validator.d.ts +15 -0
- package/src/definition/model/properties/properties-definition-validator.js +194 -0
- package/src/definition/model/properties/properties-definition-validator.spec.js +373 -0
- package/src/definition/model/properties/property-definition.d.ts +20 -0
- package/src/definition/model/relations/index.d.ts +3 -0
- package/src/definition/model/relations/index.js +2 -0
- package/src/definition/model/relations/relation-definition.d.ts +254 -0
- package/src/definition/model/relations/relation-type.d.ts +9 -0
- package/src/definition/model/relations/relation-type.js +9 -0
- package/src/definition/model/relations/relations-definition-validator.d.ts +15 -0
- package/src/definition/model/relations/relations-definition-validator.js +449 -0
- package/src/definition/model/relations/relations-definition-validator.spec.js +772 -0
- package/src/errors/index.d.ts +3 -0
- package/src/errors/index.js +3 -0
- package/src/errors/invalid-argument-error.d.ts +6 -0
- package/src/errors/invalid-argument-error.js +6 -0
- package/src/errors/invalid-argument-error.spec.js +33 -0
- package/src/errors/invalid-operator-value-error.d.ts +13 -0
- package/src/errors/invalid-operator-value-error.js +24 -0
- package/src/errors/invalid-operator-value-error.spec.js +11 -0
- package/src/errors/not-implemented-error.d.ts +6 -0
- package/src/errors/not-implemented-error.js +6 -0
- package/src/errors/not-implemented-error.spec.js +33 -0
- package/src/filter/fields-clause-tool.d.ts +38 -0
- package/src/filter/fields-clause-tool.js +88 -0
- package/src/filter/fields-clause-tool.spec.js +133 -0
- package/src/filter/filter.d.ts +335 -0
- package/src/filter/include-clause-tool.d.ts +53 -0
- package/src/filter/include-clause-tool.js +364 -0
- package/src/filter/include-clause-tool.spec.js +653 -0
- package/src/filter/index.d.ts +7 -0
- package/src/filter/index.js +6 -0
- package/src/filter/operator-clause-tool.d.ts +223 -0
- package/src/filter/operator-clause-tool.js +515 -0
- package/src/filter/operator-clause-tool.spec.js +1064 -0
- package/src/filter/order-clause-tool.d.ts +32 -0
- package/src/filter/order-clause-tool.js +97 -0
- package/src/filter/order-clause-tool.spec.js +438 -0
- package/src/filter/slice-clause-tool.d.ts +30 -0
- package/src/filter/slice-clause-tool.js +65 -0
- package/src/filter/slice-clause-tool.spec.js +117 -0
- package/src/filter/where-clause-tool.d.ts +23 -0
- package/src/filter/where-clause-tool.js +165 -0
- package/src/filter/where-clause-tool.spec.js +280 -0
- package/src/index.d.ts +9 -0
- package/src/index.js +8 -0
- package/src/relations/belongs-to-resolver.d.ts +46 -0
- package/src/relations/belongs-to-resolver.js +242 -0
- package/src/relations/belongs-to-resolver.spec.js +1047 -0
- package/src/relations/has-many-resolver.d.ts +67 -0
- package/src/relations/has-many-resolver.js +317 -0
- package/src/relations/has-many-resolver.spec.js +2911 -0
- package/src/relations/has-one-resolver.d.ts +67 -0
- package/src/relations/has-one-resolver.js +311 -0
- package/src/relations/has-one-resolver.spec.js +2274 -0
- package/src/relations/index.d.ts +4 -0
- package/src/relations/index.js +4 -0
- package/src/relations/references-many-resolver.d.ts +27 -0
- package/src/relations/references-many-resolver.js +113 -0
- package/src/relations/references-many-resolver.spec.js +631 -0
- package/src/repository/index.d.ts +2 -0
- package/src/repository/index.js +2 -0
- package/src/repository/repository-registry.d.ts +29 -0
- package/src/repository/repository-registry.js +57 -0
- package/src/repository/repository-registry.spec.js +38 -0
- package/src/repository/repository.d.ts +164 -0
- package/src/repository/repository.js +207 -0
- package/src/repository/repository.spec.js +202 -0
- package/src/schema.d.ts +37 -0
- package/src/schema.js +41 -0
- package/src/types.d.ts +30 -0
- package/src/utils/capitalize.d.ts +6 -0
- package/src/utils/capitalize.js +10 -0
- package/src/utils/capitalize.spec.js +14 -0
- package/src/utils/clone-deep.d.ts +6 -0
- package/src/utils/clone-deep.js +61 -0
- package/src/utils/clone-deep.spec.js +28 -0
- package/src/utils/exclude-object-keys.d.ts +10 -0
- package/src/utils/exclude-object-keys.js +20 -0
- package/src/utils/exclude-object-keys.spec.js +49 -0
- package/src/utils/get-ctor-name.d.ts +6 -0
- package/src/utils/get-ctor-name.js +11 -0
- package/src/utils/get-ctor-name.spec.js +17 -0
- package/src/utils/get-value-by-path.d.ts +12 -0
- package/src/utils/get-value-by-path.js +23 -0
- package/src/utils/get-value-by-path.spec.js +36 -0
- package/src/utils/index.d.ts +10 -0
- package/src/utils/index.js +10 -0
- package/src/utils/is-ctor.d.ts +7 -0
- package/src/utils/is-ctor.js +10 -0
- package/src/utils/is-ctor.spec.js +26 -0
- package/src/utils/is-pure-object.d.ts +6 -0
- package/src/utils/is-pure-object.js +15 -0
- package/src/utils/is-pure-object.spec.js +25 -0
- package/src/utils/select-object-keys.d.ts +10 -0
- package/src/utils/select-object-keys.js +37 -0
- package/src/utils/select-object-keys.spec.js +40 -0
- package/src/utils/singularize.d.ts +6 -0
- package/src/utils/singularize.js +22 -0
- package/src/utils/singularize.spec.js +23 -0
- package/src/utils/string-to-regexp.d.ts +10 -0
- package/src/utils/string-to-regexp.js +22 -0
- package/src/utils/string-to-regexp.spec.js +35 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {format} from '@e22m4u/js-format';
|
|
3
|
+
import {SliceClauseTool} from './slice-clause-tool.js';
|
|
4
|
+
|
|
5
|
+
const S = new SliceClauseTool();
|
|
6
|
+
|
|
7
|
+
describe('SliceClauseTool', function () {
|
|
8
|
+
describe('filter', function () {
|
|
9
|
+
it('does nothing if no clauses provided', function () {
|
|
10
|
+
const objects = [{id: 1}, {id: 2}, {id: 3}];
|
|
11
|
+
const result = S.slice(objects);
|
|
12
|
+
expect(result).to.be.eql(objects);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('does nothing if a given skip is zero', function () {
|
|
16
|
+
const objects = [{id: 1}, {id: 2}, {id: 3}];
|
|
17
|
+
const result = S.slice(objects, 0);
|
|
18
|
+
expect(result).to.be.eql(objects);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('uses a given skip to exclude array elements from start', function () {
|
|
22
|
+
const objects = [{id: 1}, {id: 2}, {id: 3}];
|
|
23
|
+
const result = S.slice(objects, 2);
|
|
24
|
+
expect(result).to.have.length(1);
|
|
25
|
+
expect(result[0]).to.be.eql(objects[2]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns an empty array if skipping too much', function () {
|
|
29
|
+
const objects = [{id: 1}, {id: 2}, {id: 3}];
|
|
30
|
+
const result = S.slice(objects, 10);
|
|
31
|
+
expect(result).to.have.length(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('does nothing if a given limit is zero', function () {
|
|
35
|
+
const objects = [{id: 1}, {id: 2}, {id: 3}];
|
|
36
|
+
const result = S.slice(objects, undefined, 0);
|
|
37
|
+
expect(result).to.be.eql(objects);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('uses a given limit to trim a given array', function () {
|
|
41
|
+
const objects = [{id: 1}, {id: 2}, {id: 3}];
|
|
42
|
+
const result = S.slice(objects, undefined, 2);
|
|
43
|
+
expect(result).to.have.length(2);
|
|
44
|
+
expect(result[0]).to.be.eql(objects[0]);
|
|
45
|
+
expect(result[1]).to.be.eql(objects[1]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('able to combine a skip and a limit option together', function () {
|
|
49
|
+
const objects = [{id: 1}, {id: 2}, {id: 3}];
|
|
50
|
+
const result = S.slice(objects, 1, 1);
|
|
51
|
+
expect(result).to.have.length(1);
|
|
52
|
+
expect(result[0]).to.be.eql(objects[1]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('throws an error if a first argument is not an array', function () {
|
|
56
|
+
const throwable = () => S.slice(10);
|
|
57
|
+
expect(throwable).to.throw(
|
|
58
|
+
'A first argument of SliceClauseTool.slice ' +
|
|
59
|
+
'should be an Array, but 10 given.',
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('throws an error if the given "skip" option is not a number', function () {
|
|
64
|
+
const throwable = () => S.slice([], 'invalid');
|
|
65
|
+
expect(throwable).to.throw(
|
|
66
|
+
'The provided option "skip" should be a Number, but "invalid" given.',
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('throws an error if the given "limit" option is not a number', function () {
|
|
71
|
+
const throwable = () => S.slice([], undefined, 'invalid');
|
|
72
|
+
expect(throwable).to.throw(
|
|
73
|
+
'The provided option "limit" should be a Number, but "invalid" given.',
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('validateSkipClause', function () {
|
|
79
|
+
it('requires a number value or a falsy value', function () {
|
|
80
|
+
const validate = v => () => SliceClauseTool.validateSkipClause(v);
|
|
81
|
+
const error = v =>
|
|
82
|
+
format(
|
|
83
|
+
'The provided option "skip" should be a Number, but %s given.',
|
|
84
|
+
v,
|
|
85
|
+
);
|
|
86
|
+
expect(validate('str')).to.throw(error('"str"'));
|
|
87
|
+
expect(validate(true)).to.throw(error('true'));
|
|
88
|
+
expect(validate([])).to.throw(error('Array'));
|
|
89
|
+
validate('')();
|
|
90
|
+
validate(false)();
|
|
91
|
+
validate(undefined)();
|
|
92
|
+
validate(null)();
|
|
93
|
+
validate(10)();
|
|
94
|
+
validate(0)();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('validateLimitClause', function () {
|
|
99
|
+
it('requires a number value or a falsy value', function () {
|
|
100
|
+
const validate = v => () => SliceClauseTool.validateLimitClause(v);
|
|
101
|
+
const error = v =>
|
|
102
|
+
format(
|
|
103
|
+
'The provided option "limit" should be a Number, but %s given.',
|
|
104
|
+
v,
|
|
105
|
+
);
|
|
106
|
+
expect(validate('str')).to.throw(error('"str"'));
|
|
107
|
+
expect(validate(true)).to.throw(error('true'));
|
|
108
|
+
expect(validate([])).to.throw(error('Array'));
|
|
109
|
+
validate('')();
|
|
110
|
+
validate(false)();
|
|
111
|
+
validate(undefined)();
|
|
112
|
+
validate(null)();
|
|
113
|
+
validate(10)();
|
|
114
|
+
validate(0)();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {ModelData} from '../types.js';
|
|
2
|
+
import {WhereClause} from './filter.js';
|
|
3
|
+
import {Service} from '@e22m4u/js-service';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Where clause tool.
|
|
7
|
+
*/
|
|
8
|
+
export declare class WhereClauseTool extends Service {
|
|
9
|
+
/**
|
|
10
|
+
* Filter.
|
|
11
|
+
*
|
|
12
|
+
* @param entities
|
|
13
|
+
* @param where
|
|
14
|
+
*/
|
|
15
|
+
filter(entities: ModelData[], where: WhereClause | undefined): ModelData[];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Validate where clause.
|
|
19
|
+
*
|
|
20
|
+
* @param clause
|
|
21
|
+
*/
|
|
22
|
+
static validateWhereClause(clause: WhereClause | undefined): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {Service} from '@e22m4u/js-service';
|
|
2
|
+
import {getValueByPath} from '../utils/index.js';
|
|
3
|
+
import {InvalidArgumentError} from '../errors/index.js';
|
|
4
|
+
import {OperatorClauseTool} from './operator-clause-tool.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Where clause tool.
|
|
8
|
+
*
|
|
9
|
+
* @typedef {object|Function} WhereClause
|
|
10
|
+
*/
|
|
11
|
+
export class WhereClauseTool extends Service {
|
|
12
|
+
/**
|
|
13
|
+
* Filter by where clause.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```
|
|
17
|
+
* const entities = [
|
|
18
|
+
* {foo: 1, bar: 'a'},
|
|
19
|
+
* {foo: 2, bar: 'b'},
|
|
20
|
+
* {foo: 3, bar: 'b'},
|
|
21
|
+
* {foo: 4, bar: 'b'},
|
|
22
|
+
* ];
|
|
23
|
+
*
|
|
24
|
+
* const result = filterByWhereClause(entities, {
|
|
25
|
+
* foo: {gt: 2},
|
|
26
|
+
* bar: 'b',
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* console.log(result);
|
|
30
|
+
* // [
|
|
31
|
+
* // {foo: 3, bar: 'b'},
|
|
32
|
+
* // {foo: 4, bar: 'b'},
|
|
33
|
+
* // ];
|
|
34
|
+
*
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param {object[]} entities
|
|
38
|
+
* @param {WhereClause|undefined} where
|
|
39
|
+
* @returns {object[]}
|
|
40
|
+
*/
|
|
41
|
+
filter(entities, where = undefined) {
|
|
42
|
+
if (!Array.isArray(entities))
|
|
43
|
+
throw new InvalidArgumentError(
|
|
44
|
+
'A first argument of WhereUtils.filter ' +
|
|
45
|
+
'should be an Array of Objects, but %v given.',
|
|
46
|
+
entities,
|
|
47
|
+
);
|
|
48
|
+
if (!where) return entities;
|
|
49
|
+
return entities.filter(this._createFilter(where));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Create where filter.
|
|
54
|
+
*
|
|
55
|
+
* @param {WhereClause} whereClause
|
|
56
|
+
* @returns {Function}
|
|
57
|
+
*/
|
|
58
|
+
_createFilter(whereClause) {
|
|
59
|
+
if (typeof whereClause === 'function') return whereClause;
|
|
60
|
+
if (typeof whereClause !== 'object')
|
|
61
|
+
throw new InvalidArgumentError(
|
|
62
|
+
'The provided option "where" should be an Object, but %v given.',
|
|
63
|
+
whereClause,
|
|
64
|
+
);
|
|
65
|
+
const keys = Object.keys(whereClause);
|
|
66
|
+
return data => {
|
|
67
|
+
if (typeof data !== 'object')
|
|
68
|
+
throw new InvalidArgumentError(
|
|
69
|
+
'A first argument of WhereUtils.filter ' +
|
|
70
|
+
'should be an Array of Objects, but %v given.',
|
|
71
|
+
data,
|
|
72
|
+
);
|
|
73
|
+
return keys.every(key => {
|
|
74
|
+
// AndClause (recursion)
|
|
75
|
+
if (key === 'and' && key in whereClause) {
|
|
76
|
+
const andClause = whereClause[key];
|
|
77
|
+
if (Array.isArray(andClause))
|
|
78
|
+
return andClause.every(clause => this._createFilter(clause)(data));
|
|
79
|
+
// OrClause (recursion)
|
|
80
|
+
} else if (key === 'or' && key in whereClause) {
|
|
81
|
+
const orClause = whereClause[key];
|
|
82
|
+
if (Array.isArray(orClause))
|
|
83
|
+
return orClause.some(clause => this._createFilter(clause)(data));
|
|
84
|
+
}
|
|
85
|
+
// PropertiesClause (properties)
|
|
86
|
+
const value = getValueByPath(data, key);
|
|
87
|
+
const matcher = whereClause[key];
|
|
88
|
+
// Property value is an array.
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
// {neq: ...}
|
|
91
|
+
if (
|
|
92
|
+
typeof matcher === 'object' &&
|
|
93
|
+
matcher !== null &&
|
|
94
|
+
'neq' in matcher &&
|
|
95
|
+
matcher.neq !== undefined
|
|
96
|
+
) {
|
|
97
|
+
// The following condition is for the case where
|
|
98
|
+
// we are querying with a neq filter, and when
|
|
99
|
+
// the value is an empty array ([]).
|
|
100
|
+
if (value.length === 0) return true;
|
|
101
|
+
// The neq operator requires each element
|
|
102
|
+
// of the array to be excluded.
|
|
103
|
+
return value.every((el, index) => {
|
|
104
|
+
const where = {};
|
|
105
|
+
where[index] = matcher;
|
|
106
|
+
return this._createFilter(where)({...value});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// Requires one of an array elements to be match.
|
|
110
|
+
return value.some((el, index) => {
|
|
111
|
+
const where = {};
|
|
112
|
+
where[index] = matcher;
|
|
113
|
+
return this._createFilter(where)({...value});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// Test property value.
|
|
117
|
+
if (this._test(matcher, value)) return true;
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Value testing.
|
|
124
|
+
*
|
|
125
|
+
* @param {*} example
|
|
126
|
+
* @param {*} value
|
|
127
|
+
* @returns {boolean}
|
|
128
|
+
*/
|
|
129
|
+
_test(example, value) {
|
|
130
|
+
// Test null.
|
|
131
|
+
if (example == null) {
|
|
132
|
+
return value == null;
|
|
133
|
+
}
|
|
134
|
+
// Test RegExp.
|
|
135
|
+
// noinspection ALL
|
|
136
|
+
if (example instanceof RegExp) {
|
|
137
|
+
if (typeof value === 'string') return !!value.match(example);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
// Operator clause.
|
|
141
|
+
if (typeof example === 'object') {
|
|
142
|
+
const operatorsTest = this.getService(OperatorClauseTool).testAll(
|
|
143
|
+
example,
|
|
144
|
+
value,
|
|
145
|
+
);
|
|
146
|
+
if (operatorsTest !== undefined) return operatorsTest;
|
|
147
|
+
}
|
|
148
|
+
// Not strict equality.
|
|
149
|
+
return example == value;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Validate where clause.
|
|
154
|
+
*
|
|
155
|
+
* @param {WhereClause|undefined} clause
|
|
156
|
+
*/
|
|
157
|
+
static validateWhereClause(clause) {
|
|
158
|
+
if (!clause) return;
|
|
159
|
+
if (typeof clause !== 'object' || Array.isArray(clause))
|
|
160
|
+
throw new InvalidArgumentError(
|
|
161
|
+
'The provided option "where" should be an Object, but %v given.',
|
|
162
|
+
clause,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {format} from '@e22m4u/js-format';
|
|
3
|
+
import {WhereClauseTool} from './where-clause-tool.js';
|
|
4
|
+
|
|
5
|
+
const S = new WhereClauseTool();
|
|
6
|
+
|
|
7
|
+
const OBJECTS = [
|
|
8
|
+
{
|
|
9
|
+
id: 1,
|
|
10
|
+
name: 'John',
|
|
11
|
+
surname: 'Doe',
|
|
12
|
+
age: 21,
|
|
13
|
+
hobbies: ['bicycle', 'yoga'],
|
|
14
|
+
nickname: 'Spear',
|
|
15
|
+
birthdate: '2002-04-14',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 2,
|
|
19
|
+
name: 'Mary',
|
|
20
|
+
surname: 'Smith',
|
|
21
|
+
age: 21,
|
|
22
|
+
hobbies: ['yoga', 'meditation'],
|
|
23
|
+
nickname: 'Flower',
|
|
24
|
+
birthdate: '2002-01-12',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 3,
|
|
28
|
+
name: 'James',
|
|
29
|
+
surname: 'Smith',
|
|
30
|
+
age: 21,
|
|
31
|
+
hobbies: [],
|
|
32
|
+
nickname: null,
|
|
33
|
+
birthdate: '2002-03-01',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 4,
|
|
37
|
+
name: 'Oliver',
|
|
38
|
+
surname: 'Smith',
|
|
39
|
+
age: 32,
|
|
40
|
+
hobbies: ['bicycle'],
|
|
41
|
+
birthdate: '1991-06-24',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
describe('WhereClauseTool', function () {
|
|
46
|
+
describe('filter', function () {
|
|
47
|
+
it('returns the same array if no given condition', function () {
|
|
48
|
+
const result = S.filter(OBJECTS);
|
|
49
|
+
expect(result).to.be.eql(OBJECTS);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('returns a filtered array by matched properties', function () {
|
|
53
|
+
const result = S.filter(OBJECTS, {surname: 'Smith', age: 21});
|
|
54
|
+
expect(result).to.have.length(2);
|
|
55
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
56
|
+
expect(result[1]).to.be.eql(OBJECTS[2]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('the and operator requires each given condition to be met', function () {
|
|
60
|
+
const result = S.filter(OBJECTS, {
|
|
61
|
+
and: [{name: 'James'}, {age: 21}],
|
|
62
|
+
});
|
|
63
|
+
expect(result).to.have.length(1);
|
|
64
|
+
expect(result[0]).to.be.eql(OBJECTS[2]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('the or operator requires one of a given condition to be met', function () {
|
|
68
|
+
const result = S.filter(OBJECTS, {
|
|
69
|
+
or: [{name: 'James'}, {age: 21}],
|
|
70
|
+
});
|
|
71
|
+
expect(result).to.have.length(3);
|
|
72
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
73
|
+
expect(result[1]).to.be.eql(OBJECTS[1]);
|
|
74
|
+
expect(result[2]).to.be.eql(OBJECTS[2]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('uses property value to match an array value', function () {
|
|
78
|
+
const result = S.filter(OBJECTS, {hobbies: 'yoga'});
|
|
79
|
+
expect(result).to.have.length(2);
|
|
80
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
81
|
+
expect(result[1]).to.be.eql(OBJECTS[1]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('uses given RegExp to match a property value', function () {
|
|
85
|
+
const result = S.filter(OBJECTS, {surname: /^Sm.+/});
|
|
86
|
+
expect(result).to.have.length(3);
|
|
87
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
88
|
+
expect(result[1]).to.be.eql(OBJECTS[2]);
|
|
89
|
+
expect(result[2]).to.be.eql(OBJECTS[3]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('uses given RegExp to match an array value', function () {
|
|
93
|
+
const result = S.filter(OBJECTS, {hobbies: /^\w+cycle/});
|
|
94
|
+
expect(result).to.have.length(2);
|
|
95
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
96
|
+
expect(result[1]).to.be.eql(OBJECTS[3]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('skips not supported values for RegExp', function () {
|
|
100
|
+
const result = S.filter(OBJECTS, {id: /test/});
|
|
101
|
+
expect(result).to.be.empty;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('uses an "eq" operator to match equality', function () {
|
|
105
|
+
const result = S.filter(OBJECTS, {name: {eq: 'John'}});
|
|
106
|
+
expect(result).to.have.length(1);
|
|
107
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('uses a "neq" operator to match non-equality', function () {
|
|
111
|
+
const result = S.filter(OBJECTS, {name: {neq: 'John'}});
|
|
112
|
+
expect(result).to.have.length(3);
|
|
113
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
114
|
+
expect(result[1]).to.be.eql(OBJECTS[2]);
|
|
115
|
+
expect(result[2]).to.be.eql(OBJECTS[3]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('uses a "neq" operator to match an empty array', function () {
|
|
119
|
+
const result = S.filter(OBJECTS, {hobbies: {neq: 'bicycle'}});
|
|
120
|
+
expect(result).to.have.length(2);
|
|
121
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
122
|
+
expect(result[1]).to.be.eql(OBJECTS[2]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('uses a "gt" operator to compare values', function () {
|
|
126
|
+
const result = S.filter(OBJECTS, {id: {gt: 2}});
|
|
127
|
+
expect(result).to.have.length(2);
|
|
128
|
+
expect(result[0]).to.be.eql(OBJECTS[2]);
|
|
129
|
+
expect(result[1]).to.be.eql(OBJECTS[3]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('uses a "gte" operator to compare values', function () {
|
|
133
|
+
const result = S.filter(OBJECTS, {id: {gte: 2}});
|
|
134
|
+
expect(result).to.have.length(3);
|
|
135
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
136
|
+
expect(result[1]).to.be.eql(OBJECTS[2]);
|
|
137
|
+
expect(result[2]).to.be.eql(OBJECTS[3]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('uses a "lt" operator to compare values', function () {
|
|
141
|
+
const result = S.filter(OBJECTS, {id: {lt: 3}});
|
|
142
|
+
expect(result).to.have.length(2);
|
|
143
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
144
|
+
expect(result[1]).to.be.eql(OBJECTS[1]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('uses a "lte" operator to compare values', function () {
|
|
148
|
+
const result = S.filter(OBJECTS, {id: {lte: 3}});
|
|
149
|
+
expect(result).to.have.length(3);
|
|
150
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
151
|
+
expect(result[1]).to.be.eql(OBJECTS[1]);
|
|
152
|
+
expect(result[2]).to.be.eql(OBJECTS[2]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('uses a "inq" operator to compare values', function () {
|
|
156
|
+
const result = S.filter(OBJECTS, {id: {inq: [2, 3]}});
|
|
157
|
+
expect(result).to.have.length(2);
|
|
158
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
159
|
+
expect(result[1]).to.be.eql(OBJECTS[2]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('uses a "nin" operator to compare values', function () {
|
|
163
|
+
const result = S.filter(OBJECTS, {id: {nin: [2, 3]}});
|
|
164
|
+
expect(result).to.have.length(2);
|
|
165
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
166
|
+
expect(result[1]).to.be.eql(OBJECTS[3]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('uses a "between" operator to compare values', function () {
|
|
170
|
+
const result = S.filter(OBJECTS, {id: {between: [2, 3]}});
|
|
171
|
+
expect(result).to.have.length(2);
|
|
172
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
173
|
+
expect(result[1]).to.be.eql(OBJECTS[2]);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('uses an "exists" operator to check existence', function () {
|
|
177
|
+
const result = S.filter(OBJECTS, {nickname: {exists: true}});
|
|
178
|
+
expect(result).to.have.length(3);
|
|
179
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
180
|
+
expect(result[1]).to.be.eql(OBJECTS[1]);
|
|
181
|
+
expect(result[2]).to.be.eql(OBJECTS[2]);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('uses an "exists" operator to check non-existence', function () {
|
|
185
|
+
const result = S.filter(OBJECTS, {nickname: {exists: false}});
|
|
186
|
+
expect(result).to.have.length(1);
|
|
187
|
+
expect(result[0]).to.be.eql(OBJECTS[3]);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('uses a "like" operator to match by a substring', function () {
|
|
191
|
+
const result = S.filter(OBJECTS, {name: {like: 'liv'}});
|
|
192
|
+
expect(result).to.have.length(1);
|
|
193
|
+
expect(result[0]).to.be.eql(OBJECTS[3]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('uses a "nlike" operator to exclude by a substring', function () {
|
|
197
|
+
const result = S.filter(OBJECTS, {name: {nlike: 'liv'}});
|
|
198
|
+
expect(result).to.have.length(3);
|
|
199
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
200
|
+
expect(result[1]).to.be.eql(OBJECTS[1]);
|
|
201
|
+
expect(result[2]).to.be.eql(OBJECTS[2]);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('uses a "ilike" operator to case-insensitively matching by a substring', function () {
|
|
205
|
+
const result = S.filter(OBJECTS, {name: {ilike: 'LIV'}});
|
|
206
|
+
expect(result).to.have.length(1);
|
|
207
|
+
expect(result[0]).to.be.eql(OBJECTS[3]);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('uses a "nilike" operator to exclude case-insensitively by a substring', function () {
|
|
211
|
+
const result = S.filter(OBJECTS, {name: {nilike: 'LIV'}});
|
|
212
|
+
expect(result).to.have.length(3);
|
|
213
|
+
expect(result[0]).to.be.eql(OBJECTS[0]);
|
|
214
|
+
expect(result[1]).to.be.eql(OBJECTS[1]);
|
|
215
|
+
expect(result[2]).to.be.eql(OBJECTS[2]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('uses a "regexp" operator to compare values', function () {
|
|
219
|
+
const result = S.filter(OBJECTS, {name: {regexp: '^Jam.*'}});
|
|
220
|
+
expect(result).to.have.length(1);
|
|
221
|
+
expect(result[0]).to.be.eql(OBJECTS[2]);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('uses a null to match an undefined and null value', function () {
|
|
225
|
+
const result = S.filter(OBJECTS, {nickname: null});
|
|
226
|
+
expect(result).to.have.length(2);
|
|
227
|
+
expect(result[0]).to.be.eql(OBJECTS[2]);
|
|
228
|
+
expect(result[1]).to.be.eql(OBJECTS[3]);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('uses a given function to filter values', function () {
|
|
232
|
+
const result = S.filter(OBJECTS, v => v.nickname === 'Flower');
|
|
233
|
+
expect(result).to.have.length(1);
|
|
234
|
+
expect(result[0]).to.be.eql(OBJECTS[1]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('throws an error if a first argument is not an Array', function () {
|
|
238
|
+
const throwable = () => S.filter(10, {});
|
|
239
|
+
expect(throwable).to.throw(
|
|
240
|
+
'A first argument of WhereUtils.filter ' +
|
|
241
|
+
'should be an Array of Objects, but 10 given.',
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('throws an error if elements of a first argument is not an Object', function () {
|
|
246
|
+
const throwable = () => S.filter([10], {});
|
|
247
|
+
expect(throwable).to.throw(
|
|
248
|
+
'A first argument of WhereUtils.filter ' +
|
|
249
|
+
'should be an Array of Objects, but 10 given.',
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('throws an error if a provided second argument is not an Object', function () {
|
|
254
|
+
const throwable = () => S.filter([], 10);
|
|
255
|
+
expect(throwable).to.throw(
|
|
256
|
+
'The provided option "where" should be an Object, but 10 given.',
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('validateWhereClause', function () {
|
|
262
|
+
it('requires an object value or a falsy value', function () {
|
|
263
|
+
const validate = v => () => WhereClauseTool.validateWhereClause(v);
|
|
264
|
+
const error = v =>
|
|
265
|
+
format(
|
|
266
|
+
'The provided option "where" should be an Object, but %s given.',
|
|
267
|
+
v,
|
|
268
|
+
);
|
|
269
|
+
expect(validate('str')).to.throw(error('"str"'));
|
|
270
|
+
expect(validate(10)).to.throw(error('10'));
|
|
271
|
+
expect(validate(true)).to.throw(error('true'));
|
|
272
|
+
expect(validate([])).to.throw(error('Array'));
|
|
273
|
+
validate('')();
|
|
274
|
+
validate(false)();
|
|
275
|
+
validate(undefined)();
|
|
276
|
+
validate(null)();
|
|
277
|
+
validate({})();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './schema.js';
|
|
3
|
+
export * from './utils/index.js';
|
|
4
|
+
export * from './filter/index.js';
|
|
5
|
+
export * from './errors/index.js';
|
|
6
|
+
export * from './adapter/index.js';
|
|
7
|
+
export * from './relations/index.js';
|
|
8
|
+
export * from './definition/index.js';
|
|
9
|
+
export * from './repository/index.js';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './schema.js';
|
|
2
|
+
export * from './utils/index.js';
|
|
3
|
+
export * from './errors/index.js';
|
|
4
|
+
export * from './filter/index.js';
|
|
5
|
+
export * from './adapter/index.js';
|
|
6
|
+
export * from './relations/index.js';
|
|
7
|
+
export * from './definition/index.js';
|
|
8
|
+
export * from './repository/index.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {ModelData} from '../types.js';
|
|
2
|
+
import {Filter} from '../filter/index.js';
|
|
3
|
+
import {Service} from '@e22m4u/js-service';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Belongs to resolver.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BelongsToResolver extends Service {
|
|
9
|
+
/**
|
|
10
|
+
* Include to.
|
|
11
|
+
*
|
|
12
|
+
* @param entities
|
|
13
|
+
* @param sourceName
|
|
14
|
+
* @param targetName
|
|
15
|
+
* @param relationName
|
|
16
|
+
* @param foreignKey
|
|
17
|
+
* @param scope
|
|
18
|
+
*/
|
|
19
|
+
includeTo(
|
|
20
|
+
entities: ModelData[],
|
|
21
|
+
sourceName: string,
|
|
22
|
+
targetName: string,
|
|
23
|
+
relationName: string,
|
|
24
|
+
foreignKey?: string,
|
|
25
|
+
scope?: Filter,
|
|
26
|
+
): Promise<void>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Include polymorphic to.
|
|
30
|
+
*
|
|
31
|
+
* @param entities
|
|
32
|
+
* @param sourceName
|
|
33
|
+
* @param relationName
|
|
34
|
+
* @param foreignKey
|
|
35
|
+
* @param discriminator
|
|
36
|
+
* @param scope
|
|
37
|
+
*/
|
|
38
|
+
includePolymorphicTo(
|
|
39
|
+
entities: ModelData[],
|
|
40
|
+
sourceName: string,
|
|
41
|
+
relationName: string,
|
|
42
|
+
foreignKey?: string,
|
|
43
|
+
discriminator?: string,
|
|
44
|
+
scope?: Filter,
|
|
45
|
+
): Promise<void>;
|
|
46
|
+
}
|