@e22m4u/js-repository 0.0.39 → 0.0.41
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 +110 -14
- package/package.json +6 -6
- package/src/adapter/adapter.d.ts +13 -0
- package/src/adapter/adapter.js +15 -0
- package/src/adapter/builtin/memory-adapter.d.ts +13 -0
- package/src/adapter/builtin/memory-adapter.js +41 -0
- package/src/adapter/builtin/memory-adapter.spec.js +575 -7
- package/src/adapter/decorator/data-sanitizing-decorator.js +6 -0
- package/src/adapter/decorator/data-sanitizing-decorator.spec.js +13 -0
- package/src/adapter/decorator/data-validation-decorator.js +6 -0
- package/src/adapter/decorator/data-validation-decorator.spec.js +5 -0
- package/src/adapter/decorator/default-values-decorator.js +6 -0
- package/src/adapter/decorator/default-values-decorator.spec.js +46 -0
- package/src/filter/filter-clause.d.ts +3 -3
- package/src/filter/where-clause-tool.js +6 -2
- package/src/filter/where-clause-tool.spec.js +8 -3
- package/src/repository/repository.d.ts +11 -0
- package/src/repository/repository.js +12 -0
- package/src/repository/repository.spec.js +26 -0
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
npm install @e22m4u/js-repository
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Опционально устанавливаем адаптер. Например, если
|
|
12
|
-
|
|
11
|
+
Опционально устанавливаем адаптер. Например, если используется
|
|
12
|
+
*MongoDB*, то для подключения потребуется добавить
|
|
13
13
|
[адаптер mongodb](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter)
|
|
14
14
|
как отдельную зависимость.
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ npm install @e22m4u/js-repository
|
|
|
17
17
|
npm install @e22m4u/js-repository-mongodb-adapter
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
**Список доступных адаптеров:**
|
|
21
21
|
|
|
22
22
|
| адаптер | описание |
|
|
23
23
|
|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
@@ -28,13 +28,13 @@ npm install @e22m4u/js-repository-mongodb-adapter
|
|
|
28
28
|
|
|
29
29
|
Модуль позволяет спроектировать систему связанных данных, доступ к которым
|
|
30
30
|
осуществляется посредством репозиториев. Каждый репозиторий имеет собственную
|
|
31
|
-
|
|
31
|
+
модель данных, которая описывает структуру определенной коллекции в базе,
|
|
32
32
|
а так же определяет связи к другим коллекциям.
|
|
33
33
|
|
|
34
34
|
```mermaid
|
|
35
35
|
flowchart LR
|
|
36
36
|
|
|
37
|
-
A[Datasource]-->B[Model]-->С[Repository];
|
|
37
|
+
A[Datasource]-->B[Data Model]-->С[Repository];
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Использование
|
|
@@ -54,7 +54,7 @@ const schema = new Schema();
|
|
|
54
54
|
- `defineModel(modelDef: object): this` - добавить модель
|
|
55
55
|
- `getRepository(modelName: string): Repository` - получить репозиторий
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
### Источник данных
|
|
58
58
|
|
|
59
59
|
Источник описывает способ подключения к базе и используемый адаптер.
|
|
60
60
|
Если адаптер имеет настройки, то они передаются вместе с объектом
|
|
@@ -72,7 +72,7 @@ schema.defineDatasource({
|
|
|
72
72
|
});
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
**Параметры источника:**
|
|
76
76
|
|
|
77
77
|
- `name: string` уникальное название
|
|
78
78
|
- `adapter: string` выбранный адаптер
|
|
@@ -88,7 +88,7 @@ schema.defineDatasource({
|
|
|
88
88
|
});
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
### Модель данных
|
|
92
92
|
|
|
93
93
|
Когда источники определены, можно перейти к описанию моделей данных.
|
|
94
94
|
Модель может определять как структуру какого-либо объекта,
|
|
@@ -109,7 +109,7 @@ schema.defineModel({
|
|
|
109
109
|
});
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
**Параметры модели:**
|
|
113
113
|
|
|
114
114
|
- `name: string` уникальное название (обязательно)
|
|
115
115
|
- `datasource: string` выбранный источник данных
|
|
@@ -124,15 +124,15 @@ schema.defineModel({
|
|
|
124
124
|
name: 'latLng',
|
|
125
125
|
properties: {
|
|
126
126
|
lat: DataType.NUMBER, // краткое определение поля "lat"
|
|
127
|
-
lng: { // определение поля "lng"
|
|
128
|
-
type: DataType.NUMBER,
|
|
127
|
+
lng: { // расширенное определение поля "lng"
|
|
128
|
+
type: DataType.NUMBER, // тип допустимого значения
|
|
129
129
|
required: true, // исключает null и undefined
|
|
130
130
|
},
|
|
131
131
|
},
|
|
132
132
|
});
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
**Типы данных:**
|
|
136
136
|
|
|
137
137
|
- `DataType.ANY`
|
|
138
138
|
- `DataType.STRING`
|
|
@@ -186,7 +186,7 @@ schema.defineModel({
|
|
|
186
186
|
});
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
**Параметры поля:**
|
|
190
190
|
|
|
191
191
|
- `type: string` тип допустимого значения (обязательно)
|
|
192
192
|
- `itemType: string` тип элемента массива (для `type: 'array'`)
|
|
@@ -197,7 +197,7 @@ schema.defineModel({
|
|
|
197
197
|
- `required: boolean` объявить поле обязательным
|
|
198
198
|
- `default: any` значение по умолчанию
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
### Репозиторий
|
|
201
201
|
|
|
202
202
|
В отличие от `latLng`, модель `place` имеет источник данных с названием
|
|
203
203
|
`myMemory`, который был объявлен ранее. Наличие источника позволяет получить
|
|
@@ -207,6 +207,101 @@ schema.defineModel({
|
|
|
207
207
|
const rep = schema.getRepository('place');
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
+
**Методы:**
|
|
211
|
+
|
|
212
|
+
- `create(data, filter = undefined)`
|
|
213
|
+
- `replaceById(id, data, filter = undefined)`
|
|
214
|
+
- `replaceOrCreate(data, filter = undefined)`
|
|
215
|
+
- `patch(data, where = undefined)`
|
|
216
|
+
- `patchById(id, data, filter = undefined)`
|
|
217
|
+
- `find(filter = undefined)`
|
|
218
|
+
- `findOne(filter = undefined)`
|
|
219
|
+
- `findById(id, filter = undefined)`
|
|
220
|
+
- `delete(where = undefined)`
|
|
221
|
+
- `deleteById(id)`
|
|
222
|
+
- `exists(id)`
|
|
223
|
+
- `count(where = undefined)`
|
|
224
|
+
|
|
225
|
+
#### create(data, filter = undefined)
|
|
226
|
+
|
|
227
|
+
Создадим торговую точку методом `create` используя репозиторий из примера
|
|
228
|
+
выше. Метод возвращает документ, который был записан в базу, включая присвоенный
|
|
229
|
+
идентификатор.
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
const place = await rep.create({
|
|
233
|
+
"name": "Burger King at Avenue Mall",
|
|
234
|
+
"location": {
|
|
235
|
+
"lat": 32.412891,
|
|
236
|
+
"lng": 34.7660061
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
console.log(place);
|
|
241
|
+
// {
|
|
242
|
+
// "id": 1,
|
|
243
|
+
// "name": "Burger King at Avenue Mall",
|
|
244
|
+
// "location": {
|
|
245
|
+
// "lat": 32.412891,
|
|
246
|
+
// "lng": 34.7660061
|
|
247
|
+
// }
|
|
248
|
+
// }
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### replaceById(id, data, filter = undefined)
|
|
252
|
+
|
|
253
|
+
Добавленный в базу документ можно полностью заменить зная его идентификатор.
|
|
254
|
+
Воспользуемся методом `replaceById`, который перезапишет данные по значению
|
|
255
|
+
первичного ключа.
|
|
256
|
+
|
|
257
|
+
```js
|
|
258
|
+
// {
|
|
259
|
+
// "id": 1,
|
|
260
|
+
// "name": "Burger King at Avenue Mall",
|
|
261
|
+
// "location": {
|
|
262
|
+
// "lat": 32.412891,
|
|
263
|
+
// "lng": 34.7660061
|
|
264
|
+
// }
|
|
265
|
+
// }
|
|
266
|
+
const result = rep.replaceById(place.id, {
|
|
267
|
+
name: 'Terminal 21 Shopping Mall',
|
|
268
|
+
address: 'Sukhumvit 19 Alley'
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
console.log(result);
|
|
272
|
+
// {
|
|
273
|
+
// "id": 1,
|
|
274
|
+
// "name": "Terminal 21 Shopping Mall",
|
|
275
|
+
// "address": "Sukhumvit 19 Alley"
|
|
276
|
+
// }
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### patchById(id, data, filter = undefined)
|
|
280
|
+
|
|
281
|
+
В отличие от `replaceById`, данный метод не удаляет поля, которые
|
|
282
|
+
не были переданы, что позволяет обновить только часть документа,
|
|
283
|
+
не затрагивая другие данные.
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
// {
|
|
287
|
+
// "id": 1,
|
|
288
|
+
// "name": "Terminal 21 Shopping Mall",
|
|
289
|
+
// "address": "Sukhumvit 19 Alley"
|
|
290
|
+
// }
|
|
291
|
+
const result = rep.patchById(place.id, {
|
|
292
|
+
address: 'Moo 6',
|
|
293
|
+
city: 'Pattaya',
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
console.log(result);
|
|
297
|
+
// {
|
|
298
|
+
// "id": 1,
|
|
299
|
+
// "name": "Terminal 21 Shopping Mall",
|
|
300
|
+
// "address": "Moo 6",
|
|
301
|
+
// "city": "Pattaya"
|
|
302
|
+
// }
|
|
303
|
+
```
|
|
304
|
+
|
|
210
305
|
## Пример
|
|
211
306
|
|
|
212
307
|
Создаем модель `user`
|
|
@@ -275,6 +370,7 @@ await userRep.deleteById(fedor.id); // true
|
|
|
275
370
|
- `create(data, filter = undefined)`
|
|
276
371
|
- `replaceById(id, data, filter = undefined)`
|
|
277
372
|
- `replaceOrCreate(data, filter = undefined)`
|
|
373
|
+
- `patch(data, where = undefined)`
|
|
278
374
|
- `patchById(id, data, filter = undefined)`
|
|
279
375
|
- `find(filter = undefined)`
|
|
280
376
|
- `findOne(filter = undefined)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e22m4u/js-repository",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.41",
|
|
4
4
|
"description": "Абстракция для работы с базами данных для Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -37,21 +37,21 @@
|
|
|
37
37
|
"@e22m4u/js-format": "0.0.7"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@commitlint/cli": "^17.7.
|
|
40
|
+
"@commitlint/cli": "^17.7.2",
|
|
41
41
|
"@commitlint/config-conventional": "^17.7.0",
|
|
42
42
|
"c8": "^8.0.1",
|
|
43
|
-
"chai": "^4.3.
|
|
43
|
+
"chai": "^4.3.10",
|
|
44
44
|
"chai-as-promised": "^7.1.1",
|
|
45
45
|
"chai-spies": "^1.0.0",
|
|
46
46
|
"chai-subset": "^1.6.0",
|
|
47
|
-
"eslint": "^8.
|
|
47
|
+
"eslint": "^8.51.0",
|
|
48
48
|
"eslint-config-prettier": "^9.0.0",
|
|
49
49
|
"eslint-plugin-chai-expect": "^3.0.0",
|
|
50
50
|
"eslint-plugin-jsdoc": "^46.8.2",
|
|
51
|
-
"eslint-plugin-mocha": "^10.
|
|
51
|
+
"eslint-plugin-mocha": "^10.2.0",
|
|
52
52
|
"husky": "^8.0.3",
|
|
53
53
|
"mocha": "^10.2.0",
|
|
54
|
-
"prettier": "^3.0.
|
|
54
|
+
"prettier": "^3.0.3",
|
|
55
55
|
"typescript": "^5.2.2"
|
|
56
56
|
}
|
|
57
57
|
}
|
package/src/adapter/adapter.d.ts
CHANGED
|
@@ -52,6 +52,19 @@ export declare class Adapter extends Service {
|
|
|
52
52
|
filter?: ItemFilterClause,
|
|
53
53
|
): Promise<ModelData>;
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Patch.
|
|
57
|
+
*
|
|
58
|
+
* @param modelName
|
|
59
|
+
* @param modelData
|
|
60
|
+
* @param where
|
|
61
|
+
*/
|
|
62
|
+
patch(
|
|
63
|
+
modelName: string,
|
|
64
|
+
modelData: ModelData,
|
|
65
|
+
where?: WhereClause,
|
|
66
|
+
): Promise<number>;
|
|
67
|
+
|
|
55
68
|
/**
|
|
56
69
|
* Patch by id.
|
|
57
70
|
*
|
package/src/adapter/adapter.js
CHANGED
|
@@ -78,6 +78,21 @@ export class Adapter extends Service {
|
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Patch.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} modelName
|
|
85
|
+
* @param {object} modelData
|
|
86
|
+
* @param {object|undefined} where
|
|
87
|
+
* @returns {Promise<number>}
|
|
88
|
+
*/
|
|
89
|
+
patch(modelName, modelData, where = undefined) {
|
|
90
|
+
throw new NotImplementedError(
|
|
91
|
+
'%s.patch is not implemented.',
|
|
92
|
+
this.constructor.name,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
81
96
|
/**
|
|
82
97
|
* Patch by id.
|
|
83
98
|
*
|
|
@@ -52,6 +52,19 @@ export declare class MemoryAdapter extends Adapter {
|
|
|
52
52
|
filter?: ItemFilterClause,
|
|
53
53
|
): Promise<ModelData>;
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Patch.
|
|
57
|
+
*
|
|
58
|
+
* @param modelName
|
|
59
|
+
* @param modelData
|
|
60
|
+
* @param where
|
|
61
|
+
*/
|
|
62
|
+
patch(
|
|
63
|
+
modelName: string,
|
|
64
|
+
modelData: ModelData,
|
|
65
|
+
where?: WhereClause,
|
|
66
|
+
): Promise<number>;
|
|
67
|
+
|
|
55
68
|
/**
|
|
56
69
|
* Patch by id.
|
|
57
70
|
*
|
|
@@ -154,6 +154,47 @@ export class MemoryAdapter extends Adapter {
|
|
|
154
154
|
).convertColumnNamesToPropertyNames(modelName, tableData);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Patch.
|
|
159
|
+
*
|
|
160
|
+
* @param {string} modelName
|
|
161
|
+
* @param {object} modelData
|
|
162
|
+
* @param {object|undefined} where
|
|
163
|
+
* @returns {Promise<number>}
|
|
164
|
+
*/
|
|
165
|
+
async patch(modelName, modelData, where = undefined) {
|
|
166
|
+
const table = this._getTableOrCreate(modelName);
|
|
167
|
+
const tableItems = Array.from(table.values());
|
|
168
|
+
if (!tableItems.length) return 0;
|
|
169
|
+
let modelItems = tableItems.map(tableItem =>
|
|
170
|
+
this.getService(ModelDefinitionUtils).convertColumnNamesToPropertyNames(
|
|
171
|
+
modelName,
|
|
172
|
+
tableItem,
|
|
173
|
+
),
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
if (where && typeof where === 'object')
|
|
177
|
+
modelItems = this.getService(WhereClauseTool).filter(modelItems, where);
|
|
178
|
+
const size = modelItems.length;
|
|
179
|
+
|
|
180
|
+
const pkPropName =
|
|
181
|
+
this.getService(ModelDefinitionUtils).getPrimaryKeyAsPropertyName(
|
|
182
|
+
modelName,
|
|
183
|
+
);
|
|
184
|
+
modelData = cloneDeep(modelData);
|
|
185
|
+
delete modelData[pkPropName];
|
|
186
|
+
|
|
187
|
+
modelItems.forEach(existingModelData => {
|
|
188
|
+
const mergedModelData = Object.assign({}, existingModelData, modelData);
|
|
189
|
+
const mergedTableData = this.getService(
|
|
190
|
+
ModelDefinitionUtils,
|
|
191
|
+
).convertPropertyNamesToColumnNames(modelName, mergedModelData);
|
|
192
|
+
const idValue = existingModelData[pkPropName];
|
|
193
|
+
table.set(idValue, mergedTableData);
|
|
194
|
+
});
|
|
195
|
+
return size;
|
|
196
|
+
}
|
|
197
|
+
|
|
157
198
|
/**
|
|
158
199
|
* Patch by id.
|
|
159
200
|
*
|
|
@@ -1133,6 +1133,574 @@ describe('MemoryAdapter', function () {
|
|
|
1133
1133
|
});
|
|
1134
1134
|
});
|
|
1135
1135
|
|
|
1136
|
+
describe('patch', function () {
|
|
1137
|
+
it('updates only provided properties for all items and returns their number', async function () {
|
|
1138
|
+
const schema = new Schema();
|
|
1139
|
+
schema.defineDatasource({
|
|
1140
|
+
name: 'memory',
|
|
1141
|
+
adapter: 'memory',
|
|
1142
|
+
});
|
|
1143
|
+
schema.defineModel({
|
|
1144
|
+
name: 'model',
|
|
1145
|
+
datasource: 'memory',
|
|
1146
|
+
properties: {
|
|
1147
|
+
foo: DataType.STRING,
|
|
1148
|
+
bar: DataType.STRING,
|
|
1149
|
+
},
|
|
1150
|
+
});
|
|
1151
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1152
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1153
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1154
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1155
|
+
const created1 = await adapter.create('model', input1);
|
|
1156
|
+
const created2 = await adapter.create('model', input2);
|
|
1157
|
+
const created3 = await adapter.create('model', input3);
|
|
1158
|
+
const id1 = created1[DEF_PK];
|
|
1159
|
+
const id2 = created2[DEF_PK];
|
|
1160
|
+
const id3 = created3[DEF_PK];
|
|
1161
|
+
const table = adapter._getTableOrCreate('model');
|
|
1162
|
+
const createdItems = Array.from(table.values());
|
|
1163
|
+
expect(createdItems).to.be.eql([
|
|
1164
|
+
{[DEF_PK]: id1, ...input1},
|
|
1165
|
+
{[DEF_PK]: id2, ...input2},
|
|
1166
|
+
{[DEF_PK]: id3, ...input3},
|
|
1167
|
+
]);
|
|
1168
|
+
const result = await adapter.patch('model', {foo: 'd1'});
|
|
1169
|
+
expect(result).to.be.eq(3);
|
|
1170
|
+
const patchedItems = Array.from(table.values());
|
|
1171
|
+
expect(patchedItems).to.be.eql([
|
|
1172
|
+
{[DEF_PK]: id1, foo: 'd1', bar: 'a2'},
|
|
1173
|
+
{[DEF_PK]: id2, foo: 'd1', bar: 'b2'},
|
|
1174
|
+
{[DEF_PK]: id3, foo: 'd1', bar: 'c2'},
|
|
1175
|
+
]);
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
it('does not throw an error if a partial data does not have required property', async function () {
|
|
1179
|
+
const schema = new Schema();
|
|
1180
|
+
schema.defineDatasource({
|
|
1181
|
+
name: 'memory',
|
|
1182
|
+
adapter: 'memory',
|
|
1183
|
+
});
|
|
1184
|
+
schema.defineModel({
|
|
1185
|
+
name: 'model',
|
|
1186
|
+
datasource: 'memory',
|
|
1187
|
+
properties: {
|
|
1188
|
+
foo: DataType.STRING,
|
|
1189
|
+
bar: {
|
|
1190
|
+
type: DataType.STRING,
|
|
1191
|
+
required: true,
|
|
1192
|
+
},
|
|
1193
|
+
},
|
|
1194
|
+
});
|
|
1195
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1196
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1197
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1198
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1199
|
+
const created1 = await adapter.create('model', input1);
|
|
1200
|
+
const created2 = await adapter.create('model', input2);
|
|
1201
|
+
const created3 = await adapter.create('model', input3);
|
|
1202
|
+
const id1 = created1[DEF_PK];
|
|
1203
|
+
const id2 = created2[DEF_PK];
|
|
1204
|
+
const id3 = created3[DEF_PK];
|
|
1205
|
+
const table = adapter._getTableOrCreate('model');
|
|
1206
|
+
const createdItems = Array.from(table.values());
|
|
1207
|
+
expect(createdItems).to.be.eql([
|
|
1208
|
+
{[DEF_PK]: id1, ...input1},
|
|
1209
|
+
{[DEF_PK]: id2, ...input2},
|
|
1210
|
+
{[DEF_PK]: id3, ...input3},
|
|
1211
|
+
]);
|
|
1212
|
+
const result = await adapter.patch('model', {foo: 'd1'});
|
|
1213
|
+
expect(result).to.be.eq(3);
|
|
1214
|
+
const patchedItems = Array.from(table.values());
|
|
1215
|
+
expect(patchedItems).to.be.eql([
|
|
1216
|
+
{[DEF_PK]: id1, foo: 'd1', bar: 'a2'},
|
|
1217
|
+
{[DEF_PK]: id2, foo: 'd1', bar: 'b2'},
|
|
1218
|
+
{[DEF_PK]: id3, foo: 'd1', bar: 'c2'},
|
|
1219
|
+
]);
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
it('ignores identifier value in a given data in case of a default primary key', async function () {
|
|
1223
|
+
const schema = new Schema();
|
|
1224
|
+
schema.defineDatasource({
|
|
1225
|
+
name: 'memory',
|
|
1226
|
+
adapter: 'memory',
|
|
1227
|
+
});
|
|
1228
|
+
schema.defineModel({
|
|
1229
|
+
name: 'model',
|
|
1230
|
+
datasource: 'memory',
|
|
1231
|
+
properties: {
|
|
1232
|
+
foo: DataType.STRING,
|
|
1233
|
+
bar: DataType.STRING,
|
|
1234
|
+
},
|
|
1235
|
+
});
|
|
1236
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1237
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1238
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1239
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1240
|
+
const created1 = await adapter.create('model', input1);
|
|
1241
|
+
const created2 = await adapter.create('model', input2);
|
|
1242
|
+
const created3 = await adapter.create('model', input3);
|
|
1243
|
+
const id1 = created1[DEF_PK];
|
|
1244
|
+
const id2 = created2[DEF_PK];
|
|
1245
|
+
const id3 = created3[DEF_PK];
|
|
1246
|
+
const table = adapter._getTableOrCreate('model');
|
|
1247
|
+
const createdItems = Array.from(table.values());
|
|
1248
|
+
expect(createdItems).to.be.eql([
|
|
1249
|
+
{[DEF_PK]: id1, ...input1},
|
|
1250
|
+
{[DEF_PK]: id2, ...input2},
|
|
1251
|
+
{[DEF_PK]: id3, ...input3},
|
|
1252
|
+
]);
|
|
1253
|
+
const result = await adapter.patch('model', {[DEF_PK]: 100, foo: 'd1'});
|
|
1254
|
+
expect(result).to.be.eq(3);
|
|
1255
|
+
const patchedItems = Array.from(table.values());
|
|
1256
|
+
expect(patchedItems).to.be.eql([
|
|
1257
|
+
{[DEF_PK]: id1, foo: 'd1', bar: 'a2'},
|
|
1258
|
+
{[DEF_PK]: id2, foo: 'd1', bar: 'b2'},
|
|
1259
|
+
{[DEF_PK]: id3, foo: 'd1', bar: 'c2'},
|
|
1260
|
+
]);
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
it('ignores identifier value in a given data in case of a specified primary key', async function () {
|
|
1264
|
+
const schema = new Schema();
|
|
1265
|
+
schema.defineDatasource({
|
|
1266
|
+
name: 'memory',
|
|
1267
|
+
adapter: 'memory',
|
|
1268
|
+
});
|
|
1269
|
+
schema.defineModel({
|
|
1270
|
+
name: 'model',
|
|
1271
|
+
datasource: 'memory',
|
|
1272
|
+
properties: {
|
|
1273
|
+
myId: {
|
|
1274
|
+
type: DataType.NUMBER,
|
|
1275
|
+
primaryKey: true,
|
|
1276
|
+
},
|
|
1277
|
+
foo: DataType.STRING,
|
|
1278
|
+
bar: DataType.STRING,
|
|
1279
|
+
},
|
|
1280
|
+
});
|
|
1281
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1282
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1283
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1284
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1285
|
+
const created1 = await adapter.create('model', input1);
|
|
1286
|
+
const created2 = await adapter.create('model', input2);
|
|
1287
|
+
const created3 = await adapter.create('model', input3);
|
|
1288
|
+
const id1 = created1.myId;
|
|
1289
|
+
const id2 = created2.myId;
|
|
1290
|
+
const id3 = created3.myId;
|
|
1291
|
+
const table = adapter._getTableOrCreate('model');
|
|
1292
|
+
const createdItems = Array.from(table.values());
|
|
1293
|
+
expect(createdItems).to.be.eql([
|
|
1294
|
+
{myId: id1, ...input1},
|
|
1295
|
+
{myId: id2, ...input2},
|
|
1296
|
+
{myId: id3, ...input3},
|
|
1297
|
+
]);
|
|
1298
|
+
const result = await adapter.patch('model', {myId: 100, foo: 'd1'});
|
|
1299
|
+
expect(result).to.be.eq(3);
|
|
1300
|
+
const patchedItems = Array.from(table.values());
|
|
1301
|
+
expect(patchedItems).to.be.eql([
|
|
1302
|
+
{myId: id1, foo: 'd1', bar: 'a2'},
|
|
1303
|
+
{myId: id2, foo: 'd1', bar: 'b2'},
|
|
1304
|
+
{myId: id3, foo: 'd1', bar: 'c2'},
|
|
1305
|
+
]);
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
it('sets a default values for patched properties with an undefined value', async function () {
|
|
1309
|
+
const schema = new Schema();
|
|
1310
|
+
schema.defineDatasource({
|
|
1311
|
+
name: 'memory',
|
|
1312
|
+
adapter: 'memory',
|
|
1313
|
+
});
|
|
1314
|
+
schema.defineModel({
|
|
1315
|
+
name: 'model',
|
|
1316
|
+
datasource: 'memory',
|
|
1317
|
+
properties: {
|
|
1318
|
+
foo: {
|
|
1319
|
+
type: DataType.STRING,
|
|
1320
|
+
default: 'fooVal',
|
|
1321
|
+
},
|
|
1322
|
+
bar: {
|
|
1323
|
+
type: DataType.STRING,
|
|
1324
|
+
default: 'barVal',
|
|
1325
|
+
},
|
|
1326
|
+
},
|
|
1327
|
+
});
|
|
1328
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1329
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1330
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1331
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1332
|
+
const created1 = await adapter.create('model', input1);
|
|
1333
|
+
const created2 = await adapter.create('model', input2);
|
|
1334
|
+
const created3 = await adapter.create('model', input3);
|
|
1335
|
+
const id1 = created1[DEF_PK];
|
|
1336
|
+
const id2 = created2[DEF_PK];
|
|
1337
|
+
const id3 = created3[DEF_PK];
|
|
1338
|
+
const table = adapter._getTableOrCreate('model');
|
|
1339
|
+
const createdItems = Array.from(table.values());
|
|
1340
|
+
expect(createdItems).to.be.eql([
|
|
1341
|
+
{[DEF_PK]: id1, ...input1},
|
|
1342
|
+
{[DEF_PK]: id2, ...input2},
|
|
1343
|
+
{[DEF_PK]: id3, ...input3},
|
|
1344
|
+
]);
|
|
1345
|
+
const result = await adapter.patch('model', {foo: undefined});
|
|
1346
|
+
expect(result).to.be.eq(3);
|
|
1347
|
+
const patchedItems = Array.from(table.values());
|
|
1348
|
+
expect(patchedItems).to.be.eql([
|
|
1349
|
+
{[DEF_PK]: id1, foo: 'fooVal', bar: 'a2'},
|
|
1350
|
+
{[DEF_PK]: id2, foo: 'fooVal', bar: 'b2'},
|
|
1351
|
+
{[DEF_PK]: id3, foo: 'fooVal', bar: 'c2'},
|
|
1352
|
+
]);
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
it('sets a default values for patched properties with a null value', async function () {
|
|
1356
|
+
const schema = new Schema();
|
|
1357
|
+
schema.defineDatasource({
|
|
1358
|
+
name: 'memory',
|
|
1359
|
+
adapter: 'memory',
|
|
1360
|
+
});
|
|
1361
|
+
schema.defineModel({
|
|
1362
|
+
name: 'model',
|
|
1363
|
+
datasource: 'memory',
|
|
1364
|
+
properties: {
|
|
1365
|
+
foo: {
|
|
1366
|
+
type: DataType.STRING,
|
|
1367
|
+
default: 'fooVal',
|
|
1368
|
+
},
|
|
1369
|
+
bar: {
|
|
1370
|
+
type: DataType.STRING,
|
|
1371
|
+
default: 'barVal',
|
|
1372
|
+
},
|
|
1373
|
+
},
|
|
1374
|
+
});
|
|
1375
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1376
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1377
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1378
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1379
|
+
const created1 = await adapter.create('model', input1);
|
|
1380
|
+
const created2 = await adapter.create('model', input2);
|
|
1381
|
+
const created3 = await adapter.create('model', input3);
|
|
1382
|
+
const id1 = created1[DEF_PK];
|
|
1383
|
+
const id2 = created2[DEF_PK];
|
|
1384
|
+
const id3 = created3[DEF_PK];
|
|
1385
|
+
const table = adapter._getTableOrCreate('model');
|
|
1386
|
+
const createdItems = Array.from(table.values());
|
|
1387
|
+
expect(createdItems).to.be.eql([
|
|
1388
|
+
{[DEF_PK]: id1, ...input1},
|
|
1389
|
+
{[DEF_PK]: id2, ...input2},
|
|
1390
|
+
{[DEF_PK]: id3, ...input3},
|
|
1391
|
+
]);
|
|
1392
|
+
const result = await adapter.patch('model', {foo: null});
|
|
1393
|
+
expect(result).to.be.eq(3);
|
|
1394
|
+
const patchedItems = Array.from(table.values());
|
|
1395
|
+
expect(patchedItems).to.be.eql([
|
|
1396
|
+
{[DEF_PK]: id1, foo: 'fooVal', bar: 'a2'},
|
|
1397
|
+
{[DEF_PK]: id2, foo: 'fooVal', bar: 'b2'},
|
|
1398
|
+
{[DEF_PK]: id3, foo: 'fooVal', bar: 'c2'},
|
|
1399
|
+
]);
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
it('uses a specified column name for a regular property', async function () {
|
|
1403
|
+
const schema = new Schema();
|
|
1404
|
+
schema.defineDatasource({
|
|
1405
|
+
name: 'memory',
|
|
1406
|
+
adapter: 'memory',
|
|
1407
|
+
});
|
|
1408
|
+
schema.defineModel({
|
|
1409
|
+
name: 'model',
|
|
1410
|
+
datasource: 'memory',
|
|
1411
|
+
properties: {
|
|
1412
|
+
foo: {
|
|
1413
|
+
type: DataType.STRING,
|
|
1414
|
+
columnName: 'fooCol',
|
|
1415
|
+
},
|
|
1416
|
+
bar: {
|
|
1417
|
+
type: DataType.STRING,
|
|
1418
|
+
columnName: 'barCol',
|
|
1419
|
+
},
|
|
1420
|
+
},
|
|
1421
|
+
});
|
|
1422
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1423
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1424
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1425
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1426
|
+
const created1 = await adapter.create('model', input1);
|
|
1427
|
+
const created2 = await adapter.create('model', input2);
|
|
1428
|
+
const created3 = await adapter.create('model', input3);
|
|
1429
|
+
const id1 = created1[DEF_PK];
|
|
1430
|
+
const id2 = created2[DEF_PK];
|
|
1431
|
+
const id3 = created3[DEF_PK];
|
|
1432
|
+
const table = adapter._getTableOrCreate('model');
|
|
1433
|
+
const createdItems = Array.from(table.values());
|
|
1434
|
+
expect(createdItems).to.be.eql([
|
|
1435
|
+
{[DEF_PK]: id1, fooCol: 'a1', barCol: 'a2'},
|
|
1436
|
+
{[DEF_PK]: id2, fooCol: 'b1', barCol: 'b2'},
|
|
1437
|
+
{[DEF_PK]: id3, fooCol: 'c1', barCol: 'c2'},
|
|
1438
|
+
]);
|
|
1439
|
+
const result = await adapter.patch('model', {foo: 'd1'});
|
|
1440
|
+
expect(result).to.be.eq(3);
|
|
1441
|
+
const patchedItems = Array.from(table.values());
|
|
1442
|
+
expect(patchedItems).to.be.eql([
|
|
1443
|
+
{[DEF_PK]: id1, fooCol: 'd1', barCol: 'a2'},
|
|
1444
|
+
{[DEF_PK]: id2, fooCol: 'd1', barCol: 'b2'},
|
|
1445
|
+
{[DEF_PK]: id3, fooCol: 'd1', barCol: 'c2'},
|
|
1446
|
+
]);
|
|
1447
|
+
});
|
|
1448
|
+
|
|
1449
|
+
it('uses a specified column name for a regular property with a default value', async function () {
|
|
1450
|
+
const schema = new Schema();
|
|
1451
|
+
schema.defineDatasource({
|
|
1452
|
+
name: 'memory',
|
|
1453
|
+
adapter: 'memory',
|
|
1454
|
+
});
|
|
1455
|
+
schema.defineModel({
|
|
1456
|
+
name: 'model',
|
|
1457
|
+
datasource: 'memory',
|
|
1458
|
+
properties: {
|
|
1459
|
+
foo: {
|
|
1460
|
+
type: DataType.STRING,
|
|
1461
|
+
columnName: 'fooCol',
|
|
1462
|
+
default: 'fooVal',
|
|
1463
|
+
},
|
|
1464
|
+
bar: {
|
|
1465
|
+
type: DataType.STRING,
|
|
1466
|
+
columnName: 'barCol',
|
|
1467
|
+
default: 'barVal',
|
|
1468
|
+
},
|
|
1469
|
+
},
|
|
1470
|
+
});
|
|
1471
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1472
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1473
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1474
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1475
|
+
const created1 = await adapter.create('model', input1);
|
|
1476
|
+
const created2 = await adapter.create('model', input2);
|
|
1477
|
+
const created3 = await adapter.create('model', input3);
|
|
1478
|
+
const id1 = created1[DEF_PK];
|
|
1479
|
+
const id2 = created2[DEF_PK];
|
|
1480
|
+
const id3 = created3[DEF_PK];
|
|
1481
|
+
const table = adapter._getTableOrCreate('model');
|
|
1482
|
+
const createdItems = Array.from(table.values());
|
|
1483
|
+
expect(createdItems).to.be.eql([
|
|
1484
|
+
{[DEF_PK]: id1, fooCol: 'a1', barCol: 'a2'},
|
|
1485
|
+
{[DEF_PK]: id2, fooCol: 'b1', barCol: 'b2'},
|
|
1486
|
+
{[DEF_PK]: id3, fooCol: 'c1', barCol: 'c2'},
|
|
1487
|
+
]);
|
|
1488
|
+
const result = await adapter.patch('model', {foo: undefined});
|
|
1489
|
+
expect(result).to.be.eq(3);
|
|
1490
|
+
const patchedItems = Array.from(table.values());
|
|
1491
|
+
expect(patchedItems).to.be.eql([
|
|
1492
|
+
{[DEF_PK]: id1, fooCol: 'fooVal', barCol: 'a2'},
|
|
1493
|
+
{[DEF_PK]: id2, fooCol: 'fooVal', barCol: 'b2'},
|
|
1494
|
+
{[DEF_PK]: id3, fooCol: 'fooVal', barCol: 'c2'},
|
|
1495
|
+
]);
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1498
|
+
it('returns zero if nothing matched by the "where" clause', async function () {
|
|
1499
|
+
const schema = new Schema();
|
|
1500
|
+
schema.defineDatasource({
|
|
1501
|
+
name: 'memory',
|
|
1502
|
+
adapter: 'memory',
|
|
1503
|
+
});
|
|
1504
|
+
schema.defineModel({
|
|
1505
|
+
name: 'model',
|
|
1506
|
+
datasource: 'memory',
|
|
1507
|
+
properties: {
|
|
1508
|
+
foo: DataType.STRING,
|
|
1509
|
+
bar: DataType.STRING,
|
|
1510
|
+
},
|
|
1511
|
+
});
|
|
1512
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1513
|
+
const input1 = {foo: 'a1', bar: 'a2'};
|
|
1514
|
+
const input2 = {foo: 'b1', bar: 'b2'};
|
|
1515
|
+
const input3 = {foo: 'c1', bar: 'c2'};
|
|
1516
|
+
const created1 = await adapter.create('model', input1);
|
|
1517
|
+
const created2 = await adapter.create('model', input2);
|
|
1518
|
+
const created3 = await adapter.create('model', input3);
|
|
1519
|
+
const id1 = created1[DEF_PK];
|
|
1520
|
+
const id2 = created2[DEF_PK];
|
|
1521
|
+
const id3 = created3[DEF_PK];
|
|
1522
|
+
const table = adapter._getTableOrCreate('model');
|
|
1523
|
+
const createdItems = Array.from(table.values());
|
|
1524
|
+
expect(createdItems).to.be.eql([
|
|
1525
|
+
{[DEF_PK]: id1, ...input1},
|
|
1526
|
+
{[DEF_PK]: id2, ...input2},
|
|
1527
|
+
{[DEF_PK]: id3, ...input3},
|
|
1528
|
+
]);
|
|
1529
|
+
const result = await adapter.patch('model', {foo: 'test'}, {baz: 'd3'});
|
|
1530
|
+
expect(result).to.be.eq(0);
|
|
1531
|
+
const patchedItems = Array.from(table.values());
|
|
1532
|
+
expect(patchedItems).to.be.eql([
|
|
1533
|
+
{[DEF_PK]: id1, foo: 'a1', bar: 'a2'},
|
|
1534
|
+
{[DEF_PK]: id2, foo: 'b1', bar: 'b2'},
|
|
1535
|
+
{[DEF_PK]: id3, foo: 'c1', bar: 'c2'},
|
|
1536
|
+
]);
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1539
|
+
it('uses the "where" clause to patch specific items', async function () {
|
|
1540
|
+
const schema = new Schema();
|
|
1541
|
+
schema.defineDatasource({
|
|
1542
|
+
name: 'memory',
|
|
1543
|
+
adapter: 'memory',
|
|
1544
|
+
});
|
|
1545
|
+
schema.defineModel({
|
|
1546
|
+
name: 'model',
|
|
1547
|
+
datasource: 'memory',
|
|
1548
|
+
properties: {
|
|
1549
|
+
foo: DataType.STRING,
|
|
1550
|
+
bar: DataType.STRING,
|
|
1551
|
+
},
|
|
1552
|
+
});
|
|
1553
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1554
|
+
const input1 = {foo: 'a', bar: '1'};
|
|
1555
|
+
const input2 = {foo: 'b', bar: '2'};
|
|
1556
|
+
const input3 = {foo: 'c', bar: '2'};
|
|
1557
|
+
const created1 = await adapter.create('model', input1);
|
|
1558
|
+
const created2 = await adapter.create('model', input2);
|
|
1559
|
+
const created3 = await adapter.create('model', input3);
|
|
1560
|
+
const id1 = created1[DEF_PK];
|
|
1561
|
+
const id2 = created2[DEF_PK];
|
|
1562
|
+
const id3 = created3[DEF_PK];
|
|
1563
|
+
const table = adapter._getTableOrCreate('model');
|
|
1564
|
+
const createdItems = Array.from(table.values());
|
|
1565
|
+
expect(createdItems).to.be.eql([
|
|
1566
|
+
{[DEF_PK]: id1, ...input1},
|
|
1567
|
+
{[DEF_PK]: id2, ...input2},
|
|
1568
|
+
{[DEF_PK]: id3, ...input3},
|
|
1569
|
+
]);
|
|
1570
|
+
const result = await adapter.patch('model', {foo: 'd'}, {bar: '2'});
|
|
1571
|
+
expect(result).to.be.eq(2);
|
|
1572
|
+
const patchedItems = Array.from(table.values());
|
|
1573
|
+
expect(patchedItems).to.be.eql([
|
|
1574
|
+
{[DEF_PK]: id1, foo: 'a', bar: '1'},
|
|
1575
|
+
{[DEF_PK]: id2, foo: 'd', bar: '2'},
|
|
1576
|
+
{[DEF_PK]: id3, foo: 'd', bar: '2'},
|
|
1577
|
+
]);
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
it('the "where" clause uses property names instead of column names', async function () {
|
|
1581
|
+
const schema = new Schema();
|
|
1582
|
+
schema.defineDatasource({
|
|
1583
|
+
name: 'memory',
|
|
1584
|
+
adapter: 'memory',
|
|
1585
|
+
});
|
|
1586
|
+
schema.defineModel({
|
|
1587
|
+
name: 'model',
|
|
1588
|
+
datasource: 'memory',
|
|
1589
|
+
properties: {
|
|
1590
|
+
foo: {
|
|
1591
|
+
type: DataType.STRING,
|
|
1592
|
+
columnName: 'fooVal',
|
|
1593
|
+
},
|
|
1594
|
+
bar: {
|
|
1595
|
+
type: DataType.STRING,
|
|
1596
|
+
columnName: 'barVal',
|
|
1597
|
+
},
|
|
1598
|
+
},
|
|
1599
|
+
});
|
|
1600
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1601
|
+
const input1 = {foo: 'a', bar: '1'};
|
|
1602
|
+
const input2 = {foo: 'b', bar: '2'};
|
|
1603
|
+
const input3 = {foo: 'c', bar: '2'};
|
|
1604
|
+
const created1 = await adapter.create('model', input1);
|
|
1605
|
+
const created2 = await adapter.create('model', input2);
|
|
1606
|
+
const created3 = await adapter.create('model', input3);
|
|
1607
|
+
const id1 = created1[DEF_PK];
|
|
1608
|
+
const id2 = created2[DEF_PK];
|
|
1609
|
+
const id3 = created3[DEF_PK];
|
|
1610
|
+
const table = adapter._getTableOrCreate('model');
|
|
1611
|
+
const createdItems = Array.from(table.values());
|
|
1612
|
+
expect(createdItems).to.be.eql([
|
|
1613
|
+
{[DEF_PK]: id1, fooVal: 'a', barVal: '1'},
|
|
1614
|
+
{[DEF_PK]: id2, fooVal: 'b', barVal: '2'},
|
|
1615
|
+
{[DEF_PK]: id3, fooVal: 'c', barVal: '2'},
|
|
1616
|
+
]);
|
|
1617
|
+
const result = await adapter.patch('model', {foo: 'd'}, {bar: '2'});
|
|
1618
|
+
expect(result).to.be.eq(2);
|
|
1619
|
+
const patchedItems = Array.from(table.values());
|
|
1620
|
+
expect(patchedItems).to.be.eql([
|
|
1621
|
+
{[DEF_PK]: id1, fooVal: 'a', barVal: '1'},
|
|
1622
|
+
{[DEF_PK]: id2, fooVal: 'd', barVal: '2'},
|
|
1623
|
+
{[DEF_PK]: id3, fooVal: 'd', barVal: '2'},
|
|
1624
|
+
]);
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
it('the "where" clause uses a persisted data instead of default values in case of undefined', async function () {
|
|
1628
|
+
const schema = new Schema();
|
|
1629
|
+
schema.defineDatasource({
|
|
1630
|
+
name: 'memory',
|
|
1631
|
+
adapter: 'memory',
|
|
1632
|
+
});
|
|
1633
|
+
schema.defineModel({
|
|
1634
|
+
name: 'model',
|
|
1635
|
+
datasource: 'memory',
|
|
1636
|
+
properties: {
|
|
1637
|
+
foo: DataType.STRING,
|
|
1638
|
+
bar: {
|
|
1639
|
+
type: DataType.STRING,
|
|
1640
|
+
default: 'barVal',
|
|
1641
|
+
},
|
|
1642
|
+
},
|
|
1643
|
+
});
|
|
1644
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1645
|
+
const input1 = {[DEF_PK]: 1, foo: 'a', bar: undefined};
|
|
1646
|
+
const input2 = {[DEF_PK]: 2, foo: 'b', bar: undefined};
|
|
1647
|
+
const input3 = {[DEF_PK]: 3, foo: 'c', bar: 10};
|
|
1648
|
+
const input4 = {[DEF_PK]: 4, foo: 'd', bar: null};
|
|
1649
|
+
const table = adapter._getTableOrCreate('model');
|
|
1650
|
+
table.set(input1[DEF_PK], input1);
|
|
1651
|
+
table.set(input2[DEF_PK], input2);
|
|
1652
|
+
table.set(input3[DEF_PK], input3);
|
|
1653
|
+
table.set(input4[DEF_PK], input4);
|
|
1654
|
+
const result = await adapter.patch('model', {foo: 'e'}, {bar: undefined});
|
|
1655
|
+
expect(result).to.be.eq(2);
|
|
1656
|
+
const patchedItems = Array.from(table.values());
|
|
1657
|
+
expect(patchedItems).to.be.eql([
|
|
1658
|
+
{[DEF_PK]: 1, foo: 'e', bar: undefined},
|
|
1659
|
+
{[DEF_PK]: 2, foo: 'e', bar: undefined},
|
|
1660
|
+
{[DEF_PK]: 3, foo: 'c', bar: 10},
|
|
1661
|
+
{[DEF_PK]: 4, foo: 'd', bar: null},
|
|
1662
|
+
]);
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
it('the "where" clause uses a persisted data instead of default values in case of null', async function () {
|
|
1666
|
+
const schema = new Schema();
|
|
1667
|
+
schema.defineDatasource({
|
|
1668
|
+
name: 'memory',
|
|
1669
|
+
adapter: 'memory',
|
|
1670
|
+
});
|
|
1671
|
+
schema.defineModel({
|
|
1672
|
+
name: 'model',
|
|
1673
|
+
datasource: 'memory',
|
|
1674
|
+
properties: {
|
|
1675
|
+
foo: DataType.STRING,
|
|
1676
|
+
bar: {
|
|
1677
|
+
type: DataType.STRING,
|
|
1678
|
+
default: 'barVal',
|
|
1679
|
+
},
|
|
1680
|
+
},
|
|
1681
|
+
});
|
|
1682
|
+
const adapter = new MemoryAdapter(schema.container, {});
|
|
1683
|
+
const input1 = {[DEF_PK]: 1, foo: 'a', bar: undefined};
|
|
1684
|
+
const input2 = {[DEF_PK]: 2, foo: 'b', bar: undefined};
|
|
1685
|
+
const input3 = {[DEF_PK]: 3, foo: 'c', bar: 10};
|
|
1686
|
+
const input4 = {[DEF_PK]: 4, foo: 'd', bar: null};
|
|
1687
|
+
const table = adapter._getTableOrCreate('model');
|
|
1688
|
+
table.set(input1[DEF_PK], input1);
|
|
1689
|
+
table.set(input2[DEF_PK], input2);
|
|
1690
|
+
table.set(input3[DEF_PK], input3);
|
|
1691
|
+
table.set(input4[DEF_PK], input4);
|
|
1692
|
+
const result = await adapter.patch('model', {foo: 'e'}, {bar: null});
|
|
1693
|
+
expect(result).to.be.eq(1);
|
|
1694
|
+
const patchedItems = Array.from(table.values());
|
|
1695
|
+
expect(patchedItems).to.be.eql([
|
|
1696
|
+
{[DEF_PK]: 1, foo: 'a', bar: undefined},
|
|
1697
|
+
{[DEF_PK]: 2, foo: 'b', bar: undefined},
|
|
1698
|
+
{[DEF_PK]: 3, foo: 'c', bar: 10},
|
|
1699
|
+
{[DEF_PK]: 4, foo: 'e', bar: null},
|
|
1700
|
+
]);
|
|
1701
|
+
});
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1136
1704
|
describe('patchById', function () {
|
|
1137
1705
|
it('updates only provided properties by a given identifier', async function () {
|
|
1138
1706
|
const schema = new Schema();
|
|
@@ -2050,7 +2618,7 @@ describe('MemoryAdapter', function () {
|
|
|
2050
2618
|
expect(result4[2]).to.be.eql({[DEF_PK]: 2, foo: 2, bar: 10});
|
|
2051
2619
|
});
|
|
2052
2620
|
|
|
2053
|
-
it('allows to specify
|
|
2621
|
+
it('allows to specify the "where" clause to filter a return value', async function () {
|
|
2054
2622
|
const schema = new Schema();
|
|
2055
2623
|
schema.defineDatasource({
|
|
2056
2624
|
name: 'memory',
|
|
@@ -2087,7 +2655,7 @@ describe('MemoryAdapter', function () {
|
|
|
2087
2655
|
expect(result3[1]).to.be.eql({[DEF_PK]: 2, ...input2});
|
|
2088
2656
|
});
|
|
2089
2657
|
|
|
2090
|
-
it('
|
|
2658
|
+
it('the "where" clause uses property names instead of column names', async function () {
|
|
2091
2659
|
const schema = new Schema();
|
|
2092
2660
|
schema.defineDatasource({
|
|
2093
2661
|
name: 'memory',
|
|
@@ -2144,7 +2712,7 @@ describe('MemoryAdapter', function () {
|
|
|
2144
2712
|
expect(result3[1]).to.be.eql({[DEF_PK]: 2, ...input2});
|
|
2145
2713
|
});
|
|
2146
2714
|
|
|
2147
|
-
it('
|
|
2715
|
+
it('the "where" clause uses a persisted data instead of default values', async function () {
|
|
2148
2716
|
const schema = new Schema();
|
|
2149
2717
|
schema.defineDatasource({
|
|
2150
2718
|
name: 'memory',
|
|
@@ -2585,7 +3153,7 @@ describe('MemoryAdapter', function () {
|
|
|
2585
3153
|
expect(table.get(2)).to.be.eql({[DEF_PK]: 2, foo: 10});
|
|
2586
3154
|
});
|
|
2587
3155
|
|
|
2588
|
-
it('
|
|
3156
|
+
it('the "where" clause uses property names instead of column names', async function () {
|
|
2589
3157
|
const schema = new Schema();
|
|
2590
3158
|
schema.defineDatasource({
|
|
2591
3159
|
name: 'memory',
|
|
@@ -2612,7 +3180,7 @@ describe('MemoryAdapter', function () {
|
|
|
2612
3180
|
expect(table.get(2)).to.be.eql({[DEF_PK]: 2, fooCol: 10});
|
|
2613
3181
|
});
|
|
2614
3182
|
|
|
2615
|
-
it('
|
|
3183
|
+
it('the "where" clause uses a persisted data instead of default values', async function () {
|
|
2616
3184
|
const schema = new Schema();
|
|
2617
3185
|
schema.defineDatasource({
|
|
2618
3186
|
name: 'memory',
|
|
@@ -2863,7 +3431,7 @@ describe('MemoryAdapter', function () {
|
|
|
2863
3431
|
expect(table.get(3)).to.be.eql({[DEF_PK]: 3, foo: 15});
|
|
2864
3432
|
});
|
|
2865
3433
|
|
|
2866
|
-
it('
|
|
3434
|
+
it('the "where" clause uses property names instead of column names', async function () {
|
|
2867
3435
|
const schema = new Schema();
|
|
2868
3436
|
schema.defineDatasource({
|
|
2869
3437
|
name: 'memory',
|
|
@@ -2893,7 +3461,7 @@ describe('MemoryAdapter', function () {
|
|
|
2893
3461
|
expect(table.get(3)).to.be.eql({[DEF_PK]: 3, fooCol: 15});
|
|
2894
3462
|
});
|
|
2895
3463
|
|
|
2896
|
-
it('
|
|
3464
|
+
it('the "where" clause uses a persisted data instead of default values', async function () {
|
|
2897
3465
|
const schema = new Schema();
|
|
2898
3466
|
schema.defineDatasource({
|
|
2899
3467
|
name: 'memory',
|
|
@@ -35,6 +35,12 @@ export class DataSanitizingDecorator extends Service {
|
|
|
35
35
|
return replaceById.call(this, modelName, id, modelData, filter);
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
const patch = adapter.patch;
|
|
39
|
+
adapter.patch = async function (modelName, modelData, where) {
|
|
40
|
+
modelData = sanitize(modelName, modelData);
|
|
41
|
+
return patch.call(this, modelName, modelData, where);
|
|
42
|
+
};
|
|
43
|
+
|
|
38
44
|
const patchById = adapter.patchById;
|
|
39
45
|
adapter.patchById = async function (modelName, id, modelData, filter) {
|
|
40
46
|
modelData = sanitize(modelName, modelData);
|
|
@@ -18,6 +18,11 @@ class TestAdapter extends Adapter {
|
|
|
18
18
|
return Promise.resolve({});
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// eslint-disable-next-line no-unused-vars
|
|
22
|
+
patch(modelName, modelData, where = undefined) {
|
|
23
|
+
return Promise.resolve(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
// eslint-disable-next-line no-unused-vars
|
|
22
27
|
patchById(modelName, id, modelData, filter = undefined) {
|
|
23
28
|
return Promise.resolve({});
|
|
@@ -49,6 +54,14 @@ describe('DataSanitizingDecorator', function () {
|
|
|
49
54
|
expect(V.sanitize).to.be.called.with.exactly('model', data);
|
|
50
55
|
});
|
|
51
56
|
|
|
57
|
+
it('overrides the "patch" method and sanitizes a given data', async function () {
|
|
58
|
+
sandbox.on(V, 'sanitize');
|
|
59
|
+
const data = {};
|
|
60
|
+
await A.patch('model', data);
|
|
61
|
+
expect(V.sanitize).to.be.called.once;
|
|
62
|
+
expect(V.sanitize).to.be.called.with.exactly('model', data);
|
|
63
|
+
});
|
|
64
|
+
|
|
52
65
|
it('overrides the "patchById" method and sanitizes a given data', async function () {
|
|
53
66
|
sandbox.on(V, 'sanitize');
|
|
54
67
|
const data = {};
|
|
@@ -32,6 +32,12 @@ export class DataValidationDecorator extends Service {
|
|
|
32
32
|
return replaceById.call(this, modelName, id, modelData, filter);
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
const patch = adapter.patch;
|
|
36
|
+
adapter.patch = function (modelName, modelData, where) {
|
|
37
|
+
this.getService(ModelDataValidator).validate(modelName, modelData, true);
|
|
38
|
+
return patch.call(this, modelName, modelData, where);
|
|
39
|
+
};
|
|
40
|
+
|
|
35
41
|
const patchById = adapter.patchById;
|
|
36
42
|
adapter.patchById = function (modelName, id, modelData, filter) {
|
|
37
43
|
this.getService(ModelDataValidator).validate(modelName, modelData, true);
|
|
@@ -18,6 +18,11 @@ class TestAdapter extends Adapter {
|
|
|
18
18
|
return Promise.resolve({});
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// eslint-disable-next-line no-unused-vars
|
|
22
|
+
patch(modelName, modelData, where = undefined) {
|
|
23
|
+
return Promise.resolve(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
// eslint-disable-next-line no-unused-vars
|
|
22
27
|
patchById(modelName, id, modelData, filter = undefined) {
|
|
23
28
|
return Promise.resolve({});
|
|
@@ -36,6 +36,12 @@ export class DefaultValuesDecorator extends Service {
|
|
|
36
36
|
return replaceById.call(this, modelName, id, modelData, filter);
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
const patch = adapter.patch;
|
|
40
|
+
adapter.patch = function (modelName, modelData, where) {
|
|
41
|
+
modelData = setDefaults(modelName, modelData, true);
|
|
42
|
+
return patch.call(this, modelName, modelData, where);
|
|
43
|
+
};
|
|
44
|
+
|
|
39
45
|
const patchById = adapter.patchById;
|
|
40
46
|
adapter.patchById = function (modelName, id, modelData, filter) {
|
|
41
47
|
modelData = setDefaults(modelName, modelData, true);
|
|
@@ -29,6 +29,11 @@ class TestAdapter extends Adapter {
|
|
|
29
29
|
return modelData;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// eslint-disable-next-line no-unused-vars
|
|
33
|
+
patch(modelName, modelData, where = undefined) {
|
|
34
|
+
return Promise.resolve(modelData);
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
// eslint-disable-next-line no-unused-vars
|
|
33
38
|
async patchById(modelName, id, modelData, filter = undefined) {
|
|
34
39
|
return modelData;
|
|
@@ -76,6 +81,47 @@ describe('DefaultValuesDecorator', function () {
|
|
|
76
81
|
);
|
|
77
82
|
});
|
|
78
83
|
|
|
84
|
+
describe('overrides the "patch" method and sets default values to input data', function () {
|
|
85
|
+
it('does not set default values to not existing properties of input data', async function () {
|
|
86
|
+
sandbox.on(U, 'setDefaultValuesToEmptyProperties');
|
|
87
|
+
const data = {};
|
|
88
|
+
const retval = await A.patch('model', data);
|
|
89
|
+
expect(retval).to.be.eql({});
|
|
90
|
+
expect(U.setDefaultValuesToEmptyProperties).to.be.called.once;
|
|
91
|
+
expect(U.setDefaultValuesToEmptyProperties).to.be.called.with.exactly(
|
|
92
|
+
'model',
|
|
93
|
+
data,
|
|
94
|
+
true,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('does set default values to input properties of null', async function () {
|
|
99
|
+
sandbox.on(U, 'setDefaultValuesToEmptyProperties');
|
|
100
|
+
const data = {prop: null};
|
|
101
|
+
const retval = await A.patch('model', data);
|
|
102
|
+
expect(retval).to.be.eql({prop: 'value'});
|
|
103
|
+
expect(U.setDefaultValuesToEmptyProperties).to.be.called.once;
|
|
104
|
+
expect(U.setDefaultValuesToEmptyProperties).to.be.called.with.exactly(
|
|
105
|
+
'model',
|
|
106
|
+
data,
|
|
107
|
+
true,
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('does set default values to input properties of undefined', async function () {
|
|
112
|
+
sandbox.on(U, 'setDefaultValuesToEmptyProperties');
|
|
113
|
+
const data = {prop: undefined};
|
|
114
|
+
const retval = await A.patch('model', data);
|
|
115
|
+
expect(retval).to.be.eql({prop: 'value'});
|
|
116
|
+
expect(U.setDefaultValuesToEmptyProperties).to.be.called.once;
|
|
117
|
+
expect(U.setDefaultValuesToEmptyProperties).to.be.called.with.exactly(
|
|
118
|
+
'model',
|
|
119
|
+
data,
|
|
120
|
+
true,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
79
125
|
describe('overrides the "patchById" method and sets default values to input data', function () {
|
|
80
126
|
it('does not set default values to not existing properties of input data', async function () {
|
|
81
127
|
sandbox.on(U, 'setDefaultValuesToEmptyProperties');
|
|
@@ -43,9 +43,9 @@ export declare type ItemFilterClause = Pick<FilterClause, 'fields' | 'include'>;
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
export declare type WhereClause =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
| AndClause
|
|
47
|
+
| OrClause
|
|
48
|
+
| PropertiesClause;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Properties clause.
|
|
@@ -127,8 +127,12 @@ export class WhereClauseTool extends Service {
|
|
|
127
127
|
*/
|
|
128
128
|
_test(example, value) {
|
|
129
129
|
// Test null.
|
|
130
|
-
if (example
|
|
131
|
-
return value
|
|
130
|
+
if (example === null) {
|
|
131
|
+
return value === null;
|
|
132
|
+
}
|
|
133
|
+
// Test undefined.
|
|
134
|
+
if (example === undefined) {
|
|
135
|
+
return value === undefined;
|
|
132
136
|
}
|
|
133
137
|
// Test RegExp.
|
|
134
138
|
// noinspection ALL
|
|
@@ -266,11 +266,16 @@ describe('WhereClauseTool', function () {
|
|
|
266
266
|
expect(result[0]).to.be.eql(OBJECTS[2]);
|
|
267
267
|
});
|
|
268
268
|
|
|
269
|
-
it('
|
|
269
|
+
it('does not use null to match an undefined value', function () {
|
|
270
270
|
const result = S.filter(OBJECTS, {nickname: null});
|
|
271
|
-
expect(result).to.have.length(
|
|
271
|
+
expect(result).to.have.length(1);
|
|
272
272
|
expect(result[0]).to.be.eql(OBJECTS[2]);
|
|
273
|
-
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('does not use undefined to match a null value', function () {
|
|
276
|
+
const result = S.filter(OBJECTS, {nickname: undefined});
|
|
277
|
+
expect(result).to.have.length(1);
|
|
278
|
+
expect(result[0]).to.be.eql(OBJECTS[3]);
|
|
274
279
|
});
|
|
275
280
|
});
|
|
276
281
|
|
|
@@ -77,6 +77,17 @@ export declare class Repository<
|
|
|
77
77
|
filter?: ItemFilterClause,
|
|
78
78
|
): Promise<FlatData>;
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Patch.
|
|
82
|
+
*
|
|
83
|
+
* @param data
|
|
84
|
+
* @param where
|
|
85
|
+
*/
|
|
86
|
+
patch(
|
|
87
|
+
data: PartialWithoutId<IdName, Data>,
|
|
88
|
+
where?: WhereClause,
|
|
89
|
+
): Promise<number>;
|
|
90
|
+
|
|
80
91
|
/**
|
|
81
92
|
* Patch by id.
|
|
82
93
|
*
|
|
@@ -111,6 +111,18 @@ export class Repository extends Service {
|
|
|
111
111
|
return this.replaceById(pkValue, data, filter);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Patch.
|
|
116
|
+
*
|
|
117
|
+
* @param {object} data
|
|
118
|
+
* @param {object|undefined} where
|
|
119
|
+
* @returns {Promise<number>}
|
|
120
|
+
*/
|
|
121
|
+
async patch(data, where = undefined) {
|
|
122
|
+
const adapter = await this.getAdapter();
|
|
123
|
+
return adapter.patch(this.modelName, data, where);
|
|
124
|
+
}
|
|
125
|
+
|
|
114
126
|
/**
|
|
115
127
|
* Patch by id.
|
|
116
128
|
*
|
|
@@ -50,6 +50,32 @@ describe('Repository', function () {
|
|
|
50
50
|
});
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
+
describe('patch', function () {
|
|
54
|
+
it('patches all items', async function () {
|
|
55
|
+
const schema = new Schema();
|
|
56
|
+
schema.defineDatasource({name: 'datasource', adapter: 'memory'});
|
|
57
|
+
schema.defineModel({name: 'model', datasource: 'datasource'});
|
|
58
|
+
const rep = schema.getRepository('model');
|
|
59
|
+
await rep.create({foo: 'a1', bar: 'b1'});
|
|
60
|
+
await rep.create({foo: 'a2', bar: 'b2'});
|
|
61
|
+
await rep.create({foo: 'a3', bar: 'b3'});
|
|
62
|
+
const result = await rep.patch({foo: 'test'});
|
|
63
|
+
expect(result).to.be.eq(3);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('patches found items by the "where" clause', async function () {
|
|
67
|
+
const schema = new Schema();
|
|
68
|
+
schema.defineDatasource({name: 'datasource', adapter: 'memory'});
|
|
69
|
+
schema.defineModel({name: 'model', datasource: 'datasource'});
|
|
70
|
+
const rep = schema.getRepository('model');
|
|
71
|
+
await rep.create({foo: 'a', bar: '1'});
|
|
72
|
+
await rep.create({foo: 'b', bar: '2'});
|
|
73
|
+
await rep.create({foo: 'c', bar: '2'});
|
|
74
|
+
const result = await rep.patch({foo: 'test'}, {bar: '2'});
|
|
75
|
+
expect(result).to.be.eq(2);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
53
79
|
describe('patchById', function () {
|
|
54
80
|
it('patches an item by the given id', async function () {
|
|
55
81
|
const schema = new Schema();
|