@e22m4u/js-repository 0.8.3 → 0.8.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/js-repository",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Реализация репозитория для работы с базами данных",
5
5
  "author": "Mikhail Evstropov <e22m4u@yandex.ru>",
6
6
  "license": "MIT",
@@ -39,13 +39,12 @@
39
39
  "prepare": "husky"
40
40
  },
41
41
  "dependencies": {
42
- "@e22m4u/js-empty-values": "~0.3.2",
43
42
  "@e22m4u/js-format": "~0.3.2",
44
43
  "@e22m4u/js-service": "~0.5.1"
45
44
  },
46
45
  "devDependencies": {
47
- "@commitlint/cli": "~20.2.0",
48
- "@commitlint/config-conventional": "~20.2.0",
46
+ "@commitlint/cli": "~20.3.0",
47
+ "@commitlint/config-conventional": "~20.3.0",
49
48
  "@e22m4u/js-spy": "~0.3.5",
50
49
  "@types/chai": "~5.2.3",
51
50
  "@types/chai-as-promised": "~8.0.2",
@@ -8,6 +8,7 @@ import {
8
8
  DefaultValuesDecorator,
9
9
  DataSanitizingDecorator,
10
10
  FieldsFilteringDecorator,
11
+ RequiredPropertyDecorator,
11
12
  PropertyUniquenessDecorator,
12
13
  } from './decorator/index.js';
13
14
 
@@ -58,6 +59,7 @@ export class Adapter extends Service {
58
59
  if (this.constructor !== Adapter) {
59
60
  this.getService(DataSanitizingDecorator).decorate(this);
60
61
  this.getService(DefaultValuesDecorator).decorate(this);
62
+ this.getService(RequiredPropertyDecorator).decorate(this);
61
63
  this.getService(PropertyUniquenessDecorator).decorate(this);
62
64
  this.getService(FieldsFilteringDecorator).decorate(this);
63
65
  this.getService(InclusionDecorator).decorate(this);
@@ -9,13 +9,14 @@ import {
9
9
  DefaultValuesDecorator,
10
10
  DataSanitizingDecorator,
11
11
  FieldsFilteringDecorator,
12
+ RequiredPropertyDecorator,
12
13
  PropertyUniquenessDecorator,
13
14
  } from './decorator/index.js';
14
15
 
15
16
  const sandbox = createSandbox();
16
17
 
17
18
  describe('Adapter', function () {
18
- it('exposes static property "kinds"', function () {
19
+ it('should expose the static property "kinds"', function () {
19
20
  const kinds = [...Service.kinds, ADAPTER_CLASS_NAME];
20
21
  expect(Adapter.kinds).to.be.eql(kinds);
21
22
  const MyAdapter = class extends Adapter {};
@@ -27,12 +28,12 @@ describe('Adapter', function () {
27
28
  sandbox.restore();
28
29
  });
29
30
 
30
- it('inherits from the Service class', function () {
31
+ it('should extend the Service class', function () {
31
32
  const adapter = new Adapter();
32
33
  expect(adapter).to.be.instanceof(Service);
33
34
  });
34
35
 
35
- it('sets given service container and settings', function () {
36
+ it('should set given service container and settings', function () {
36
37
  const container = new ServiceContainer();
37
38
  const settings = {};
38
39
  const adapter = new Adapter(container, settings);
@@ -40,47 +41,36 @@ describe('Adapter', function () {
40
41
  expect(adapter._settings).to.be.eq(settings);
41
42
  });
42
43
 
43
- it('decorates only extended adapter', function () {
44
+ it('should decorate only when the instance inherits the Adapter class', function () {
45
+ const decCtors = [
46
+ DataSanitizingDecorator,
47
+ DefaultValuesDecorator,
48
+ RequiredPropertyDecorator,
49
+ PropertyUniquenessDecorator,
50
+ FieldsFilteringDecorator,
51
+ InclusionDecorator,
52
+ ];
44
53
  const dbs = new DatabaseSchema();
45
- const dec1 = dbs.getService(DataSanitizingDecorator);
46
- const dec2 = dbs.getService(DefaultValuesDecorator);
47
- const dec3 = dbs.getService(PropertyUniquenessDecorator);
48
- const dec4 = dbs.getService(FieldsFilteringDecorator);
49
- const dec5 = dbs.getService(InclusionDecorator);
54
+ const decs = decCtors.map(ctor => dbs.getService(ctor));
50
55
  const order = [];
51
- const decorate = function (ctx) {
52
- expect(ctx).to.be.instanceof(Adapter);
56
+ const decorate = function (...args) {
57
+ expect(args[0]).to.be.instanceof(Adapter);
58
+ expect(args).to.have.length(1);
53
59
  order.push(this);
54
60
  };
55
- sandbox.on(dec1, 'decorate', decorate);
56
- sandbox.on(dec2, 'decorate', decorate);
57
- sandbox.on(dec3, 'decorate', decorate);
58
- sandbox.on(dec4, 'decorate', decorate);
59
- sandbox.on(dec5, 'decorate', decorate);
61
+ decs.forEach(dec => sandbox.on(dec, 'decorate', decorate));
60
62
  new Adapter(dbs.container);
61
63
  expect(order).to.be.empty;
62
- expect(dec1.decorate).to.be.not.called;
63
- expect(dec2.decorate).to.be.not.called;
64
- expect(dec3.decorate).to.be.not.called;
65
- expect(dec4.decorate).to.be.not.called;
66
- expect(dec5.decorate).to.be.not.called;
64
+ decs.forEach(dec => expect(dec.decorate).to.be.not.called);
67
65
  class ExtendedAdapter extends Adapter {}
68
66
  new ExtendedAdapter(dbs.container);
69
- expect(order[0]).to.be.eql(dec1);
70
- expect(order[1]).to.be.eql(dec2);
71
- expect(order[2]).to.be.eql(dec3);
72
- expect(order[3]).to.be.eql(dec4);
73
- expect(order[4]).to.be.eql(dec5);
74
- expect(dec1.decorate).to.be.called.once;
75
- expect(dec2.decorate).to.be.called.once;
76
- expect(dec3.decorate).to.be.called.once;
77
- expect(dec4.decorate).to.be.called.once;
78
- expect(dec5.decorate).to.be.called.once;
67
+ decs.forEach((dec, index) => expect(order[index]).to.be.eq(dec));
68
+ decs.forEach(dec => expect(dec.decorate).to.be.called.once);
79
69
  });
80
70
  });
81
71
 
82
72
  describe('create', function () {
83
- it('throws the "Not implemented"', function () {
73
+ it('should throw the "Not implemented"', function () {
84
74
  const adapter = new Adapter();
85
75
  const throwable = () => adapter.create();
86
76
  expect(throwable).to.throw('Adapter.create is not implemented.');
@@ -88,7 +78,7 @@ describe('Adapter', function () {
88
78
  });
89
79
 
90
80
  describe('replaceById', function () {
91
- it('throws the "Not implemented"', function () {
81
+ it('should throw the "Not implemented"', function () {
92
82
  const adapter = new Adapter();
93
83
  const throwable = () => adapter.replaceById();
94
84
  expect(throwable).to.throw('Adapter.replaceById is not implemented.');
@@ -96,15 +86,23 @@ describe('Adapter', function () {
96
86
  });
97
87
 
98
88
  describe('replaceOrCreate', function () {
99
- it('throws the "Not implemented"', function () {
89
+ it('should throw the "Not implemented"', function () {
100
90
  const adapter = new Adapter();
101
91
  const throwable = () => adapter.replaceOrCreate();
102
92
  expect(throwable).to.throw('Adapter.replaceOrCreate is not implemented.');
103
93
  });
104
94
  });
105
95
 
96
+ describe('patch', function () {
97
+ it('should throw the "Not implemented"', function () {
98
+ const adapter = new Adapter();
99
+ const throwable = () => adapter.patch();
100
+ expect(throwable).to.throw('Adapter.patch is not implemented.');
101
+ });
102
+ });
103
+
106
104
  describe('patchById', function () {
107
- it('throws the "Not implemented"', function () {
105
+ it('should throw the "Not implemented"', function () {
108
106
  const adapter = new Adapter();
109
107
  const throwable = () => adapter.patchById();
110
108
  expect(throwable).to.throw('Adapter.patchById is not implemented.');
@@ -112,7 +110,7 @@ describe('Adapter', function () {
112
110
  });
113
111
 
114
112
  describe('find', function () {
115
- it('throws the "Not implemented"', function () {
113
+ it('should throw the "Not implemented"', function () {
116
114
  const adapter = new Adapter();
117
115
  const throwable = () => adapter.find();
118
116
  expect(throwable).to.throw('Adapter.find is not implemented.');
@@ -120,7 +118,7 @@ describe('Adapter', function () {
120
118
  });
121
119
 
122
120
  describe('findById', function () {
123
- it('throws the "Not implemented"', function () {
121
+ it('should throw the "Not implemented"', function () {
124
122
  const adapter = new Adapter();
125
123
  const throwable = () => adapter.findById();
126
124
  expect(throwable).to.throw('Adapter.findById is not implemented.');
@@ -128,7 +126,7 @@ describe('Adapter', function () {
128
126
  });
129
127
 
130
128
  describe('delete', function () {
131
- it('throws the "Not implemented"', function () {
129
+ it('should throw the "Not implemented"', function () {
132
130
  const adapter = new Adapter();
133
131
  const throwable = () => adapter.delete();
134
132
  expect(throwable).to.throw('Adapter.delete is not implemented.');
@@ -136,7 +134,7 @@ describe('Adapter', function () {
136
134
  });
137
135
 
138
136
  describe('deleteById', function () {
139
- it('throws the "Not implemented"', function () {
137
+ it('should throw the "Not implemented"', function () {
140
138
  const adapter = new Adapter();
141
139
  const throwable = () => adapter.deleteById();
142
140
  expect(throwable).to.throw('Adapter.deleteById is not implemented.');
@@ -144,7 +142,7 @@ describe('Adapter', function () {
144
142
  });
145
143
 
146
144
  describe('exists', function () {
147
- it('throws the "Not implemented"', function () {
145
+ it('should throw the "Not implemented"', function () {
148
146
  const adapter = new Adapter();
149
147
  const throwable = () => adapter.exists();
150
148
  expect(throwable).to.throw('Adapter.exists is not implemented.');
@@ -152,7 +150,7 @@ describe('Adapter', function () {
152
150
  });
153
151
 
154
152
  describe('count', function () {
155
- it('throws the "Not implemented"', function () {
153
+ it('should throw the "Not implemented"', function () {
156
154
  const adapter = new Adapter();
157
155
  const throwable = () => adapter.count();
158
156
  expect(throwable).to.throw('Adapter.count is not implemented.');
@@ -2,4 +2,5 @@ export * from './inclusion-decorator.js';
2
2
  export * from './default-values-decorator.js';
3
3
  export * from './data-sanitizing-decorator.js';
4
4
  export * from './fields-filtering-decorator.js';
5
+ export * from './required-property-decorator.js';
5
6
  export * from './property-uniqueness-decorator.js';
@@ -2,4 +2,5 @@ export * from './inclusion-decorator.js';
2
2
  export * from './default-values-decorator.js';
3
3
  export * from './data-sanitizing-decorator.js';
4
4
  export * from './fields-filtering-decorator.js';
5
+ export * from './required-property-decorator.js';
5
6
  export * from './property-uniqueness-decorator.js';
@@ -0,0 +1,14 @@
1
+ import {Adapter} from '../adapter.js';
2
+ import {Service} from '@e22m4u/js-service';
3
+
4
+ /**
5
+ * Required property decorator.
6
+ */
7
+ export declare class RequiredPropertyDecorator extends Service {
8
+ /**
9
+ * Decorate.
10
+ *
11
+ * @param adapter
12
+ */
13
+ decorate(adapter: Adapter): void;
14
+ }
@@ -0,0 +1,54 @@
1
+ import {Adapter} from '../adapter.js';
2
+ import {Service} from '@e22m4u/js-service';
3
+ import {InvalidArgumentError} from '../../errors/index.js';
4
+ import {RequiredPropertyValidator} from '../../definition/index.js';
5
+
6
+ /**
7
+ * Required property decorator.
8
+ */
9
+ export class RequiredPropertyDecorator extends Service {
10
+ /**
11
+ * Decorate.
12
+ *
13
+ * @param {Adapter} adapter
14
+ */
15
+ decorate(adapter) {
16
+ if (!adapter || !(adapter instanceof Adapter))
17
+ throw new InvalidArgumentError(
18
+ 'The first argument of RequiredPropertyDecorator.decorate should be ' +
19
+ 'an Adapter instance, but %v was given.',
20
+ adapter,
21
+ );
22
+ const validator = this.getService(RequiredPropertyValidator);
23
+
24
+ const create = adapter.create;
25
+ adapter.create = async function (modelName, modelData, filter) {
26
+ validator.validate(modelName, modelData);
27
+ return create.call(this, modelName, modelData, filter);
28
+ };
29
+
30
+ const replaceById = adapter.replaceById;
31
+ adapter.replaceById = async function (modelName, id, modelData, filter) {
32
+ validator.validate(modelName, modelData);
33
+ return replaceById.call(this, modelName, id, modelData, filter);
34
+ };
35
+
36
+ const replaceOrCreate = adapter.replaceOrCreate;
37
+ adapter.replaceOrCreate = async function (modelName, modelData, filter) {
38
+ validator.validate(modelName, modelData);
39
+ return replaceOrCreate.call(this, modelName, modelData, filter);
40
+ };
41
+
42
+ const patch = adapter.patch;
43
+ adapter.patch = async function (modelName, modelData, where) {
44
+ validator.validate(modelName, modelData, true);
45
+ return patch.call(this, modelName, modelData, where);
46
+ };
47
+
48
+ const patchById = adapter.patchById;
49
+ adapter.patchById = async function (modelName, id, modelData, filter) {
50
+ validator.validate(modelName, modelData, true);
51
+ return patchById.call(this, modelName, id, modelData, filter);
52
+ };
53
+ }
54
+ }
@@ -0,0 +1,105 @@
1
+ import {expect} from 'chai';
2
+ import {Adapter} from '../adapter.js';
3
+ import {createSandbox} from '@e22m4u/js-spy';
4
+ import {DatabaseSchema} from '../../database-schema.js';
5
+ import {RequiredPropertyValidator} from '../../definition/index.js';
6
+
7
+ const dbs = new DatabaseSchema();
8
+ dbs.defineModel({name: 'model'});
9
+
10
+ class TestAdapter extends Adapter {
11
+ // eslint-disable-next-line no-unused-vars
12
+ create(modelName, modelData, filter = undefined) {
13
+ return Promise.resolve(modelData);
14
+ }
15
+
16
+ // eslint-disable-next-line no-unused-vars
17
+ replaceById(modelName, id, modelData, filter = undefined) {
18
+ return Promise.resolve(modelData);
19
+ }
20
+
21
+ // eslint-disable-next-line no-unused-vars
22
+ replaceOrCreate(modelName, modelData, filter = undefined) {
23
+ return Promise.resolve(modelData);
24
+ }
25
+
26
+ // eslint-disable-next-line no-unused-vars
27
+ patch(modelName, modelData, where = undefined) {
28
+ return Promise.resolve(modelData);
29
+ }
30
+
31
+ // eslint-disable-next-line no-unused-vars
32
+ patchById(modelName, id, modelData, filter = undefined) {
33
+ return Promise.resolve(modelData);
34
+ }
35
+ }
36
+
37
+ const A = dbs.getService(TestAdapter);
38
+ const V = dbs.getService(RequiredPropertyValidator);
39
+ const sandbox = createSandbox();
40
+
41
+ describe('RequiredPropertyDecorator', function () {
42
+ afterEach(function () {
43
+ sandbox.restore();
44
+ });
45
+
46
+ it('overrides the "create" method and validates a given data', async function () {
47
+ const data = {kind: 'data'};
48
+ sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
49
+ expect(modelName).to.be.eq('model');
50
+ expect(modelData).to.be.eql(data);
51
+ expect(isPartial).to.be.false;
52
+ });
53
+ const res = await A.create('model', data);
54
+ expect(res).to.be.eql(data);
55
+ expect(V.validate).to.be.called.once;
56
+ });
57
+
58
+ it('overrides the "replaceById" method and validates a given data', async function () {
59
+ const data = {kind: 'data'};
60
+ sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
61
+ expect(modelName).to.be.eq('model');
62
+ expect(modelData).to.be.eql(data);
63
+ expect(isPartial).to.be.false;
64
+ });
65
+ const res = await A.replaceById('model', 1, data);
66
+ expect(res).to.be.eql(data);
67
+ expect(V.validate).to.be.called.once;
68
+ });
69
+
70
+ it('overrides the "replaceOrCreate" method and validates a given data', async function () {
71
+ const data = {kind: 'data'};
72
+ sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
73
+ expect(modelName).to.be.eq('model');
74
+ expect(modelData).to.be.eql(data);
75
+ expect(isPartial).to.be.false;
76
+ });
77
+ const res = await A.replaceOrCreate('model', data);
78
+ expect(res).to.be.eql(data);
79
+ expect(V.validate).to.be.called.once;
80
+ });
81
+
82
+ it('overrides the "patch" method and validates a given data', async function () {
83
+ const data = {kind: 'data'};
84
+ sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
85
+ expect(modelName).to.be.eq('model');
86
+ expect(modelData).to.be.eql(data);
87
+ expect(isPartial).to.be.true;
88
+ });
89
+ const res = await A.patch('model', data);
90
+ expect(res).to.be.eql(data);
91
+ expect(V.validate).to.be.called.once;
92
+ });
93
+
94
+ it('overrides the "patchById" method and validates a given data', async function () {
95
+ const data = {kind: 'data'};
96
+ sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
97
+ expect(modelName).to.be.eq('model');
98
+ expect(modelData).to.be.eql(data);
99
+ expect(isPartial).to.be.true;
100
+ });
101
+ const res = await A.patchById('model', 1, data);
102
+ expect(res).to.be.eql(data);
103
+ expect(V.validate).to.be.called.once;
104
+ });
105
+ });
@@ -1,6 +1,5 @@
1
1
  import {Service} from '@e22m4u/js-service';
2
2
  import {DataType} from './properties/index.js';
3
- import {EmptyValuesService} from '@e22m4u/js-empty-values';
4
3
  import {InvalidArgumentError} from '../../errors/index.js';
5
4
  import {DefinitionRegistry} from '../definition-registry.js';
6
5
  import {cloneDeep, excludeObjectKeys} from '../../utils/index.js';
@@ -121,7 +120,7 @@ export class ModelDefinitionUtils extends Service {
121
120
  }
122
121
 
123
122
  /**
124
- * Set default values for empty properties.
123
+ * Set default values to empty properties.
125
124
  *
126
125
  * @param {string} modelName
127
126
  * @param {object} modelData
@@ -139,16 +138,14 @@ export class ModelDefinitionUtils extends Service {
139
138
  ? Object.keys(modelData)
140
139
  : Object.keys(propDefs);
141
140
  const extendedData = cloneDeep(modelData);
142
- const emptyValuesService = this.getService(EmptyValuesService);
143
141
  propNames.forEach(propName => {
144
142
  const propDef = propDefs[propName];
145
143
  const propValue = extendedData[propName];
146
- const propType =
147
- propDef != null
148
- ? this.getDataTypeFromPropertyDefinition(propDef)
149
- : DataType.ANY;
150
- const isEmpty = emptyValuesService.isEmptyOf(propType, propValue);
151
- if (!isEmpty) return;
144
+ // если значение свойства не является undefined и null,
145
+ // то свойство пропускается (остается исходное значение)
146
+ if (propValue != null) {
147
+ return;
148
+ }
152
149
  if (
153
150
  propDef &&
154
151
  typeof propDef === 'object' &&
@@ -4,7 +4,6 @@ import {createSandbox} from '@e22m4u/js-spy';
4
4
  import {DataType} from './properties/index.js';
5
5
  import {RelationType} from './relations/index.js';
6
6
  import {DatabaseSchema} from '../../database-schema.js';
7
- import {EmptyValuesService} from '@e22m4u/js-empty-values';
8
7
  import {InvalidArgumentError} from '../../errors/index.js';
9
8
 
10
9
  import {
@@ -480,7 +479,7 @@ describe('ModelDefinitionUtils', function () {
480
479
  expect(result).to.be.eql({foo: 'string'});
481
480
  });
482
481
 
483
- it('sets a default value if a property has an empty value', function () {
482
+ it('sets a default value if a property value is undefined', function () {
484
483
  const dbs = new DatabaseSchema();
485
484
  dbs.defineModel({
486
485
  name: 'model',
@@ -491,12 +490,9 @@ describe('ModelDefinitionUtils', function () {
491
490
  },
492
491
  },
493
492
  });
494
- dbs
495
- .getService(EmptyValuesService)
496
- .setEmptyValuesOf(DataType.STRING, ['empty']);
497
493
  const result = dbs
498
494
  .getService(ModelDefinitionUtils)
499
- .setDefaultValuesToEmptyProperties('model', {foo: 'empty'});
495
+ .setDefaultValuesToEmptyProperties('model', {foo: undefined});
500
496
  expect(result).to.be.eql({foo: 'placeholder'});
501
497
  });
502
498
 
@@ -1,6 +1,7 @@
1
1
  export * from './data-type.js';
2
2
  export * from './property-definition.js';
3
3
  export * from './property-uniqueness.js';
4
+ export * from './required-property-validator.js';
4
5
  export * from './property-uniqueness-validator.js';
5
6
  export * from './properties-definition-validator.js';
6
7
  export * from './primary-keys-definition-validator.js';
@@ -1,6 +1,7 @@
1
1
  export * from './data-type.js';
2
2
  export * from './property-definition.js';
3
3
  export * from './property-uniqueness.js';
4
+ export * from './required-property-validator.js';
4
5
  export * from './property-uniqueness-validator.js';
5
6
  export * from './properties-definition-validator.js';
6
7
  export * from './primary-keys-definition-validator.js';
@@ -1,7 +1,5 @@
1
- import {DataType} from './data-type.js';
2
1
  import {Service} from '@e22m4u/js-service';
3
2
  import {isPlainObject} from '../../../utils/index.js';
4
- import {EmptyValuesService} from '@e22m4u/js-empty-values';
5
3
  import {PropertyUniqueness} from './property-uniqueness.js';
6
4
  import {InvalidArgumentError} from '../../../errors/index.js';
7
5
  import {ModelDefinitionUtils} from '../model-definition-utils.js';
@@ -70,7 +68,6 @@ export class PropertyUniquenessValidator extends Service {
70
68
  propValue,
71
69
  );
72
70
  let willBeReplaced = undefined;
73
- const emptyValuesService = this.getService(EmptyValuesService);
74
71
  for (const propName of propNames) {
75
72
  const propDef = propDefs[propName];
76
73
  if (
@@ -81,12 +78,11 @@ export class PropertyUniquenessValidator extends Service {
81
78
  ) {
82
79
  continue;
83
80
  }
84
- // sparse
81
+ // в режиме "sparse" проверка на уникальность
82
+ // должна игнорировать ложные значения
85
83
  const propValue = modelData[propName];
86
- if (propDef.unique === PropertyUniqueness.SPARSE) {
87
- const propType = propDef.type || DataType.ANY;
88
- const isEmpty = emptyValuesService.isEmptyOf(propType, propValue);
89
- if (isEmpty) continue;
84
+ if (propDef.unique === PropertyUniqueness.SPARSE && !propValue) {
85
+ continue;
90
86
  }
91
87
  // create
92
88
  if (methodName === 'create') {