@e22m4u/js-repository 0.0.37 → 0.0.39
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 +180 -9
- package/package.json +1 -1
- package/src/adapter/adapter.js +1 -1
- package/src/adapter/adapter.spec.js +3 -3
- package/src/definition/model/model-data-validator.js +2 -2
- package/src/definition/model/model-data-validator.spec.js +108 -0
- package/src/definition/model/properties/index.d.ts +0 -1
- package/src/definition/model/properties/index.js +0 -1
- package/src/definition/model/properties/properties-definition-validator.js +26 -13
- package/src/definition/model/properties/properties-definition-validator.spec.js +37 -20
- package/src/filter/filter-clause.d.ts +1 -3
- package/src/definition/model/properties/default-values-definition-validator.d.ts +0 -15
- package/src/definition/model/properties/default-values-definition-validator.js +0 -53
- package/src/definition/model/properties/default-values-definition-validator.spec.js +0 -136
package/README.md
CHANGED
|
@@ -2,26 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
Абстракция для работы с базами данных для Node.js
|
|
4
4
|
|
|
5
|
-
| адаптер | описание |
|
|
6
|
-
|---------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
7
|
-
| memory | виртуальная база в памяти процесса (для разработки и тестирования) |
|
|
8
|
-
| mongodb | MongoDB - система управления NoSQL базами данных (*[требует установки](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
|
|
9
|
-
|
|
10
5
|
## Установка
|
|
11
6
|
|
|
12
7
|
```bash
|
|
13
8
|
npm install @e22m4u/js-repository
|
|
14
9
|
```
|
|
15
10
|
|
|
16
|
-
Опционально устанавливаем
|
|
11
|
+
Опционально устанавливаем адаптер. Например, если используемой базой
|
|
12
|
+
является *MongoDB*, то для подключения потребуется добавить
|
|
13
|
+
[адаптер mongodb](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter)
|
|
14
|
+
как отдельную зависимость.
|
|
17
15
|
|
|
18
16
|
```bash
|
|
19
17
|
npm install @e22m4u/js-repository-mongodb-adapter
|
|
20
18
|
```
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
Список доступных адаптеров:
|
|
21
|
+
|
|
22
|
+
| адаптер | описание |
|
|
23
|
+
|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
24
|
+
| `memory` | виртуальная база в памяти процесса (для разработки и тестирования) |
|
|
25
|
+
| `mongodb` | MongoDB - система управления NoSQL базами (*[требует установки](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
|
|
26
|
+
|
|
27
|
+
## Концепция
|
|
28
|
+
|
|
29
|
+
Модуль позволяет спроектировать систему связанных данных, доступ к которым
|
|
30
|
+
осуществляется посредством репозиториев. Каждый репозиторий имеет собственную
|
|
31
|
+
модель, которая описывает структуру определенной коллекции в базе,
|
|
32
|
+
а так же определяет связи к другим коллекциям.
|
|
23
33
|
|
|
24
|
-
|
|
34
|
+
```mermaid
|
|
35
|
+
flowchart LR
|
|
36
|
+
|
|
37
|
+
A[Datasource]-->B[Model]-->С[Repository];
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Использование
|
|
41
|
+
|
|
42
|
+
Определения источников и моделей хранятся в экземпляре класса `Schema`,
|
|
43
|
+
и первым шагом будет создание данного экземпляра.
|
|
25
44
|
|
|
26
45
|
```js
|
|
27
46
|
import {Schema} from '@e22m4u/js-repository';
|
|
@@ -29,7 +48,38 @@ import {Schema} from '@e22m4u/js-repository';
|
|
|
29
48
|
const schema = new Schema();
|
|
30
49
|
```
|
|
31
50
|
|
|
32
|
-
|
|
51
|
+
Интерфейс `Schema` содержит три основных метода:
|
|
52
|
+
|
|
53
|
+
- `defineDatasource(datasourceDef: object): this` - добавить источник
|
|
54
|
+
- `defineModel(modelDef: object): this` - добавить модель
|
|
55
|
+
- `getRepository(modelName: string): Repository` - получить репозиторий
|
|
56
|
+
|
|
57
|
+
#### Источник данных
|
|
58
|
+
|
|
59
|
+
Источник описывает способ подключения к базе и используемый адаптер.
|
|
60
|
+
Если адаптер имеет настройки, то они передаются вместе с объектом
|
|
61
|
+
определения источника методом `defineDatasource`, как это показано
|
|
62
|
+
ниже.
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
schema.defineDatasource({
|
|
66
|
+
name: 'myMongo', // название нового источника
|
|
67
|
+
adapter: 'mongodb', // название выбранного адаптера
|
|
68
|
+
// настройки адаптера mongodb
|
|
69
|
+
host: '127.0.0.1',
|
|
70
|
+
port: 27017,
|
|
71
|
+
database: 'data'
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Параметры источника:
|
|
76
|
+
|
|
77
|
+
- `name: string` уникальное название
|
|
78
|
+
- `adapter: string` выбранный адаптер
|
|
79
|
+
|
|
80
|
+
При желании можно использовать встроенный адаптер `memory`, который хранит
|
|
81
|
+
данные в памяти процесса. У него нет специальных настроек, и он отлично
|
|
82
|
+
подходит для тестов и прототипирования.
|
|
33
83
|
|
|
34
84
|
```js
|
|
35
85
|
schema.defineDatasource({
|
|
@@ -38,6 +88,127 @@ schema.defineDatasource({
|
|
|
38
88
|
});
|
|
39
89
|
```
|
|
40
90
|
|
|
91
|
+
#### Модель данных
|
|
92
|
+
|
|
93
|
+
Когда источники определены, можно перейти к описанию моделей данных.
|
|
94
|
+
Модель может определять как структуру какого-либо объекта,
|
|
95
|
+
так и являться отражением реальной коллекции базы.
|
|
96
|
+
|
|
97
|
+
Представьте себе коллекцию торговых точек, у каждой из которых имеются
|
|
98
|
+
координаты `lat` и `lng`. Мы можем заранее определить модель для
|
|
99
|
+
объекта координат методом `defineModel` и использовать ее в других
|
|
100
|
+
коллекциях.
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
schema.defineModel({
|
|
104
|
+
name: 'latLng', // название новой модели
|
|
105
|
+
properties: { // поля модели
|
|
106
|
+
lat: DataType.NUMBER, // поле широты
|
|
107
|
+
lng: DataType.NUMBER, // поле долготы
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Параметры модели:
|
|
113
|
+
|
|
114
|
+
- `name: string` уникальное название (обязательно)
|
|
115
|
+
- `datasource: string` выбранный источник данных
|
|
116
|
+
- `properties: object` определения полей модели
|
|
117
|
+
- `relations: object` определения связей модели
|
|
118
|
+
|
|
119
|
+
Параметр `properties` принимает объект, ключи которого являются именами
|
|
120
|
+
полей, а значением тип поля или объект с дополнительными параметрами.
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
schema.defineModel({
|
|
124
|
+
name: 'latLng',
|
|
125
|
+
properties: {
|
|
126
|
+
lat: DataType.NUMBER, // краткое определение поля "lat"
|
|
127
|
+
lng: { // определение поля "lng" с параметром "required"
|
|
128
|
+
type: DataType.NUMBER,
|
|
129
|
+
required: true, // исключает null и undefined
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Типы данных:
|
|
136
|
+
|
|
137
|
+
- `DataType.ANY`
|
|
138
|
+
- `DataType.STRING`
|
|
139
|
+
- `DataType.NUMBER`
|
|
140
|
+
- `DataType.BOOLEAN`
|
|
141
|
+
- `DataType.ARRAY`
|
|
142
|
+
- `DataType.OBJECT`
|
|
143
|
+
|
|
144
|
+
Модель `latLng` всего лишь описывает структуру объекта координат, тогда
|
|
145
|
+
как торговая точка должна иметь реальную таблицу в базе. По аналогии с
|
|
146
|
+
предыдущим примером, добавим модель `place`, но дополнительно укажем
|
|
147
|
+
источник данных в параметре `datasource`
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
schema.defineModel({
|
|
151
|
+
name: 'place',
|
|
152
|
+
datasource: 'myMemory', // выбранный источник данных
|
|
153
|
+
properties: {
|
|
154
|
+
name: DataType.STRING, // поле для названия торговой точки
|
|
155
|
+
location: { // поле объекта координат
|
|
156
|
+
type: DataType.OBJECT, // допускать только объекты
|
|
157
|
+
model: 'latLng', // определение структуры объекта
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
В примере выше мы использовали модель `latLng` как структуру допустимого
|
|
164
|
+
значения поля `location`. Возможный документ данной коллекции может
|
|
165
|
+
выглядеть так:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"id": 1,
|
|
170
|
+
"name": "Burger King at Avenue Mall",
|
|
171
|
+
"location": {
|
|
172
|
+
"lat": 32.412891,
|
|
173
|
+
"lng": 34.7660061
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Стоит обратить внимание, что мы могли бы не объявлять параметр `properties`,
|
|
179
|
+
при этом теряя возможность проверки данных перед записью в базу.
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
schema.defineModel({
|
|
183
|
+
name: 'place',
|
|
184
|
+
adapter: 'myMemory',
|
|
185
|
+
// параметр "properties" не указан
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Параметры поля:
|
|
190
|
+
|
|
191
|
+
- `type: string` тип допустимого значения (обязательно)
|
|
192
|
+
- `itemType: string` тип элемента массива (для `type: 'array'`)
|
|
193
|
+
- `model: string` модель объекта (для `type: 'object'`)
|
|
194
|
+
- `primaryKey: boolean` объявить поле первичным ключом
|
|
195
|
+
- `columnName: string` переопределение названия колонки
|
|
196
|
+
- `columnType: string` тип колонки (определяется адаптером)
|
|
197
|
+
- `required: boolean` объявить поле обязательным
|
|
198
|
+
- `default: any` значение по умолчанию
|
|
199
|
+
|
|
200
|
+
#### Репозиторий
|
|
201
|
+
|
|
202
|
+
В отличие от `latLng`, модель `place` имеет источник данных с названием
|
|
203
|
+
`myMemory`, который был объявлен ранее. Наличие источника позволяет получить
|
|
204
|
+
репозиторий по названию модели.
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
const rep = schema.getRepository('place');
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Пример
|
|
211
|
+
|
|
41
212
|
Создаем модель `user`
|
|
42
213
|
|
|
43
214
|
```js
|
package/package.json
CHANGED
package/src/adapter/adapter.js
CHANGED
|
@@ -39,9 +39,9 @@ export class Adapter extends Service {
|
|
|
39
39
|
this._settings = settings;
|
|
40
40
|
// decorate only extended classes
|
|
41
41
|
if (this.constructor !== Adapter) {
|
|
42
|
-
this.getService(DataValidationDecorator).decorate(this);
|
|
43
42
|
this.getService(DataSanitizingDecorator).decorate(this);
|
|
44
43
|
this.getService(DefaultValuesDecorator).decorate(this);
|
|
44
|
+
this.getService(DataValidationDecorator).decorate(this);
|
|
45
45
|
this.getService(FieldsFilteringDecorator).decorate(this);
|
|
46
46
|
this.getService(InclusionDecorator).decorate(this);
|
|
47
47
|
}
|
|
@@ -33,9 +33,9 @@ describe('Adapter', function () {
|
|
|
33
33
|
|
|
34
34
|
it('decorates only extended adapter', function () {
|
|
35
35
|
const schema = new Schema();
|
|
36
|
-
const dec1 = schema.getService(
|
|
37
|
-
const dec2 = schema.getService(
|
|
38
|
-
const dec3 = schema.getService(
|
|
36
|
+
const dec1 = schema.getService(DataSanitizingDecorator);
|
|
37
|
+
const dec2 = schema.getService(DefaultValuesDecorator);
|
|
38
|
+
const dec3 = schema.getService(DataValidationDecorator);
|
|
39
39
|
const dec4 = schema.getService(FieldsFilteringDecorator);
|
|
40
40
|
const dec5 = schema.getService(InclusionDecorator);
|
|
41
41
|
const order = [];
|
|
@@ -31,7 +31,7 @@ export class ModelDataValidator extends Service {
|
|
|
31
31
|
propNames.forEach(propName => {
|
|
32
32
|
const propDef = propDefs[propName];
|
|
33
33
|
if (!propDef) return;
|
|
34
|
-
this.
|
|
34
|
+
this._validatePropertyValue(
|
|
35
35
|
modelName,
|
|
36
36
|
propName,
|
|
37
37
|
propDef,
|
|
@@ -48,7 +48,7 @@ export class ModelDataValidator extends Service {
|
|
|
48
48
|
* @param {string|object} propDef
|
|
49
49
|
* @param {*} propValue
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
_validatePropertyValue(modelName, propName, propDef, propValue) {
|
|
52
52
|
// undefined and null
|
|
53
53
|
if (propValue == null) {
|
|
54
54
|
const isRequired =
|
|
@@ -1587,6 +1587,61 @@ describe('ModelDataValidator', function () {
|
|
|
1587
1587
|
'an Array, but Object given.',
|
|
1588
1588
|
);
|
|
1589
1589
|
});
|
|
1590
|
+
|
|
1591
|
+
describe('the "model" option', function () {
|
|
1592
|
+
it('throws an error when the given object element has an invalid model', function () {
|
|
1593
|
+
const S = new Schema();
|
|
1594
|
+
S.defineModel({
|
|
1595
|
+
name: 'modelA',
|
|
1596
|
+
properties: {
|
|
1597
|
+
foo: DataType.STRING,
|
|
1598
|
+
},
|
|
1599
|
+
});
|
|
1600
|
+
S.defineModel({
|
|
1601
|
+
name: 'modelB',
|
|
1602
|
+
datasource: 'datasource',
|
|
1603
|
+
properties: {
|
|
1604
|
+
bar: {
|
|
1605
|
+
type: DataType.ARRAY,
|
|
1606
|
+
itemType: DataType.OBJECT,
|
|
1607
|
+
model: 'modelA',
|
|
1608
|
+
},
|
|
1609
|
+
},
|
|
1610
|
+
});
|
|
1611
|
+
const throwable = () =>
|
|
1612
|
+
S.getService(ModelDataValidator).validate('modelB', {
|
|
1613
|
+
bar: [{foo: 10}],
|
|
1614
|
+
});
|
|
1615
|
+
expect(throwable).to.throw(
|
|
1616
|
+
'The property "foo" of the model "modelA" must have ' +
|
|
1617
|
+
'a String, but Number given.',
|
|
1618
|
+
);
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
it('does not throw an error when the given object element has a valid model', function () {
|
|
1622
|
+
const S = new Schema();
|
|
1623
|
+
S.defineModel({
|
|
1624
|
+
name: 'modelA',
|
|
1625
|
+
properties: {
|
|
1626
|
+
foo: DataType.STRING,
|
|
1627
|
+
},
|
|
1628
|
+
});
|
|
1629
|
+
S.defineModel({
|
|
1630
|
+
name: 'modelB',
|
|
1631
|
+
datasource: 'datasource',
|
|
1632
|
+
properties: {
|
|
1633
|
+
bar: {
|
|
1634
|
+
type: DataType.ARRAY,
|
|
1635
|
+
itemType: DataType.OBJECT,
|
|
1636
|
+
model: 'modelA',
|
|
1637
|
+
},
|
|
1638
|
+
},
|
|
1639
|
+
});
|
|
1640
|
+
S.getService(ModelDataValidator).validate('modelB', {
|
|
1641
|
+
bar: [{foo: '10'}],
|
|
1642
|
+
});
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1590
1645
|
});
|
|
1591
1646
|
});
|
|
1592
1647
|
|
|
@@ -1883,6 +1938,59 @@ describe('ModelDataValidator', function () {
|
|
|
1883
1938
|
foo: {},
|
|
1884
1939
|
});
|
|
1885
1940
|
});
|
|
1941
|
+
|
|
1942
|
+
describe('the "model" option', function () {
|
|
1943
|
+
it('throws an error when the given object has an invalid model', function () {
|
|
1944
|
+
const S = new Schema();
|
|
1945
|
+
S.defineModel({
|
|
1946
|
+
name: 'modelA',
|
|
1947
|
+
properties: {
|
|
1948
|
+
foo: DataType.STRING,
|
|
1949
|
+
},
|
|
1950
|
+
});
|
|
1951
|
+
S.defineModel({
|
|
1952
|
+
name: 'modelB',
|
|
1953
|
+
datasource: 'datasource',
|
|
1954
|
+
properties: {
|
|
1955
|
+
bar: {
|
|
1956
|
+
type: DataType.OBJECT,
|
|
1957
|
+
model: 'modelA',
|
|
1958
|
+
},
|
|
1959
|
+
},
|
|
1960
|
+
});
|
|
1961
|
+
const throwable = () =>
|
|
1962
|
+
S.getService(ModelDataValidator).validate('modelB', {
|
|
1963
|
+
bar: {foo: 10},
|
|
1964
|
+
});
|
|
1965
|
+
expect(throwable).to.throw(
|
|
1966
|
+
'The property "foo" of the model "modelA" must have ' +
|
|
1967
|
+
'a String, but Number given.',
|
|
1968
|
+
);
|
|
1969
|
+
});
|
|
1970
|
+
|
|
1971
|
+
it('does not throw an error when the given object has a valid model', function () {
|
|
1972
|
+
const S = new Schema();
|
|
1973
|
+
S.defineModel({
|
|
1974
|
+
name: 'modelA',
|
|
1975
|
+
properties: {
|
|
1976
|
+
foo: DataType.STRING,
|
|
1977
|
+
},
|
|
1978
|
+
});
|
|
1979
|
+
S.defineModel({
|
|
1980
|
+
name: 'modelB',
|
|
1981
|
+
datasource: 'datasource',
|
|
1982
|
+
properties: {
|
|
1983
|
+
bar: {
|
|
1984
|
+
type: DataType.OBJECT,
|
|
1985
|
+
model: 'modelA',
|
|
1986
|
+
},
|
|
1987
|
+
},
|
|
1988
|
+
});
|
|
1989
|
+
S.getService(ModelDataValidator).validate('modelB', {
|
|
1990
|
+
bar: {foo: '10'},
|
|
1991
|
+
});
|
|
1992
|
+
});
|
|
1993
|
+
});
|
|
1886
1994
|
});
|
|
1887
1995
|
});
|
|
1888
1996
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {Service} from '@e22m4u/js-service';
|
|
2
2
|
import {DataType as Type} from './data-type.js';
|
|
3
|
+
import {capitalize} from '../../../utils/index.js';
|
|
3
4
|
import {InvalidArgumentError} from '../../../errors/index.js';
|
|
4
5
|
import {PrimaryKeysDefinitionValidator} from './primary-keys-definition-validator.js';
|
|
5
|
-
import {DefaultValuesDefinitionValidator} from './default-values-definition-validator.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Properties definition validator.
|
|
@@ -38,10 +38,6 @@ export class PropertiesDefinitionValidator extends Service {
|
|
|
38
38
|
modelName,
|
|
39
39
|
propDefs,
|
|
40
40
|
);
|
|
41
|
-
this.getService(DefaultValuesDefinitionValidator).validate(
|
|
42
|
-
modelName,
|
|
43
|
-
propDefs,
|
|
44
|
-
);
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
/**
|
|
@@ -182,13 +178,30 @@ export class PropertiesDefinitionValidator extends Service {
|
|
|
182
178
|
modelName,
|
|
183
179
|
propDef.type,
|
|
184
180
|
);
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
181
|
+
if (
|
|
182
|
+
propDef.model &&
|
|
183
|
+
propDef.type !== Type.OBJECT &&
|
|
184
|
+
propDef.itemType !== Type.OBJECT
|
|
185
|
+
) {
|
|
186
|
+
if (propDef.type !== Type.ARRAY) {
|
|
187
|
+
throw new InvalidArgumentError(
|
|
188
|
+
'The option "model" is not supported for %s property type, ' +
|
|
189
|
+
'so the property %v of the model %v should not have ' +
|
|
190
|
+
'the option "model" to be provided.',
|
|
191
|
+
capitalize(propDef.type),
|
|
192
|
+
propName,
|
|
193
|
+
modelName,
|
|
194
|
+
);
|
|
195
|
+
} else {
|
|
196
|
+
throw new InvalidArgumentError(
|
|
197
|
+
'The option "model" is not supported for Array property type of %s, ' +
|
|
198
|
+
'so the property %v of the model %v should not have ' +
|
|
199
|
+
'the option "model" to be provided.',
|
|
200
|
+
capitalize(propDef.itemType),
|
|
201
|
+
propName,
|
|
202
|
+
modelName,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
193
206
|
}
|
|
194
207
|
}
|
|
@@ -4,7 +4,6 @@ import {DataType} from './data-type.js';
|
|
|
4
4
|
import {format} from '@e22m4u/js-format';
|
|
5
5
|
import {PropertiesDefinitionValidator} from './properties-definition-validator.js';
|
|
6
6
|
import {PrimaryKeysDefinitionValidator} from './primary-keys-definition-validator.js';
|
|
7
|
-
import {DefaultValuesDefinitionValidator} from './default-values-definition-validator.js';
|
|
8
7
|
|
|
9
8
|
const S = new PropertiesDefinitionValidator();
|
|
10
9
|
const sandbox = chai.spy.sandbox();
|
|
@@ -333,7 +332,7 @@ describe('PropertiesDefinitionValidator', function () {
|
|
|
333
332
|
validate(DataType.ARRAY);
|
|
334
333
|
});
|
|
335
334
|
|
|
336
|
-
it('
|
|
335
|
+
it('the option "model" requires the "object" property type', function () {
|
|
337
336
|
const validate = v => () => {
|
|
338
337
|
const foo = {
|
|
339
338
|
type: v,
|
|
@@ -341,28 +340,46 @@ describe('PropertiesDefinitionValidator', function () {
|
|
|
341
340
|
};
|
|
342
341
|
S.validate('model', {foo});
|
|
343
342
|
};
|
|
344
|
-
const error =
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
expect(validate(DataType.
|
|
352
|
-
validate(DataType.
|
|
343
|
+
const error = v =>
|
|
344
|
+
format(
|
|
345
|
+
'The option "model" is not supported for %s property type, ' +
|
|
346
|
+
'so the property "foo" of the model "model" should not have ' +
|
|
347
|
+
'the option "model" to be provided.',
|
|
348
|
+
v,
|
|
349
|
+
);
|
|
350
|
+
expect(validate(DataType.ANY)).to.throw(error('Any'));
|
|
351
|
+
expect(validate(DataType.STRING)).to.throw(error('String'));
|
|
352
|
+
expect(validate(DataType.NUMBER)).to.throw(error('Number'));
|
|
353
|
+
expect(validate(DataType.BOOLEAN)).to.throw(error('Boolean'));
|
|
354
|
+
validate(DataType.OBJECT)();
|
|
353
355
|
});
|
|
354
356
|
|
|
355
|
-
it('
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
357
|
+
it('the option "model" requires the "object" item type', function () {
|
|
358
|
+
const validate = v => () => {
|
|
359
|
+
const foo = {
|
|
360
|
+
type: DataType.ARRAY,
|
|
361
|
+
itemType: v,
|
|
362
|
+
model: 'model',
|
|
363
|
+
};
|
|
364
|
+
S.validate('model', {foo});
|
|
365
|
+
};
|
|
366
|
+
const error = v =>
|
|
367
|
+
format(
|
|
368
|
+
'The option "model" is not supported for Array property type of %s, ' +
|
|
369
|
+
'so the property "foo" of the model "model" should not have ' +
|
|
370
|
+
'the option "model" to be provided.',
|
|
371
|
+
v,
|
|
372
|
+
);
|
|
373
|
+
expect(validate(DataType.ANY)).to.throw(error('Any'));
|
|
374
|
+
expect(validate(DataType.STRING)).to.throw(error('String'));
|
|
375
|
+
expect(validate(DataType.NUMBER)).to.throw(error('Number'));
|
|
376
|
+
expect(validate(DataType.BOOLEAN)).to.throw(error('Boolean'));
|
|
377
|
+
expect(validate(DataType.ARRAY)).to.throw(error('Array'));
|
|
378
|
+
validate(DataType.OBJECT)();
|
|
362
379
|
});
|
|
363
380
|
|
|
364
|
-
it('uses
|
|
365
|
-
const V = S.getService(
|
|
381
|
+
it('uses PrimaryKeysDefinitionValidator to validate primary keys', function () {
|
|
382
|
+
const V = S.getService(PrimaryKeysDefinitionValidator);
|
|
366
383
|
sandbox.on(V, 'validate');
|
|
367
384
|
const propDefs = {};
|
|
368
385
|
S.validate('model', propDefs);
|
|
@@ -237,11 +237,9 @@ export type NormalizedFieldsClause = string[];
|
|
|
237
237
|
*/
|
|
238
238
|
export declare type IncludeClause =
|
|
239
239
|
| string
|
|
240
|
-
| string[]
|
|
241
240
|
| NestedIncludeClause
|
|
242
|
-
| NestedIncludeClause[]
|
|
243
241
|
| NormalizedIncludeClause
|
|
244
|
-
|
|
|
242
|
+
| IncludeClause[];
|
|
245
243
|
|
|
246
244
|
/**
|
|
247
245
|
* Nested include clause.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {Service} from '@e22m4u/js-service';
|
|
2
|
-
import {PropertyDefinitionMap} from '../model-definition.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Default values definition validator.
|
|
6
|
-
*/
|
|
7
|
-
export declare class DefaultValuesDefinitionValidator extends Service {
|
|
8
|
-
/**
|
|
9
|
-
* Validate.
|
|
10
|
-
*
|
|
11
|
-
* @param modelName
|
|
12
|
-
* @param propDefs
|
|
13
|
-
*/
|
|
14
|
-
validate(modelName: string, propDefs: PropertyDefinitionMap): void;
|
|
15
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import {Service} from '@e22m4u/js-service';
|
|
2
|
-
import {ModelDataValidator} from '../model-data-validator.js';
|
|
3
|
-
import {InvalidArgumentError} from '../../../errors/index.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Default values definition validator.
|
|
7
|
-
*/
|
|
8
|
-
export class DefaultValuesDefinitionValidator extends Service {
|
|
9
|
-
/**
|
|
10
|
-
* Validate.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} modelName
|
|
13
|
-
* @param {object} propDefs
|
|
14
|
-
*/
|
|
15
|
-
validate(modelName, propDefs) {
|
|
16
|
-
if (!modelName || typeof modelName !== 'string')
|
|
17
|
-
throw new InvalidArgumentError(
|
|
18
|
-
'The first argument of DefaultValuesDefinitionValidator.validate ' +
|
|
19
|
-
'should be a non-empty String, but %v given.',
|
|
20
|
-
modelName,
|
|
21
|
-
);
|
|
22
|
-
if (!propDefs || typeof propDefs !== 'object' || Array.isArray(propDefs))
|
|
23
|
-
throw new InvalidArgumentError(
|
|
24
|
-
'The provided option "properties" of the model %v ' +
|
|
25
|
-
'should be an Object, but %v given.',
|
|
26
|
-
modelName,
|
|
27
|
-
propDefs,
|
|
28
|
-
);
|
|
29
|
-
Object.keys(propDefs).forEach(propName => {
|
|
30
|
-
const propDef = propDefs[propName];
|
|
31
|
-
if (typeof propDef === 'string') return;
|
|
32
|
-
if (!('default' in propDef)) return;
|
|
33
|
-
const propValue =
|
|
34
|
-
propDef.default instanceof Function
|
|
35
|
-
? propDef.default()
|
|
36
|
-
: propDef.default;
|
|
37
|
-
try {
|
|
38
|
-
this.getService(ModelDataValidator).validatePropertyValue(
|
|
39
|
-
modelName,
|
|
40
|
-
propName,
|
|
41
|
-
propDef,
|
|
42
|
-
propValue,
|
|
43
|
-
);
|
|
44
|
-
} catch (error) {
|
|
45
|
-
if (error instanceof InvalidArgumentError)
|
|
46
|
-
throw new InvalidArgumentError(
|
|
47
|
-
`A default value is invalid. ${error.message}`,
|
|
48
|
-
);
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import {expect} from 'chai';
|
|
2
|
-
import {DataType} from './data-type.js';
|
|
3
|
-
import {format} from '@e22m4u/js-format';
|
|
4
|
-
import {DefaultValuesDefinitionValidator} from './default-values-definition-validator.js';
|
|
5
|
-
|
|
6
|
-
const S = new DefaultValuesDefinitionValidator();
|
|
7
|
-
|
|
8
|
-
describe('DefaultValuesDefinitionValidator', function () {
|
|
9
|
-
describe('validate', function () {
|
|
10
|
-
it('requires a first argument to be a non-empty string', function () {
|
|
11
|
-
const validate = v => () => S.validate(v, {});
|
|
12
|
-
const error = v =>
|
|
13
|
-
format(
|
|
14
|
-
'The first argument of DefaultValuesDefinitionValidator.validate ' +
|
|
15
|
-
'should be a non-empty String, but %s given.',
|
|
16
|
-
v,
|
|
17
|
-
);
|
|
18
|
-
expect(validate('')).to.throw(error('""'));
|
|
19
|
-
expect(validate(10)).to.throw(error('10'));
|
|
20
|
-
expect(validate(true)).to.throw(error('true'));
|
|
21
|
-
expect(validate(false)).to.throw(error('false'));
|
|
22
|
-
expect(validate([])).to.throw(error('Array'));
|
|
23
|
-
expect(validate({})).to.throw(error('Object'));
|
|
24
|
-
expect(validate(undefined)).to.throw(error('undefined'));
|
|
25
|
-
expect(validate(null)).to.throw(error('null'));
|
|
26
|
-
validate('model')();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('requires a second argument to be an object', function () {
|
|
30
|
-
const validate = v => () => S.validate('model', v);
|
|
31
|
-
const error = v =>
|
|
32
|
-
format(
|
|
33
|
-
'The provided option "properties" of the model "model" ' +
|
|
34
|
-
'should be an Object, but %s given.',
|
|
35
|
-
v,
|
|
36
|
-
);
|
|
37
|
-
expect(validate('str')).to.throw(error('"str"'));
|
|
38
|
-
expect(validate(10)).to.throw(error('10'));
|
|
39
|
-
expect(validate(true)).to.throw(error('true'));
|
|
40
|
-
expect(validate(false)).to.throw(error('false'));
|
|
41
|
-
expect(validate([])).to.throw(error('Array'));
|
|
42
|
-
expect(validate(undefined)).to.throw(error('undefined'));
|
|
43
|
-
expect(validate(null)).to.throw(error('null'));
|
|
44
|
-
validate({})();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('does not throw an error if no properties defined', function () {
|
|
48
|
-
S.validate('model', {});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('does not throw an error if no default value specified for a required property', function () {
|
|
52
|
-
S.validate('model', {
|
|
53
|
-
foo: {
|
|
54
|
-
type: DataType.STRING,
|
|
55
|
-
required: true,
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('does not throw an error if a default value matches a property type', function () {
|
|
61
|
-
S.validate('model', {
|
|
62
|
-
foo: {
|
|
63
|
-
type: DataType.BOOLEAN,
|
|
64
|
-
default: false,
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('does not throw an error if a default value from a factory function matches a property type', function () {
|
|
70
|
-
S.validate('model', {
|
|
71
|
-
foo: {
|
|
72
|
-
type: DataType.BOOLEAN,
|
|
73
|
-
default: () => false,
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('throws an error if a default value does not match a property type', function () {
|
|
79
|
-
const throwable = () =>
|
|
80
|
-
S.validate('model', {
|
|
81
|
-
foo: {
|
|
82
|
-
type: DataType.STRING,
|
|
83
|
-
default: 10,
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
expect(throwable).to.throw(
|
|
87
|
-
'A default value is invalid. The property "foo" of the model ' +
|
|
88
|
-
'"model" must have a String, but Number given.',
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('throws an error if a default value from a factory function does not match a property type', function () {
|
|
93
|
-
const throwable = () =>
|
|
94
|
-
S.validate('model', {
|
|
95
|
-
foo: {
|
|
96
|
-
type: DataType.STRING,
|
|
97
|
-
default: () => 10,
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
expect(throwable).to.throw(
|
|
101
|
-
'A default value is invalid. The property "foo" of the model ' +
|
|
102
|
-
'"model" must have a String, but Number given.',
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('throws an error if an array element of a default value does not match an item type', function () {
|
|
107
|
-
const throwable = () =>
|
|
108
|
-
S.validate('model', {
|
|
109
|
-
foo: {
|
|
110
|
-
type: DataType.ARRAY,
|
|
111
|
-
itemType: DataType.STRING,
|
|
112
|
-
default: [10],
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
expect(throwable).to.throw(
|
|
116
|
-
'A default value is invalid. The array property "foo" of the model "model" ' +
|
|
117
|
-
'must have a String element, but Number given.',
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('throws an error if an array element from a default value factory does not match an item type', function () {
|
|
122
|
-
const throwable = () =>
|
|
123
|
-
S.validate('model', {
|
|
124
|
-
foo: {
|
|
125
|
-
type: DataType.ARRAY,
|
|
126
|
-
itemType: DataType.STRING,
|
|
127
|
-
default: () => [10],
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
expect(throwable).to.throw(
|
|
131
|
-
'A default value is invalid. The array property "foo" of the model "model" ' +
|
|
132
|
-
'must have a String element, but Number given.',
|
|
133
|
-
);
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
});
|