@verisoft/core 19.0.0-rc001 → 20.1.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/.eslintrc.json +43 -0
- package/README.md +285 -1
- package/jest.config.ts +22 -0
- package/ng-package.json +7 -0
- package/package.json +6 -23
- package/project.json +36 -0
- package/src/lib/index.ts +1 -0
- package/{lib/models/all-item.datasource.d.ts → src/lib/models/all-item.datasource.ts} +2 -1
- package/src/lib/models/base-http.models.ts +144 -0
- package/src/lib/models/constants.ts +8 -0
- package/src/lib/models/datasource.model.ts +77 -0
- package/src/lib/models/error-provider.model.ts +20 -0
- package/{lib/models/event.models.d.ts → src/lib/models/event.models.ts} +3 -1
- package/{lib/models/index.d.ts → src/lib/models/index.ts} +1 -1
- package/src/lib/services/base-http.service.ts +114 -0
- package/src/lib/services/error-provider.service.ts +33 -0
- package/{lib/services/index.d.ts → src/lib/services/index.ts} +1 -1
- package/src/lib/services/local-storage.service.ts +13 -0
- package/src/lib/services/storage.service.ts +13 -0
- package/src/lib/utils/array.utils.spec.ts +49 -0
- package/src/lib/utils/array.utils.ts +54 -0
- package/src/lib/utils/clear.utils.ts +53 -0
- package/src/lib/utils/data.utils.ts +34 -0
- package/src/lib/utils/date.utils.ts +30 -0
- package/{lib/utils/index.d.ts → src/lib/utils/index.ts} +6 -6
- package/src/lib/utils/keyOrFn.utils.ts +15 -0
- package/src/lib/utils/object.utils.spec.ts +69 -0
- package/src/lib/utils/object.utils.ts +47 -0
- package/src/test-setup.ts +8 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +16 -0
- package/fesm2022/verisoft-core.mjs +0 -465
- package/fesm2022/verisoft-core.mjs.map +0 -1
- package/lib/models/base-http.models.d.ts +0 -41
- package/lib/models/constants.d.ts +0 -3
- package/lib/models/datasource.model.d.ts +0 -8
- package/lib/models/error-provider.model.d.ts +0 -11
- package/lib/services/base-http.service.d.ts +0 -21
- package/lib/services/error-provider.service.d.ts +0 -12
- package/lib/services/local-storage.service.d.ts +0 -5
- package/lib/utils/array.utils.d.ts +0 -5
- package/lib/utils/clear.utils.d.ts +0 -7
- package/lib/utils/data.utils.d.ts +0 -3
- package/lib/utils/date.utils.d.ts +0 -1
- package/lib/utils/keyOrFn.utils.d.ts +0 -1
- package/lib/utils/object.utils.d.ts +0 -2
- /package/{index.d.ts → src/index.ts} +0 -0
- /package/{lib/models/environment.model.d.ts → src/lib/models/environment.model.ts} +0 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../../../.eslintrc.base.json"],
|
|
3
|
+
"ignorePatterns": ["!**/*"],
|
|
4
|
+
"overrides": [
|
|
5
|
+
{
|
|
6
|
+
"files": ["*.ts"],
|
|
7
|
+
"extends": [
|
|
8
|
+
"plugin:@nx/angular",
|
|
9
|
+
"plugin:@angular-eslint/template/process-inline-templates"
|
|
10
|
+
],
|
|
11
|
+
"rules": {
|
|
12
|
+
"@angular-eslint/directive-selector": [
|
|
13
|
+
"error",
|
|
14
|
+
{
|
|
15
|
+
"type": "attribute",
|
|
16
|
+
"prefix": "v",
|
|
17
|
+
"style": "camelCase"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"@angular-eslint/component-selector": [
|
|
21
|
+
"error",
|
|
22
|
+
{
|
|
23
|
+
"type": "element",
|
|
24
|
+
"prefix": "v",
|
|
25
|
+
"style": "kebab-case"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"files": ["*.html"],
|
|
32
|
+
"extends": ["plugin:@nx/angular-template"],
|
|
33
|
+
"rules": {}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"files": ["*.json"],
|
|
37
|
+
"parser": "jsonc-eslint-parser",
|
|
38
|
+
"rules": {
|
|
39
|
+
"@nx/dependency-checks": "error"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
package/README.md
CHANGED
|
@@ -1,5 +1,289 @@
|
|
|
1
|
-
# core
|
|
1
|
+
# @verisoft/core
|
|
2
2
|
|
|
3
|
+
Core utilities and HTTP services for Verisoft Angular applications, providing foundational functionality for data access, HTTP communication, and utility operations.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @verisoft/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **BaseHttpService**: Generic HTTP service with CRUD operations
|
|
14
|
+
- **HTTP Models**: Type-safe request/response models
|
|
15
|
+
- **Utility Functions**: Common operations for arrays, objects, dates, and data manipulation
|
|
16
|
+
- **Storage Services**: Local storage abstraction
|
|
17
|
+
- **Error Handling**: Centralized error provider service
|
|
18
|
+
|
|
19
|
+
## Core Services
|
|
20
|
+
|
|
21
|
+
### BaseHttpService
|
|
22
|
+
|
|
23
|
+
Generic HTTP service providing standardized CRUD operations for REST APIs.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Injectable } from '@angular/core';
|
|
27
|
+
import { BaseHttpService } from '@verisoft/core';
|
|
28
|
+
|
|
29
|
+
@Injectable({ providedIn: 'root' })
|
|
30
|
+
export class UserService extends BaseHttpService<User> {
|
|
31
|
+
constructor() {
|
|
32
|
+
super('users'); // Base endpoint path
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Usage
|
|
37
|
+
export class UserComponent {
|
|
38
|
+
constructor(private userService: UserService) {}
|
|
39
|
+
|
|
40
|
+
loadUsers() {
|
|
41
|
+
this.userService.fetchList({ page: 1, size: 10 })
|
|
42
|
+
.subscribe(page => {
|
|
43
|
+
console.log(page.data); // User[]
|
|
44
|
+
console.log(page.total); // Total count
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getUser(id: number) {
|
|
49
|
+
this.userService.get(id)
|
|
50
|
+
.subscribe(user => console.log(user));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
createUser(user: CreateUserDto) {
|
|
54
|
+
this.userService.post(user)
|
|
55
|
+
.subscribe(created => console.log(created));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
updateUser(id: number, user: UpdateUserDto) {
|
|
59
|
+
this.userService.put(id, user)
|
|
60
|
+
.subscribe(updated => console.log(updated));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
deleteUser(id: number) {
|
|
64
|
+
this.userService.delete(id)
|
|
65
|
+
.subscribe(() => console.log('Deleted'));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### HTTP Models
|
|
71
|
+
|
|
72
|
+
Type-safe models for API communication:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { RequestParams, Page, SortDirection } from '@verisoft/core';
|
|
76
|
+
|
|
77
|
+
interface UserFilter {
|
|
78
|
+
name?: string;
|
|
79
|
+
email?: string;
|
|
80
|
+
active?: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Request parameters with filtering and sorting
|
|
84
|
+
const params: RequestParams<UserFilter> = {
|
|
85
|
+
page: 1,
|
|
86
|
+
size: 20,
|
|
87
|
+
filter: {
|
|
88
|
+
active: true,
|
|
89
|
+
name: 'John'
|
|
90
|
+
},
|
|
91
|
+
sort: {
|
|
92
|
+
field: 'createdAt',
|
|
93
|
+
direction: SortDirection.DESC
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Response pagination
|
|
98
|
+
interface UserPage extends Page<User> {
|
|
99
|
+
data: User[];
|
|
100
|
+
total: number;
|
|
101
|
+
size: number;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Utility Functions
|
|
106
|
+
|
|
107
|
+
Common utility operations for data manipulation:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import {
|
|
111
|
+
ArrayUtils,
|
|
112
|
+
ObjectUtils,
|
|
113
|
+
DateUtils,
|
|
114
|
+
DataUtils,
|
|
115
|
+
ClearUtils
|
|
116
|
+
} from '@verisoft/core';
|
|
117
|
+
|
|
118
|
+
// Array utilities
|
|
119
|
+
const uniqueItems = ArrayUtils.unique([1, 2, 2, 3]);
|
|
120
|
+
const groupedData = ArrayUtils.groupBy(users, 'department');
|
|
121
|
+
|
|
122
|
+
// Object utilities
|
|
123
|
+
const merged = ObjectUtils.merge(obj1, obj2);
|
|
124
|
+
const cloned = ObjectUtils.deepClone(originalObject);
|
|
125
|
+
|
|
126
|
+
// Date utilities
|
|
127
|
+
const formatted = DateUtils.format(new Date(), 'DD/MM/YYYY');
|
|
128
|
+
const isValid = DateUtils.isValid('2023-12-31');
|
|
129
|
+
|
|
130
|
+
// Data utilities
|
|
131
|
+
const filtered = DataUtils.filterBy(items, 'status', 'active');
|
|
132
|
+
const sorted = DataUtils.sortBy(items, 'name');
|
|
133
|
+
|
|
134
|
+
// Clear utilities
|
|
135
|
+
const cleaned = ClearUtils.removeEmpty(objectWithNulls);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Storage Services
|
|
139
|
+
|
|
140
|
+
Local storage abstraction with type safety:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { LocalStorageService } from '@verisoft/core';
|
|
144
|
+
|
|
145
|
+
export class SettingsService {
|
|
146
|
+
constructor(private storage: LocalStorageService) {}
|
|
147
|
+
|
|
148
|
+
saveUserPreferences(prefs: UserPreferences) {
|
|
149
|
+
this.storage.setItem('userPrefs', prefs);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
getUserPreferences(): UserPreferences | null {
|
|
153
|
+
return this.storage.getItem<UserPreferences>('userPrefs');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
clearPreferences() {
|
|
157
|
+
this.storage.removeItem('userPrefs');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Error Handling
|
|
163
|
+
|
|
164
|
+
Centralized error provider for consistent error management:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
import { ErrorProviderService } from '@verisoft/core';
|
|
168
|
+
|
|
169
|
+
@Component({...})
|
|
170
|
+
export class MyComponent {
|
|
171
|
+
constructor(private errorProvider: ErrorProviderService) {}
|
|
172
|
+
|
|
173
|
+
handleApiError(error: any) {
|
|
174
|
+
this.errorProvider.handleError(error);
|
|
175
|
+
// Automatically handles common HTTP errors
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Configuration
|
|
181
|
+
|
|
182
|
+
### Base URL Configuration
|
|
183
|
+
|
|
184
|
+
Configure the base URL for HTTP services:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { BASE_URL_PATH } from '@verisoft/core';
|
|
188
|
+
|
|
189
|
+
@NgModule({
|
|
190
|
+
providers: [
|
|
191
|
+
{
|
|
192
|
+
provide: BASE_URL_PATH,
|
|
193
|
+
useValue: 'https://api.example.com/v1'
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
})
|
|
197
|
+
export class AppModule {}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Service Configuration
|
|
201
|
+
|
|
202
|
+
Customize BaseHttpService behavior:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
@Injectable()
|
|
206
|
+
export class CustomUserService extends BaseHttpService<User> {
|
|
207
|
+
constructor() {
|
|
208
|
+
super('users', {
|
|
209
|
+
timeout: 10000,
|
|
210
|
+
retries: 3,
|
|
211
|
+
headers: {
|
|
212
|
+
'Custom-Header': 'value'
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Override default methods
|
|
218
|
+
protected override handleError(error: any) {
|
|
219
|
+
// Custom error handling
|
|
220
|
+
return super.handleError(error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Advanced Usage
|
|
226
|
+
|
|
227
|
+
### Custom Request Parameters
|
|
228
|
+
|
|
229
|
+
Extend request parameters for specific needs:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
interface CustomRequestParams<T> extends RequestParams<T> {
|
|
233
|
+
includeDeleted?: boolean;
|
|
234
|
+
expand?: string[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
class ExtendedService extends BaseHttpService<MyEntity> {
|
|
238
|
+
fetchWithOptions(params: CustomRequestParams<MyFilter>) {
|
|
239
|
+
return this.httpClient.get<Page<MyEntity>>(
|
|
240
|
+
this.buildUrl('', params)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Response Transformation
|
|
247
|
+
|
|
248
|
+
Transform API responses:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
class TransformingService extends BaseHttpService<User> {
|
|
252
|
+
fetchList(params: RequestParams<UserFilter>) {
|
|
253
|
+
return super.fetchList(params).pipe(
|
|
254
|
+
map(page => ({
|
|
255
|
+
...page,
|
|
256
|
+
data: page.data.map(user => ({
|
|
257
|
+
...user,
|
|
258
|
+
fullName: `${user.firstName} ${user.lastName}`
|
|
259
|
+
}))
|
|
260
|
+
}))
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Best Practices
|
|
267
|
+
|
|
268
|
+
1. **Extend BaseHttpService**: Create specific services for each entity
|
|
269
|
+
2. **Use Type Safety**: Define proper interfaces for filters and DTOs
|
|
270
|
+
3. **Handle Errors**: Implement proper error handling strategies
|
|
271
|
+
4. **Cache Responses**: Consider implementing caching for frequently accessed data
|
|
272
|
+
5. **Test Services**: Write unit tests for service methods
|
|
273
|
+
|
|
274
|
+
## Dependencies
|
|
275
|
+
|
|
276
|
+
- `@angular/common/http`
|
|
277
|
+
- `rxjs`
|
|
278
|
+
- `moment` (for date utilities)
|
|
279
|
+
|
|
280
|
+
## Contributing
|
|
281
|
+
|
|
282
|
+
This library is part of the Verisoft framework ecosystem. Follow the established patterns and ensure compatibility with other @verisoft packages.
|
|
283
|
+
|
|
284
|
+
## Running unit tests
|
|
285
|
+
|
|
286
|
+
Run `nx test core` to execute the unit tests.
|
|
3
287
|
This library was generated with [Nx](https://nx.dev).
|
|
4
288
|
|
|
5
289
|
## Running unit tests
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
export default {
|
|
3
|
+
displayName: 'core',
|
|
4
|
+
preset: '../../../jest.preset.js',
|
|
5
|
+
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
|
6
|
+
coverageDirectory: '../../../coverage/src/libs/core',
|
|
7
|
+
transform: {
|
|
8
|
+
'^.+\\.(ts|mjs|js|html)$': [
|
|
9
|
+
'jest-preset-angular',
|
|
10
|
+
{
|
|
11
|
+
tsconfig: '<rootDir>/tsconfig.spec.json',
|
|
12
|
+
stringifyContentPathRegex: '\\.(html|svg)$',
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
|
17
|
+
snapshotSerializers: [
|
|
18
|
+
'jest-preset-angular/build/serializers/no-ng-attributes',
|
|
19
|
+
'jest-preset-angular/build/serializers/ng-snapshot',
|
|
20
|
+
'jest-preset-angular/build/serializers/html-comment',
|
|
21
|
+
],
|
|
22
|
+
};
|
package/ng-package.json
ADDED
package/package.json
CHANGED
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verisoft/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "20.1.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@angular/common": "
|
|
7
|
-
"@angular/core": "
|
|
8
|
-
"@angular/forms": "
|
|
9
|
-
"@ngx-translate/core": "^
|
|
6
|
+
"@angular/common": "~20.2.0",
|
|
7
|
+
"@angular/core": "~20.2.0",
|
|
8
|
+
"@angular/forms": "~20.2.0",
|
|
9
|
+
"@ngx-translate/core": "^17.0.0",
|
|
10
10
|
"moment": "^2.30.1",
|
|
11
11
|
"rxjs": "~7.8.0"
|
|
12
|
-
},
|
|
13
|
-
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
15
|
-
},
|
|
16
|
-
"module": "fesm2022/verisoft-core.mjs",
|
|
17
|
-
"typings": "index.d.ts",
|
|
18
|
-
"exports": {
|
|
19
|
-
"./package.json": {
|
|
20
|
-
"default": "./package.json"
|
|
21
|
-
},
|
|
22
|
-
".": {
|
|
23
|
-
"types": "./index.d.ts",
|
|
24
|
-
"default": "./fesm2022/verisoft-core.mjs"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"dependencies": {
|
|
28
|
-
"tslib": "^2.3.0"
|
|
29
12
|
}
|
|
30
|
-
}
|
|
13
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "core",
|
|
3
|
+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "src/libs/core/src",
|
|
5
|
+
"prefix": "lib",
|
|
6
|
+
"projectType": "library",
|
|
7
|
+
"tags": [],
|
|
8
|
+
"targets": {
|
|
9
|
+
"build": {
|
|
10
|
+
"executor": "@nx/angular:package",
|
|
11
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}"],
|
|
12
|
+
"options": {
|
|
13
|
+
"project": "src/libs/core/ng-package.json"
|
|
14
|
+
},
|
|
15
|
+
"configurations": {
|
|
16
|
+
"production": {
|
|
17
|
+
"tsConfig": "src/libs/core/tsconfig.lib.prod.json"
|
|
18
|
+
},
|
|
19
|
+
"development": {
|
|
20
|
+
"tsConfig": "src/libs/core/tsconfig.lib.json"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"defaultConfiguration": "production"
|
|
24
|
+
},
|
|
25
|
+
"test": {
|
|
26
|
+
"executor": "@nx/jest:jest",
|
|
27
|
+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
28
|
+
"options": {
|
|
29
|
+
"jestConfig": "src/libs/core/jest.config.ts"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"lint": {
|
|
33
|
+
"executor": "@nx/eslint:lint"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './models';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { HttpParams, HttpResponse } from '@angular/common/http';
|
|
3
|
+
import { InjectionToken } from '@angular/core';
|
|
4
|
+
|
|
5
|
+
export const BASE_URL_PATH = new InjectionToken<string>('BASE_URL_PATH');
|
|
6
|
+
|
|
7
|
+
export function requestParamsToHttpParams<T>(
|
|
8
|
+
requestParams: Partial<RequestParams<T>>,
|
|
9
|
+
httpParams: HttpParams = new HttpParams()
|
|
10
|
+
): HttpParams {
|
|
11
|
+
if (requestParams.limit != undefined) {
|
|
12
|
+
httpParams = httpParams.append('limit', requestParams.limit.toString());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (requestParams.offset != undefined) {
|
|
16
|
+
httpParams = httpParams.append('offset', requestParams.offset.toString());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (requestParams.id != '' && requestParams.id != undefined) {
|
|
20
|
+
httpParams = httpParams.append('id', requestParams.id as any);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
httpParams = getFilter(requestParams, httpParams);
|
|
24
|
+
httpParams = getSort(requestParams, httpParams);
|
|
25
|
+
|
|
26
|
+
return httpParams;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getFilter<T>(
|
|
30
|
+
requestParams: Partial<RequestParams<any>>,
|
|
31
|
+
httpParams: HttpParams
|
|
32
|
+
): HttpParams {
|
|
33
|
+
if (!requestParams.filter) {
|
|
34
|
+
return httpParams;
|
|
35
|
+
}
|
|
36
|
+
Object.keys(requestParams?.filter).forEach((key) => {
|
|
37
|
+
const value = requestParams.filter?.[key as keyof Partial<T>];
|
|
38
|
+
if (value != undefined && !(typeof value === 'string' && value.trim() === "")) {
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
value.forEach((valueItem: any) => {
|
|
41
|
+
httpParams = httpParams.append(
|
|
42
|
+
'Filter.' + key, valueItem
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
httpParams = httpParams.append('Filter.' + key, value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return httpParams;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getSort(
|
|
54
|
+
requestParams: Partial<RequestParams<any>>,
|
|
55
|
+
httpParams: HttpParams
|
|
56
|
+
): HttpParams {
|
|
57
|
+
if (!requestParams.sort) {
|
|
58
|
+
return httpParams;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
requestParams.sort?.forEach((sort) => {
|
|
62
|
+
httpParams = httpParams
|
|
63
|
+
.append('Sort.Field', sort.field)
|
|
64
|
+
.append('Sort.Direction', sort.direction);
|
|
65
|
+
});
|
|
66
|
+
return httpParams;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function saveFile(response: HttpResponse<Blob>) {
|
|
70
|
+
const fileName = response.headers
|
|
71
|
+
.get('Content-Disposition')
|
|
72
|
+
?.split(';')[1]
|
|
73
|
+
.split('=')[1];
|
|
74
|
+
const blob: Blob = response.body as Blob;
|
|
75
|
+
const a = document.createElement('a');
|
|
76
|
+
a.download = fileName ?? 'undefined';
|
|
77
|
+
a.href = window.URL.createObjectURL(blob);
|
|
78
|
+
a.click();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export enum SortDirection {
|
|
82
|
+
asc = 'asc',
|
|
83
|
+
desc = 'desc',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export declare type SortDirectionType = keyof typeof SortDirection;
|
|
87
|
+
|
|
88
|
+
export declare interface Sort {
|
|
89
|
+
field: string;
|
|
90
|
+
direction: SortDirectionType;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface RequestParams<T> extends AllDataRequestParams<T> {
|
|
94
|
+
offset: number;
|
|
95
|
+
limit: number;
|
|
96
|
+
id?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface AllDataRequestParams<T> {
|
|
100
|
+
filter?: Partial<T>;
|
|
101
|
+
sort?: Sort[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface Page<T> {
|
|
105
|
+
data: T[];
|
|
106
|
+
total: number;
|
|
107
|
+
limit: number;
|
|
108
|
+
offset: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface CustomExport<T> {
|
|
112
|
+
sortDefinition?: Sort;
|
|
113
|
+
filter?: Partial<T>;
|
|
114
|
+
columnsToExport: ColumnExportSpecification[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface ColumnExportSpecification {
|
|
118
|
+
name: string;
|
|
119
|
+
header: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const DEFAULT_SEARCH_LIMIT = 50;
|
|
123
|
+
|
|
124
|
+
export const DEFAULT_SEARCH_PARAMS: RequestParams<any> = {
|
|
125
|
+
offset: 0,
|
|
126
|
+
limit: DEFAULT_SEARCH_LIMIT,
|
|
127
|
+
id: '',
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export function normalizeRequest<T>(
|
|
131
|
+
request: Partial<RequestParams<T>>,
|
|
132
|
+
minLimit: number | undefined = undefined
|
|
133
|
+
): RequestParams<T> {
|
|
134
|
+
return {
|
|
135
|
+
offset: request?.offset ?? 0,
|
|
136
|
+
limit: !request?.limit
|
|
137
|
+
? DEFAULT_SEARCH_LIMIT
|
|
138
|
+
: minLimit && request.limit < minLimit
|
|
139
|
+
? minLimit
|
|
140
|
+
: request.limit,
|
|
141
|
+
filter: request?.filter,
|
|
142
|
+
sort: request?.sort,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { isObservable, map, Observable, of } from 'rxjs';
|
|
3
|
+
import { BaseHttpService } from '../services/base-http.service';
|
|
4
|
+
import { AllItemDatasource } from './all-item.datasource';
|
|
5
|
+
import { Page, RequestParams } from './base-http.models';
|
|
6
|
+
import { DEFAULT_PAGE_SIZE } from './constants';
|
|
7
|
+
|
|
8
|
+
export type DatasourceType<T> =
|
|
9
|
+
| string
|
|
10
|
+
| Observable<T[]>
|
|
11
|
+
| Observable<Page<T>>
|
|
12
|
+
| BaseHttpService<T>
|
|
13
|
+
| T[]
|
|
14
|
+
| Page<T>
|
|
15
|
+
| AllItemDatasource<T>;
|
|
16
|
+
|
|
17
|
+
export type DataSourceFunctionType<T> = (
|
|
18
|
+
requestParams: RequestParams<T>
|
|
19
|
+
) => Observable<Page<T>>;
|
|
20
|
+
|
|
21
|
+
export function convertDatasource<T>(
|
|
22
|
+
datasource: DatasourceType<T>,
|
|
23
|
+
basePath: string,
|
|
24
|
+
httpClient: HttpClient
|
|
25
|
+
): DataSourceFunctionType<T> {
|
|
26
|
+
if (!datasource) {
|
|
27
|
+
throw new Error('Datasource is not defined');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (typeof datasource === 'string') {
|
|
31
|
+
const service = new BaseHttpService<T>(httpClient, basePath, datasource);
|
|
32
|
+
return (requestParams: RequestParams<T>) =>
|
|
33
|
+
service.fetchList(requestParams);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (datasource instanceof BaseHttpService) {
|
|
37
|
+
return (requestParams: RequestParams<T>) =>
|
|
38
|
+
datasource.fetchList(requestParams);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (isObservable(datasource)) {
|
|
42
|
+
return () =>
|
|
43
|
+
(datasource as Observable<Page<T> | T[]>).pipe(map(convertArrayToPage));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const allItemDatasource = <AllItemDatasource<T>>datasource;
|
|
47
|
+
if (allItemDatasource.getData$) {
|
|
48
|
+
return () =>
|
|
49
|
+
allItemDatasource
|
|
50
|
+
.getData$()
|
|
51
|
+
.pipe(map((data) => convertArrayToPage(data)));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const page = <Page<T>>datasource;
|
|
55
|
+
if (page.data) {
|
|
56
|
+
return () => of(page);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (Array.isArray(datasource)) {
|
|
60
|
+
return () => of(convertArrayToPage(datasource));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
throw new Error('Datasource is not supported');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function convertArrayToPage<T>(data: Page<T> | T[]): Page<T> {
|
|
67
|
+
if (Array.isArray(data)) {
|
|
68
|
+
return {
|
|
69
|
+
data: data ?? [],
|
|
70
|
+
total: data?.length ?? 0,
|
|
71
|
+
limit: data?.length ?? DEFAULT_PAGE_SIZE,
|
|
72
|
+
offset: 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return data as Page<T>;
|
|
77
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { ValidationErrors } from '@angular/forms';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
|
|
5
|
+
export interface ErrorProvider {
|
|
6
|
+
mapError(errors: ValidationErrors): Observable<string | undefined>;
|
|
7
|
+
errors: Record<string, (value?: any) => string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ERROR_PROVIDER_TOKEN
|
|
11
|
+
= new InjectionToken<ErrorProvider>('ERROR_PROVIDER_TOKEN');
|
|
12
|
+
|
|
13
|
+
export enum CustomValidationCodes {}
|
|
14
|
+
|
|
15
|
+
export const ErrorMap: Record<string, (value?: any) => string> = {
|
|
16
|
+
required: () => `VALIDATIONS.REQUIRED`,
|
|
17
|
+
email: () => `VALIDATIONS.EMAIL`,
|
|
18
|
+
minLength: () => 'VALIDATIONS.MIN_LENGTH',
|
|
19
|
+
maxLength: () => 'VALIDATIONS.MAX_LENGTH',
|
|
20
|
+
};
|