@travetto/model-query 3.1.3 → 3.1.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/README.md +7 -0
- package/package.json +2 -2
- package/src/internal/service/query.ts +15 -0
- package/src/service/crud.ts +7 -0
- package/support/test/crud.ts +81 -1
- package/support/test/types.ts +7 -0
package/README.md
CHANGED
|
@@ -51,6 +51,13 @@ Reinforcing the complexity provided in these contracts, the [Query Crud](https:/
|
|
|
51
51
|
**Code: Query Crud**
|
|
52
52
|
```typescript
|
|
53
53
|
export interface ModelQueryCrudSupport extends ModelCrudSupport, ModelQuerySupport {
|
|
54
|
+
/**
|
|
55
|
+
* A standard update operation, but ensures the data matches the query before the update is finalized
|
|
56
|
+
* @param cls The model class
|
|
57
|
+
* @param data The data
|
|
58
|
+
* @param query The additional query to validate
|
|
59
|
+
*/
|
|
60
|
+
updateOneWithQuery<T extends ModelType>(cls: Class<T>, data: T, query: ModelQuery<T>): Promise<T>;
|
|
54
61
|
/**
|
|
55
62
|
* Update/replace all with partial data, by query
|
|
56
63
|
* @param cls The model class
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-query",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.5",
|
|
4
4
|
"description": "Datastore abstraction for advanced query support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"datastore",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@travetto/di": "^3.1.1",
|
|
31
|
-
"@travetto/model": "^3.1.
|
|
31
|
+
"@travetto/model": "^3.1.7",
|
|
32
32
|
"@travetto/schema": "^3.1.2"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
@@ -78,4 +78,19 @@ export class ModelQueryUtil {
|
|
|
78
78
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
79
79
|
return query as U & { where: WhereClause<T> };
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get query with an id enforced
|
|
84
|
+
*/
|
|
85
|
+
static getQueryWithId<T extends ModelType, U extends Query<T> | ModelQuery<T>>(
|
|
86
|
+
cls: Class<T>,
|
|
87
|
+
item: T,
|
|
88
|
+
query: U
|
|
89
|
+
): U & { where: WhereClause<T> & { id: string } } {
|
|
90
|
+
query.where = this.getWhereClause(cls, query.where);
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
92
|
+
(query.where as WhereClauseRaw<ModelType>).id = item.id;
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
94
|
+
return query as U & { where: WhereClause<T> & { id: string } };
|
|
95
|
+
}
|
|
81
96
|
}
|
package/src/service/crud.ts
CHANGED
|
@@ -9,6 +9,13 @@ import { ModelQuery } from '../model/query';
|
|
|
9
9
|
* @concrete ../internal/service/common:ModelQueryCrudSupportTarget
|
|
10
10
|
*/
|
|
11
11
|
export interface ModelQueryCrudSupport extends ModelCrudSupport, ModelQuerySupport {
|
|
12
|
+
/**
|
|
13
|
+
* A standard update operation, but ensures the data matches the query before the update is finalized
|
|
14
|
+
* @param cls The model class
|
|
15
|
+
* @param data The data
|
|
16
|
+
* @param query The additional query to validate
|
|
17
|
+
*/
|
|
18
|
+
updateOneWithQuery<T extends ModelType>(cls: Class<T>, data: T, query: ModelQuery<T>): Promise<T>;
|
|
12
19
|
/**
|
|
13
20
|
* Update/replace all with partial data, by query
|
|
14
21
|
* @param cls The model class
|
package/support/test/crud.ts
CHANGED
|
@@ -3,12 +3,92 @@ import assert from 'assert';
|
|
|
3
3
|
import { Suite, Test } from '@travetto/test';
|
|
4
4
|
import { BaseModelSuite } from '@travetto/model/support/test/base';
|
|
5
5
|
import { ModelCrudSupport } from '@travetto/model/src/service/crud';
|
|
6
|
+
import { NotFoundError } from '@travetto/model';
|
|
6
7
|
|
|
7
|
-
import { Address, Person } from './types';
|
|
8
|
+
import { Address, Person, Todo } from './types';
|
|
8
9
|
import { ModelQueryCrudSupport } from '../../src/service/crud';
|
|
9
10
|
|
|
10
11
|
@Suite()
|
|
11
12
|
export abstract class ModelQueryCrudSuite extends BaseModelSuite<ModelQueryCrudSupport & ModelCrudSupport> {
|
|
13
|
+
@Test()
|
|
14
|
+
async testUpdateOneWithQuery() {
|
|
15
|
+
const svc = await this.service;
|
|
16
|
+
|
|
17
|
+
const todo1 = await svc.create(Todo, Todo.from({ text: 'bob' }));
|
|
18
|
+
|
|
19
|
+
assert(todo1.id);
|
|
20
|
+
assert(todo1.version === 0);
|
|
21
|
+
|
|
22
|
+
const todo1v2 = await svc.updateOneWithQuery(Todo, Todo.from({
|
|
23
|
+
id: todo1.id,
|
|
24
|
+
text: `${todo1.text}!!`,
|
|
25
|
+
version: todo1.version + 1
|
|
26
|
+
}), { where: { id: todo1.id, version: todo1.version } },);
|
|
27
|
+
|
|
28
|
+
assert(todo1v2.id === todo1.id);
|
|
29
|
+
assert(todo1v2.version > todo1.version);
|
|
30
|
+
|
|
31
|
+
await assert.rejects(
|
|
32
|
+
() => svc.updateOneWithQuery(Todo, Todo.from({
|
|
33
|
+
id: todo1.id,
|
|
34
|
+
text: `${todo1.text}!!`,
|
|
35
|
+
version: todo1.version + 1
|
|
36
|
+
}), { where: { id: todo1.id, version: todo1.version } }),
|
|
37
|
+
NotFoundError
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const todo2 = await svc.create(Todo, Todo.from({ text: 'bob2' }));
|
|
42
|
+
|
|
43
|
+
const result = await svc.updateOneWithQuery(Todo, Todo.from({
|
|
44
|
+
id: todo2.id,
|
|
45
|
+
text: `${todo1.text}!!`,
|
|
46
|
+
version: todo1.version + 1
|
|
47
|
+
}), { where: { id: todo2.id, text: 'bob2' } });
|
|
48
|
+
|
|
49
|
+
assert(result.id === todo2.id);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Test()
|
|
53
|
+
async testUpdateOneWithQueryMultiple() {
|
|
54
|
+
const svc = await this.service;
|
|
55
|
+
|
|
56
|
+
const todo1 = await svc.create(Todo, Todo.from({ text: 'bob', version: 1 }));
|
|
57
|
+
|
|
58
|
+
assert(todo1.id);
|
|
59
|
+
assert(todo1.version === 1);
|
|
60
|
+
|
|
61
|
+
const todo1v = ['a', 'b', 'c', 'd', 'e'].map(x => Todo.from({ ...todo1, text: `${todo1.text}-${x}`, version: todo1.version + 1 }));
|
|
62
|
+
|
|
63
|
+
const promises = todo1v.map(x => svc.updateOneWithQuery(Todo, x, { where: { version: todo1.version } }));
|
|
64
|
+
|
|
65
|
+
const results = await Promise.allSettled(promises);
|
|
66
|
+
const rejected = results.filter((x): x is PromiseRejectedResult => x.status === 'rejected');
|
|
67
|
+
const fulfilled = results.filter((x): x is PromiseFulfilledResult<Todo> => x.status === 'fulfilled');
|
|
68
|
+
|
|
69
|
+
for (const el of rejected) {
|
|
70
|
+
assert(el.reason instanceof NotFoundError);
|
|
71
|
+
}
|
|
72
|
+
assert(fulfilled.length === 1);
|
|
73
|
+
assert(rejected.length === todo1v.length - 1);
|
|
74
|
+
|
|
75
|
+
const succeeded = fulfilled[0];
|
|
76
|
+
assert(succeeded.value.id === todo1.id);
|
|
77
|
+
assert(succeeded.value.version === todo1.version + 1);
|
|
78
|
+
assert(succeeded.value.text !== todo1.text);
|
|
79
|
+
|
|
80
|
+
const todo2 = await svc.get(Todo, todo1.id);
|
|
81
|
+
assert.deepStrictEqual(todo2, succeeded.value);
|
|
82
|
+
|
|
83
|
+
const promises2 = ['a', 'b', 'c', 'd', 'e'].map(x => Todo.from({ ...todo2, text: `${todo2.text}-${x}`, version: todo2.version + 2 }));
|
|
84
|
+
const results2 = await Promise.allSettled(promises2.map(x => svc.updateOneWithQuery(Todo, x, { where: { version: todo2.version + 1 } })));
|
|
85
|
+
for (const el of results2) {
|
|
86
|
+
assert(el.status === 'rejected');
|
|
87
|
+
assert(el.reason instanceof NotFoundError);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
12
92
|
@Test()
|
|
13
93
|
async testDeleteByQuery() {
|
|
14
94
|
const svc = await this.service;
|
package/support/test/types.ts
CHANGED