http-request-manager 18.7.12 → 18.7.14
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 +1282 -223
- package/fesm2022/http-request-manager.mjs +2371 -2297
- package/fesm2022/http-request-manager.mjs.map +1 -1
- package/http-request-manager-18.7.14.tgz +0 -0
- package/package.json +1 -1
- package/types/http-request-manager.d.ts +46 -31
- package/http-request-manager-18.7.12.tgz +0 -0
package/README.md
CHANGED
|
@@ -1,44 +1,128 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Request Manager Service Documentation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://angular.io/)
|
|
5
|
-
[](https://rxjs.dev/)
|
|
3
|
+
## Overview
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## 🚀 Features
|
|
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.
|
|
10
6
|
|
|
11
7
|
### Core Capabilities
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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()`
|
|
21
60
|
|
|
22
61
|
### Key Benefits
|
|
23
62
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
|
|
89
|
+
## Demo Component (`HttpRequestServicesDemoComponent`)
|
|
90
|
+
|
|
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.
|
|
92
|
+
|
|
93
|
+
### Usage
|
|
94
|
+
|
|
95
|
+
To use the demo component in your application, simply add the selector to your template and provide the necessary inputs.
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<app-http-request-services-demo
|
|
99
|
+
[server]="'http://localhost:8080'"
|
|
100
|
+
[wsServer]="'ws://localhost:8080'"
|
|
101
|
+
[jwtToken]="'your-jwt-token'"
|
|
102
|
+
[adapter]="myAdapterFunction"
|
|
103
|
+
[mapper]="myMapperFunction">
|
|
104
|
+
</app-http-request-services-demo>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Inputs
|
|
108
|
+
|
|
109
|
+
| Input | Type | Description | Default |
|
|
110
|
+
| :--- | :--- | :--- | :--- |
|
|
111
|
+
| `server` | `string` | The base URL for HTTP requests used in the demo. | `'http://localhost:8080'` |
|
|
112
|
+
| `wsServer` | `string` | The WebSocket server URL for testing WS connections. | `'ws://localhost:8080'` |
|
|
113
|
+
| `jwtToken` | `string` | A JWT token used for authenticating WebSocket connections. | `''` |
|
|
114
|
+
| `adapter` | `Function` | An optional adapter function to transform incoming API data. | `undefined` |
|
|
115
|
+
| `mapper` | `Function` | An optional mapper function to transform outgoing request payloads. | `undefined` |
|
|
30
116
|
|
|
31
|
-
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Summary
|
|
32
120
|
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- [Architecture](#architecture)
|
|
37
|
-
- [Interceptors](#interceptors)
|
|
38
|
-
- [Demo Examples](#demo-examples)
|
|
39
|
-
- [Migration Guide](#migration-guide)
|
|
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.
|
|
122
|
+
|
|
123
|
+
---
|
|
40
124
|
|
|
41
|
-
##
|
|
125
|
+
## Quick Start Guide
|
|
42
126
|
|
|
43
127
|
### Installation & Setup (2 minutes)
|
|
44
128
|
|
|
@@ -76,9 +160,9 @@ import { APP_ID } from '@angular/core';
|
|
|
76
160
|
export class AppModule { }
|
|
77
161
|
```
|
|
78
162
|
|
|
79
|
-
###
|
|
163
|
+
### Quick Examples
|
|
80
164
|
|
|
81
|
-
####
|
|
165
|
+
#### Example 1: Simple HTTP GET Request
|
|
82
166
|
|
|
83
167
|
```typescript
|
|
84
168
|
import { Component, inject } from '@angular/core';
|
|
@@ -107,9 +191,18 @@ export class UsersComponent {
|
|
|
107
191
|
}
|
|
108
192
|
```
|
|
109
193
|
|
|
110
|
-
#### State Management with CRUD
|
|
194
|
+
#### Example 2: State Management with CRUD
|
|
111
195
|
|
|
112
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
|
+
|
|
113
206
|
@Injectable({ providedIn: 'root' })
|
|
114
207
|
export class UsersStore extends HTTPManagerStateService<User> {
|
|
115
208
|
|
|
@@ -146,11 +239,288 @@ export class UsersComponent {
|
|
|
146
239
|
}
|
|
147
240
|
```
|
|
148
241
|
|
|
149
|
-
|
|
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
|
+
|
|
474
|
+
## Initialization
|
|
475
|
+
|
|
476
|
+
### 1. Module Initialization (`forRoot`)
|
|
477
|
+
|
|
478
|
+
To use the library, import `HttpRequestManagerModule` in your `AppModule` and configure it using the `forRoot` method.
|
|
479
|
+
|
|
480
|
+
#### Configuration Options (`ConfigOptions`)
|
|
481
|
+
|
|
482
|
+
| Option | Type | Description | Default |
|
|
483
|
+
| :--- | :--- | :--- | :--- |
|
|
484
|
+
| `httpRequestOptions` | `ConfigHTTPOptions` | Global configuration for HTTP requests. | `undefined` |
|
|
485
|
+
| `LocalStorageOptions` | `LocalStorageOptions` | Global configuration for Local Storage management. | `undefined` |
|
|
486
|
+
|
|
487
|
+
#### HTTP Options (`ConfigHTTPOptions`)
|
|
488
|
+
|
|
489
|
+
| Option | Type | Description | Default |
|
|
490
|
+
| :--- | :--- | :--- | :--- |
|
|
491
|
+
| `server` | `string` | The base URL for API requests. | `''` |
|
|
492
|
+
| `path` | `any[]` | Default path segments to append to the server URL. | `[]` |
|
|
493
|
+
| `headers` | `any` | Default headers to include in every request. | `{}` |
|
|
494
|
+
| `polling` | `number` | Default polling interval in seconds (0 = disabled). | `0` |
|
|
495
|
+
| `retry` | `RetryOptions` | Default retry configuration. | `{ times: 0, delay: 3 }` |
|
|
496
|
+
| `stream` | `boolean` | Whether to enable streaming by default. | `false` |
|
|
497
|
+
| `displayError` | `boolean` | Whether to display toast notifications for errors by default. | `false` |
|
|
498
|
+
|
|
499
|
+
#### Retry Options (`RetryOptions`)
|
|
500
|
+
|
|
501
|
+
| Option | Type | Description | Default |
|
|
502
|
+
| :--- | :--- | :--- | :--- |
|
|
503
|
+
| `times` | `number` | The number of retry attempts. | `0` |
|
|
504
|
+
| `delay` | `number` | The delay between retries in seconds. | `3` |
|
|
505
|
+
|
|
506
|
+
#### Local Storage Options (`LocalStorageOptions`)
|
|
507
|
+
|
|
508
|
+
| Option | Type | Description | Default |
|
|
509
|
+
| :--- | :--- | :--- | :--- |
|
|
510
|
+
| `storageName` | `string` | The key used to store data in localStorage/sessionStorage. | `'storage'` |
|
|
511
|
+
| `storageSettingsName` | `string` | The key used to store settings metadata. | `'global-storage'` |
|
|
512
|
+
| `options` | `SettingOptions` | Default settings for stored items. | `{ storage: StorageType.GLOBAL, expires: 0, expiresIn: '', encrypted: false }` |
|
|
513
|
+
|
|
514
|
+
#### Setting Options (`SettingOptions`)
|
|
150
515
|
|
|
151
|
-
|
|
516
|
+
| Option | Type | Description | Default |
|
|
517
|
+
| :--- | :--- | :--- | :--- |
|
|
518
|
+
| `storage` | `StorageType` | The type of storage to use (`StorageType.GLOBAL` or `StorageType.SESSION`). | `StorageType.GLOBAL` |
|
|
519
|
+
| `expires` | `number` | Expiration timestamp (epoch). Usually calculated from `expiresIn`. | `0` |
|
|
520
|
+
| `expiresIn` | `string` | Duration string (e.g., '1d', '2h') for expiration. | `''` |
|
|
521
|
+
| `encrypted` | `boolean` | Whether to encrypt the stored data. | `false` |
|
|
152
522
|
|
|
153
|
-
|
|
523
|
+
#### Example
|
|
154
524
|
|
|
155
525
|
```typescript
|
|
156
526
|
import { HttpRequestManagerModule } from 'http-request-manager';
|
|
@@ -173,254 +543,943 @@ import { HttpRequestManagerModule } from 'http-request-manager';
|
|
|
173
543
|
}
|
|
174
544
|
}
|
|
175
545
|
})
|
|
176
|
-
]
|
|
546
|
+
],
|
|
547
|
+
// ...
|
|
177
548
|
})
|
|
178
549
|
export class AppModule { }
|
|
179
550
|
```
|
|
180
551
|
|
|
181
|
-
###
|
|
552
|
+
### 2. AppService Initialization
|
|
182
553
|
|
|
183
|
-
|
|
554
|
+
The `AppService` (used by `LocalStorageManagerService` for encryption) requires a unique `APP_ID` to be provided in your `AppModule`.
|
|
184
555
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
| `server` | `string` | Base URL for API requests | `''` |
|
|
188
|
-
| `path` | `any[]` | Default path segments | `[]` |
|
|
189
|
-
| `headers` | `any` | Default headers | `{}` |
|
|
190
|
-
| `polling` | `number` | Default polling interval (seconds) | `0` |
|
|
191
|
-
| `retry` | `RetryOptions` | Default retry configuration | `{ times: 0, delay: 3 }` |
|
|
192
|
-
| `stream` | `boolean` | Enable streaming by default | `false` |
|
|
193
|
-
| `displayError` | `boolean` | Show toast errors by default | `false` |
|
|
556
|
+
```typescript
|
|
557
|
+
import { APP_ID } from '@angular/core';
|
|
194
558
|
|
|
195
|
-
|
|
559
|
+
@NgModule({
|
|
560
|
+
providers: [
|
|
561
|
+
{
|
|
562
|
+
provide: APP_ID,
|
|
563
|
+
useValue: "your-unique-guid-here", // e.g., "056991ac-3537-43ab-b5b9-83edf6554eff"
|
|
564
|
+
},
|
|
565
|
+
// ...
|
|
566
|
+
],
|
|
567
|
+
// ...
|
|
568
|
+
})
|
|
569
|
+
export class AppModule { }
|
|
570
|
+
```
|
|
196
571
|
|
|
197
|
-
|
|
198
|
-
|--------|------|-------------|---------|
|
|
199
|
-
| `storageName` | `string` | Key for storing data | `'storage'` |
|
|
200
|
-
| `storageSettingsName` | `string` | Key for settings metadata | `'global-storage'` |
|
|
201
|
-
| `options` | `SettingOptions` | Default storage settings | `{ storage: StorageType.GLOBAL, expires: 0, expiresIn: '', encrypted: false }` |
|
|
572
|
+
## Services Documentation
|
|
202
573
|
|
|
203
|
-
|
|
574
|
+
### 1. Request Manager Service (`HTTPManagerService`)
|
|
575
|
+
|
|
576
|
+
**Path:** `request-manager-services/http-manager.service.ts`
|
|
577
|
+
|
|
578
|
+
A robust HTTP client service using RxJS `BehaviorSubject` for state management.
|
|
579
|
+
|
|
580
|
+
**What it does:**
|
|
581
|
+
This service acts as a wrapper around Angular's `HttpClient`, providing a centralized way to manage HTTP requests. It handles the entire lifecycle of a request, including loading states, error handling, and data updates, exposing them as Observables.
|
|
582
|
+
|
|
583
|
+
**Features:**
|
|
584
|
+
|
|
585
|
+
- **State Management:** Exposes `data$`, `isPending$`, `error$`, and `countdown$` observables.
|
|
586
|
+
- **Polling:** Supports automatic polling of endpoints with a configurable interval and countdown timer.
|
|
587
|
+
- **Retries:** Automatically retries failed requests based on configuration (number of attempts and delay).
|
|
588
|
+
- **Adapters & Mappers:** Supports `adapter` functions to transform incoming data and `mapper` functions for outgoing payloads.
|
|
589
|
+
- **Streaming:** Handles streaming responses when configured.
|
|
590
|
+
- **Error Handling:** Centralized error handling with optional Toast notifications.
|
|
591
|
+
- **CRUD Support:** Provides methods for `getRequest`, `postRequest`, `putRequest`, `deleteRequest`, and `downloadRequest`.
|
|
592
|
+
|
|
593
|
+
**Usage:**
|
|
594
|
+
Inject `HTTPManagerService` to make API requests. It handles loading state, errors, and data updates via Observables.
|
|
595
|
+
|
|
596
|
+
#### API Request Options (`ApiRequest`)
|
|
204
597
|
|
|
205
598
|
| Option | Type | Description | Default |
|
|
206
|
-
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
|
213
|
-
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
| [`StoreStateManagerService`](services/store-state-manager/README.md) | Persistent ComponentStore | Application state persistence |
|
|
221
|
-
| [`UtilsService`](services/utils/README.md) | Utility functions | JSON handling, expiration, conversions |
|
|
599
|
+
| :--- | :--- | :--- | :--- |
|
|
600
|
+
| `server` | `string` | The base URL for the request. | `''` |
|
|
601
|
+
| `path` | `any[]` | Path segments to append to the URL. | `[]` |
|
|
602
|
+
| `headers` | `any` | Custom headers for the request. | `{}` |
|
|
603
|
+
| `adapter` | `function` | Function to transform the response data. | `undefined` |
|
|
604
|
+
| `mapper` | `function` | Function to transform the request payload. | `undefined` |
|
|
605
|
+
| `polling` | `number` | Polling interval in seconds (0 = disabled). | `0` |
|
|
606
|
+
| `retry` | `RetryOptions` | Retry configuration. | `{ times: 0, delay: 3 }` |
|
|
607
|
+
| `stream` | `boolean` | Enable streaming response handling. | `false` |
|
|
608
|
+
| `displayError` | `boolean` | Show toast notification on error. | `false` |
|
|
609
|
+
| `saveAs` | `string` | Filename for file downloads. | `undefined` |
|
|
610
|
+
| `ws` | `WSOptions` | WebSocket configuration options. | `undefined` |
|
|
611
|
+
|
|
612
|
+
#### HTTPManagerService Example
|
|
222
613
|
|
|
223
|
-
|
|
614
|
+
```typescript
|
|
615
|
+
export class MyComponent {
|
|
616
|
+
httpManager = inject(HTTPManagerService);
|
|
224
617
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
618
|
+
data$ = this.httpManager.data$;
|
|
619
|
+
isLoading$ = this.httpManager.isPending$;
|
|
620
|
+
error$ = this.httpManager.error$;
|
|
621
|
+
|
|
622
|
+
fetchData() {
|
|
623
|
+
const options = ApiRequest.adapt({
|
|
624
|
+
path: ['users'],
|
|
625
|
+
polling: 5, // Poll every 5 seconds
|
|
626
|
+
retry: { times: 3, delay: 1 },
|
|
627
|
+
adapter: User.adapt // Transform response to User model
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
this.httpManager.getRequest(options).subscribe();
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
#### Advanced Features
|
|
235
636
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
```
|
|
245
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
246
|
-
│ Angular Application │
|
|
247
|
-
├─────────────────────────────────────────────────────────────────┤
|
|
248
|
-
│ ┌────────────────┐ ┌────────────────┐ ┌──────────────────┐ │
|
|
249
|
-
│ │ Components │ │ Components │ │ Components │ │
|
|
250
|
-
│ │ (Signals) │ │ (Observables) │ │ (State Store) │ │
|
|
251
|
-
│ └───────┬────────┘ └───────┬────────┘ └────────┬─────────┘ │
|
|
252
|
-
│ │ │ │ │
|
|
253
|
-
│ ▼ ▼ ▼ │
|
|
254
|
-
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
255
|
-
│ │HTTPManager │ │HTTPManager │ │HTTPManager │ │
|
|
256
|
-
│ │SignalsService│ │Service │ │StateService │ │
|
|
257
|
-
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
|
|
258
|
-
│ │ │ │ │
|
|
259
|
-
│ └───────────────────┴──────────────────────┘ │
|
|
260
|
-
│ │ │
|
|
261
|
-
│ ▼ │
|
|
262
|
-
│ ┌─────────────────┐ │
|
|
263
|
-
│ │ HttpClient │ │
|
|
264
|
-
│ │ (Angular) │ │
|
|
265
|
-
│ └────────┬────────┘ │
|
|
266
|
-
│ │ │
|
|
267
|
-
├─────────────────────────────┼────────────────────────────────────┤
|
|
268
|
-
│ │ │
|
|
269
|
-
│ ┌──────────────────────────┼──────────────────────┐ │
|
|
270
|
-
│ │ Storage Layer │ │
|
|
271
|
-
│ ├──────────────────────────┼──────────────────────┤ │
|
|
272
|
-
│ │ ┌────────────────┐ │ ┌─────────────┐│ │
|
|
273
|
-
│ │ │LocalStorage │ │ │IndexedDB ││ │
|
|
274
|
-
│ │ │Manager Service │ │ │(Dexie.js) ││ │
|
|
275
|
-
│ │ └────────────────┘ │ └─────────────┘│ │
|
|
276
|
-
│ └──────────────────────────┼──────────────────────┘ │
|
|
277
|
-
│ │ │
|
|
278
|
-
│ ┌──────────────────────────┼──────────────────────┐ │
|
|
279
|
-
│ │ WebSocket Layer │ │
|
|
280
|
-
│ ├──────────────────────────┼──────────────────────┤ │
|
|
281
|
-
│ │ ┌─────────────────────────────────────┐ │ │
|
|
282
|
-
│ │ │ WebsocketService │ │ │
|
|
283
|
-
│ │ └─────────────────────────────────────┘ │ │
|
|
284
|
-
│ └─────────────────────────────────────────────────┘ │
|
|
285
|
-
└─────────────────────────────────────────────────────────────────┘
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
## 🔧 Interceptors
|
|
289
|
-
|
|
290
|
-
The library provides several HTTP interceptors that are automatically configured:
|
|
291
|
-
|
|
292
|
-
📋 **[Interceptors Documentation](interceptors/README.md)**
|
|
293
|
-
|
|
294
|
-
### Available Interceptors
|
|
295
|
-
|
|
296
|
-
- **RequestErrorInterceptor** - Handles 400/500 errors with toast notifications
|
|
297
|
-
- **WithCredentialsInterceptor** - Adds `withCredentials: true` to requests
|
|
298
|
-
- **RequestHeadersInterceptor** - Adds common headers (Content-Type, Accept-Language, Current-Date)
|
|
299
|
-
|
|
300
|
-
### Manual Configuration
|
|
637
|
+
| Feature | Description | Configuration |
|
|
638
|
+
| :--- | :--- | :--- |
|
|
639
|
+
| **Streaming** | Handle streaming responses (e.g., NDJSON). | Set `stream: true` in `ApiRequest`. |
|
|
640
|
+
| **Polling** | Automatically poll the endpoint at a specified interval. | Set `polling: number` (seconds) in `ApiRequest`. |
|
|
641
|
+
| **Retry** | Automatically retry failed requests. | Configure `retry: { times: 3, delay: 3 }` in `ApiRequest`. |
|
|
642
|
+
| **Adapters** | Transform incoming API response data before it reaches the state. | Use `adapter: (data) => transformedData` in `ApiRequest`. |
|
|
643
|
+
| **Mappers** | Transform outgoing payload data before sending to the API. | Use `mapper: (data) => transformedData` in `ApiRequest`. |
|
|
301
644
|
|
|
302
645
|
```typescript
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
646
|
+
const options = ApiRequest.adapt({
|
|
647
|
+
path: ['users'],
|
|
648
|
+
polling: 5, // Poll every 5 seconds
|
|
649
|
+
retry: { times: 3, delay: 1 },
|
|
650
|
+
adapter: User.adapt // Transform response to User model
|
|
651
|
+
});
|
|
309
652
|
```
|
|
310
653
|
|
|
311
|
-
|
|
654
|
+
### 2. Request Manager Signal Service (`HTTPManagerSignalsService`)
|
|
312
655
|
|
|
313
|
-
|
|
656
|
+
**Path:** `request-manager-services/http-manager-signals.service.ts`
|
|
314
657
|
|
|
315
|
-
|
|
658
|
+
A modern alternative to `HTTPManagerService` using Angular Signals for state management.
|
|
316
659
|
|
|
317
|
-
|
|
660
|
+
**What it does:**
|
|
661
|
+
This service provides the same functionality as `HTTPManagerService` but exposes state using Angular Signals instead of RxJS Observables. This is ideal for modern Angular applications leveraging fine-grained reactivity.
|
|
318
662
|
|
|
319
|
-
|
|
320
|
-
- **RequestManagerDemoComponent** - HTTP service demonstrations
|
|
321
|
-
- **RequestManagerStateDemoComponent** - State management examples
|
|
322
|
-
- **RequestManagerWsDemoComponent** - WebSocket functionality
|
|
323
|
-
- **LocalStorageDemoComponent** - Local storage examples
|
|
324
|
-
- **DatabaseDataDemoComponent** - IndexedDB operations
|
|
663
|
+
**Features:**
|
|
325
664
|
|
|
326
|
-
|
|
665
|
+
- **Signal-Based State:** Exposes `data`, `isPending`, `error`, and `countdown` as Signals.
|
|
666
|
+
- **Feature Parity:** Supports all features of `HTTPManagerService` including polling, retries, adapters, and streaming.
|
|
667
|
+
- **Optimized for Signals:** Designed to work seamlessly with Angular's Signal-based components and templates.
|
|
668
|
+
|
|
669
|
+
**Usage:**
|
|
670
|
+
Ideal for components using Angular Signals.
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
export class MyComponent {
|
|
674
|
+
httpManager = inject(HTTPManagerSignalsService);
|
|
675
|
+
|
|
676
|
+
data = this.httpManager.data; // Signal
|
|
677
|
+
isLoading = this.httpManager.isPending; // Signal
|
|
678
|
+
error = this.httpManager.error; // Signal
|
|
679
|
+
|
|
680
|
+
fetchData() {
|
|
681
|
+
const options = ApiRequest.adapt({ path: ['users'] });
|
|
682
|
+
this.httpManager.getRequest(options).subscribe();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### 3. Request Manager State Service (`HTTPManagerStateService`)
|
|
688
|
+
|
|
689
|
+
**Path:** `request-manager-state-service/http-manager-state.store.ts`
|
|
690
|
+
|
|
691
|
+
An extension of `ComponentStore` designed for managing entity state (CRUD) with built-in HTTP request handling and WebSocket integration.
|
|
692
|
+
|
|
693
|
+
**What it does:**
|
|
694
|
+
This service combines HTTP requests with local state management. It automatically updates the local store when CRUD operations succeed, eliminating the need to manually refresh data. It also integrates with WebSockets to keep the state synchronized with server-side changes.
|
|
695
|
+
|
|
696
|
+
**Features:**
|
|
697
|
+
|
|
698
|
+
- **ComponentStore Integration:** Extends NGRX `ComponentStore` for robust state management.
|
|
699
|
+
- **Automatic CRUD Updates:** `createRecord`, `updateRecord`, and `deleteRecord` automatically update the local state upon success.
|
|
700
|
+
- **WebSocket Sync:** Listens for WebSocket messages to update, create, or delete records in real-time.
|
|
701
|
+
- **Database Support:** Can be configured to use IndexedDB for caching (via `DatabaseStorage`).
|
|
702
|
+
- **Selectors:** Provides selectors like `data$` and `selectRecord$(id)` for easy data access.
|
|
703
|
+
- **Pagination Support:** Includes state for pagination (`page$`, `totalPages$`).
|
|
704
|
+
|
|
705
|
+
**Usage:**
|
|
706
|
+
Extend this service to create a store for a specific feature. You must call `super()` with `ApiRequest`, `DataType`, and `DatabaseStorage`.
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
@Injectable()
|
|
710
|
+
export class UsersStore extends HTTPManagerStateService<User> {
|
|
711
|
+
constructor() {
|
|
712
|
+
super(
|
|
713
|
+
ApiRequest.adapt({
|
|
714
|
+
server: 'api',
|
|
715
|
+
path: ['users'],
|
|
716
|
+
adapter: User.adapt // Required for Database Storage
|
|
717
|
+
}),
|
|
718
|
+
DataType.ARRAY,
|
|
719
|
+
DatabaseStorage.adapt({
|
|
720
|
+
table: 'users-cache',
|
|
721
|
+
expiresIn: '1d'
|
|
722
|
+
})
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Wrap CRUD methods for cleaner component usage
|
|
727
|
+
getUsers() {
|
|
728
|
+
this.fetchRecords();
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
addUser(user: User) {
|
|
732
|
+
this.createRecord(user);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
**Features:**
|
|
738
|
+
|
|
739
|
+
- **Automatic State Updates:** `createRecord`, `updateRecord`, and `deleteRecord` automatically update the local state upon success.
|
|
740
|
+
- **Selectors:** `data$`, `selectRecord$(id)`.
|
|
741
|
+
- **WebSocket Integration:** Automatically connects to WS if configured in `ApiRequest`. Syncs state on 'create', 'update', 'delete' events from WS.
|
|
742
|
+
- **Runtime Configuration:** Use `setApiRequestOptions()` to update API or WS settings dynamically.
|
|
743
|
+
|
|
744
|
+
### 4. Request Manager Signal State Service
|
|
745
|
+
|
|
746
|
+
*Note: Currently, the state management service (`HTTPManagerStateService`) is based on `ComponentStore` (RxJS). A dedicated Signal-based state manager is not yet available in this library.*
|
|
747
|
+
|
|
748
|
+
### 5. Local Storage Manager Service (`LocalStorageManagerService`)
|
|
749
|
+
|
|
750
|
+
**Path:** `local-storage-manager-service/local-storage-manager.service.ts`
|
|
751
|
+
|
|
752
|
+
Manages `localStorage` and `sessionStorage` with encryption and expiration support.
|
|
753
|
+
|
|
754
|
+
**What it does:**
|
|
755
|
+
This service provides a secure and managed way to interact with browser storage. It treats storage as a state, allowing you to subscribe to changes. It also handles data encryption and expiration automatically.
|
|
756
|
+
|
|
757
|
+
**Features:**
|
|
758
|
+
|
|
759
|
+
- **Encryption:** Encrypts data before saving to storage using `SymmetricalEncryptionService` (requires `APP_ID`).
|
|
760
|
+
- **Expiration:** Supports setting an expiration time (`expiresIn`) for stored items. Automatically cleans up expired items.
|
|
761
|
+
- **Storage Types:** Supports both `localStorage` (Global) and `sessionStorage` (Session).
|
|
762
|
+
- **Reactive:** Exposes storage items as Observables, allowing components to react to storage changes.
|
|
763
|
+
- **Metadata Management:** Keeps track of storage settings (encryption, expiration) separately from the data.
|
|
764
|
+
|
|
765
|
+
**Usage:**
|
|
766
|
+
|
|
767
|
+
```typescript
|
|
768
|
+
localStorageManager = inject(LocalStorageManagerService);
|
|
769
|
+
|
|
770
|
+
// Create/Set a store
|
|
771
|
+
this.localStorageManager.setStore({
|
|
772
|
+
name: 'user-settings',
|
|
773
|
+
data: { theme: 'dark' },
|
|
774
|
+
options: {
|
|
775
|
+
storage: StorageType.GLOBAL, // or StorageType.SESSION
|
|
776
|
+
encrypted: true,
|
|
777
|
+
expiresIn: '1d'
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
// Select a store (returns data)
|
|
782
|
+
settings$ = this.localStorageManager.store$('user-settings');
|
|
783
|
+
|
|
784
|
+
// Select store settings (returns metadata)
|
|
785
|
+
meta$ = this.localStorageManager.setting$('user-settings');
|
|
786
|
+
|
|
787
|
+
// Reset all stores
|
|
788
|
+
this.localStorageManager.resetStore();
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### 6. Store State Manager Service (`StoreStateManagerService`)
|
|
792
|
+
|
|
793
|
+
**Path:** `store-state-manager-service/store-state-manager.service.ts`
|
|
794
|
+
|
|
795
|
+
A service that extends `ComponentStore` to provide persistent state management synchronized with local or session storage.
|
|
796
|
+
|
|
797
|
+
**What it does:**
|
|
798
|
+
This service bridges the gap between in-memory state (ComponentStore) and persistent storage. It ensures that your state is automatically saved to storage when it changes and restored from storage when the service is initialized.
|
|
799
|
+
|
|
800
|
+
**Features:**
|
|
801
|
+
|
|
802
|
+
- **Persistence:** Automatically saves state changes to `localStorage` or `sessionStorage`.
|
|
803
|
+
- **State Restoration:** Restores state from storage on initialization.
|
|
804
|
+
- **Reactivity:** Extends `ComponentStore`, providing all its benefits (selectors, updaters, effects).
|
|
805
|
+
- **Encryption Support:** Leverages `LocalStorageManagerService` for optional encryption.
|
|
806
|
+
- **Model Adaptation:** Supports using a model adapter to ensure state structure.
|
|
807
|
+
|
|
808
|
+
**Usage:**
|
|
809
|
+
Extend this service to create a persistent store.
|
|
810
|
+
|
|
811
|
+
```typescript
|
|
812
|
+
@Injectable({
|
|
813
|
+
providedIn: 'root'
|
|
814
|
+
})
|
|
815
|
+
export class SettingsStateService extends StoreStateManagerService {
|
|
816
|
+
|
|
817
|
+
constructor() {
|
|
818
|
+
super(
|
|
819
|
+
StateStorageOptions.adapt({
|
|
820
|
+
store: 'my-settings-store', // Unique store name
|
|
821
|
+
options: SettingOptions.adapt({
|
|
822
|
+
storage: StorageType.SESSION, // or StorageType.GLOBAL (Local Storage)
|
|
823
|
+
encrypted: false
|
|
824
|
+
}),
|
|
825
|
+
model: Settings.adapt, // Model adapter for initial state
|
|
826
|
+
})
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Update specific part of state
|
|
831
|
+
updateSetting(value: any) {
|
|
832
|
+
this.updateData(value);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Select specific part of state
|
|
836
|
+
getSetting(key: string) {
|
|
837
|
+
return this.data$.pipe(map(state => state[key]));
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
**Features:**
|
|
843
|
+
|
|
844
|
+
- **Persistence:** Automatically saves state changes to LocalStorage or SessionStorage.
|
|
845
|
+
- **Reactivity:** Extends `ComponentStore`, providing `data$` selector and `updater` methods.
|
|
846
|
+
- **Encryption:** Supports encrypted storage via `LocalStorageManagerService`.
|
|
847
|
+
|
|
848
|
+
### 7. WebSocket Manager Service (`WebsocketService`)
|
|
849
|
+
|
|
850
|
+
**Path:** `ws-manager-service/services/websocket.service.ts`
|
|
851
|
+
|
|
852
|
+
Handles WebSocket connections, channel subscriptions, and messaging. It is used internally by `HTTPManagerStateService` but can be used standalone.
|
|
853
|
+
|
|
854
|
+
**What it does:**
|
|
855
|
+
This service manages the lifecycle of a WebSocket connection. It handles authentication, connection stability, and message routing. It supports a channel-based architecture for organizing communication.
|
|
856
|
+
|
|
857
|
+
**Features:**
|
|
858
|
+
|
|
859
|
+
- **Connection Management:** Handles connecting, disconnecting, and reconnecting.
|
|
860
|
+
- **Authentication:** Supports JWT-based authentication for secure connections.
|
|
861
|
+
- **Channels:** Allows subscribing and unsubscribing to specific channels.
|
|
862
|
+
- **Channel Management:** Create and delete channels dynamically.
|
|
863
|
+
- **Messaging:** Supports sending messages to specific channels, users, or broadcasting to all.
|
|
864
|
+
- **Observables:** Exposes `messages$`, `connectionStatus$`, and `subscribedChannels$` for reactive integration.
|
|
865
|
+
- **Session Persistence:** Automatically generates and persists session IDs for reconnection scenarios.
|
|
866
|
+
|
|
867
|
+
**Usage:**
|
|
868
|
+
|
|
869
|
+
```typescript
|
|
870
|
+
wsService = inject(WebsocketService);
|
|
871
|
+
|
|
872
|
+
// Connect with options
|
|
873
|
+
this.wsService.connect({
|
|
874
|
+
id: 'my-primary-channel',
|
|
875
|
+
wsServer: 'wss://api.example.com/ws',
|
|
876
|
+
user: { ldap: 'jdoe', name: 'John Doe' },
|
|
877
|
+
channels: ['PUB-notifications', 'PUB-updates']
|
|
878
|
+
}, 'jwt-token');
|
|
879
|
+
|
|
880
|
+
// Subscribe to additional channels
|
|
881
|
+
this.wsService.subscribeToChannel('PUB-my-channel');
|
|
882
|
+
|
|
883
|
+
// Unsubscribe from a channel
|
|
884
|
+
this.wsService.unsubscribeFromChannel('PUB-my-channel');
|
|
885
|
+
|
|
886
|
+
// Listen to messages
|
|
887
|
+
this.wsService.messages$.subscribe(msg => console.log(msg));
|
|
888
|
+
|
|
889
|
+
// Send message to a channel
|
|
890
|
+
this.wsService.sendChannelMessage('PUB-my-channel', { text: 'Hello!' });
|
|
891
|
+
|
|
892
|
+
// Track subscribed channels
|
|
893
|
+
this.wsService.subscribedChannels$.subscribe(channels => console.log([...channels]));
|
|
894
|
+
|
|
895
|
+
// Create/Delete channels
|
|
896
|
+
this.wsService.createChannel('PUB-new-channel');
|
|
897
|
+
this.wsService.deleteChannel('PUB-old-channel');
|
|
898
|
+
|
|
899
|
+
// Get all available channels
|
|
900
|
+
this.wsService.getAllChannels();
|
|
901
|
+
|
|
902
|
+
// Get users in a channel
|
|
903
|
+
this.wsService.getUsersInChannel('PUB-my-channel');
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
#### Channel Naming Conventions
|
|
907
|
+
|
|
908
|
+
The WebSocket service and state manager use a **channel type system** to organize communication. Channel prefixes are managed automatically by the state service.
|
|
909
|
+
|
|
910
|
+
##### Channel Types (`ChannelType` Enum)
|
|
911
|
+
|
|
912
|
+
```typescript
|
|
913
|
+
import { ChannelType, createChannelName } from 'http-request-manager';
|
|
914
|
+
|
|
915
|
+
export enum ChannelType {
|
|
916
|
+
STATE = 'SYS', // Private state synchronization channels
|
|
917
|
+
MESSAGE = 'MES', // Messaging/communication channels (persisted notifications)
|
|
918
|
+
NOTIFICATION = 'PUB' // Public notification/broadcast channels
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Utility function to create prefixed channel name
|
|
922
|
+
createChannelName(ChannelType.STATE, 'USERS123'); // Returns 'SYS-USERS123'
|
|
923
|
+
createChannelName(ChannelType.MESSAGE, 'alerts'); // Returns 'MES-alerts'
|
|
924
|
+
createChannelName(ChannelType.NOTIFICATION, 'chat'); // Returns 'PUB-chat'
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
| Channel Type | Prefix | Description | Visibility | Persistence |
|
|
928
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
929
|
+
| `STATE` | `SYS-` | Private channels for state synchronization (CRUD operations) | Hidden from users | In-memory only |
|
|
930
|
+
| `MESSAGE` | `MES-` | Messaging channels for notifications (persisted to database) | Visible to users | Database persisted |
|
|
931
|
+
| `NOTIFICATION` | `PUB-` | Public channels for real-time messaging/chat | Visible to users | In-memory only |
|
|
932
|
+
|
|
933
|
+
**Important:** When using `HTTPManagerStateService`, channel prefixes are automatically managed. You only need to provide the base channel name.
|
|
934
|
+
|
|
935
|
+
**Example:**
|
|
936
|
+
|
|
937
|
+
```typescript
|
|
938
|
+
// State service automatically adds SYS- prefix for state channels
|
|
939
|
+
// User provides: 'USERS123'
|
|
940
|
+
// State service uses: 'SYS-USERS123'
|
|
941
|
+
|
|
942
|
+
// For messaging channels, use subscribeToMessageChannel()
|
|
943
|
+
stateService.subscribeToMessageChannel('alerts'); // Uses 'MES-alerts'
|
|
944
|
+
|
|
945
|
+
// For notification channels, use subscribeToNotificationChannel()
|
|
946
|
+
stateService.subscribeToNotificationChannel('announcements'); // Uses 'PUB-announcements'
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
#### WebSocket Message Types
|
|
950
|
+
|
|
951
|
+
| Type | Description | Direction |
|
|
952
|
+
| :--- | :--- | :--- |
|
|
953
|
+
| `subscribe` | Subscribe to a channel | Client → Server |
|
|
954
|
+
| `unsubscribe` | Unsubscribe from a channel | Client → Server |
|
|
955
|
+
| `message` | Send message to channel subscribers | Client → Server |
|
|
956
|
+
| `broadcast` | Broadcast to all connected clients | Client → Server |
|
|
957
|
+
| `userMessage` | Send message to specific user | Client → Server |
|
|
958
|
+
| `createChannel` | Create a new channel | Client → Server |
|
|
959
|
+
| `deleteChannel` | Delete an existing channel | Client → Server |
|
|
960
|
+
| `getChannels` | Request list of all channels | Client → Server |
|
|
961
|
+
| `getUsers` | Get users in a channel | Client → Server |
|
|
962
|
+
| `channelsList` | List of available channels | Server → Client |
|
|
963
|
+
| `subscribed` | Subscription confirmation | Server → Client |
|
|
964
|
+
| `unsubscribed` | Unsubscription confirmation | Server → Client |
|
|
965
|
+
| `incomingMessage` | Message received in a channel | Server → Client |
|
|
966
|
+
|
|
967
|
+
#### WebSocket Options (`WSOptions`)
|
|
968
|
+
|
|
969
|
+
| Option | Type | Description | Default |
|
|
970
|
+
| :--- | :--- | :--- | :--- |
|
|
971
|
+
| `id` | `string` | Unique identifier for the WebSocket connection. | `''` |
|
|
972
|
+
| `wsServer` | `string` | The WebSocket server URL. | `''` |
|
|
973
|
+
| `jwtToken` | `string` | JWT token for authentication. | `''` |
|
|
974
|
+
| `permissions` | `string[]` | List of permissions associated with the connection. | `[]` |
|
|
975
|
+
| `channels` | `string[]` | List of channels to subscribe to upon connection. | `undefined` |
|
|
976
|
+
| `userAdapter` | `WSUser` | Adapter for mapping user data. | `undefined` |
|
|
977
|
+
| `retry` | `RetryOptions` | Retry configuration for connection attempts. | `undefined` |
|
|
978
|
+
|
|
979
|
+
#### Integration with HTTPManagerStateService
|
|
980
|
+
|
|
981
|
+
You can configure `HTTPManagerStateService` to automatically handle WebSocket connections by providing `ws` options in `ApiRequest`.
|
|
982
|
+
|
|
983
|
+
```typescript
|
|
984
|
+
@Injectable()
|
|
985
|
+
export class UsersStore extends HTTPManagerStateService<User> {
|
|
986
|
+
constructor() {
|
|
987
|
+
super(
|
|
988
|
+
ApiRequest.adapt({
|
|
989
|
+
server: 'api',
|
|
990
|
+
path: ['users'],
|
|
991
|
+
// WebSocket Configuration
|
|
992
|
+
ws: {
|
|
993
|
+
id: 'WS-USERS',
|
|
994
|
+
wsServer: 'wss://api.example.com/ws',
|
|
995
|
+
jwtToken: 'your-token',
|
|
996
|
+
wsChannels: ['allChannels'],
|
|
997
|
+
retry: { times: 3, delay: 5 }
|
|
998
|
+
}
|
|
999
|
+
}),
|
|
1000
|
+
DataType.ARRAY,
|
|
1001
|
+
DatabaseStorage.adapt()
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
## Real-Time Features
|
|
1010
|
+
|
|
1011
|
+
The `http-request-manager` library provides two distinct real-time communication features:
|
|
1012
|
+
|
|
1013
|
+
1. **Messaging (PUB- channels)** - Real-time chat/messaging between users
|
|
1014
|
+
2. **Notifications (MES- channels)** - Persistent notifications stored in database
|
|
1015
|
+
|
|
1016
|
+
Both features integrate seamlessly with `HTTPManagerStateService` and automatically manage channel prefixes.
|
|
1017
|
+
|
|
1018
|
+
### Messaging Feature (PUB- Channels)
|
|
1019
|
+
|
|
1020
|
+
**Purpose:** Real-time messaging/chat functionality for communication between users.
|
|
1021
|
+
|
|
1022
|
+
**Characteristics:**
|
|
1023
|
+
|
|
1024
|
+
- Messages are broadcast in real-time to subscribed users
|
|
1025
|
+
- Messages are stored in-memory only (not persisted to database)
|
|
1026
|
+
- Uses `PUB-` prefix (automatically added)
|
|
1027
|
+
- Ideal for chat applications, live collaboration, etc.
|
|
1028
|
+
|
|
1029
|
+
#### Messaging Observables
|
|
1030
|
+
|
|
1031
|
+
| Observable | Type | Description |
|
|
1032
|
+
| :--- | :--- | :--- |
|
|
1033
|
+
| `channels$` | `Observable<string[]>` | List of all available channels |
|
|
1034
|
+
| `communicationMessages$` | `Observable<any[]>` | All received messages |
|
|
1035
|
+
| `latestCommunicationMessages$` | `Observable<any>` | Most recent message received |
|
|
1036
|
+
| `connectionStatus$` | `Observable<boolean>` | WebSocket connection status |
|
|
1037
|
+
| `user$` | `Observable<WSUser>` | Current user information |
|
|
1038
|
+
|
|
1039
|
+
#### Messaging Methods
|
|
1040
|
+
|
|
1041
|
+
```typescript
|
|
1042
|
+
// Create a new messaging channel
|
|
1043
|
+
stateService.createChannel('general-chat'); // Creates PUB-general-chat
|
|
1044
|
+
|
|
1045
|
+
// Subscribe to a channel to receive messages
|
|
1046
|
+
stateService.subscribeToChannel('general-chat'); // Subscribes to PUB-general-chat
|
|
1047
|
+
|
|
1048
|
+
// Unsubscribe from a channel
|
|
1049
|
+
stateService.unsubscribeFromChannel('general-chat');
|
|
1050
|
+
|
|
1051
|
+
// Get all available channels
|
|
1052
|
+
stateService.getAllChannels();
|
|
1053
|
+
|
|
1054
|
+
// Send a message to specific channels
|
|
1055
|
+
stateService.wsMessaging(
|
|
1056
|
+
ChannelMessage.adapt({
|
|
1057
|
+
sessionId: currentUser,
|
|
1058
|
+
content: { message: 'Hello everyone!' }
|
|
1059
|
+
}),
|
|
1060
|
+
['general-chat', 'team-updates'] // Channels (PUB- prefix added automatically)
|
|
1061
|
+
);
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
#### Demo Component: `WsMessagingComponent`
|
|
1065
|
+
|
|
1066
|
+
The library includes a demo component showcasing messaging functionality:
|
|
327
1067
|
|
|
328
1068
|
```html
|
|
329
|
-
<app-
|
|
1069
|
+
<app-ws-messaging
|
|
330
1070
|
[server]="'http://localhost:8080'"
|
|
331
1071
|
[wsServer]="'ws://localhost:8080'"
|
|
332
1072
|
[jwtToken]="'your-jwt-token'"
|
|
333
|
-
[
|
|
334
|
-
|
|
335
|
-
</app-http-request-services-demo>
|
|
1073
|
+
[user]="{ ldap: 'jdoe', name: 'John Doe', email: 'jdoe@example.com' }">
|
|
1074
|
+
</app-ws-messaging>
|
|
336
1075
|
```
|
|
337
1076
|
|
|
338
|
-
|
|
1077
|
+
**Features demonstrated:**
|
|
339
1078
|
|
|
340
|
-
|
|
1079
|
+
- Create public channels
|
|
1080
|
+
- Subscribe/unsubscribe to channels
|
|
1081
|
+
- Send messages to multiple channels
|
|
1082
|
+
- View real-time message stream
|
|
1083
|
+
- Toast notifications for incoming messages
|
|
1084
|
+
|
|
1085
|
+
---
|
|
1086
|
+
|
|
1087
|
+
### Notifications Feature (MES- Channels)
|
|
1088
|
+
|
|
1089
|
+
**Purpose:** Persistent notifications that are stored in the database and can be retrieved with date filters.
|
|
1090
|
+
|
|
1091
|
+
**Characteristics:**
|
|
1092
|
+
|
|
1093
|
+
- Notifications are persisted to the server database
|
|
1094
|
+
- Supports date-range queries (startEpoch, endEpoch)
|
|
1095
|
+
- Uses `MES-` prefix (automatically added)
|
|
1096
|
+
- Retrieves historical notifications on subscription
|
|
1097
|
+
- Ideal for announcements, alerts, audit logs, etc.
|
|
1098
|
+
|
|
1099
|
+
#### Notification Observables
|
|
1100
|
+
|
|
1101
|
+
| Observable | Type | Description |
|
|
1102
|
+
| :--- | :--- | :--- |
|
|
1103
|
+
| `notificationChannels$` | `Observable<string[]>` | In-memory notification channels from server |
|
|
1104
|
+
| `todaysNotificationChannels$` | `Observable<string[]>` | Channels with notifications posted today (from DB) |
|
|
1105
|
+
| `notificationMessages$` | `Observable<any[]>` | All notification messages |
|
|
1106
|
+
| `latestNotification$` | `Observable<any>` | Most recent notification received |
|
|
1107
|
+
|
|
1108
|
+
#### Notification Methods
|
|
341
1109
|
|
|
342
|
-
**Before:**
|
|
343
1110
|
```typescript
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
1111
|
+
// Create a notification channel
|
|
1112
|
+
stateService.createNotificationChannel('alerts'); // Creates MES-alerts
|
|
1113
|
+
|
|
1114
|
+
// Get all notification channels (in-memory)
|
|
1115
|
+
stateService.getNotificationChannels();
|
|
1116
|
+
|
|
1117
|
+
// Get today's notification channels (from database)
|
|
1118
|
+
stateService.getTodaysNotificationChannels();
|
|
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
|
+
|
|
1125
|
+
// Subscribe to notifications with optional date filter
|
|
1126
|
+
stateService.subscribeToNotificationChannel('alerts', {
|
|
1127
|
+
startEpoch: Math.floor(Date.now() / 1000) - 86400, // Last 24 hours
|
|
1128
|
+
endEpoch: Math.floor(Date.now() / 1000)
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
// Unsubscribe from notifications
|
|
1132
|
+
stateService.unsubscribeFromNotificationChannel('alerts');
|
|
1133
|
+
|
|
1134
|
+
// Send a notification (persisted to database)
|
|
1135
|
+
stateService.sendNotification('alerts', {
|
|
1136
|
+
title: 'System Alert',
|
|
1137
|
+
message: 'Scheduled maintenance at midnight',
|
|
1138
|
+
priority: 'high'
|
|
347
1139
|
});
|
|
348
1140
|
```
|
|
349
1141
|
|
|
350
|
-
|
|
1142
|
+
#### Notification Message Structure
|
|
1143
|
+
|
|
351
1144
|
```typescript
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
1145
|
+
interface NotificationMessage {
|
|
1146
|
+
id: number;
|
|
1147
|
+
channel: string; // e.g., 'MES-alerts'
|
|
1148
|
+
created: number; // epoch seconds
|
|
1149
|
+
user_name: string; // sender's name
|
|
1150
|
+
sessionId: any; // sender's session info
|
|
1151
|
+
content: any; // notification payload
|
|
1152
|
+
}
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
#### Demo Component: `WsNotificationsComponent`
|
|
355
1156
|
|
|
356
|
-
|
|
357
|
-
|
|
1157
|
+
The library includes a demo component showcasing notification functionality:
|
|
1158
|
+
|
|
1159
|
+
```html
|
|
1160
|
+
<app-ws-notifications
|
|
1161
|
+
[server]="'http://localhost:8080'"
|
|
1162
|
+
[wsServer]="'ws://localhost:8080'"
|
|
1163
|
+
[jwtToken]="'your-jwt-token'"
|
|
1164
|
+
[user]="{ ldap: 'jdoe', name: 'John Doe', email: 'jdoe@example.com' }">
|
|
1165
|
+
</app-ws-notifications>
|
|
358
1166
|
```
|
|
359
1167
|
|
|
360
|
-
|
|
1168
|
+
**Features demonstrated:**
|
|
1169
|
+
|
|
1170
|
+
- Create notification channels
|
|
1171
|
+
- View today's notification channels (from database)
|
|
1172
|
+
- Define previous day's channels (loads channels from previous day's data)
|
|
1173
|
+
- Subscribe with date range filters
|
|
1174
|
+
- Connect/disconnect from channels
|
|
1175
|
+
- Send notifications (persisted)
|
|
1176
|
+
- View historical notifications in a table
|
|
1177
|
+
|
|
1178
|
+
---
|
|
1179
|
+
|
|
1180
|
+
### Creating a Custom State Service with Messaging & Notifications
|
|
1181
|
+
|
|
1182
|
+
Here's a complete example of a state service that utilizes all real-time features:
|
|
361
1183
|
|
|
362
|
-
**Before:**
|
|
363
1184
|
```typescript
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
});
|
|
1185
|
+
import { Injectable } from '@angular/core';
|
|
1186
|
+
import { HTTPManagerStateService, ChannelType, createChannelName } from 'http-request-manager';
|
|
1187
|
+
import { ApiRequest, DataType, DatabaseStorage } from 'http-request-manager';
|
|
1188
|
+
|
|
1189
|
+
export interface User {
|
|
1190
|
+
id: number;
|
|
1191
|
+
name: string;
|
|
1192
|
+
email: string;
|
|
373
1193
|
}
|
|
374
1194
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
1195
|
+
@Injectable({
|
|
1196
|
+
providedIn: 'root'
|
|
1197
|
+
})
|
|
1198
|
+
export class UserStateService extends HTTPManagerStateService<User> {
|
|
1199
|
+
|
|
1200
|
+
constructor() {
|
|
1201
|
+
super(
|
|
1202
|
+
ApiRequest.adapt({
|
|
1203
|
+
server: 'http://localhost:8080',
|
|
1204
|
+
path: ['api', 'users'],
|
|
1205
|
+
adapter: (data: any) => data as User,
|
|
1206
|
+
ws: {
|
|
1207
|
+
id: 'USERS123', // Base name - automatically becomes SYS-USERS123
|
|
1208
|
+
wsServer: 'ws://localhost:8080',
|
|
1209
|
+
jwtToken: '',
|
|
1210
|
+
retry: { times: 3, delay: 5 }
|
|
1211
|
+
}
|
|
1212
|
+
}),
|
|
1213
|
+
DataType.ARRAY,
|
|
1214
|
+
DatabaseStorage.adapt({ table: 'users-cache', expiresIn: '1d' })
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// Initialize connection with user info
|
|
1219
|
+
initializeConnection(wsServer: string, jwtToken: string, user: any) {
|
|
1220
|
+
this.setApiRequestOptions(
|
|
1221
|
+
ApiRequest.adapt({
|
|
1222
|
+
...this.ApiRequestOptions,
|
|
1223
|
+
ws: {
|
|
1224
|
+
...this.ApiRequestOptions.ws,
|
|
1225
|
+
wsServer,
|
|
1226
|
+
jwtToken,
|
|
1227
|
+
user
|
|
1228
|
+
}
|
|
1229
|
+
})
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// === MESSAGING (PUB- channels) ===
|
|
1234
|
+
|
|
1235
|
+
createChatRoom(roomName: string) {
|
|
1236
|
+
this.createChannel(roomName); // Creates PUB-{roomName}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
joinChatRoom(roomName: string) {
|
|
1240
|
+
this.subscribeToChannel(roomName);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
leaveChatRoom(roomName: string) {
|
|
1244
|
+
this.unsubscribeFromChannel(roomName);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
sendChatMessage(content: string, rooms: string[]) {
|
|
1248
|
+
this.wsMessaging(
|
|
1249
|
+
{ content: { message: content } },
|
|
1250
|
+
rooms
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// === NOTIFICATIONS (MES- channels) ===
|
|
1255
|
+
|
|
1256
|
+
createNotificationStream(streamName: string) {
|
|
1257
|
+
this.createNotificationChannel(streamName); // Creates MES-{streamName}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
subscribeToAlerts(streamName: string, lastHours: number = 24) {
|
|
1261
|
+
const now = Math.floor(Date.now() / 1000);
|
|
1262
|
+
const startEpoch = now - (lastHours * 3600);
|
|
1263
|
+
|
|
1264
|
+
this.subscribeToNotificationChannel(streamName, {
|
|
1265
|
+
startEpoch,
|
|
1266
|
+
endEpoch: now
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
sendAlert(streamName: string, alert: { title: string; message: string; priority: string }) {
|
|
1271
|
+
this.sendNotification(streamName, alert);
|
|
1272
|
+
}
|
|
379
1273
|
}
|
|
380
1274
|
```
|
|
381
1275
|
|
|
382
|
-
**
|
|
1276
|
+
**Usage in Component:**
|
|
1277
|
+
|
|
383
1278
|
```typescript
|
|
384
|
-
@
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
1279
|
+
@Component({
|
|
1280
|
+
selector: 'app-dashboard',
|
|
1281
|
+
template: `
|
|
1282
|
+
<div *ngIf="connectionStatus$ | async">
|
|
1283
|
+
<!-- Chat Messages -->
|
|
1284
|
+
<div *ngFor="let msg of chatMessages$ | async">
|
|
1285
|
+
{{ msg.content.message }}
|
|
1286
|
+
</div>
|
|
1287
|
+
|
|
1288
|
+
<!-- Notifications -->
|
|
1289
|
+
<div *ngFor="let notification of notifications$ | async">
|
|
1290
|
+
{{ notification.content.title }}: {{ notification.content.message }}
|
|
1291
|
+
</div>
|
|
1292
|
+
</div>
|
|
1293
|
+
`
|
|
1294
|
+
})
|
|
1295
|
+
export class DashboardComponent implements OnInit {
|
|
1296
|
+
userState = inject(UserStateService);
|
|
1297
|
+
|
|
1298
|
+
connectionStatus$ = this.userState.connectionStatus$;
|
|
1299
|
+
chatMessages$ = this.userState.communicationMessages$;
|
|
1300
|
+
notifications$ = this.userState.notificationMessages$;
|
|
1301
|
+
|
|
1302
|
+
ngOnInit() {
|
|
1303
|
+
// Initialize WebSocket connection
|
|
1304
|
+
this.userState.initializeConnection(
|
|
1305
|
+
'ws://localhost:8080',
|
|
1306
|
+
'jwt-token',
|
|
1307
|
+
{ ldap: 'jdoe', name: 'John Doe' }
|
|
1308
|
+
);
|
|
1309
|
+
|
|
1310
|
+
// Join a chat room
|
|
1311
|
+
this.userState.joinChatRoom('general');
|
|
1312
|
+
|
|
1313
|
+
// Subscribe to alerts from last 24 hours
|
|
1314
|
+
this.userState.subscribeToAlerts('system-alerts', 24);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
sendMessage(text: string) {
|
|
1318
|
+
this.userState.sendChatMessage(text, ['general']);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
sendAlert() {
|
|
1322
|
+
this.userState.sendAlert('system-alerts', {
|
|
1323
|
+
title: 'Update Available',
|
|
1324
|
+
message: 'A new version is available',
|
|
1325
|
+
priority: 'medium'
|
|
1326
|
+
});
|
|
388
1327
|
}
|
|
389
|
-
|
|
390
|
-
loadUsers() { this.fetchRecords(); }
|
|
391
|
-
addUser(user: User) { this.createRecord(user); }
|
|
392
1328
|
}
|
|
1329
|
+
```
|
|
393
1330
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
1331
|
+
---
|
|
1332
|
+
|
|
1333
|
+
### 8. Database Manager Service (`DatabaseManagerService`)
|
|
1334
|
+
|
|
1335
|
+
**Path:** `database-manager-service/database.manager.service.ts`
|
|
1336
|
+
|
|
1337
|
+
A wrapper service for Dexie.js to manage IndexedDB interactions using RxJS Observables.
|
|
1338
|
+
|
|
1339
|
+
**What it does:**
|
|
1340
|
+
|
|
1341
|
+
This service simplifies interactions with IndexedDB by providing a clean, Observable-based API. It handles database initialization, table creation, and all standard CRUD operations. It is designed to work seamlessly with the `HTTPManagerStateService` for caching but can also be used independently.
|
|
1342
|
+
|
|
1343
|
+
**Features:**
|
|
1344
|
+
|
|
1345
|
+
- **Table Management:** Create and manage database tables with flexible schemas.
|
|
1346
|
+
- **CRUD Operations:** Full support for Create, Read, Update, and Delete operations on single or multiple records.
|
|
1347
|
+
- **Querying:** Find records by specific column values.
|
|
1348
|
+
- **Bulk Operations:** Optimized methods for creating and deleting multiple records at once.
|
|
1349
|
+
- **Schema Inspection:** Methods to check for table existence and retrieve schema definitions.
|
|
1350
|
+
|
|
1351
|
+
**Usage:**
|
|
1352
|
+
|
|
1353
|
+
```typescript
|
|
1354
|
+
dbManager = inject(DatabaseManagerService);
|
|
1355
|
+
|
|
1356
|
+
// 1. Initialize Table
|
|
1357
|
+
const tableDef = TableSchemaDef.adapt({
|
|
1358
|
+
table: 'users',
|
|
1359
|
+
schema: '++id, name, age' // Dexie.js schema syntax
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
this.dbManager.createDatabaseTable(tableDef).subscribe(() => {
|
|
1363
|
+
console.log('Table initialized');
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
// 2. CRUD Operations
|
|
1367
|
+
// Create
|
|
1368
|
+
this.dbManager.createTableRecord('users', { name: 'John', age: 30 }).subscribe();
|
|
1369
|
+
|
|
1370
|
+
// Read
|
|
1371
|
+
this.dbManager.getTableRecords('users').subscribe(users => console.log(users));
|
|
1372
|
+
this.dbManager.getTableRecord('users', 1).subscribe(user => console.log(user));
|
|
1373
|
+
|
|
1374
|
+
// Update
|
|
1375
|
+
this.dbManager.updateTableRecord('users', { id: 1, name: 'John Doe', age: 31 }).subscribe();
|
|
1376
|
+
|
|
1377
|
+
// Delete
|
|
1378
|
+
this.dbManager.deleteTableRecord('users', 1).subscribe();
|
|
1379
|
+
|
|
1380
|
+
// 3. Bulk Operations
|
|
1381
|
+
const users = [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 28 }];
|
|
1382
|
+
this.dbManager.createTableRecords('users', users).subscribe();
|
|
1383
|
+
|
|
1384
|
+
// 4. Querying
|
|
1385
|
+
this.dbManager.findTableRecords('users', 'age', 25).subscribe(results => console.log(results));
|
|
1386
|
+
|
|
1387
|
+
// 5. Metadata
|
|
1388
|
+
this.dbManager.hasDatabaseTable('users').subscribe(exists => console.log(exists));
|
|
397
1389
|
```
|
|
398
1390
|
|
|
399
|
-
|
|
1391
|
+
#### Database Storage Configuration (`DatabaseStorage`)
|
|
400
1392
|
|
|
401
|
-
|
|
1393
|
+
When using `HTTPManagerStateService`, you can configure local database caching using `DatabaseStorage.adapt()`.
|
|
402
1394
|
|
|
403
|
-
|
|
404
|
-
- **RetryOptions** - Retry behavior settings
|
|
405
|
-
- **DataType** - Data structure type (ARRAY, OBJECT)
|
|
406
|
-
- **DatabaseStorage** - IndexedDB configuration
|
|
407
|
-
- **SettingOptions** - Storage settings
|
|
408
|
-
- **WSOptions** - WebSocket configuration
|
|
1395
|
+
##### Options
|
|
409
1396
|
|
|
410
|
-
|
|
1397
|
+
| Option | Type | Description | Default |
|
|
1398
|
+
| :--- | :--- | :--- | :--- |
|
|
1399
|
+
| `table` | `string` | The name of the table in IndexedDB where records will be stored. | `''` |
|
|
1400
|
+
| `expiresIn` | `string` | The duration for which the data remains valid. Format: `'1d'` (days), `'1h'` (hours), `'30m'` (minutes). | `''` |
|
|
411
1401
|
|
|
412
|
-
|
|
413
|
-
- **StorageType** - Storage scope (GLOBAL, SESSION)
|
|
414
|
-
- **CommunicationType** - WebSocket message types
|
|
1402
|
+
##### Database Storage Example
|
|
415
1403
|
|
|
416
|
-
|
|
1404
|
+
```typescript
|
|
1405
|
+
import { DatabaseStorage } from 'http-request-manager';
|
|
417
1406
|
|
|
418
|
-
|
|
1407
|
+
const dbConfig = DatabaseStorage.adapt({
|
|
1408
|
+
table: 'my-feature-cache',
|
|
1409
|
+
expiresIn: '6h' // Cache expires in 6 hours
|
|
1410
|
+
});
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
### 9. Utils (`UtilsService`)
|
|
1414
|
+
|
|
1415
|
+
**Path:** `utils/utils.service.ts`
|
|
1416
|
+
|
|
1417
|
+
Provides utility functions for common tasks.
|
|
1418
|
+
|
|
1419
|
+
**What it does:**
|
|
1420
|
+
A collection of helper functions used throughout the library and available for application use. It simplifies common tasks related to data manipulation, validation, and time calculations.
|
|
1421
|
+
|
|
1422
|
+
**Features:**
|
|
1423
|
+
|
|
1424
|
+
- **JSON Handling:** Safe `stringToJSON` and `JSONToString` methods.
|
|
1425
|
+
- **Type Checking:** `isString`, `isObject`, `isJSON`.
|
|
1426
|
+
- **Expiration:** `expires(string)` converts duration strings (e.g., '1d', '2h') to epoch timestamps. `hasExpired(timestamp)` checks if a timestamp has passed.
|
|
1427
|
+
- **Conversions:** `base32ToHex`, `binaryToHex`.
|
|
1428
|
+
- **Object Equality:** `objectsEqual` for deep comparison.
|
|
1429
|
+
|
|
1430
|
+
**Usage:**
|
|
419
1431
|
|
|
420
|
-
|
|
1432
|
+
```typescript
|
|
1433
|
+
utils = inject(UtilsService);
|
|
1434
|
+
|
|
1435
|
+
// Check expiration
|
|
1436
|
+
const isExpired = this.utils.hasExpired(expiryTimestamp);
|
|
421
1437
|
|
|
422
|
-
|
|
1438
|
+
// Convert JSON
|
|
1439
|
+
const json = this.utils.stringToJSON(jsonString);
|
|
1440
|
+
|
|
1441
|
+
// Calculate Expiry
|
|
1442
|
+
const expiryEpoch = this.utils.expires('1d'); // Returns epoch for 1 day from now
|
|
1443
|
+
```
|
|
423
1444
|
|
|
424
1445
|
---
|
|
425
1446
|
|
|
426
|
-
|
|
1447
|
+
## Available Interceptors
|
|
1448
|
+
|
|
1449
|
+
There are 3 interceptors that you can import into your project for api requests.
|
|
1450
|
+
|
|
1451
|
+
You may add these interceptors in your `AppModule` file as providers and import them into as providers.
|
|
1452
|
+
|
|
1453
|
+
```typescript
|
|
1454
|
+
providers: [
|
|
1455
|
+
{ provide: HTTP_INTERCEPTORS, useClass: WithCredentialsInterceptor, multi: true },
|
|
1456
|
+
{ provide: HTTP_INTERCEPTORS, useClass: RequestHeadersInterceptor, multi: true },
|
|
1457
|
+
{ provide: HTTP_INTERCEPTORS, useClass: RequestErrorInterceptor, multi: true }
|
|
1458
|
+
],
|
|
1459
|
+
```
|
|
1460
|
+
|
|
1461
|
+
Or you can use all intercepts above by importing the `HttpRequestManagerModule` in your `AppModule`
|
|
1462
|
+
|
|
1463
|
+
- **RequestErrorInterceptor**
|
|
1464
|
+
|
|
1465
|
+
This interceptor handles errors of type 400 and 500. This interceptor is applicable when you need to display UI for the user to indicate a server or request error has occurred. The Status and Error Text is displayed in a `toast-message` modal.
|
|
1466
|
+
|
|
1467
|
+
- **WithCredentialsInterceptor**
|
|
1468
|
+
|
|
1469
|
+
Adds to your request header
|
|
1470
|
+
|
|
1471
|
+
```json
|
|
1472
|
+
{ credentials: true }
|
|
1473
|
+
```
|
|
1474
|
+
|
|
1475
|
+
- **RequestHeadersInterceptor**
|
|
1476
|
+
|
|
1477
|
+
Adds to your request - current local `language` (i18n) selection and `current date` to every request
|
|
1478
|
+
|
|
1479
|
+
```json
|
|
1480
|
+
{
|
|
1481
|
+
'Content-Type': 'application/json',
|
|
1482
|
+
'Accept-Language': this.language || 'en-CA',
|
|
1483
|
+
'Current-Date': this.currentDate
|
|
1484
|
+
}
|
|
1485
|
+
```
|