http-request-manager 18.7.8 → 18.7.10
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
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# Request Manager Service Documentation
|
|
2
2
|
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `http-request-manager` is a comprehensive Angular library that provides enterprise-grade HTTP request management, state management, real-time communication, and local data persistence. It simplifies complex data flows and provides a unified API for handling HTTP, WebSocket, and storage operations.
|
|
6
|
+
|
|
7
|
+
### Core Capabilities
|
|
8
|
+
|
|
9
|
+
#### 🌐 HTTP Request Management
|
|
10
|
+
|
|
11
|
+
- **Observable-based** service (`HTTPManagerService`) for reactive programming
|
|
12
|
+
- **Signal-based** service (`HTTPManagerSignalsService`) for modern Angular applications
|
|
13
|
+
- Automatic retry logic with configurable attempts and delays
|
|
14
|
+
- Polling support with countdown timers
|
|
15
|
+
- Streaming response handling (NDJSON, Server-Sent Events)
|
|
16
|
+
- Request/response adapters and mappers for data transformation
|
|
17
|
+
- Built-in error handling with optional toast notifications
|
|
18
|
+
- File download support with progress tracking
|
|
19
|
+
|
|
20
|
+
#### 🔄 State Management
|
|
21
|
+
|
|
22
|
+
- **ComponentStore integration** (`HTTPManagerStateService`) for robust state management
|
|
23
|
+
- Automatic CRUD state updates (Create, Read, Update, Delete)
|
|
24
|
+
- Built-in selectors for data access and filtering
|
|
25
|
+
- Pagination support with state tracking
|
|
26
|
+
- Real-time state synchronization via WebSocket
|
|
27
|
+
- IndexedDB caching for offline-first applications
|
|
28
|
+
- Persistent state with LocalStorage/SessionStorage
|
|
29
|
+
|
|
30
|
+
#### 💬 Real-Time Communication (WebSocket)
|
|
31
|
+
|
|
32
|
+
- **Dual-mode messaging system:**
|
|
33
|
+
- **Messaging (PUB- channels)**: Real-time chat/collaboration
|
|
34
|
+
- **Notifications (MES- channels)**: Database-persisted notifications with date-range queries
|
|
35
|
+
- Automatic reconnection with exponential backoff
|
|
36
|
+
- JWT authentication support
|
|
37
|
+
- Channel-based architecture (STATE, MESSAGE, NOTIFICATION)
|
|
38
|
+
- Subscribe/unsubscribe to channels dynamically
|
|
39
|
+
- Broadcast, channel, and user-specific messaging
|
|
40
|
+
- Connection status monitoring
|
|
41
|
+
- Session persistence across reconnections
|
|
42
|
+
|
|
43
|
+
#### 💾 Data Persistence
|
|
44
|
+
|
|
45
|
+
- **LocalStorage/SessionStorage** with encryption and expiration
|
|
46
|
+
- **IndexedDB** integration via Dexie.js for complex queries
|
|
47
|
+
- Automatic cache invalidation based on time
|
|
48
|
+
- Secure encryption using symmetrical encryption service
|
|
49
|
+
- Reactive storage observables for UI synchronization
|
|
50
|
+
|
|
51
|
+
#### 🎯 Advanced Features
|
|
52
|
+
|
|
53
|
+
- Request interception (credentials, headers, error handling)
|
|
54
|
+
- Automatic loading state management
|
|
55
|
+
- Database-backed notification history with epoch filtering
|
|
56
|
+
- Define and load previous day's notification channels
|
|
57
|
+
- Bulk operations for IndexedDB
|
|
58
|
+
- Model adapters for type safety
|
|
59
|
+
- Configurable global defaults via `forRoot()`
|
|
60
|
+
|
|
61
|
+
### Key Benefits
|
|
62
|
+
|
|
63
|
+
✅ **Reduced Boilerplate** - Eliminates repetitive HTTP and state management code
|
|
64
|
+
✅ **Type-Safe** - Full TypeScript support with generics
|
|
65
|
+
✅ **Offline-First** - Built-in IndexedDB caching for resilient applications
|
|
66
|
+
✅ **Real-Time Ready** - Seamless WebSocket integration with state management
|
|
67
|
+
✅ **Secure** - Encryption support for sensitive local data
|
|
68
|
+
✅ **Scalable** - ComponentStore-based architecture for complex state
|
|
69
|
+
✅ **Flexible** - Works with Observables or Signals based on preference
|
|
70
|
+
✅ **Battle-Tested** - Production-ready with comprehensive error handling
|
|
71
|
+
|
|
72
|
+
### Use Cases
|
|
73
|
+
|
|
74
|
+
| Scenario | Solution |
|
|
75
|
+
|----------|----------|
|
|
76
|
+
| Simple API calls with loading states | `HTTPManagerService` |
|
|
77
|
+
| Modern reactive UI with Signals | `HTTPManagerSignalsService` |
|
|
78
|
+
| CRUD operations with auto state sync | `HTTPManagerStateService` |
|
|
79
|
+
| Real-time chat/collaboration | `HTTPManagerStateService` + WebSocket (PUB- channels) |
|
|
80
|
+
| Persistent notifications/alerts | `HTTPManagerStateService` + WebSocket (MES- channels) |
|
|
81
|
+
| User preferences/settings | `LocalStorageManagerService` |
|
|
82
|
+
| Offline-first data access | `DatabaseManagerService` + `HTTPManagerStateService` |
|
|
83
|
+
| Large local datasets | `DatabaseManagerService` with Dexie.js |
|
|
84
|
+
| Polling for live updates | `HTTPManagerService` with polling |
|
|
85
|
+
| File downloads | `HTTPManagerService.downloadRequest()` |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
3
89
|
## Demo Component (`HttpRequestServicesDemoComponent`)
|
|
4
90
|
|
|
5
91
|
The `HttpRequestServicesDemoComponent` is a comprehensive demonstration component that showcases the various services available in the `http-request-manager` library. It allows you to interactively test and visualize the functionality of HTTP services, state management, WebSockets, and local storage.
|
|
@@ -34,6 +120,357 @@ To use the demo component in your application, simply add the selector to your t
|
|
|
34
120
|
|
|
35
121
|
The `http-request-manager` library provides a comprehensive set of services for handling HTTP requests, state management, local storage, and WebSocket communications in Angular applications.
|
|
36
122
|
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Quick Start Guide
|
|
126
|
+
|
|
127
|
+
### Installation & Setup (2 minutes)
|
|
128
|
+
|
|
129
|
+
#### 1. Import Module
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// app.module.ts
|
|
133
|
+
import { HttpRequestManagerModule } from 'http-request-manager';
|
|
134
|
+
|
|
135
|
+
@NgModule({
|
|
136
|
+
imports: [
|
|
137
|
+
HttpRequestManagerModule.forRoot({
|
|
138
|
+
httpRequestOptions: {
|
|
139
|
+
server: 'http://localhost:8080', // Your API base URL
|
|
140
|
+
retry: { times: 3, delay: 2 },
|
|
141
|
+
displayError: true
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
]
|
|
145
|
+
})
|
|
146
|
+
export class AppModule { }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### 2. Provide APP_ID (if using encryption)
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// app.module.ts
|
|
153
|
+
import { APP_ID } from '@angular/core';
|
|
154
|
+
|
|
155
|
+
@NgModule({
|
|
156
|
+
providers: [
|
|
157
|
+
{ provide: APP_ID, useValue: "your-unique-guid-here" }
|
|
158
|
+
]
|
|
159
|
+
})
|
|
160
|
+
export class AppModule { }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Quick Examples
|
|
164
|
+
|
|
165
|
+
#### Example 1: Simple HTTP GET Request
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { Component, inject } from '@angular/core';
|
|
169
|
+
import { HTTPManagerService, ApiRequest } from 'http-request-manager';
|
|
170
|
+
|
|
171
|
+
@Component({
|
|
172
|
+
selector: 'app-users',
|
|
173
|
+
template: `
|
|
174
|
+
<div *ngIf="isLoading$ | async">Loading...</div>
|
|
175
|
+
<div *ngFor="let user of data$ | async">
|
|
176
|
+
{{ user.name }}
|
|
177
|
+
</div>
|
|
178
|
+
`
|
|
179
|
+
})
|
|
180
|
+
export class UsersComponent {
|
|
181
|
+
httpManager = inject(HTTPManagerService);
|
|
182
|
+
|
|
183
|
+
data$ = this.httpManager.data$;
|
|
184
|
+
isLoading$ = this.httpManager.isPending$;
|
|
185
|
+
|
|
186
|
+
ngOnInit() {
|
|
187
|
+
this.httpManager.getRequest(
|
|
188
|
+
ApiRequest.adapt({ path: ['users'] })
|
|
189
|
+
).subscribe();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### Example 2: State Management with CRUD
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { Injectable } from '@angular/core';
|
|
198
|
+
import { HTTPManagerStateService, ApiRequest, DataType } from 'http-request-manager';
|
|
199
|
+
|
|
200
|
+
interface User {
|
|
201
|
+
id: number;
|
|
202
|
+
name: string;
|
|
203
|
+
email: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@Injectable({ providedIn: 'root' })
|
|
207
|
+
export class UsersStore extends HTTPManagerStateService<User> {
|
|
208
|
+
|
|
209
|
+
constructor() {
|
|
210
|
+
super(
|
|
211
|
+
ApiRequest.adapt({
|
|
212
|
+
server: 'http://localhost:8080',
|
|
213
|
+
path: ['users']
|
|
214
|
+
}),
|
|
215
|
+
DataType.ARRAY
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Public API
|
|
220
|
+
loadUsers() { this.fetchRecords(); }
|
|
221
|
+
addUser(user: User) { this.createRecord(user); }
|
|
222
|
+
updateUser(user: User) { this.updateRecord(user); }
|
|
223
|
+
deleteUser(id: number) { this.deleteRecord(id); }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Component
|
|
227
|
+
@Component({
|
|
228
|
+
selector: 'app-users',
|
|
229
|
+
template: `
|
|
230
|
+
<button (click)="store.loadUsers()">Load</button>
|
|
231
|
+
<div *ngFor="let user of store.data$ | async">
|
|
232
|
+
{{ user.name }}
|
|
233
|
+
<button (click)="store.deleteUser(user.id)">Delete</button>
|
|
234
|
+
</div>
|
|
235
|
+
`
|
|
236
|
+
})
|
|
237
|
+
export class UsersComponent {
|
|
238
|
+
store = inject(UsersStore);
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
#### Example 3: WebSocket Real-Time Messaging
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
@Injectable({ providedIn: 'root' })
|
|
246
|
+
export class ChatStore extends HTTPManagerStateService<Message> {
|
|
247
|
+
|
|
248
|
+
constructor() {
|
|
249
|
+
super(
|
|
250
|
+
ApiRequest.adapt({
|
|
251
|
+
server: 'http://localhost:8080',
|
|
252
|
+
path: ['messages'],
|
|
253
|
+
ws: {
|
|
254
|
+
id: 'CHAT_WS',
|
|
255
|
+
wsServer: 'ws://localhost:8080',
|
|
256
|
+
jwtToken: '' // Add token if needed
|
|
257
|
+
}
|
|
258
|
+
}),
|
|
259
|
+
DataType.ARRAY
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
joinChat(channel: string) {
|
|
264
|
+
this.createChannel(channel);
|
|
265
|
+
this.subscribeToChannel(channel);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
sendMessage(channel: string, text: string, user: any) {
|
|
269
|
+
this.wsMessaging(
|
|
270
|
+
ChannelMessage.adapt({
|
|
271
|
+
sessionId: user,
|
|
272
|
+
content: { message: text }
|
|
273
|
+
}),
|
|
274
|
+
[channel]
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Component
|
|
280
|
+
@Component({
|
|
281
|
+
template: `
|
|
282
|
+
<div *ngFor="let msg of chatStore.communicationMessages$ | async">
|
|
283
|
+
{{ msg.content.message }}
|
|
284
|
+
</div>
|
|
285
|
+
<input [(ngModel)]="messageText">
|
|
286
|
+
<button (click)="send()">Send</button>
|
|
287
|
+
`
|
|
288
|
+
})
|
|
289
|
+
export class ChatComponent {
|
|
290
|
+
chatStore = inject(ChatStore);
|
|
291
|
+
messageText = '';
|
|
292
|
+
|
|
293
|
+
ngOnInit() {
|
|
294
|
+
this.chatStore.joinChat('general');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
send() {
|
|
298
|
+
this.chatStore.sendMessage('general', this.messageText, {
|
|
299
|
+
id: 1,
|
|
300
|
+
name: 'John'
|
|
301
|
+
});
|
|
302
|
+
this.messageText = '';
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### Example 4: Persistent Notifications with Database
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
@Injectable({ providedIn: 'root' })
|
|
311
|
+
export class NotificationStore extends HTTPManagerStateService<any> {
|
|
312
|
+
|
|
313
|
+
constructor() {
|
|
314
|
+
super(
|
|
315
|
+
ApiRequest.adapt({
|
|
316
|
+
server: 'http://localhost:8080',
|
|
317
|
+
ws: {
|
|
318
|
+
id: 'NOTIFICATIONS',
|
|
319
|
+
wsServer: 'ws://localhost:8080'
|
|
320
|
+
}
|
|
321
|
+
}),
|
|
322
|
+
DataType.ARRAY
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
initNotifications(user: any) {
|
|
327
|
+
// Get today's notification channels
|
|
328
|
+
this.getTodaysNotificationChannels();
|
|
329
|
+
|
|
330
|
+
// Subscribe to alerts with 24h history
|
|
331
|
+
this.subscribeToNotificationChannel('alerts', {
|
|
332
|
+
startEpoch: Math.floor(Date.now() / 1000) - 86400
|
|
333
|
+
}, user);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
sendAlert(message: string, priority: 'low' | 'high') {
|
|
337
|
+
this.sendNotification('alerts', {
|
|
338
|
+
message,
|
|
339
|
+
priority,
|
|
340
|
+
timestamp: Date.now()
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
loadPreviousDayChannels() {
|
|
345
|
+
this.definePreviousNotificationChannels();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Component
|
|
350
|
+
@Component({
|
|
351
|
+
template: `
|
|
352
|
+
<div *ngFor="let notif of store.notificationMessages$ | async">
|
|
353
|
+
<strong>{{ notif.user_name }}</strong>: {{ notif.content.message }}
|
|
354
|
+
<span>{{ notif.created * 1000 | date:'short' }}</span>
|
|
355
|
+
</div>
|
|
356
|
+
<button (click)="store.loadPreviousDayChannels()">
|
|
357
|
+
Load Previous Day Channels
|
|
358
|
+
</button>
|
|
359
|
+
`
|
|
360
|
+
})
|
|
361
|
+
export class NotificationsComponent {
|
|
362
|
+
store = inject(NotificationStore);
|
|
363
|
+
|
|
364
|
+
ngOnInit() {
|
|
365
|
+
this.store.initNotifications({
|
|
366
|
+
id: 1,
|
|
367
|
+
name: 'John',
|
|
368
|
+
email: 'john@example.com'
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
#### Example 5: Local Storage with Encryption
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { Component, inject } from '@angular/core';
|
|
378
|
+
import { LocalStorageManagerService, SettingOptions } from 'http-request-manager';
|
|
379
|
+
|
|
380
|
+
@Component({
|
|
381
|
+
selector: 'app-settings'
|
|
382
|
+
})
|
|
383
|
+
export class SettingsComponent {
|
|
384
|
+
storage = inject(LocalStorageManagerService);
|
|
385
|
+
|
|
386
|
+
saveSettings(settings: any) {
|
|
387
|
+
this.storage.setItem(
|
|
388
|
+
'user-preferences',
|
|
389
|
+
settings,
|
|
390
|
+
SettingOptions.adapt({
|
|
391
|
+
encrypted: true,
|
|
392
|
+
expiresIn: '30d'
|
|
393
|
+
})
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
loadSettings() {
|
|
398
|
+
return this.storage.getItem('user-preferences');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
clearSettings() {
|
|
402
|
+
this.storage.removeItem('user-preferences');
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
#### Example 6: IndexedDB Caching for Offline-First
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
@Injectable({ providedIn: 'root' })
|
|
411
|
+
export class ProductStore extends HTTPManagerStateService<Product> {
|
|
412
|
+
|
|
413
|
+
constructor() {
|
|
414
|
+
super(
|
|
415
|
+
ApiRequest.adapt({
|
|
416
|
+
server: 'http://localhost:8080',
|
|
417
|
+
path: ['products'],
|
|
418
|
+
adapter: (data: any) => data as Product // Required for DB
|
|
419
|
+
}),
|
|
420
|
+
DataType.ARRAY,
|
|
421
|
+
DatabaseStorage.adapt({
|
|
422
|
+
table: 'products-cache',
|
|
423
|
+
expiresIn: '1d' // Cache for 1 day
|
|
424
|
+
})
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
loadProducts() {
|
|
429
|
+
// First checks IndexedDB cache, then fetches from server if expired
|
|
430
|
+
this.fetchRecords();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### Example 7: Polling for Live Updates
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
export class StatusComponent {
|
|
439
|
+
httpManager = inject(HTTPManagerService);
|
|
440
|
+
|
|
441
|
+
status$ = this.httpManager.data$;
|
|
442
|
+
countdown$ = this.httpManager.countdown$;
|
|
443
|
+
|
|
444
|
+
ngOnInit() {
|
|
445
|
+
this.httpManager.getRequest(
|
|
446
|
+
ApiRequest.adapt({
|
|
447
|
+
path: ['server-status'],
|
|
448
|
+
polling: 10 // Poll every 10 seconds
|
|
449
|
+
})
|
|
450
|
+
).subscribe();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
ngOnDestroy() {
|
|
454
|
+
this.httpManager.stopPolling();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Common Use Cases
|
|
460
|
+
|
|
461
|
+
| Use Case | Service to Use | Key Features |
|
|
462
|
+
|----------|---------------|--------------|
|
|
463
|
+
| Simple API calls | `HTTPManagerService` | Observables, retry, polling |
|
|
464
|
+
| Modern reactive UI | `HTTPManagerSignalsService` | Angular Signals |
|
|
465
|
+
| CRUD operations | `HTTPManagerStateService` | Auto state updates |
|
|
466
|
+
| Real-time chat | `HTTPManagerStateService` + WS | Messaging channels |
|
|
467
|
+
| Persistent notifications | `HTTPManagerStateService` + WS | Database-backed |
|
|
468
|
+
| User preferences | `LocalStorageManagerService` | Encryption, expiration |
|
|
469
|
+
| Offline-first | `DatabaseManagerService` | IndexedDB |
|
|
470
|
+
| Large local datasets | `DatabaseManagerService` | Querying, indexing |
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
37
474
|
## Initialization
|
|
38
475
|
|
|
39
476
|
### 1. Module Initialization (`forRoot`)
|
|
@@ -680,6 +1117,11 @@ stateService.getNotificationChannels();
|
|
|
680
1117
|
// Get today's notification channels (from database)
|
|
681
1118
|
stateService.getTodaysNotificationChannels();
|
|
682
1119
|
|
|
1120
|
+
// Define and load previous day's notification channels
|
|
1121
|
+
// This retrieves channels from the previous day's database records,
|
|
1122
|
+
// creates them in memory if needed, and broadcasts the updated list
|
|
1123
|
+
stateService.definePreviousNotificationChannels();
|
|
1124
|
+
|
|
683
1125
|
// Subscribe to notifications with optional date filter
|
|
684
1126
|
stateService.subscribeToNotificationChannel('alerts', {
|
|
685
1127
|
startEpoch: Math.floor(Date.now() / 1000) - 86400, // Last 24 hours
|
|
@@ -727,6 +1169,7 @@ The library includes a demo component showcasing notification functionality:
|
|
|
727
1169
|
|
|
728
1170
|
- Create notification channels
|
|
729
1171
|
- View today's notification channels (from database)
|
|
1172
|
+
- Define previous day's channels (loads channels from previous day's data)
|
|
730
1173
|
- Subscribe with date range filters
|
|
731
1174
|
- Connect/disconnect from channels
|
|
732
1175
|
- Send notifications (persisted)
|