@leancodepl/rx-cqrs-client 8.5.0 → 8.5.1
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 +138 -0
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +185 -0
- package/index.cjs.mjs +2 -0
- package/index.d.ts +1 -0
- package/index.esm.js +180 -0
- package/package.json +3 -8
- package/src/index.d.ts +4 -0
- package/src/lib/authGuard.d.ts +3 -0
- package/src/lib/handleCommandResponse.d.ts +22 -0
- package/src/lib/mkCqrsClient.d.ts +39 -0
- package/src/lib/reduceBoolean.d.ts +19 -0
- package/src/lib/reduceObject.d.ts +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# @leancodepl/rx-cqrs-client
|
|
2
|
+
|
|
3
|
+
RxJS-based CQRS client for reactive command and query operations.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **RxJS integration** - Reactive streams with operators for real-time data handling
|
|
8
|
+
- **CQRS pattern** - Separate queries, commands, and operations with proper typing
|
|
9
|
+
- **Observable operations** - Stream-based data handling with composable operators
|
|
10
|
+
- **Error handling** - Validation errors with custom error codes and stream operators
|
|
11
|
+
- **Authentication** - Token handling with automatic refresh and retry logic
|
|
12
|
+
- **Custom operators** - Command response handling and stream transformation utilities
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @leancodepl/rx-cqrs-client
|
|
18
|
+
# or
|
|
19
|
+
yarn add @leancodepl/rx-cqrs-client
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## API
|
|
23
|
+
|
|
24
|
+
### `mkCqrsClient(cqrsEndpoint, tokenProvider, ajaxOptions, tokenHeader)`
|
|
25
|
+
|
|
26
|
+
Creates CQRS client for command and query operations.
|
|
27
|
+
|
|
28
|
+
**Parameters:**
|
|
29
|
+
|
|
30
|
+
- `cqrsEndpoint: string` - Base URL for CQRS API endpoints
|
|
31
|
+
- `tokenProvider?: TokenProvider` - Optional token provider for authentication
|
|
32
|
+
- `ajaxOptions?: Omit<AjaxConfig, ...>` - Optional RxJS Ajax configuration options
|
|
33
|
+
- `tokenHeader?: string` - Header name for authentication token (default: "Authorization")
|
|
34
|
+
|
|
35
|
+
**Returns:** Object with `createQuery`, `createOperation`, and `createCommand` observable factories
|
|
36
|
+
|
|
37
|
+
### `handleCommandResponse(handlerFunc)`
|
|
38
|
+
|
|
39
|
+
Handles command responses with RxJS operators for validation error processing.
|
|
40
|
+
|
|
41
|
+
**Parameters:**
|
|
42
|
+
|
|
43
|
+
- `handlerFunc: Function` - Function that processes validation error handlers
|
|
44
|
+
|
|
45
|
+
**Returns:** RxJS operator function for handling command responses
|
|
46
|
+
|
|
47
|
+
### `reduceBoolean()`
|
|
48
|
+
|
|
49
|
+
Reduces boolean values in RxJS streams using logical AND operation.
|
|
50
|
+
|
|
51
|
+
**Returns:** RxJS operator function that reduces boolean values
|
|
52
|
+
|
|
53
|
+
### `reduceObject()`
|
|
54
|
+
|
|
55
|
+
Reduces object values in RxJS streams by merging properties.
|
|
56
|
+
|
|
57
|
+
**Returns:** RxJS operator function that reduces object values
|
|
58
|
+
|
|
59
|
+
## Usage Examples
|
|
60
|
+
|
|
61
|
+
### Basic Setup
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { mkCqrsClient } from "@leancodepl/rx-cqrs-client"
|
|
65
|
+
|
|
66
|
+
const client = mkCqrsClient({
|
|
67
|
+
cqrsEndpoint: "https://api.example.com",
|
|
68
|
+
tokenProvider: {
|
|
69
|
+
getToken: () => Promise.resolve(localStorage.getItem("token")),
|
|
70
|
+
invalidateToken: () => Promise.resolve(true),
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Query Operations
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { switchMap } from "rxjs/operators"
|
|
79
|
+
|
|
80
|
+
interface GetUserQuery {
|
|
81
|
+
userId: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface UserResult {
|
|
85
|
+
id: string
|
|
86
|
+
name: string
|
|
87
|
+
email: string
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const getUser = client.createQuery<GetUserQuery, UserResult>("GetUser")
|
|
91
|
+
|
|
92
|
+
getUser({ userId: "123" }).subscribe({
|
|
93
|
+
next: user => console.log("User:", user),
|
|
94
|
+
error: error => console.error("Error:", error),
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Command Operations
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { catchError } from "rxjs/operators"
|
|
102
|
+
import { of } from "rxjs"
|
|
103
|
+
|
|
104
|
+
interface CreateUserCommand {
|
|
105
|
+
name: string
|
|
106
|
+
email: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const errorCodes = { EmailExists: 1, InvalidEmail: 2 } as const
|
|
110
|
+
const createUser = client.createCommand<CreateUserCommand, typeof errorCodes>("CreateUser", errorCodes)
|
|
111
|
+
|
|
112
|
+
createUser
|
|
113
|
+
.handle({ name: "John", email: "john@example.com" })
|
|
114
|
+
.handle("success", () => console.log("User created"))
|
|
115
|
+
.handle("EmailExists", () => console.log("Email already exists"))
|
|
116
|
+
.handle("failure", () => console.log("Creation failed"))
|
|
117
|
+
.check()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Reactive Patterns
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { fromEvent, switchMap, debounceTime } from "rxjs"
|
|
124
|
+
|
|
125
|
+
const searchInput = document.getElementById("search") as HTMLInputElement
|
|
126
|
+
|
|
127
|
+
fromEvent(searchInput, "input")
|
|
128
|
+
.pipe(
|
|
129
|
+
debounceTime(300),
|
|
130
|
+
switchMap(event => {
|
|
131
|
+
const query = (event.target as HTMLInputElement).value
|
|
132
|
+
return client.createQuery("SearchUsers")({ query })
|
|
133
|
+
}),
|
|
134
|
+
)
|
|
135
|
+
.subscribe(results => {
|
|
136
|
+
console.log("Search results:", results)
|
|
137
|
+
})
|
|
138
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exports._default = require('./index.cjs.js').default;
|
package/index.cjs.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var rxjs = require('rxjs');
|
|
4
|
+
var operators = require('rxjs/operators');
|
|
5
|
+
var ajax = require('rxjs/ajax');
|
|
6
|
+
var validation = require('@leancodepl/validation');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Handles command responses with RxJS operators for validation error processing.
|
|
10
|
+
*
|
|
11
|
+
* Creates an operator that processes validation error handlers and transforms them
|
|
12
|
+
* into the desired result type using a reducer pattern.
|
|
13
|
+
*
|
|
14
|
+
* @param handlerFunc - Function that processes validation error handlers
|
|
15
|
+
* @returns RxJS operator function for handling command responses
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { handleCommandResponse } from '@leancodepl/rx-cqrs-client';
|
|
19
|
+
*
|
|
20
|
+
* const handler = handleCommandResponse((handle) =>
|
|
21
|
+
* handle('success', () => 'Success!')
|
|
22
|
+
* .handle('failure', () => 'Failed!')
|
|
23
|
+
* .check()
|
|
24
|
+
* );
|
|
25
|
+
* ```
|
|
26
|
+
*/ function handleCommandResponse(handlerFunc) {
|
|
27
|
+
return (source)=>source.pipe(operators.concatMap((handler)=>{
|
|
28
|
+
const subj = new rxjs.ReplaySubject();
|
|
29
|
+
handlerFunc(handler)({
|
|
30
|
+
reducer: (prev, cur)=>subj.next(cur),
|
|
31
|
+
initialValue: null
|
|
32
|
+
});
|
|
33
|
+
subj.complete();
|
|
34
|
+
return subj;
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function authGuard(tokenProvider) {
|
|
39
|
+
return (response)=>response.pipe(operators.retryWhen((errors$)=>errors$.pipe(operators.mergeMap((error, i)=>{
|
|
40
|
+
if (i === 0 && error.status === 401) {
|
|
41
|
+
return rxjs.from(tokenProvider.invalidateToken()).pipe(operators.mergeMap((success)=>success ? rxjs.of(success) : rxjs.throwError(()=>error)));
|
|
42
|
+
}
|
|
43
|
+
return rxjs.throwError(()=>error);
|
|
44
|
+
}))));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates RxJS-based CQRS client for reactive command and query operations.
|
|
49
|
+
*
|
|
50
|
+
* Provides observable-based methods for CQRS operations with automatic authentication,
|
|
51
|
+
* retry logic, and error handling.
|
|
52
|
+
*
|
|
53
|
+
* @param cqrsEndpoint - Base URL for CQRS API endpoints
|
|
54
|
+
* @param tokenProvider - Optional token provider for authentication
|
|
55
|
+
* @param ajaxOptions - Optional RxJS Ajax configuration options
|
|
56
|
+
* @param tokenHeader - Header name for authentication token (default: "Authorization")
|
|
57
|
+
* @returns Object with `createQuery`, `createOperation`, and `createCommand` observable factories
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const client = mkCqrsClient({
|
|
61
|
+
* cqrsEndpoint: 'https://api.example.com',
|
|
62
|
+
* tokenProvider: { getToken: () => Promise.resolve('token') }
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/ function mkCqrsClient({ cqrsEndpoint, tokenProvider, ajaxOptions, tokenHeader = "Authorization" }) {
|
|
66
|
+
return {
|
|
67
|
+
createQuery (type) {
|
|
68
|
+
const queryCall = (dto, token)=>ajax.ajax({
|
|
69
|
+
...ajaxOptions,
|
|
70
|
+
headers: {
|
|
71
|
+
[tokenHeader]: token,
|
|
72
|
+
"Content-Type": "application/json"
|
|
73
|
+
},
|
|
74
|
+
url: `${cqrsEndpoint}/query/${type}`,
|
|
75
|
+
method: "POST",
|
|
76
|
+
responseType: "json",
|
|
77
|
+
body: dto
|
|
78
|
+
});
|
|
79
|
+
if (tokenProvider) {
|
|
80
|
+
return (dto)=>rxjs.from(tokenProvider.getToken()).pipe(operators.mergeMap((token)=>queryCall(dto, token).pipe(authGuard(tokenProvider))), operators.map(({ response })=>response));
|
|
81
|
+
}
|
|
82
|
+
return (dto)=>queryCall(dto).pipe(operators.map(({ response })=>response));
|
|
83
|
+
},
|
|
84
|
+
createOperation (type) {
|
|
85
|
+
const operationCall = (dto, token)=>ajax.ajax({
|
|
86
|
+
...ajaxOptions,
|
|
87
|
+
headers: {
|
|
88
|
+
Authorization: token,
|
|
89
|
+
"Content-Type": "application/json"
|
|
90
|
+
},
|
|
91
|
+
url: `${cqrsEndpoint}/operation/${type}`,
|
|
92
|
+
method: "POST",
|
|
93
|
+
responseType: "json",
|
|
94
|
+
body: dto
|
|
95
|
+
});
|
|
96
|
+
if (tokenProvider) {
|
|
97
|
+
return (dto)=>rxjs.from(tokenProvider.getToken()).pipe(operators.mergeMap((token)=>operationCall(dto, token).pipe(authGuard(tokenProvider))), operators.map(({ response })=>response));
|
|
98
|
+
}
|
|
99
|
+
return (dto)=>operationCall(dto).pipe(operators.map(({ response })=>response));
|
|
100
|
+
},
|
|
101
|
+
createCommand (type, errorCodesMap) {
|
|
102
|
+
const commandCall = (dto, token)=>ajax.ajax({
|
|
103
|
+
...ajaxOptions,
|
|
104
|
+
headers: {
|
|
105
|
+
Authorization: token,
|
|
106
|
+
"Content-Type": "application/json"
|
|
107
|
+
},
|
|
108
|
+
url: `${cqrsEndpoint}/command/${type}`,
|
|
109
|
+
method: "POST",
|
|
110
|
+
responseType: "json",
|
|
111
|
+
body: dto
|
|
112
|
+
});
|
|
113
|
+
function call(dto) {
|
|
114
|
+
if (tokenProvider) {
|
|
115
|
+
return rxjs.from(tokenProvider.getToken()).pipe(operators.mergeMap((token)=>commandCall(dto, token).pipe(authGuard(tokenProvider))), operators.map(({ response })=>response));
|
|
116
|
+
}
|
|
117
|
+
return commandCall(dto).pipe(operators.map(({ response })=>response));
|
|
118
|
+
}
|
|
119
|
+
call.handle = (dto)=>call(dto).pipe(operators.map((result)=>({
|
|
120
|
+
isSuccess: true,
|
|
121
|
+
result
|
|
122
|
+
})), rxjs.catchError((e)=>{
|
|
123
|
+
if (e instanceof ajax.AjaxError && e.status === 422) {
|
|
124
|
+
return rxjs.of({
|
|
125
|
+
isSuccess: true,
|
|
126
|
+
result: e.response
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return rxjs.of({
|
|
130
|
+
isSuccess: false,
|
|
131
|
+
error: e
|
|
132
|
+
});
|
|
133
|
+
}), operators.map((response)=>validation.handleResponse(response, errorCodesMap)));
|
|
134
|
+
return call;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Reduces boolean values in RxJS streams using logical AND operation.
|
|
141
|
+
*
|
|
142
|
+
* Creates an operator that combines multiple boolean values into a single result
|
|
143
|
+
* by applying logical AND. Returns true only if all values are true.
|
|
144
|
+
*
|
|
145
|
+
* @returns RxJS operator function that reduces boolean values
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* import { of } from 'rxjs';
|
|
149
|
+
* import { reduceBoolean } from '@leancodepl/rx-cqrs-client';
|
|
150
|
+
*
|
|
151
|
+
* of(true, true, false).pipe(reduceBoolean()).subscribe(result => {
|
|
152
|
+
* console.log(result); // false
|
|
153
|
+
* });
|
|
154
|
+
* ```
|
|
155
|
+
*/ function reduceBoolean() {
|
|
156
|
+
return (source)=>source.pipe(rxjs.reduce((prev, cur)=>prev && cur, true));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Reduces object values in RxJS streams by merging properties.
|
|
161
|
+
*
|
|
162
|
+
* Creates an operator that combines multiple objects into a single result
|
|
163
|
+
* by spreading properties. Later objects override earlier ones.
|
|
164
|
+
*
|
|
165
|
+
* @returns RxJS operator function that reduces object values
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* import { of } from 'rxjs';
|
|
169
|
+
* import { reduceObject } from '@leancodepl/rx-cqrs-client';
|
|
170
|
+
*
|
|
171
|
+
* of({ a: 1 }, { b: 2 }, { a: 3 }).pipe(reduceObject()).subscribe(result => {
|
|
172
|
+
* console.log(result); // { a: 3, b: 2 }
|
|
173
|
+
* });
|
|
174
|
+
* ```
|
|
175
|
+
*/ function reduceObject() {
|
|
176
|
+
return (source)=>source.pipe(rxjs.reduce((prev, cur)=>({
|
|
177
|
+
...prev,
|
|
178
|
+
...cur
|
|
179
|
+
}), {}));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
exports.handleCommandResponse = handleCommandResponse;
|
|
183
|
+
exports.mkCqrsClient = mkCqrsClient;
|
|
184
|
+
exports.reduceBoolean = reduceBoolean;
|
|
185
|
+
exports.reduceObject = reduceObject;
|
package/index.cjs.mjs
ADDED
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./src/index";
|
package/index.esm.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { ReplaySubject, from, of, throwError, catchError, reduce } from 'rxjs';
|
|
2
|
+
import { concatMap, retryWhen, mergeMap, map } from 'rxjs/operators';
|
|
3
|
+
import { AjaxError, ajax } from 'rxjs/ajax';
|
|
4
|
+
import { handleResponse } from '@leancodepl/validation';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Handles command responses with RxJS operators for validation error processing.
|
|
8
|
+
*
|
|
9
|
+
* Creates an operator that processes validation error handlers and transforms them
|
|
10
|
+
* into the desired result type using a reducer pattern.
|
|
11
|
+
*
|
|
12
|
+
* @param handlerFunc - Function that processes validation error handlers
|
|
13
|
+
* @returns RxJS operator function for handling command responses
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { handleCommandResponse } from '@leancodepl/rx-cqrs-client';
|
|
17
|
+
*
|
|
18
|
+
* const handler = handleCommandResponse((handle) =>
|
|
19
|
+
* handle('success', () => 'Success!')
|
|
20
|
+
* .handle('failure', () => 'Failed!')
|
|
21
|
+
* .check()
|
|
22
|
+
* );
|
|
23
|
+
* ```
|
|
24
|
+
*/ function handleCommandResponse(handlerFunc) {
|
|
25
|
+
return (source)=>source.pipe(concatMap((handler)=>{
|
|
26
|
+
const subj = new ReplaySubject();
|
|
27
|
+
handlerFunc(handler)({
|
|
28
|
+
reducer: (prev, cur)=>subj.next(cur),
|
|
29
|
+
initialValue: null
|
|
30
|
+
});
|
|
31
|
+
subj.complete();
|
|
32
|
+
return subj;
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function authGuard(tokenProvider) {
|
|
37
|
+
return (response)=>response.pipe(retryWhen((errors$)=>errors$.pipe(mergeMap((error, i)=>{
|
|
38
|
+
if (i === 0 && error.status === 401) {
|
|
39
|
+
return from(tokenProvider.invalidateToken()).pipe(mergeMap((success)=>success ? of(success) : throwError(()=>error)));
|
|
40
|
+
}
|
|
41
|
+
return throwError(()=>error);
|
|
42
|
+
}))));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates RxJS-based CQRS client for reactive command and query operations.
|
|
47
|
+
*
|
|
48
|
+
* Provides observable-based methods for CQRS operations with automatic authentication,
|
|
49
|
+
* retry logic, and error handling.
|
|
50
|
+
*
|
|
51
|
+
* @param cqrsEndpoint - Base URL for CQRS API endpoints
|
|
52
|
+
* @param tokenProvider - Optional token provider for authentication
|
|
53
|
+
* @param ajaxOptions - Optional RxJS Ajax configuration options
|
|
54
|
+
* @param tokenHeader - Header name for authentication token (default: "Authorization")
|
|
55
|
+
* @returns Object with `createQuery`, `createOperation`, and `createCommand` observable factories
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const client = mkCqrsClient({
|
|
59
|
+
* cqrsEndpoint: 'https://api.example.com',
|
|
60
|
+
* tokenProvider: { getToken: () => Promise.resolve('token') }
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/ function mkCqrsClient({ cqrsEndpoint, tokenProvider, ajaxOptions, tokenHeader = "Authorization" }) {
|
|
64
|
+
return {
|
|
65
|
+
createQuery (type) {
|
|
66
|
+
const queryCall = (dto, token)=>ajax({
|
|
67
|
+
...ajaxOptions,
|
|
68
|
+
headers: {
|
|
69
|
+
[tokenHeader]: token,
|
|
70
|
+
"Content-Type": "application/json"
|
|
71
|
+
},
|
|
72
|
+
url: `${cqrsEndpoint}/query/${type}`,
|
|
73
|
+
method: "POST",
|
|
74
|
+
responseType: "json",
|
|
75
|
+
body: dto
|
|
76
|
+
});
|
|
77
|
+
if (tokenProvider) {
|
|
78
|
+
return (dto)=>from(tokenProvider.getToken()).pipe(mergeMap((token)=>queryCall(dto, token).pipe(authGuard(tokenProvider))), map(({ response })=>response));
|
|
79
|
+
}
|
|
80
|
+
return (dto)=>queryCall(dto).pipe(map(({ response })=>response));
|
|
81
|
+
},
|
|
82
|
+
createOperation (type) {
|
|
83
|
+
const operationCall = (dto, token)=>ajax({
|
|
84
|
+
...ajaxOptions,
|
|
85
|
+
headers: {
|
|
86
|
+
Authorization: token,
|
|
87
|
+
"Content-Type": "application/json"
|
|
88
|
+
},
|
|
89
|
+
url: `${cqrsEndpoint}/operation/${type}`,
|
|
90
|
+
method: "POST",
|
|
91
|
+
responseType: "json",
|
|
92
|
+
body: dto
|
|
93
|
+
});
|
|
94
|
+
if (tokenProvider) {
|
|
95
|
+
return (dto)=>from(tokenProvider.getToken()).pipe(mergeMap((token)=>operationCall(dto, token).pipe(authGuard(tokenProvider))), map(({ response })=>response));
|
|
96
|
+
}
|
|
97
|
+
return (dto)=>operationCall(dto).pipe(map(({ response })=>response));
|
|
98
|
+
},
|
|
99
|
+
createCommand (type, errorCodesMap) {
|
|
100
|
+
const commandCall = (dto, token)=>ajax({
|
|
101
|
+
...ajaxOptions,
|
|
102
|
+
headers: {
|
|
103
|
+
Authorization: token,
|
|
104
|
+
"Content-Type": "application/json"
|
|
105
|
+
},
|
|
106
|
+
url: `${cqrsEndpoint}/command/${type}`,
|
|
107
|
+
method: "POST",
|
|
108
|
+
responseType: "json",
|
|
109
|
+
body: dto
|
|
110
|
+
});
|
|
111
|
+
function call(dto) {
|
|
112
|
+
if (tokenProvider) {
|
|
113
|
+
return from(tokenProvider.getToken()).pipe(mergeMap((token)=>commandCall(dto, token).pipe(authGuard(tokenProvider))), map(({ response })=>response));
|
|
114
|
+
}
|
|
115
|
+
return commandCall(dto).pipe(map(({ response })=>response));
|
|
116
|
+
}
|
|
117
|
+
call.handle = (dto)=>call(dto).pipe(map((result)=>({
|
|
118
|
+
isSuccess: true,
|
|
119
|
+
result
|
|
120
|
+
})), catchError((e)=>{
|
|
121
|
+
if (e instanceof AjaxError && e.status === 422) {
|
|
122
|
+
return of({
|
|
123
|
+
isSuccess: true,
|
|
124
|
+
result: e.response
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return of({
|
|
128
|
+
isSuccess: false,
|
|
129
|
+
error: e
|
|
130
|
+
});
|
|
131
|
+
}), map((response)=>handleResponse(response, errorCodesMap)));
|
|
132
|
+
return call;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Reduces boolean values in RxJS streams using logical AND operation.
|
|
139
|
+
*
|
|
140
|
+
* Creates an operator that combines multiple boolean values into a single result
|
|
141
|
+
* by applying logical AND. Returns true only if all values are true.
|
|
142
|
+
*
|
|
143
|
+
* @returns RxJS operator function that reduces boolean values
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* import { of } from 'rxjs';
|
|
147
|
+
* import { reduceBoolean } from '@leancodepl/rx-cqrs-client';
|
|
148
|
+
*
|
|
149
|
+
* of(true, true, false).pipe(reduceBoolean()).subscribe(result => {
|
|
150
|
+
* console.log(result); // false
|
|
151
|
+
* });
|
|
152
|
+
* ```
|
|
153
|
+
*/ function reduceBoolean() {
|
|
154
|
+
return (source)=>source.pipe(reduce((prev, cur)=>prev && cur, true));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Reduces object values in RxJS streams by merging properties.
|
|
159
|
+
*
|
|
160
|
+
* Creates an operator that combines multiple objects into a single result
|
|
161
|
+
* by spreading properties. Later objects override earlier ones.
|
|
162
|
+
*
|
|
163
|
+
* @returns RxJS operator function that reduces object values
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* import { of } from 'rxjs';
|
|
167
|
+
* import { reduceObject } from '@leancodepl/rx-cqrs-client';
|
|
168
|
+
*
|
|
169
|
+
* of({ a: 1 }, { b: 2 }, { a: 3 }).pipe(reduceObject()).subscribe(result => {
|
|
170
|
+
* console.log(result); // { a: 3, b: 2 }
|
|
171
|
+
* });
|
|
172
|
+
* ```
|
|
173
|
+
*/ function reduceObject() {
|
|
174
|
+
return (source)=>source.pipe(reduce((prev, cur)=>({
|
|
175
|
+
...prev,
|
|
176
|
+
...cur
|
|
177
|
+
}), {}));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { handleCommandResponse, mkCqrsClient, reduceBoolean, reduceObject };
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leancodepl/rx-cqrs-client",
|
|
3
|
-
"version": "8.5.
|
|
3
|
+
"version": "8.5.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@leancodepl/cqrs-client-base": "8.5.
|
|
7
|
-
"@leancodepl/validation": "8.5.
|
|
6
|
+
"@leancodepl/cqrs-client-base": "8.5.1",
|
|
7
|
+
"@leancodepl/validation": "8.5.1",
|
|
8
8
|
"rxjs": ">=7.0.0"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
@@ -38,11 +38,6 @@
|
|
|
38
38
|
"name": "LeanCode",
|
|
39
39
|
"url": "https://leancode.co"
|
|
40
40
|
},
|
|
41
|
-
"files": [
|
|
42
|
-
"dist",
|
|
43
|
-
"README.md",
|
|
44
|
-
"CHANGELOG.md"
|
|
45
|
-
],
|
|
46
41
|
"sideEffects": false,
|
|
47
42
|
"exports": {
|
|
48
43
|
"./package.json": "./package.json",
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { OperatorFunction } from "rxjs";
|
|
2
|
+
import type { ReducerDescription, ValidationErrorsHandler } from "@leancodepl/validation";
|
|
3
|
+
/**
|
|
4
|
+
* Handles command responses with RxJS operators for validation error processing.
|
|
5
|
+
*
|
|
6
|
+
* Creates an operator that processes validation error handlers and transforms them
|
|
7
|
+
* into the desired result type using a reducer pattern.
|
|
8
|
+
*
|
|
9
|
+
* @param handlerFunc - Function that processes validation error handlers
|
|
10
|
+
* @returns RxJS operator function for handling command responses
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { handleCommandResponse } from '@leancodepl/rx-cqrs-client';
|
|
14
|
+
*
|
|
15
|
+
* const handler = handleCommandResponse((handle) =>
|
|
16
|
+
* handle('success', () => 'Success!')
|
|
17
|
+
* .handle('failure', () => 'Failed!')
|
|
18
|
+
* .check()
|
|
19
|
+
* );
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleCommandResponse<TErrorCodes extends Record<string, number>, THandlerResult>(handlerFunc: (handler: ValidationErrorsHandler<TErrorCodes, never>) => (reducer: ReducerDescription<THandlerResult, any>) => any): OperatorFunction<ValidationErrorsHandler<TErrorCodes, never>, THandlerResult>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AjaxConfig } from "rxjs/ajax";
|
|
2
|
+
import { CommandResult, TokenProvider } from "@leancodepl/cqrs-client-base";
|
|
3
|
+
/**
|
|
4
|
+
* Creates RxJS-based CQRS client for reactive command and query operations.
|
|
5
|
+
*
|
|
6
|
+
* Provides observable-based methods for CQRS operations with automatic authentication,
|
|
7
|
+
* retry logic, and error handling.
|
|
8
|
+
*
|
|
9
|
+
* @param cqrsEndpoint - Base URL for CQRS API endpoints
|
|
10
|
+
* @param tokenProvider - Optional token provider for authentication
|
|
11
|
+
* @param ajaxOptions - Optional RxJS Ajax configuration options
|
|
12
|
+
* @param tokenHeader - Header name for authentication token (default: "Authorization")
|
|
13
|
+
* @returns Object with `createQuery`, `createOperation`, and `createCommand` observable factories
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const client = mkCqrsClient({
|
|
17
|
+
* cqrsEndpoint: 'https://api.example.com',
|
|
18
|
+
* tokenProvider: { getToken: () => Promise.resolve('token') }
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function mkCqrsClient({ cqrsEndpoint, tokenProvider, ajaxOptions, tokenHeader, }: {
|
|
23
|
+
cqrsEndpoint: string;
|
|
24
|
+
tokenProvider?: TokenProvider;
|
|
25
|
+
ajaxOptions?: Omit<AjaxConfig, "body" | "headers" | "method" | "responseType" | "url">;
|
|
26
|
+
tokenHeader?: string;
|
|
27
|
+
}): {
|
|
28
|
+
createQuery<TQuery, TResult>(type: string): (dto: TQuery) => import("rxjs").Observable<TResult>;
|
|
29
|
+
createOperation<TOperation, TResult>(type: string): (dto: TOperation) => import("rxjs").Observable<TResult>;
|
|
30
|
+
createCommand<TCommand, TErrorCodes extends {
|
|
31
|
+
[name: string]: number;
|
|
32
|
+
}>(type: string, errorCodesMap: TErrorCodes): {
|
|
33
|
+
(dto: TCommand): import("rxjs").Observable<CommandResult<TErrorCodes>>;
|
|
34
|
+
handle(dto: TCommand): import("rxjs").Observable<import("@leancodepl/validation").ValidationErrorsHandler<TErrorCodes & {
|
|
35
|
+
readonly success: -1;
|
|
36
|
+
readonly failure: -2;
|
|
37
|
+
}, never>>;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OperatorFunction } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Reduces boolean values in RxJS streams using logical AND operation.
|
|
4
|
+
*
|
|
5
|
+
* Creates an operator that combines multiple boolean values into a single result
|
|
6
|
+
* by applying logical AND. Returns true only if all values are true.
|
|
7
|
+
*
|
|
8
|
+
* @returns RxJS operator function that reduces boolean values
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { of } from 'rxjs';
|
|
12
|
+
* import { reduceBoolean } from '@leancodepl/rx-cqrs-client';
|
|
13
|
+
*
|
|
14
|
+
* of(true, true, false).pipe(reduceBoolean()).subscribe(result => {
|
|
15
|
+
* console.log(result); // false
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function reduceBoolean(): OperatorFunction<boolean, boolean>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OperatorFunction } from "rxjs";
|
|
2
|
+
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
|
|
3
|
+
/**
|
|
4
|
+
* Reduces object values in RxJS streams by merging properties.
|
|
5
|
+
*
|
|
6
|
+
* Creates an operator that combines multiple objects into a single result
|
|
7
|
+
* by spreading properties. Later objects override earlier ones.
|
|
8
|
+
*
|
|
9
|
+
* @returns RxJS operator function that reduces object values
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { of } from 'rxjs';
|
|
13
|
+
* import { reduceObject } from '@leancodepl/rx-cqrs-client';
|
|
14
|
+
*
|
|
15
|
+
* of({ a: 1 }, { b: 2 }, { a: 3 }).pipe(reduceObject()).subscribe(result => {
|
|
16
|
+
* console.log(result); // { a: 3, b: 2 }
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function reduceObject<T>(): OperatorFunction<T, Partial<UnionToIntersection<T>>>;
|
|
21
|
+
export {};
|