@ahoo-wang/fetcher-wow 1.1.0 โ 1.2.0
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 +275 -357
- package/README.zh-CN.md +283 -279
- package/dist/command/commandClient.d.ts +146 -0
- package/dist/command/commandClient.d.ts.map +1 -0
- package/dist/command/{commandHeaders.d.ts โ commandHttpHeaders.d.ts} +6 -7
- package/dist/command/commandHttpHeaders.d.ts.map +1 -0
- package/dist/command/commandRequest.d.ts +26 -62
- package/dist/command/commandRequest.d.ts.map +1 -1
- package/dist/command/index.d.ts +2 -3
- package/dist/command/index.d.ts.map +1 -1
- package/dist/index.es.js +467 -226
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/query/event/domainEventStream.d.ts +49 -4
- package/dist/query/event/domainEventStream.d.ts.map +1 -1
- package/dist/query/event/eventStreamQueryApi.d.ts +6 -0
- package/dist/query/event/eventStreamQueryApi.d.ts.map +1 -1
- package/dist/query/event/eventStreamQueryClient.d.ts +46 -0
- package/dist/query/event/eventStreamQueryClient.d.ts.map +1 -0
- package/dist/query/event/index.d.ts +1 -0
- package/dist/query/event/index.d.ts.map +1 -1
- package/dist/query/queryApi.d.ts +62 -0
- package/dist/query/queryApi.d.ts.map +1 -1
- package/dist/query/snapshot/index.d.ts +1 -0
- package/dist/query/snapshot/index.d.ts.map +1 -1
- package/dist/query/snapshot/snapshot.d.ts +2 -2
- package/dist/query/snapshot/snapshot.d.ts.map +1 -1
- package/dist/query/snapshot/snapshotQueryApi.d.ts +28 -0
- package/dist/query/snapshot/snapshotQueryApi.d.ts.map +1 -1
- package/dist/query/snapshot/snapshotQueryClient.d.ts +74 -0
- package/dist/query/snapshot/snapshotQueryClient.d.ts.map +1 -0
- package/dist/query/sort.d.ts +4 -0
- package/dist/query/sort.d.ts.map +1 -1
- package/dist/types/client.d.ts +15 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/endpoints.d.ts +28 -0
- package/dist/types/endpoints.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/command/commandHeaders.d.ts.map +0 -1
- package/dist/command/commandHttpClient.d.ts +0 -133
- package/dist/command/commandHttpClient.d.ts.map +0 -1
- package/dist/command/commandHttpRequest.d.ts +0 -48
- package/dist/command/commandHttpRequest.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -8,16 +8,23 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-wow)
|
|
9
9
|
[](https://deepwiki.com/Ahoo-Wang/fetcher)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Support for [Wow](https://github.com/Ahoo-Wang/Wow) framework in Fetcher. Provides TypeScript types and utilities for
|
|
12
|
+
working with the Wow CQRS/DDD framework.
|
|
12
13
|
|
|
13
14
|
## ๐ Features
|
|
14
15
|
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
-
|
|
16
|
+
- **๐ CQRS Pattern Implementation**: First-class support for Command Query Responsibility Segregation architectural
|
|
17
|
+
pattern
|
|
18
|
+
- **๐งฑ DDD Primitives**: Essential Domain-Driven Design building blocks including aggregates, events, and value objects
|
|
19
|
+
- **๐ฆ Complete TypeScript Support**: Full type definitions for all Wow framework entities including commands, events,
|
|
20
|
+
and queries
|
|
21
|
+
- **๐ก Real-time Event Streaming**: Built-in support for Server-Sent Events to receive real-time command results and data
|
|
22
|
+
updates
|
|
23
|
+
- **๐ Command Client**: High-level client for sending commands to Wow services with both synchronous and streaming
|
|
24
|
+
responses
|
|
25
|
+
- **๐ Powerful Query DSL**: Rich query condition builder with comprehensive operator support for complex querying
|
|
26
|
+
- **๐ Query Clients**: Specialized clients for querying snapshot and event stream data with comprehensive query
|
|
27
|
+
operations
|
|
21
28
|
|
|
22
29
|
## ๐ Quick Start
|
|
23
30
|
|
|
@@ -38,61 +45,6 @@ yarn add @ahoo-wang/fetcher-wow
|
|
|
38
45
|
|
|
39
46
|
### Command Module
|
|
40
47
|
|
|
41
|
-
#### CommandHeaders
|
|
42
|
-
|
|
43
|
-
Constants for standard HTTP headers used in Wow command processing:
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
import { CommandHeaders } from '@ahoo-wang/fetcher-wow';
|
|
47
|
-
|
|
48
|
-
// Example usage
|
|
49
|
-
const request = {
|
|
50
|
-
method: 'POST',
|
|
51
|
-
headers: {
|
|
52
|
-
[CommandHeaders.TENANT_ID]: 'tenant-123',
|
|
53
|
-
[CommandHeaders.AGGREGATE_ID]: 'aggregate-456',
|
|
54
|
-
[CommandHeaders.REQUEST_ID]: 'request-789',
|
|
55
|
-
},
|
|
56
|
-
body: command,
|
|
57
|
-
};
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Key headers include:
|
|
61
|
-
|
|
62
|
-
- `TENANT_ID` - Tenant identifier
|
|
63
|
-
- `OWNER_ID` - Owner identifier
|
|
64
|
-
- `AGGREGATE_ID` - Aggregate root identifier
|
|
65
|
-
- `AGGREGATE_VERSION` - Expected aggregate version
|
|
66
|
-
- `REQUEST_ID` - Request tracking ID
|
|
67
|
-
- `WAIT_*` - Various wait condition headers
|
|
68
|
-
- `LOCAL_FIRST` - Local processing preference
|
|
69
|
-
- And many more...
|
|
70
|
-
|
|
71
|
-
#### CommandRequest
|
|
72
|
-
|
|
73
|
-
Interface for command requests with full configuration options:
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
import { CommandRequest, CommandHeaders } from '@ahoo-wang/fetcher-wow';
|
|
77
|
-
|
|
78
|
-
const commandRequest: CommandRequest = {
|
|
79
|
-
path: '/commands/CreateUser',
|
|
80
|
-
method: 'POST',
|
|
81
|
-
headers: {
|
|
82
|
-
[CommandHeaders.TENANT_ID]: 'tenant-123',
|
|
83
|
-
},
|
|
84
|
-
body: {
|
|
85
|
-
name: 'John Doe',
|
|
86
|
-
email: 'john@example.com',
|
|
87
|
-
},
|
|
88
|
-
timeout: 5000,
|
|
89
|
-
aggregateId: 'user-456',
|
|
90
|
-
requestId: 'req-789',
|
|
91
|
-
localFirst: true,
|
|
92
|
-
stream: false,
|
|
93
|
-
};
|
|
94
|
-
```
|
|
95
|
-
|
|
96
48
|
#### CommandResult
|
|
97
49
|
|
|
98
50
|
Interface representing the result of command execution:
|
|
@@ -101,56 +53,55 @@ Interface representing the result of command execution:
|
|
|
101
53
|
import { CommandResult, CommandStage } from '@ahoo-wang/fetcher-wow';
|
|
102
54
|
```
|
|
103
55
|
|
|
104
|
-
####
|
|
56
|
+
#### CommandClient
|
|
105
57
|
|
|
106
|
-
HTTP client for sending commands to the Wow framework.
|
|
58
|
+
HTTP client for sending commands to the Wow framework. The client provides methods to send commands and receive results
|
|
107
59
|
either synchronously or as a stream of events.
|
|
108
60
|
|
|
109
|
-
##### Usage
|
|
110
|
-
|
|
111
|
-
First, create a fetcher instance with base configuration and add the EventStreamInterceptor:
|
|
112
|
-
|
|
113
61
|
```typescript
|
|
114
|
-
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
115
|
-
import
|
|
62
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
63
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
64
|
+
import {
|
|
65
|
+
CommandClient,
|
|
66
|
+
CommandRequest,
|
|
67
|
+
HttpMethod,
|
|
68
|
+
CommandHttpHeaders,
|
|
69
|
+
CommandStage,
|
|
70
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
71
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
116
72
|
|
|
73
|
+
// Create a fetcher instance
|
|
117
74
|
const wowFetcher = new Fetcher({
|
|
118
75
|
baseURL: 'http://localhost:8080/',
|
|
119
76
|
});
|
|
120
77
|
|
|
121
|
-
// Add
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
78
|
+
// Add interceptor to handle URL parameters
|
|
79
|
+
const ownerId = idGenerator.generateId();
|
|
80
|
+
wowFetcher.interceptors.request.use({
|
|
81
|
+
name: 'AppendOwnerId',
|
|
82
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
83
|
+
intercept(exchange) {
|
|
84
|
+
exchange.request.urlParams = {
|
|
85
|
+
path: {
|
|
86
|
+
...exchange.request.urlParams?.path,
|
|
87
|
+
ownerId,
|
|
88
|
+
},
|
|
89
|
+
query: exchange.request.urlParams?.query,
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
});
|
|
136
93
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
} from '@ahoo-wang/fetcher-wow';
|
|
94
|
+
// Create the command client
|
|
95
|
+
const commandClient = new CommandClient({
|
|
96
|
+
fetcher: wowFetcher,
|
|
97
|
+
basePath: 'owner/{ownerId}/cart',
|
|
98
|
+
});
|
|
143
99
|
|
|
144
|
-
|
|
145
|
-
|
|
100
|
+
// Define a command request
|
|
101
|
+
const command: CommandRequest = {
|
|
146
102
|
method: HttpMethod.POST,
|
|
147
103
|
headers: {
|
|
148
|
-
[
|
|
149
|
-
},
|
|
150
|
-
urlParams: {
|
|
151
|
-
path: {
|
|
152
|
-
ownerId: 'ownerId',
|
|
153
|
-
},
|
|
104
|
+
[CommandHttpHeaders.WAIT_STAGE]: CommandStage.SNAPSHOT,
|
|
154
105
|
},
|
|
155
106
|
body: {
|
|
156
107
|
productId: 'productId',
|
|
@@ -158,74 +109,25 @@ const command = {
|
|
|
158
109
|
},
|
|
159
110
|
};
|
|
160
111
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
##### Sending a Command and Receiving Stream Results
|
|
166
|
-
|
|
167
|
-
For long-running commands, you can receive results as a stream of events:
|
|
112
|
+
// Send command and wait for result
|
|
113
|
+
const commandResult = await commandClient.send('add_cart_item', command);
|
|
168
114
|
|
|
169
|
-
|
|
170
|
-
const commandResultStream = await
|
|
115
|
+
// Send command and receive results as a stream of events
|
|
116
|
+
const commandResultStream = await commandClient.sendAndWaitStream(
|
|
117
|
+
'add_cart_item',
|
|
118
|
+
command,
|
|
119
|
+
);
|
|
171
120
|
for await (const commandResultEvent of commandResultStream) {
|
|
172
|
-
console.log('Received:', commandResultEvent.data);
|
|
121
|
+
console.log('Received command result:', commandResultEvent.data);
|
|
173
122
|
}
|
|
174
123
|
```
|
|
175
124
|
|
|
176
|
-
#####
|
|
177
|
-
|
|
178
|
-
**Constructor**
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
new CommandHttpClient(fetcher
|
|
182
|
-
:
|
|
183
|
-
Fetcher
|
|
184
|
-
)
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Creates a new CommandHttpClient instance.
|
|
188
|
-
|
|
189
|
-
- `fetcher` - The Fetcher instance to use for HTTP requests
|
|
190
|
-
|
|
191
|
-
**send**
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
send(commandHttpRequest
|
|
195
|
-
:
|
|
196
|
-
CommandHttpRequest
|
|
197
|
-
):
|
|
198
|
-
Promise<CommandResult>
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Sends a command and waits for the result. This method sends a command to the Wow framework and waits for the processing
|
|
202
|
-
to complete before returning the result.
|
|
203
|
-
|
|
204
|
-
**sendAndWaitStream**
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
sendAndWaitStream(commandHttpRequest
|
|
208
|
-
:
|
|
209
|
-
CommandHttpRequest
|
|
210
|
-
):
|
|
211
|
-
Promise<CommandResultEventStream>
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Sends a command and returns a stream of results as events. This method sets the Accept header to text/event-stream to
|
|
215
|
-
receive Server-Sent Events, and uses a JSON event stream result extractor to parse the response. It's useful for
|
|
216
|
-
long-running commands where you want to receive progress updates or multiple results.
|
|
217
|
-
|
|
218
|
-
#### CommandResultEventStream
|
|
125
|
+
##### Methods
|
|
219
126
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
import { JsonServerSentEventStream } from '@ahoo-wang/fetcher-eventstream';
|
|
225
|
-
|
|
226
|
-
// CommandResultEventStream is a JsonServerSentEventStream of CommandResult
|
|
227
|
-
type CommandResultEventStream = JsonServerSentEventStream<CommandResult>;
|
|
228
|
-
```
|
|
127
|
+
- `send(path: string, commandRequest: CommandRequest): Promise<CommandResult>` - Sends a command and waits for the
|
|
128
|
+
result.
|
|
129
|
+
- `sendAndWaitStream(path: string, commandRequest: CommandRequest): Promise<CommandResultEventStream>` - Sends a command
|
|
130
|
+
and returns a stream of results as Server-Sent Events.
|
|
229
131
|
|
|
230
132
|
### Query Module
|
|
231
133
|
|
|
@@ -277,245 +179,261 @@ const dateConditions = [
|
|
|
277
179
|
];
|
|
278
180
|
```
|
|
279
181
|
|
|
280
|
-
####
|
|
182
|
+
#### SnapshotQueryClient
|
|
281
183
|
|
|
282
|
-
|
|
184
|
+
Client for querying materialized snapshots:
|
|
283
185
|
|
|
284
186
|
```typescript
|
|
285
|
-
import {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
Operator.GTE,
|
|
296
|
-
Operator.LTE);
|
|
297
|
-
|
|
298
|
-
// Membership operators
|
|
299
|
-
(Operator.IN, Operator.NOT_IN, Operator.ALL_IN, Operator.BETWEEN);
|
|
300
|
-
|
|
301
|
-
// String operators
|
|
302
|
-
(Operator.CONTAINS, Operator.STARTS_WITH, Operator.ENDS_WITH);
|
|
303
|
-
|
|
304
|
-
// Existence operators
|
|
305
|
-
(Operator.NULL, Operator.NOT_NULL, Operator.EXISTS);
|
|
306
|
-
|
|
307
|
-
// Boolean operators
|
|
308
|
-
(Operator.TRUE, Operator.FALSE);
|
|
309
|
-
|
|
310
|
-
// Date operators
|
|
311
|
-
(Operator.TODAY,
|
|
312
|
-
Operator.BEFORE_TODAY,
|
|
313
|
-
Operator.TOMORROW,
|
|
314
|
-
Operator.THIS_WEEK,
|
|
315
|
-
Operator.NEXT_WEEK,
|
|
316
|
-
Operator.LAST_WEEK,
|
|
317
|
-
Operator.THIS_MONTH,
|
|
318
|
-
Operator.LAST_MONTH,
|
|
319
|
-
Operator.RECENT_DAYS,
|
|
320
|
-
Operator.EARLIER_DAYS);
|
|
321
|
-
|
|
322
|
-
// Special operators
|
|
323
|
-
(Operator.ID,
|
|
324
|
-
Operator.IDS,
|
|
325
|
-
Operator.AGGREGATE_ID,
|
|
326
|
-
Operator.AGGREGATE_IDS,
|
|
327
|
-
Operator.TENANT_ID,
|
|
328
|
-
Operator.OWNER_ID,
|
|
329
|
-
Operator.DELETED,
|
|
330
|
-
Operator.ALL,
|
|
331
|
-
Operator.ELEM_MATCH,
|
|
332
|
-
Operator.RAW);
|
|
333
|
-
```
|
|
187
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
188
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
189
|
+
import {
|
|
190
|
+
SnapshotQueryClient,
|
|
191
|
+
all,
|
|
192
|
+
ListQuery,
|
|
193
|
+
PagedQuery,
|
|
194
|
+
SingleQuery,
|
|
195
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
196
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
334
197
|
|
|
335
|
-
|
|
198
|
+
interface CartItem {
|
|
199
|
+
productId: string;
|
|
200
|
+
quantity: number;
|
|
201
|
+
}
|
|
336
202
|
|
|
337
|
-
|
|
203
|
+
interface CartState {
|
|
204
|
+
id: string;
|
|
205
|
+
items: CartItem[];
|
|
206
|
+
}
|
|
338
207
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
DEFAULT_PAGINATION,
|
|
344
|
-
} from '@ahoo-wang/fetcher-wow';
|
|
208
|
+
// Create a fetcher instance
|
|
209
|
+
const wowFetcher = new Fetcher({
|
|
210
|
+
baseURL: 'http://localhost:8080/',
|
|
211
|
+
});
|
|
345
212
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
213
|
+
// Add interceptor to handle URL parameters
|
|
214
|
+
const ownerId = idGenerator.generateId();
|
|
215
|
+
wowFetcher.interceptors.request.use({
|
|
216
|
+
name: 'AppendOwnerId',
|
|
217
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
218
|
+
intercept(exchange) {
|
|
219
|
+
exchange.request.urlParams = {
|
|
220
|
+
path: {
|
|
221
|
+
...exchange.request.urlParams?.path,
|
|
222
|
+
ownerId,
|
|
223
|
+
},
|
|
224
|
+
query: exchange.request.urlParams?.query,
|
|
225
|
+
};
|
|
355
226
|
},
|
|
356
|
-
};
|
|
227
|
+
});
|
|
357
228
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
},
|
|
364
|
-
};
|
|
365
|
-
```
|
|
229
|
+
// Create the snapshot query client
|
|
230
|
+
const snapshotQueryClient = new SnapshotQueryClient<CartState>({
|
|
231
|
+
fetcher: wowFetcher,
|
|
232
|
+
basePath: 'owner/{ownerId}/cart',
|
|
233
|
+
});
|
|
366
234
|
|
|
367
|
-
|
|
235
|
+
// Count snapshots
|
|
236
|
+
const count = await snapshotQueryClient.count(all());
|
|
368
237
|
|
|
369
|
-
|
|
238
|
+
// List snapshots
|
|
239
|
+
const listQuery: ListQuery = {
|
|
240
|
+
condition: all(),
|
|
241
|
+
};
|
|
242
|
+
const list = await snapshotQueryClient.list(listQuery);
|
|
370
243
|
|
|
371
|
-
|
|
244
|
+
// List snapshots as stream
|
|
245
|
+
const listStream = await snapshotQueryClient.listStream(listQuery);
|
|
246
|
+
for await (const event of listStream) {
|
|
247
|
+
const snapshot = event.data;
|
|
248
|
+
console.log('Received snapshot:', snapshot);
|
|
249
|
+
}
|
|
372
250
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
Identifier,
|
|
376
|
-
Version,
|
|
377
|
-
TenantId,
|
|
378
|
-
OwnerId,
|
|
379
|
-
NamedAggregate,
|
|
380
|
-
AggregateId,
|
|
381
|
-
StateCapable,
|
|
382
|
-
} from '@ahoo-wang/fetcher-wow';
|
|
251
|
+
// List snapshot states
|
|
252
|
+
const stateList = await snapshotQueryClient.listState(listQuery);
|
|
383
253
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
NamedAggregate,
|
|
390
|
-
StateCapable<UserState> {
|
|
391
|
-
id: string;
|
|
392
|
-
version: number;
|
|
393
|
-
tenantId: string;
|
|
394
|
-
ownerId: string;
|
|
395
|
-
contextName: string;
|
|
396
|
-
aggregateName: string;
|
|
397
|
-
state: UserState;
|
|
254
|
+
// List snapshot states as stream
|
|
255
|
+
const stateStream = await snapshotQueryClient.listStateStream(listQuery);
|
|
256
|
+
for await (const event of stateStream) {
|
|
257
|
+
const state = event.data;
|
|
258
|
+
console.log('Received state:', state);
|
|
398
259
|
}
|
|
399
260
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
261
|
+
// Paged snapshots
|
|
262
|
+
const pagedQuery: PagedQuery = {
|
|
263
|
+
condition: all(),
|
|
264
|
+
};
|
|
265
|
+
const paged = await snapshotQueryClient.paged(pagedQuery);
|
|
266
|
+
|
|
267
|
+
// Paged snapshot states
|
|
268
|
+
const pagedState = await snapshotQueryClient.pagedState(pagedQuery);
|
|
269
|
+
|
|
270
|
+
// Single snapshot
|
|
271
|
+
const singleQuery: SingleQuery = {
|
|
272
|
+
condition: all(),
|
|
273
|
+
};
|
|
274
|
+
const single = await snapshotQueryClient.single(singleQuery);
|
|
275
|
+
|
|
276
|
+
// Single snapshot state
|
|
277
|
+
const singleState = await snapshotQueryClient.singleState(singleQuery);
|
|
406
278
|
```
|
|
407
279
|
|
|
408
|
-
####
|
|
280
|
+
#### EventStreamQueryClient
|
|
409
281
|
|
|
410
|
-
|
|
282
|
+
Client for querying domain event streams:
|
|
411
283
|
|
|
412
284
|
```typescript
|
|
413
|
-
import {
|
|
285
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
286
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
287
|
+
import {
|
|
288
|
+
EventStreamQueryClient,
|
|
289
|
+
all,
|
|
290
|
+
ListQuery,
|
|
291
|
+
PagedQuery,
|
|
292
|
+
} from '@ahoo-wang/fetcher-wow';
|
|
293
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
414
294
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
};
|
|
295
|
+
// Create a fetcher instance
|
|
296
|
+
const wowFetcher = new Fetcher({
|
|
297
|
+
baseURL: 'http://localhost:8080/',
|
|
298
|
+
});
|
|
420
299
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
300
|
+
// Add interceptor to handle URL parameters
|
|
301
|
+
const ownerId = idGenerator.generateId();
|
|
302
|
+
wowFetcher.interceptors.request.use({
|
|
303
|
+
name: 'AppendOwnerId',
|
|
304
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
305
|
+
intercept(exchange) {
|
|
306
|
+
exchange.request.urlParams = {
|
|
307
|
+
path: {
|
|
308
|
+
...exchange.request.urlParams?.path,
|
|
309
|
+
ownerId,
|
|
310
|
+
},
|
|
311
|
+
query: exchange.request.urlParams?.query,
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
});
|
|
428
315
|
|
|
429
|
-
|
|
316
|
+
// Create the event stream query client
|
|
317
|
+
const eventStreamQueryClient = new EventStreamQueryClient({
|
|
318
|
+
fetcher: wowFetcher,
|
|
319
|
+
basePath: 'owner/{ownerId}/cart',
|
|
320
|
+
});
|
|
430
321
|
|
|
431
|
-
|
|
322
|
+
// Count event streams
|
|
323
|
+
const count = await eventStreamQueryClient.count(all());
|
|
432
324
|
|
|
433
|
-
|
|
434
|
-
|
|
325
|
+
// List event streams
|
|
326
|
+
const listQuery: ListQuery = {
|
|
327
|
+
condition: all(),
|
|
328
|
+
};
|
|
329
|
+
const list = await eventStreamQueryClient.list(listQuery);
|
|
435
330
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
331
|
+
// List event streams as stream
|
|
332
|
+
const listStream = await eventStreamQueryClient.listStream(listQuery);
|
|
333
|
+
for await (const event of listStream) {
|
|
334
|
+
const domainEventStream = event.data;
|
|
335
|
+
console.log('Received event stream:', domainEventStream);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Paged event streams
|
|
339
|
+
const pagedQuery: PagedQuery = {
|
|
340
|
+
condition: all(),
|
|
441
341
|
};
|
|
342
|
+
const paged = await eventStreamQueryClient.paged(pagedQuery);
|
|
442
343
|
```
|
|
443
344
|
|
|
444
345
|
## ๐ ๏ธ Advanced Usage
|
|
445
346
|
|
|
446
|
-
###
|
|
347
|
+
### Complete Example with Command and Query Flow
|
|
447
348
|
|
|
448
349
|
```typescript
|
|
350
|
+
import { Fetcher, URL_RESOLVE_INTERCEPTOR_ORDER } from '@ahoo-wang/fetcher';
|
|
351
|
+
import '@ahoo-wang/fetcher-eventstream';
|
|
449
352
|
import {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
notIn,
|
|
459
|
-
between,
|
|
460
|
-
startsWith,
|
|
461
|
-
endsWith,
|
|
462
|
-
elemMatch,
|
|
463
|
-
isNull,
|
|
464
|
-
notNull,
|
|
465
|
-
exists,
|
|
466
|
-
today,
|
|
467
|
-
thisWeek,
|
|
468
|
-
recentDays,
|
|
353
|
+
CommandClient,
|
|
354
|
+
CommandRequest,
|
|
355
|
+
CommandHttpHeaders,
|
|
356
|
+
CommandStage,
|
|
357
|
+
HttpMethod,
|
|
358
|
+
SnapshotQueryClient,
|
|
359
|
+
all,
|
|
360
|
+
ListQuery,
|
|
469
361
|
} from '@ahoo-wang/fetcher-wow';
|
|
362
|
+
import { idGenerator } from '@ahoo-wang/fetcher-cosec';
|
|
470
363
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
),
|
|
481
|
-
// Age and score filters
|
|
482
|
-
gt('age', 18),
|
|
483
|
-
between('score', 50, 100),
|
|
484
|
-
|
|
485
|
-
// Department filters
|
|
486
|
-
isIn('departments', 'engineering', 'marketing'),
|
|
487
|
-
notIn('blockedDepartments', 'hr', 'finance'),
|
|
488
|
-
|
|
489
|
-
// String pattern matching
|
|
490
|
-
startsWith('employeeId', 'EMP-'),
|
|
491
|
-
endsWith('domain', '.com'),
|
|
492
|
-
|
|
493
|
-
// Array matching
|
|
494
|
-
elemMatch('roles', eq('name', 'admin')),
|
|
495
|
-
|
|
496
|
-
// Date filters
|
|
497
|
-
recentDays('lastLogin', 30),
|
|
498
|
-
thisWeek('createdAt'),
|
|
499
|
-
|
|
500
|
-
// Existence checks
|
|
501
|
-
exists('phoneNumber'),
|
|
502
|
-
notNull('address'),
|
|
503
|
-
),
|
|
364
|
+
interface CartItem {
|
|
365
|
+
productId: string;
|
|
366
|
+
quantity: number;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
interface CartState {
|
|
370
|
+
id: string;
|
|
371
|
+
items: CartItem[];
|
|
372
|
+
}
|
|
504
373
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
374
|
+
// Create a fetcher instance
|
|
375
|
+
const wowFetcher = new Fetcher({
|
|
376
|
+
baseURL: 'http://localhost:8080/',
|
|
377
|
+
});
|
|
509
378
|
|
|
510
|
-
|
|
511
|
-
|
|
379
|
+
// Add interceptor to handle URL parameters
|
|
380
|
+
const ownerId = idGenerator.generateId();
|
|
381
|
+
wowFetcher.interceptors.request.use({
|
|
382
|
+
name: 'AppendOwnerId',
|
|
383
|
+
order: URL_RESOLVE_INTERCEPTOR_ORDER - 1,
|
|
384
|
+
intercept(exchange) {
|
|
385
|
+
exchange.request.urlParams = {
|
|
386
|
+
path: {
|
|
387
|
+
...exchange.request.urlParams?.path,
|
|
388
|
+
ownerId,
|
|
389
|
+
},
|
|
390
|
+
query: exchange.request.urlParams?.query,
|
|
391
|
+
};
|
|
512
392
|
},
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Create clients
|
|
396
|
+
const commandClient = new CommandClient({
|
|
397
|
+
fetcher: wowFetcher,
|
|
398
|
+
basePath: 'owner/{ownerId}/cart',
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const snapshotQueryClient = new SnapshotQueryClient<CartState>({
|
|
402
|
+
fetcher: wowFetcher,
|
|
403
|
+
basePath: 'owner/{ownerId}/cart',
|
|
404
|
+
});
|
|
513
405
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
406
|
+
// 1. Send command to add item to cart
|
|
407
|
+
const addItemCommand: CommandRequest = {
|
|
408
|
+
method: HttpMethod.POST,
|
|
409
|
+
headers: {
|
|
410
|
+
[CommandHttpHeaders.WAIT_STAGE]: CommandStage.SNAPSHOT,
|
|
517
411
|
},
|
|
412
|
+
body: {
|
|
413
|
+
productId: 'product-123',
|
|
414
|
+
quantity: 2,
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const commandResult = await commandClient.send('add_cart_item', addItemCommand);
|
|
419
|
+
console.log('Command executed:', commandResult);
|
|
420
|
+
|
|
421
|
+
// 2. Query the updated cart
|
|
422
|
+
const listQuery: ListQuery = {
|
|
423
|
+
condition: all(),
|
|
518
424
|
};
|
|
425
|
+
const carts = await snapshotQueryClient.list(listQuery);
|
|
426
|
+
|
|
427
|
+
for (const cart of carts) {
|
|
428
|
+
console.log('Cart:', cart.state);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// 3. Stream cart updates
|
|
432
|
+
const listStream = await snapshotQueryClient.listStream(listQuery);
|
|
433
|
+
for await (const event of listStream) {
|
|
434
|
+
const cart = event.data;
|
|
435
|
+
console.log('Cart updated:', cart.state);
|
|
436
|
+
}
|
|
519
437
|
```
|
|
520
438
|
|
|
521
439
|
## ๐งช Testing
|