@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.
Files changed (50) hide show
  1. package/.eslintrc.json +43 -0
  2. package/README.md +285 -1
  3. package/jest.config.ts +22 -0
  4. package/ng-package.json +7 -0
  5. package/package.json +6 -23
  6. package/project.json +36 -0
  7. package/src/lib/index.ts +1 -0
  8. package/{lib/models/all-item.datasource.d.ts → src/lib/models/all-item.datasource.ts} +2 -1
  9. package/src/lib/models/base-http.models.ts +144 -0
  10. package/src/lib/models/constants.ts +8 -0
  11. package/src/lib/models/datasource.model.ts +77 -0
  12. package/src/lib/models/error-provider.model.ts +20 -0
  13. package/{lib/models/event.models.d.ts → src/lib/models/event.models.ts} +3 -1
  14. package/{lib/models/index.d.ts → src/lib/models/index.ts} +1 -1
  15. package/src/lib/services/base-http.service.ts +114 -0
  16. package/src/lib/services/error-provider.service.ts +33 -0
  17. package/{lib/services/index.d.ts → src/lib/services/index.ts} +1 -1
  18. package/src/lib/services/local-storage.service.ts +13 -0
  19. package/src/lib/services/storage.service.ts +13 -0
  20. package/src/lib/utils/array.utils.spec.ts +49 -0
  21. package/src/lib/utils/array.utils.ts +54 -0
  22. package/src/lib/utils/clear.utils.ts +53 -0
  23. package/src/lib/utils/data.utils.ts +34 -0
  24. package/src/lib/utils/date.utils.ts +30 -0
  25. package/{lib/utils/index.d.ts → src/lib/utils/index.ts} +6 -6
  26. package/src/lib/utils/keyOrFn.utils.ts +15 -0
  27. package/src/lib/utils/object.utils.spec.ts +69 -0
  28. package/src/lib/utils/object.utils.ts +47 -0
  29. package/src/test-setup.ts +8 -0
  30. package/tsconfig.json +29 -0
  31. package/tsconfig.lib.json +17 -0
  32. package/tsconfig.lib.prod.json +9 -0
  33. package/tsconfig.spec.json +16 -0
  34. package/fesm2022/verisoft-core.mjs +0 -465
  35. package/fesm2022/verisoft-core.mjs.map +0 -1
  36. package/lib/models/base-http.models.d.ts +0 -41
  37. package/lib/models/constants.d.ts +0 -3
  38. package/lib/models/datasource.model.d.ts +0 -8
  39. package/lib/models/error-provider.model.d.ts +0 -11
  40. package/lib/services/base-http.service.d.ts +0 -21
  41. package/lib/services/error-provider.service.d.ts +0 -12
  42. package/lib/services/local-storage.service.d.ts +0 -5
  43. package/lib/utils/array.utils.d.ts +0 -5
  44. package/lib/utils/clear.utils.d.ts +0 -7
  45. package/lib/utils/data.utils.d.ts +0 -3
  46. package/lib/utils/date.utils.d.ts +0 -1
  47. package/lib/utils/keyOrFn.utils.d.ts +0 -1
  48. package/lib/utils/object.utils.d.ts +0 -2
  49. /package/{index.d.ts → src/index.ts} +0 -0
  50. /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
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../../dist/src/libs/core",
4
+ "lib": {
5
+ "entryFile": "src/index.ts"
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,30 +1,13 @@
1
1
  {
2
2
  "name": "@verisoft/core",
3
- "version": "19.0.0-rc001",
3
+ "version": "20.1.0",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
- "@angular/common": "19.2.9",
7
- "@angular/core": "19.2.9",
8
- "@angular/forms": "19.2.9",
9
- "@ngx-translate/core": "^16.0.4",
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
+ }
@@ -0,0 +1 @@
1
+ export * from './models';
@@ -1,4 +1,5 @@
1
1
  import { Observable } from 'rxjs';
2
+
2
3
  export interface AllItemDatasource<T> {
3
- getData$: () => Observable<T[]>;
4
+ getData$: () => Observable<T[]>
4
5
  }
@@ -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,8 @@
1
+ import { LazyLoadEvent } from './event.models';
2
+
3
+ export const DEFAULT_PAGE_SIZE = 50;
4
+
5
+ export const DEFAULT_LAZYLOAD_OPTIONS: LazyLoadEvent = {
6
+ offset: 0,
7
+ limit: DEFAULT_PAGE_SIZE,
8
+ };
@@ -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
+ };