@libs-ui/services-base-request-abstract 0.2.355-8 → 0.2.356-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,3 +1,212 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Base Request Abstract Service
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Abstract base class cung cấp toàn bộ infrastructure cho HTTP requests trong Angular: GET, POST, PUT, PATCH, DELETE, file upload với progress tracking, response normalization, error handling tự động, caching với IndexedDB và page zero conversion.
|
|
4
|
+
|
|
5
|
+
## Tính năng
|
|
6
|
+
|
|
7
|
+
- ✅ HTTP methods đầy đủ: GET, POST, PUT, PATCH, DELETE
|
|
8
|
+
- ✅ URL-encoded form: `postUrlEndCode`, `patchUrlEndCode` (content-type `application/x-www-form-urlencoded`)
|
|
9
|
+
- ✅ File upload với progress tracking qua XHR native (`sendWithFile`)
|
|
10
|
+
- ✅ Response normalization tự động — hỗ trợ nhiều backend format
|
|
11
|
+
- ✅ Pagination tự động từ nhiều format (paging, Spring pagination, cursor-based)
|
|
12
|
+
- ✅ Error handling: 401 → auto redirect, phân biệt 4xx vs 5xx
|
|
13
|
+
- ✅ Caching với IndexedDB (`cacheIndexDB`) — phân biệt theo user
|
|
14
|
+
- ✅ Page zero conversion (`isStartZeroPage`) — UI 1-based ↔ API 0-based
|
|
15
|
+
|
|
16
|
+
## Cài đặt
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @libs-ui/services-base-request-abstract
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Import
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { LibsUiBaseRequestAbstractService } from '@libs-ui/services-base-request-abstract';
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Cách sử dụng
|
|
29
|
+
|
|
30
|
+
### Bước 1: Extend và implement 3 abstract methods bắt buộc
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Injectable } from '@angular/core';
|
|
34
|
+
import { HttpHeaders, HttpParams } from '@angular/common/http';
|
|
35
|
+
import { Router } from '@angular/router';
|
|
36
|
+
import { LibsUiBaseRequestAbstractService } from '@libs-ui/services-base-request-abstract';
|
|
37
|
+
import { UtilsHttpParamsRequest } from '@libs-ui/utils';
|
|
38
|
+
|
|
39
|
+
@Injectable({ providedIn: 'root' })
|
|
40
|
+
export class ApiService extends LibsUiBaseRequestAbstractService {
|
|
41
|
+
protected override baseUrl = 'https://api.example.com';
|
|
42
|
+
|
|
43
|
+
constructor(private router: Router) {
|
|
44
|
+
super();
|
|
45
|
+
this.keyCacheUniqueByIdUserLogin = localStorage.getItem('userId') || '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected getOptions<T>(params: UtilsHttpParamsRequest<T>, contentType?: string) {
|
|
49
|
+
const token = localStorage.getItem('accessToken');
|
|
50
|
+
return {
|
|
51
|
+
headers: new HttpHeaders({
|
|
52
|
+
'Content-Type': contentType || 'application/json',
|
|
53
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
54
|
+
}),
|
|
55
|
+
params,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
protected redirectToLogin(): void {
|
|
60
|
+
this.router.navigate(['/login']);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected replaceURLByPattern<T>(url: string, params: UtilsHttpParamsRequest<T>) {
|
|
64
|
+
let resultUrl = url;
|
|
65
|
+
const pattern = /:([\w]+)/g;
|
|
66
|
+
let match;
|
|
67
|
+
while ((match = pattern.exec(url)) !== null) {
|
|
68
|
+
const key = match[1];
|
|
69
|
+
if (params.has(key)) {
|
|
70
|
+
resultUrl = resultUrl.replace(`:${key}`, params.get(key)!);
|
|
71
|
+
params = params.delete(key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { url: resultUrl, params };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Bước 2: Tạo feature service kế thừa ApiService
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
@Injectable({ providedIn: 'root' })
|
|
83
|
+
export class UserService extends ApiService {
|
|
84
|
+
getUsers(params?: { page?: number; size?: number }) {
|
|
85
|
+
return this.get<User[]>('/users', new UtilsHttpParamsRequest(undefined, params));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getUserById(id: string) {
|
|
89
|
+
return this.get<User>('/users/:id', new UtilsHttpParamsRequest(undefined, { id }));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
createUser(body: CreateUserDto) {
|
|
93
|
+
return this.post<User>('/users', body);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
updateUser(id: string, body: UpdateUserDto) {
|
|
97
|
+
return this.put<User>('/users/:id', body, new UtilsHttpParamsRequest(undefined, { id }));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
deleteUser(id: string) {
|
|
101
|
+
return this.delete<void>('/users/:id', new UtilsHttpParamsRequest(undefined, { id }));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### File Upload với Progress Tracking
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
uploadAvatar(userId: string, file: File, onProgress?: (percent: number) => void) {
|
|
110
|
+
return this.sendWithFile<{ url: string }>(
|
|
111
|
+
'/users/:userId/avatar',
|
|
112
|
+
new UtilsHttpParamsRequest(undefined, { userId }),
|
|
113
|
+
{
|
|
114
|
+
method: 'POST',
|
|
115
|
+
bodyData: { file },
|
|
116
|
+
keepFileOfBody: true,
|
|
117
|
+
processUpload: onProgress ? ({ percent }) => onProgress(percent) : undefined,
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Caching với IndexedDB
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
getUsersCached(params?: any) {
|
|
127
|
+
const observable = this.get<User[]>('/users', new UtilsHttpParamsRequest(undefined, params));
|
|
128
|
+
return this.cacheIndexDB(observable, 'users-list', [params], 5 * 60 * 1000);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Xóa cache sau mutation
|
|
132
|
+
async createUser(body: CreateUserDto) {
|
|
133
|
+
const result = await lastValueFrom(this.post<User>('/users', body));
|
|
134
|
+
await this.deleteCacheKeyStartWidth('users-list');
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## API
|
|
140
|
+
|
|
141
|
+
### Abstract Methods (bắt buộc implement)
|
|
142
|
+
|
|
143
|
+
| Method | Returns | Mô tả |
|
|
144
|
+
| ----------------------------------------- | --------------------- | ------------------------------------------------ |
|
|
145
|
+
| `getOptions<T>(httpParams, contentType?)` | `{ headers, params }` | Inject auth token, Content-Type vào mỗi request |
|
|
146
|
+
| `redirectToLogin()` | `void` | Được gọi khi nhận 401 Unauthorized |
|
|
147
|
+
| `replaceURLByPattern<T>(url, params)` | `{ url, params }` | Thay thế URL dynamic segments (`:id`, `:userId`) |
|
|
148
|
+
|
|
149
|
+
### Public Methods
|
|
150
|
+
|
|
151
|
+
| Method | Returns | Mô tả |
|
|
152
|
+
| --------------------------------------------------- | ------------------------------ | ------------------------------------ |
|
|
153
|
+
| `get<T, P>(path, params?)` | `Observable<IHttpResponse<T>>` | HTTP GET |
|
|
154
|
+
| `post<T, P, B>(path, body, params?)` | `Observable<IHttpResponse<T>>` | HTTP POST |
|
|
155
|
+
| `postUrlEndCode<T, P, B>(path, body, params?)` | `Observable<IHttpResponse<T>>` | POST URL-encoded form |
|
|
156
|
+
| `put<T, P, B>(path, body, params?)` | `Observable<IHttpResponse<T>>` | HTTP PUT |
|
|
157
|
+
| `patch<T, P>(path, body, params?)` | `Observable<IHttpResponse<T>>` | HTTP PATCH |
|
|
158
|
+
| `patchUrlEndCode<T, P>(path, body, params?)` | `Observable<IHttpResponse<T>>` | PATCH URL-encoded form |
|
|
159
|
+
| `delete<T, P>(path, params?)` | `Observable<IHttpResponse<T>>` | HTTP DELETE |
|
|
160
|
+
| `sendWithFile<T, P, B>(path, params, args)` | `Observable<IHttpResponse<T>>` | Upload file với progress |
|
|
161
|
+
| `cacheIndexDB<T>(obs, key, params, time?, reload?)` | `Observable<IHttpResponse<T>>` | Cache IndexedDB ✅ |
|
|
162
|
+
| `deleteCacheKeyStartWidth(key)` | `Promise<void>` | Xóa cache theo prefix |
|
|
163
|
+
| `cacheResponseData<T>(...)` | `Observable<IHttpResponse<T>>` | **Deprecated** — dùng `cacheIndexDB` |
|
|
164
|
+
|
|
165
|
+
### Protected Properties (override trong subclass)
|
|
166
|
+
|
|
167
|
+
| Property | Type | Default | Mô tả |
|
|
168
|
+
| ----------------------------- | ---------------------- | ----------- | ------------------------------ |
|
|
169
|
+
| `baseUrl` | `string` | `''` | Base URL của API |
|
|
170
|
+
| `keyCacheUniqueByIdUserLogin` | `string` | `''` | User ID suffix cho cache key |
|
|
171
|
+
| `ignoreRedirect401` | `boolean?` | `undefined` | Tắt auto redirect khi 401 |
|
|
172
|
+
| `isStartZeroPage` | `boolean` | `false` | Convert page 1-based → 0-based |
|
|
173
|
+
| `observeResponse` | `'response' \| 'body'` | `'body'` | Mode nhận HTTP response |
|
|
174
|
+
|
|
175
|
+
## Types
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
interface IHttpResponse<T> {
|
|
179
|
+
code: number;
|
|
180
|
+
message?: string;
|
|
181
|
+
data?: T;
|
|
182
|
+
paging?: IPaging;
|
|
183
|
+
feCloneCode?: any;
|
|
184
|
+
statusCodeHttp?: number;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
interface IPaging {
|
|
188
|
+
page?: number;
|
|
189
|
+
per_page?: number;
|
|
190
|
+
total_items?: number;
|
|
191
|
+
total_pages?: number;
|
|
192
|
+
before?: string;
|
|
193
|
+
after?: string;
|
|
194
|
+
previous?: string;
|
|
195
|
+
next?: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface IHttpProcessUpload {
|
|
199
|
+
loaded: number;
|
|
200
|
+
total: number;
|
|
201
|
+
percent: number;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Lưu ý quan trọng
|
|
206
|
+
|
|
207
|
+
- **Không inject trực tiếp** — class là abstract, phải extend
|
|
208
|
+
- **3 abstract methods** phải implement: `getOptions`, `redirectToLogin`, `replaceURLByPattern`
|
|
209
|
+
- **`cacheIndexDB` > `cacheResponseData`** — `cacheResponseData` đã deprecated
|
|
210
|
+
- **`keyCacheUniqueByIdUserLogin`** — set user ID để tránh cache conflict giữa các user
|
|
211
|
+
- **`sendWithFile` dùng XHR native** — không phải Angular HttpClient (để có progress events)
|
|
212
|
+
- **Response normalization** hỗ trợ nhiều backend format: REST standard, Spring Boot, cursor-based pagination
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libs-ui/services-base-request-abstract",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.356-0",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": ">=18.0.0",
|
|
6
6
|
"@angular/core": ">=18.0.0",
|
|
7
|
-
"@libs-ui/interfaces-types": "0.2.
|
|
8
|
-
"@libs-ui/utils": "0.2.
|
|
7
|
+
"@libs-ui/interfaces-types": "0.2.356-0",
|
|
8
|
+
"@libs-ui/utils": "0.2.356-0",
|
|
9
9
|
"rxjs": "~7.8.0"
|
|
10
10
|
},
|
|
11
11
|
"sideEffects": false,
|