@intellegens/cornerstone-client 0.0.45 → 0.0.9999-alpha-1
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/LICENSE.md +0 -0
- package/README.md +133 -12
- package/adapters/CollectionViewAdapter/index.d.ts +154 -0
- package/adapters/CollectionViewAdapter/index.js +281 -0
- package/adapters/index.d.ts +1 -0
- package/adapters/index.js +1 -0
- package/data/api/dto/PropertyPathDto.d.ts +4 -0
- package/data/api/dto/{crud/CrudMetadata.js → PropertyPathDto.js} +0 -0
- package/data/api/dto/ReadOptionsDto.d.ts +8 -0
- package/data/api/dto/{read/ReadMetadata.js → ReadOptionsDto.js} +0 -0
- package/data/api/dto/ReadResultDto.d.ts +12 -0
- package/data/api/dto/{response/ApiError.js → ReadResultDto.js} +0 -0
- package/data/api/dto/ReadResultMetadataDto.d.ts +8 -0
- package/data/api/dto/{response/ApiResponse.js → ReadResultMetadataDto.js} +0 -0
- package/data/api/dto/crud/{CrudMetadata.d.ts → CrudMetadataDto.d.ts} +1 -1
- package/data/api/dto/{response/ApiSuccessResponse.js → crud/CrudMetadataDto.js} +0 -0
- package/data/api/dto/crud/index.d.ts +1 -1
- package/data/api/dto/crud/index.js +1 -1
- package/data/api/dto/index.d.ts +1 -0
- package/data/api/dto/index.js +1 -0
- package/data/api/dto/read/{ReadMetadata.d.ts → ReadMetadataDto.d.ts} +1 -1
- package/data/api/dto/{response/EmptyMetadata.js → read/ReadMetadataDto.js} +0 -0
- package/data/api/dto/read/ReadSelectedDefinitionDto.d.ts +0 -0
- package/data/api/dto/read/ReadSelectedDefinitionDto.js +0 -0
- package/data/api/dto/read/ReadSelectedOrderingDefinitionDto.d.ts +0 -0
- package/data/api/dto/read/ReadSelectedOrderingDefinitionDto.js +0 -0
- package/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.d.ts +2 -2
- package/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.js +0 -0
- package/data/api/dto/read/ReadSelectedPaginationDefinitionDto.d.ts +0 -0
- package/data/api/dto/read/ReadSelectedPaginationDefinitionDto.js +0 -0
- package/data/api/dto/read/ReadSelectedSearchDefinitionDto.d.ts +0 -0
- package/data/api/dto/read/ReadSelectedSearchDefinitionDto.js +0 -0
- package/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.d.ts +2 -2
- package/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.js +0 -0
- package/data/api/dto/read/index.d.ts +1 -1
- package/data/api/dto/read/index.js +1 -1
- package/data/api/dto/response/{ApiError.d.ts → ApiErrorDto.d.ts} +1 -1
- package/data/api/dto/response/ApiErrorDto.js +1 -0
- package/data/api/dto/response/{ApiResponse.d.ts → ApiResponseDto.d.ts} +3 -3
- package/data/api/dto/response/ApiResponseDto.js +1 -0
- package/data/api/dto/response/{ApiSuccessResponse.d.ts → ApiSuccessResponseDto.d.ts} +1 -1
- package/data/api/dto/response/ApiSuccessResponseDto.js +1 -0
- package/data/api/dto/response/EmptyMetadataDto.d.ts +4 -0
- package/data/api/dto/response/EmptyMetadataDto.js +1 -0
- package/data/api/dto/response/index.d.ts +4 -4
- package/data/api/dto/response/index.js +4 -4
- package/data/api/enum/index.d.ts +0 -0
- package/data/api/enum/index.js +0 -0
- package/data/api/enum/read/ReadSelectedComparisonOperator.d.ts +0 -0
- package/data/api/enum/read/ReadSelectedComparisonOperator.js +0 -0
- package/data/api/enum/read/ReadSelectedLogicalOperator.d.ts +0 -0
- package/data/api/enum/read/ReadSelectedLogicalOperator.js +0 -0
- package/data/api/enum/read/ReadSelectedOrderingDirection.d.ts +0 -0
- package/data/api/enum/read/ReadSelectedOrderingDirection.js +0 -0
- package/data/api/enum/read/ReadSelectedPropertyType.d.ts +0 -0
- package/data/api/enum/read/ReadSelectedPropertyType.js +0 -0
- package/data/api/enum/read/index.d.ts +0 -0
- package/data/api/enum/read/index.js +0 -0
- package/data/api/enum/response/ApiErrorCodes.d.ts +0 -0
- package/data/api/enum/response/ApiErrorCodes.js +0 -0
- package/data/api/enum/response/index.d.ts +0 -0
- package/data/api/enum/response/index.js +0 -0
- package/data/api/index.d.ts +0 -0
- package/data/api/index.js +0 -0
- package/data/api/interface/IConcurrencySafe.d.ts +9 -0
- package/data/api/interface/IConcurrencySafe.js +2 -0
- package/data/api/interface/IIdentifiable.d.ts +4 -3
- package/data/api/interface/IIdentifiable.js +0 -0
- package/data/api/interface/IIdentifiableSecondary.d.ts +3 -3
- package/data/api/interface/IIdentifiableSecondary.js +0 -0
- package/data/api/interface/index.d.ts +1 -0
- package/data/api/interface/index.js +1 -0
- package/data/auth/dto/ClaimDto.d.ts +0 -0
- package/data/auth/dto/ClaimDto.js +0 -0
- package/data/auth/dto/RegisterRequestDto.d.ts +0 -0
- package/data/auth/dto/RegisterRequestDto.js +0 -0
- package/data/auth/dto/RoleDto.d.ts +0 -0
- package/data/auth/dto/RoleDto.js +0 -0
- package/data/auth/dto/SignInRequestDto.d.ts +0 -0
- package/data/auth/dto/SignInRequestDto.js +0 -0
- package/data/auth/dto/TokensDto.d.ts +0 -0
- package/data/auth/dto/TokensDto.js +0 -0
- package/data/auth/dto/UserDto.d.ts +0 -0
- package/data/auth/dto/UserDto.js +0 -0
- package/data/auth/dto/UserInfoDto.d.ts +0 -0
- package/data/auth/dto/UserInfoDto.js +0 -0
- package/data/auth/dto/index.d.ts +0 -0
- package/data/auth/dto/index.js +0 -0
- package/data/auth/index.d.ts +0 -0
- package/data/auth/index.js +0 -0
- package/data/index.d.ts +0 -0
- package/data/index.js +0 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package.json +15 -10
- package/services/api/ApiCrudControllerClient/index.d.ts +9 -5
- package/services/api/ApiCrudControllerClient/index.js +15 -9
- package/services/api/ApiInitializationService/index.d.ts +21 -3
- package/services/api/ApiInitializationService/index.js +36 -7
- package/services/api/ApiReadControllerClient/index.d.ts +12 -13
- package/services/api/ApiReadControllerClient/index.js +17 -18
- package/services/api/HttpService/FetchHttpService.d.ts +1 -1
- package/services/api/HttpService/FetchHttpService.js +2 -1
- package/services/api/HttpService/HttpRequestConfig.d.ts +1 -0
- package/services/api/HttpService/HttpRequestConfig.js +0 -0
- package/services/api/HttpService/HttpResponse.d.ts +0 -0
- package/services/api/HttpService/HttpResponse.js +0 -0
- package/services/api/HttpService/IHttpService.d.ts +1 -1
- package/services/api/HttpService/IHttpService.js +0 -0
- package/services/api/HttpService/index.d.ts +0 -2
- package/services/api/HttpService/index.js +0 -2
- package/services/api/UserManagementControllerClient/index.d.ts +9 -5
- package/services/api/UserManagementControllerClient/index.js +20 -12
- package/services/api/index.d.ts +0 -0
- package/services/api/index.js +0 -0
- package/services/auth/client/AuthService/index.d.ts +5 -5
- package/services/auth/client/AuthService/index.js +9 -9
- package/services/auth/client/AuthorizationManagementControllerClient/index.d.ts +5 -5
- package/services/auth/client/AuthorizationManagementControllerClient/index.js +8 -8
- package/services/auth/client/index.d.ts +0 -0
- package/services/auth/client/index.js +0 -0
- package/services/auth/index.d.ts +0 -0
- package/services/auth/index.js +0 -0
- package/services/index.d.ts +0 -0
- package/services/index.js +0 -0
- package/utils/index.d.ts +37 -0
- package/utils/index.js +106 -0
- package/data/api/dto/response/EmptyMetadata.d.ts +0 -4
- package/services/api/HttpService/AngularHttpService.d.ts +0 -9
- package/services/api/HttpService/AngularHttpService.js +0 -86
- package/services/api/HttpService/AxiosHttpService.d.ts +0 -10
- package/services/api/HttpService/AxiosHttpService.js +0 -53
package/LICENSE.md
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -30,28 +30,35 @@ http://localhost:3000
|
|
|
30
30
|
|
|
31
31
|
### API Initialization service
|
|
32
32
|
|
|
33
|
-
This service provides way to
|
|
33
|
+
This service provides a centralized way to initialize API configuration for a Cornerstone client application, including automatic API base URL detection and shared settings fetching:
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
36
|
import { ApiInitializationService } from '@intellegens/cornerstone-client';
|
|
37
37
|
const apiInitializationService = new ApiInitializationService();
|
|
38
|
-
await apiInitializationService.
|
|
38
|
+
await apiInitializationService.initializeAsync();
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
or use the existing singleton instance
|
|
42
42
|
|
|
43
43
|
```ts
|
|
44
44
|
import { apiInitializationService } from '@intellegens/cornerstone-client';
|
|
45
|
-
await apiInitializationService.
|
|
45
|
+
await apiInitializationService.initializeAsync();
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
#### Key Features
|
|
49
|
+
|
|
50
|
+
- **API Base URL Detection**: Automatically detects the API base URL through multiple methods
|
|
51
|
+
- **Shared Settings Management**: Fetches and caches server-side configuration that's safe to share with clients
|
|
52
|
+
- **HTTP Service Configuration**: Allows customization of the global HTTP service
|
|
53
|
+
- **Automatic Initialization**: Handles both API detection and configuration fetching in a single call
|
|
54
|
+
|
|
48
55
|
It also provides a way of customizing global defaults for Cornerstone client application:
|
|
49
56
|
|
|
50
57
|
- `httpService` Configures a default HTTP Service to be used across the application, unless specified otherwise (See [HTTP Service Abstraction](#http-service-abstraction))
|
|
51
58
|
|
|
52
59
|
```ts
|
|
53
60
|
import { apiInitializationService, FetchHttpService } from '@intellegens/cornerstone-client';
|
|
54
|
-
await apiInitializationService.
|
|
61
|
+
await apiInitializationService.initializeAsync({
|
|
55
62
|
httpService: new FetchHttpService(),
|
|
56
63
|
});
|
|
57
64
|
```
|
|
@@ -67,16 +74,66 @@ Base API URL is detected by checking the `CORNERSTONE-API-BASEURL` response head
|
|
|
67
74
|
Before, or after the base API URL has been detected, you can get any API endpoint URL by calling:
|
|
68
75
|
|
|
69
76
|
```ts
|
|
70
|
-
const fetchUrl = `${await apiInitializationService.
|
|
77
|
+
const fetchUrl = `${await apiInitializationService.getApiUrlAsync('/my/endpoint')}`;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Shared Settings Access
|
|
81
|
+
|
|
82
|
+
After initialization, you can access server-side configuration that's been shared with the client:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Get cached app settings
|
|
86
|
+
const settings = apiInitializationService.appConfig;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The app shared settings contain only client-safe configuration properties as defined by the server-side `AppSettingsShared` class, plus any additional runtime settings. Sensitive server-only settings are never exposed to clients.
|
|
90
|
+
|
|
91
|
+
### ~~System Service~~ (Deprecated)
|
|
92
|
+
|
|
93
|
+
> **Note**: The SystemService has been deprecated and its functionality has been integrated into the ApiInitializationService. Use `apiInitializationService.appConfig` instead of the SystemService methods.
|
|
94
|
+
|
|
95
|
+
The System Service functionality is now handled directly by the ApiInitializationService during initialization. The service automatically fetches and caches shared configuration settings from the server's `/api/system/init` endpoint.
|
|
96
|
+
|
|
97
|
+
#### Migration Guide
|
|
98
|
+
|
|
99
|
+
**Old approach:**
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { systemService } from '@intellegens/cornerstone-client';
|
|
103
|
+
|
|
104
|
+
// Initialize and fetch shared settings
|
|
105
|
+
await systemService.initializeAsync();
|
|
106
|
+
|
|
107
|
+
// Get cached settings
|
|
108
|
+
const settings = systemService.getInitializationInfo();
|
|
71
109
|
```
|
|
72
110
|
|
|
111
|
+
**New approach:**
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { apiInitializationService } from '@intellegens/cornerstone-client';
|
|
115
|
+
|
|
116
|
+
// Initialize (automatically fetches shared settings)
|
|
117
|
+
await apiInitializationService.initializeAsync();
|
|
118
|
+
|
|
119
|
+
// Get cached settings
|
|
120
|
+
const settings = apiInitializationService.appConfig;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### Key Features
|
|
124
|
+
|
|
125
|
+
- **Automatic Integration**: Settings are fetched during API initialization
|
|
126
|
+
- **Type Safety**: Supports generic typed settings interfaces
|
|
127
|
+
- **Error Handling**: Gracefully handles network errors and returns undefined for failed requests
|
|
128
|
+
- **Server Integration**: Works seamlessly with Cornerstone server-side configuration system
|
|
129
|
+
|
|
73
130
|
### HTTP Service Abstraction
|
|
74
131
|
|
|
75
|
-
The Cornerstone client provides a flexible HTTP service abstraction that allows you to use different HTTP libraries (Fetch API,
|
|
132
|
+
The Cornerstone client provides a flexible HTTP service abstraction that allows you to use different HTTP libraries (Fetch API, Angular HttpClient) with the same API clients. This abstraction provides consistent behavior across different HTTP implementations while maintaining framework independence.
|
|
76
133
|
|
|
77
134
|
#### Key Features
|
|
78
135
|
|
|
79
|
-
- **Pluggable HTTP implementations** - Switch between Fetch
|
|
136
|
+
- **Pluggable HTTP implementations** - Switch between Fetch and Angular HttpClient
|
|
80
137
|
- **Consistent interface** - All HTTP services implement the same `IHttpService` interface
|
|
81
138
|
- **Framework agnostic** - Use the same API clients in React, Angular, Vue, or any other framework
|
|
82
139
|
- **Easy testing** - Mock HTTP requests easily for unit testing
|
|
@@ -85,14 +142,10 @@ The Cornerstone client provides a flexible HTTP service abstraction that allows
|
|
|
85
142
|
#### Quick Example
|
|
86
143
|
|
|
87
144
|
```ts
|
|
88
|
-
import { ApiReadControllerClient, FetchHttpService
|
|
145
|
+
import { ApiReadControllerClient, FetchHttpService } from '@intellegens/cornerstone-client';
|
|
89
146
|
|
|
90
147
|
// Using default Fetch implementation
|
|
91
148
|
const client = new ApiReadControllerClient('/api/users');
|
|
92
|
-
|
|
93
|
-
// Or specify a custom HTTP service
|
|
94
|
-
const axiosService = new AxiosHttpService(axiosInstance);
|
|
95
|
-
const clientWithAxios = new ApiReadControllerClient('/api/users', axiosService);
|
|
96
149
|
```
|
|
97
150
|
|
|
98
151
|
For detailed documentation, examples, and advanced usage, see: [HTTP Service Documentation](./src/services/api/HttpService/README.md)
|
|
@@ -133,3 +186,71 @@ authService.logout(): Promise<UserDto | undefined>
|
|
|
133
186
|
```
|
|
134
187
|
|
|
135
188
|
This method logs the user out by sending a request to the API to end the session. It returns undefined if the logout is successful
|
|
189
|
+
|
|
190
|
+
### CollectionViewAdapter
|
|
191
|
+
|
|
192
|
+
TS class for handling sorting, filtering and paginating collections
|
|
193
|
+
|
|
194
|
+
Note that the collection is fetched on a page by page basis. Page size determines the number of items returned for display.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
ngOnInit() {
|
|
198
|
+
this._adapter = new CollectionViewAdapter<number, ExampleDto>(
|
|
199
|
+
'Example', // controller name
|
|
200
|
+
(isLoading, data) => {
|
|
201
|
+
// data: ExampleDto[]
|
|
202
|
+
// handle as needed
|
|
203
|
+
},
|
|
204
|
+
this._options, // pass along initial options CollectionViewAdapterOptions<TDto>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### utils
|
|
210
|
+
|
|
211
|
+
Helper functions for constructing `ReadSelectedSearchDefinitionDto`.
|
|
212
|
+
|
|
213
|
+
Example usage:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const search = condition(
|
|
217
|
+
ReadSelectedLogicalOperator.Or,
|
|
218
|
+
{ path: 'clients', operator: ReadSelectedComparisonOperator.Contains, value: 'search-term', valueType: ReadSelectedPropertyType.String },
|
|
219
|
+
{ path: 'suppliers', operator: ReadSelectedComparisonOperator.Contains, value: 'search-term', valueType: ReadSelectedPropertyType.String },
|
|
220
|
+
{ path: 'facilities', operator: ReadSelectedComparisonOperator.Contains, value: 'search-term', valueType: ReadSelectedPropertyType.String },
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const dateRange = condition(
|
|
224
|
+
ReadSelectedLogicalOperator.And,
|
|
225
|
+
{
|
|
226
|
+
path: 'createdDate',
|
|
227
|
+
operator: ReadSelectedComparisonOperator.GreaterOrEqual,
|
|
228
|
+
value: new Date('2024-01-01'),
|
|
229
|
+
valueType: ReadSelectedPropertyType.DateTime,
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
path: 'createdDate',
|
|
233
|
+
operator: ReadSelectedComparisonOperator.LessOrEqual,
|
|
234
|
+
value: new Date('2024-12-31'),
|
|
235
|
+
valueType: ReadSelectedPropertyType.DateTime,
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const definition = and(search, dateRange);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Functions `searchTerm` and `dateInterval` have been prepared to simplify usage for these common needs:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const definition = and(
|
|
246
|
+
searchTerm('search-term', textSearchablePaths, numericSearchablePaths),
|
|
247
|
+
dateInterval(
|
|
248
|
+
from,
|
|
249
|
+
to,
|
|
250
|
+
ReadSelectedComparisonOperator.GreaterOrEqual,
|
|
251
|
+
ReadSelectedComparisonOperator.LessOrEqual,
|
|
252
|
+
ReadSelectedPropertyType.DateTimeOffset,
|
|
253
|
+
'exampleColumnName',
|
|
254
|
+
),
|
|
255
|
+
);
|
|
256
|
+
```
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { IIdentifiable, ReadSelectedOrderingDirection, ReadSelectedSearchDefinitionDto } from '../../data';
|
|
2
|
+
import { PropertyPathDto } from '../../data';
|
|
3
|
+
/**
|
|
4
|
+
* Used with CollectionViewAdapter, a simplified configuration object for handling reading/writing from/to ReadSelectedDefinitionDto.
|
|
5
|
+
*
|
|
6
|
+
* @template TDto The type of the collection being sorted/filtered/paginated.
|
|
7
|
+
*
|
|
8
|
+
* @property {object} pagination - Pagination configuration
|
|
9
|
+
* @property {boolean} pagination.useTotalItemCount - Used for last page related logic of the paginator.
|
|
10
|
+
* Set to true if the total count for the collection is available; false assumes the last page is not known
|
|
11
|
+
* and adapts the pagination display accordingly.
|
|
12
|
+
* @property {number} [pagination.pageSize] - Number of items/rows to be fetched for the current page.
|
|
13
|
+
* @property {number} [pagination.pageNumber] - Current page index (skips fetching `(n-1) * pageSize` items/rows).
|
|
14
|
+
*
|
|
15
|
+
* @property {object} sorting - Sorting configuration
|
|
16
|
+
* @property {PropertyPathDto[]} [sorting.sortByPath] - Array of column/property names that are sortable.
|
|
17
|
+
* @property {ReadSelectedOrderingDirection} [sorting.sortDirection] - Enum for ascending/descending sort order.
|
|
18
|
+
*
|
|
19
|
+
* @property {object} searching - Search configuration
|
|
20
|
+
* @property {PropertyPathDto[]} [searching.textSearchablePaths] - Array of column/property names that are searchable where the value type is string.
|
|
21
|
+
* @property {PropertyPathDto[]} [searching.numericSearchablePaths] - Array of column/property names that are searchable where the value type is number.
|
|
22
|
+
* @property {ReadSelectedSearchDefinitionDto} [searching.searchDefinition] - The search definition by which the collection will be filtered.
|
|
23
|
+
*/
|
|
24
|
+
export type CollectionViewAdapterOptions = {
|
|
25
|
+
pagination: {
|
|
26
|
+
useTotalItemCount: boolean;
|
|
27
|
+
pageSize?: number;
|
|
28
|
+
pageNumber?: number;
|
|
29
|
+
};
|
|
30
|
+
sorting: {
|
|
31
|
+
sortByPath?: PropertyPathDto[];
|
|
32
|
+
sortDirection?: ReadSelectedOrderingDirection;
|
|
33
|
+
};
|
|
34
|
+
searching: {
|
|
35
|
+
textSearchablePaths?: PropertyPathDto[];
|
|
36
|
+
numericSearchablePaths?: PropertyPathDto[];
|
|
37
|
+
searchDefinition?: ReadSelectedSearchDefinitionDto;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* TS class for handling sorting, filtering and paginating collections
|
|
42
|
+
*
|
|
43
|
+
* @param controllerName - specify the target endpoint controller name.
|
|
44
|
+
* @param dataChangedCallback - Function invoked whenever the data changes.
|
|
45
|
+
* Receives a loading state flag and the updated data collection.
|
|
46
|
+
* @param options - Optional settings of type `CollectionViewAdapterOptions<TDto>` to customize the behavior of the adapter.
|
|
47
|
+
*/
|
|
48
|
+
export declare class CollectionViewAdapter<TKey, TDto extends IIdentifiable<TKey>> {
|
|
49
|
+
private _readClient;
|
|
50
|
+
private _isLoading;
|
|
51
|
+
private _pageData;
|
|
52
|
+
private _defaultOptions;
|
|
53
|
+
private _initialOptions;
|
|
54
|
+
private _currentOptions;
|
|
55
|
+
constructor(controllerName: string, dataChangedCallback: (isLoading: boolean, data: TDto[] | undefined, error: string | undefined) => void, options?: CollectionViewAdapterOptions);
|
|
56
|
+
/**
|
|
57
|
+
* Allows for adding any filtering criteria. Label is required to distinguish custom search categories (e.g. searchTerm, dateRange, anyCustom ...)
|
|
58
|
+
*/
|
|
59
|
+
setSearchDefinition(searchDefinition: ReadSelectedSearchDefinitionDto): Promise<TDto[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Public method for sorting the collection by property path / column name.
|
|
62
|
+
*
|
|
63
|
+
* @param propertyPath - must match camel-cased property/column name being sorted
|
|
64
|
+
* @param sortDirection - use `ReadSelectedOrderingDirection` enum choices
|
|
65
|
+
*/
|
|
66
|
+
setSorting(propertyPath: PropertyPathDto, sortDirection: ReadSelectedOrderingDirection): Promise<TDto[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Returns the camel-cased property path of the current column/property on which sorting is applied, along with the `ReadSelectedOrderingDirection` direction
|
|
69
|
+
*/
|
|
70
|
+
getCurrentSort(): {
|
|
71
|
+
sortByPath: PropertyPathDto | undefined;
|
|
72
|
+
sortDirection: ReadSelectedOrderingDirection | undefined;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Returns the current page's page number
|
|
76
|
+
*/
|
|
77
|
+
get currentPage(): number | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Sets the current page to `pageNumber` and triggers data re-fetch
|
|
80
|
+
*
|
|
81
|
+
* @param pageNumber
|
|
82
|
+
*/
|
|
83
|
+
jumpToPage(pageNumber: number): Promise<TDto[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Returns the currently set page size value
|
|
86
|
+
*/
|
|
87
|
+
get pageSize(): number | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Page size dictates the number of items or rows being fetched in a single API call
|
|
90
|
+
*
|
|
91
|
+
* @param pageSize
|
|
92
|
+
*/
|
|
93
|
+
setPageSize(pageSize: number): Promise<TDto[]>;
|
|
94
|
+
private _totalItemCount;
|
|
95
|
+
/**
|
|
96
|
+
* Returns the total count value of the collection.
|
|
97
|
+
*
|
|
98
|
+
* If `CollectionViewAdapterOptions` has set `pagination.useTotalItemCount` to `true`, the API will return this value along with the collection and it will be set automatically.
|
|
99
|
+
* Else this adapter will attempt to dynamically calculate this value via `_calculateTotal()`.
|
|
100
|
+
*/
|
|
101
|
+
get totalItemCount(): number;
|
|
102
|
+
/**
|
|
103
|
+
* Callback function invoked whenever data changes.
|
|
104
|
+
* Receives a loading state flag and the updated data collection.
|
|
105
|
+
*/
|
|
106
|
+
private _dataChangedCallback;
|
|
107
|
+
/**
|
|
108
|
+
* Timeout reference used to debounce calls to `_fetchCurrentPageData`.
|
|
109
|
+
*/
|
|
110
|
+
private _fetchCurrentPageDataTimeout?;
|
|
111
|
+
/**
|
|
112
|
+
* Queue of pending promises waiting for `_fetchCurrentPageData` to resolve or reject.
|
|
113
|
+
*/
|
|
114
|
+
private _fetchCurrentPageDataPromises;
|
|
115
|
+
/**
|
|
116
|
+
* Tracks the current in-flight request controller to allow cancellation when a new fetch starts.
|
|
117
|
+
*/
|
|
118
|
+
private _currentAbortController?;
|
|
119
|
+
/**
|
|
120
|
+
* Fetches the current page of data with debouncing support.
|
|
121
|
+
* Consolidates multiple concurrent calls into a single execution,
|
|
122
|
+
* resolving all queued promises with the same result.
|
|
123
|
+
*
|
|
124
|
+
* @returns A promise resolving to the fetched page of data.
|
|
125
|
+
*/
|
|
126
|
+
private _fetchCurrentPageData;
|
|
127
|
+
/**
|
|
128
|
+
* Executes the actual data fetch operation after debounce delay.
|
|
129
|
+
* Updates the loading state, triggers data change callbacks,
|
|
130
|
+
* and handles total item count calculation if pagination is enabled.
|
|
131
|
+
*
|
|
132
|
+
* @returns A promise resolving to the fetched page of data.
|
|
133
|
+
*/
|
|
134
|
+
private _fetchCurrentPageDataDebounced;
|
|
135
|
+
/**
|
|
136
|
+
* Takes current `CollectionViewAdapterOptions` and parses to `ReadSelectedDefinitionDto` which the cornerstone API expects.
|
|
137
|
+
*
|
|
138
|
+
* @returns - `ReadSelectedDefinitionDto` object
|
|
139
|
+
*/
|
|
140
|
+
private _parseOptionsToDefinition;
|
|
141
|
+
/**
|
|
142
|
+
* Calculates the total item count given pagination inputs.
|
|
143
|
+
*
|
|
144
|
+
* For example, with 10 items per page, on page 2 with 3 items, total = 13.
|
|
145
|
+
* If the total cannot be inferred (e.g., a full page is returned), the previous total is returned unchanged.
|
|
146
|
+
*
|
|
147
|
+
* @param pageSize - Number of items per page
|
|
148
|
+
* @param currentPageNumber - Current page number (1-based)
|
|
149
|
+
* @param itemsOnCurrentPageCount - Number of items in the fetched page
|
|
150
|
+
* @param previousTotal - Previous known total to preserve when indeterminate
|
|
151
|
+
* @returns The computed total item count
|
|
152
|
+
*/
|
|
153
|
+
private _calculateTotal;
|
|
154
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { ReadSelectedOrderingDirection } from '../../data';
|
|
2
|
+
import { toPascalCase } from '../../utils';
|
|
3
|
+
import { ApiReadControllerClient } from '../../services';
|
|
4
|
+
/**
|
|
5
|
+
* TS class for handling sorting, filtering and paginating collections
|
|
6
|
+
*
|
|
7
|
+
* @param controllerName - specify the target endpoint controller name.
|
|
8
|
+
* @param dataChangedCallback - Function invoked whenever the data changes.
|
|
9
|
+
* Receives a loading state flag and the updated data collection.
|
|
10
|
+
* @param options - Optional settings of type `CollectionViewAdapterOptions<TDto>` to customize the behavior of the adapter.
|
|
11
|
+
*/
|
|
12
|
+
export class CollectionViewAdapter {
|
|
13
|
+
_readClient;
|
|
14
|
+
_isLoading = false;
|
|
15
|
+
_pageData = [];
|
|
16
|
+
_defaultOptions = {
|
|
17
|
+
pagination: {
|
|
18
|
+
useTotalItemCount: false,
|
|
19
|
+
pageSize: 10,
|
|
20
|
+
pageNumber: 1,
|
|
21
|
+
},
|
|
22
|
+
sorting: {
|
|
23
|
+
sortByPath: [],
|
|
24
|
+
sortDirection: ReadSelectedOrderingDirection.Ascending,
|
|
25
|
+
},
|
|
26
|
+
searching: {
|
|
27
|
+
textSearchablePaths: [],
|
|
28
|
+
numericSearchablePaths: [],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
_initialOptions;
|
|
32
|
+
_currentOptions;
|
|
33
|
+
constructor(controllerName, dataChangedCallback, options) {
|
|
34
|
+
this._readClient = new ApiReadControllerClient(controllerName);
|
|
35
|
+
this._initialOptions = {
|
|
36
|
+
pagination: {
|
|
37
|
+
...this._defaultOptions.pagination,
|
|
38
|
+
...options?.pagination,
|
|
39
|
+
},
|
|
40
|
+
sorting: {
|
|
41
|
+
...this._defaultOptions.sorting,
|
|
42
|
+
...options?.sorting,
|
|
43
|
+
},
|
|
44
|
+
searching: {
|
|
45
|
+
...this._defaultOptions.searching,
|
|
46
|
+
...options?.searching,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
this._currentOptions = {
|
|
50
|
+
pagination: { ...this._initialOptions.pagination },
|
|
51
|
+
sorting: { ...this._initialOptions.sorting },
|
|
52
|
+
searching: { ...this._initialOptions.searching },
|
|
53
|
+
};
|
|
54
|
+
this._dataChangedCallback = dataChangedCallback;
|
|
55
|
+
this._fetchCurrentPageData();
|
|
56
|
+
}
|
|
57
|
+
//#region SEARCHING ------------------------------------------
|
|
58
|
+
/**
|
|
59
|
+
* Allows for adding any filtering criteria. Label is required to distinguish custom search categories (e.g. searchTerm, dateRange, anyCustom ...)
|
|
60
|
+
*/
|
|
61
|
+
setSearchDefinition(searchDefinition) {
|
|
62
|
+
this._currentOptions.searching.searchDefinition = searchDefinition;
|
|
63
|
+
this._currentOptions.pagination.pageNumber = 1; // Reset to first page
|
|
64
|
+
return this._fetchCurrentPageData();
|
|
65
|
+
}
|
|
66
|
+
//#endregion -----------------------------------------------
|
|
67
|
+
//#region SORTING ------------------------------------------
|
|
68
|
+
/**
|
|
69
|
+
* Public method for sorting the collection by property path / column name.
|
|
70
|
+
*
|
|
71
|
+
* @param propertyPath - must match camel-cased property/column name being sorted
|
|
72
|
+
* @param sortDirection - use `ReadSelectedOrderingDirection` enum choices
|
|
73
|
+
*/
|
|
74
|
+
setSorting(propertyPath, sortDirection) {
|
|
75
|
+
this._currentOptions.sorting.sortByPath = [propertyPath];
|
|
76
|
+
this._currentOptions.sorting.sortDirection = sortDirection;
|
|
77
|
+
this._currentOptions.pagination.pageNumber = 1; // Reset to first page
|
|
78
|
+
return this._fetchCurrentPageData();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the camel-cased property path of the current column/property on which sorting is applied, along with the `ReadSelectedOrderingDirection` direction
|
|
82
|
+
*/
|
|
83
|
+
getCurrentSort() {
|
|
84
|
+
return { sortByPath: this._currentOptions.sorting.sortByPath?.[0], sortDirection: this._currentOptions.sorting.sortDirection };
|
|
85
|
+
}
|
|
86
|
+
//#endregion -----------------------------------------------
|
|
87
|
+
//#region PAGINATION ---------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Returns the current page's page number
|
|
90
|
+
*/
|
|
91
|
+
get currentPage() {
|
|
92
|
+
return this._currentOptions.pagination.pageNumber;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Sets the current page to `pageNumber` and triggers data re-fetch
|
|
96
|
+
*
|
|
97
|
+
* @param pageNumber
|
|
98
|
+
*/
|
|
99
|
+
jumpToPage(pageNumber) {
|
|
100
|
+
this._currentOptions.pagination.pageNumber = pageNumber;
|
|
101
|
+
return this._fetchCurrentPageData();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Returns the currently set page size value
|
|
105
|
+
*/
|
|
106
|
+
get pageSize() {
|
|
107
|
+
return this._currentOptions.pagination.pageSize;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Page size dictates the number of items or rows being fetched in a single API call
|
|
111
|
+
*
|
|
112
|
+
* @param pageSize
|
|
113
|
+
*/
|
|
114
|
+
setPageSize(pageSize) {
|
|
115
|
+
this._currentOptions.pagination.pageSize = pageSize;
|
|
116
|
+
this._currentOptions.pagination.pageNumber = 1; // Reset to first page
|
|
117
|
+
return this._fetchCurrentPageData();
|
|
118
|
+
}
|
|
119
|
+
_totalItemCount = -1;
|
|
120
|
+
/**
|
|
121
|
+
* Returns the total count value of the collection.
|
|
122
|
+
*
|
|
123
|
+
* If `CollectionViewAdapterOptions` has set `pagination.useTotalItemCount` to `true`, the API will return this value along with the collection and it will be set automatically.
|
|
124
|
+
* Else this adapter will attempt to dynamically calculate this value via `_calculateTotal()`.
|
|
125
|
+
*/
|
|
126
|
+
get totalItemCount() {
|
|
127
|
+
return this._totalItemCount;
|
|
128
|
+
}
|
|
129
|
+
//#endregion -----------------------------------------------
|
|
130
|
+
/**
|
|
131
|
+
* Callback function invoked whenever data changes.
|
|
132
|
+
* Receives a loading state flag and the updated data collection.
|
|
133
|
+
*/
|
|
134
|
+
_dataChangedCallback;
|
|
135
|
+
/**
|
|
136
|
+
* Timeout reference used to debounce calls to `_fetchCurrentPageData`.
|
|
137
|
+
*/
|
|
138
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
139
|
+
_fetchCurrentPageDataTimeout;
|
|
140
|
+
/**
|
|
141
|
+
* Queue of pending promises waiting for `_fetchCurrentPageData` to resolve or reject.
|
|
142
|
+
*/
|
|
143
|
+
_fetchCurrentPageDataPromises = [];
|
|
144
|
+
/**
|
|
145
|
+
* Tracks the current in-flight request controller to allow cancellation when a new fetch starts.
|
|
146
|
+
*/
|
|
147
|
+
_currentAbortController;
|
|
148
|
+
/**
|
|
149
|
+
* Fetches the current page of data with debouncing support.
|
|
150
|
+
* Consolidates multiple concurrent calls into a single execution,
|
|
151
|
+
* resolving all queued promises with the same result.
|
|
152
|
+
*
|
|
153
|
+
* @returns A promise resolving to the fetched page of data.
|
|
154
|
+
*/
|
|
155
|
+
async _fetchCurrentPageData() {
|
|
156
|
+
return new Promise((resolve, reject) => {
|
|
157
|
+
this._fetchCurrentPageDataPromises.push({ resolve, reject });
|
|
158
|
+
if (this._fetchCurrentPageDataTimeout !== undefined) {
|
|
159
|
+
clearTimeout(this._fetchCurrentPageDataTimeout);
|
|
160
|
+
}
|
|
161
|
+
this._fetchCurrentPageDataTimeout = setTimeout(async () => {
|
|
162
|
+
try {
|
|
163
|
+
const result = await this._fetchCurrentPageDataDebounced();
|
|
164
|
+
const promises = this._fetchCurrentPageDataPromises.splice(0, this._fetchCurrentPageDataPromises.length);
|
|
165
|
+
for (const p of promises)
|
|
166
|
+
p.resolve(result);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
const promises = this._fetchCurrentPageDataPromises.splice(0, this._fetchCurrentPageDataPromises.length);
|
|
170
|
+
for (const p of promises)
|
|
171
|
+
p.reject(err);
|
|
172
|
+
}
|
|
173
|
+
}, 50);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Executes the actual data fetch operation after debounce delay.
|
|
178
|
+
* Updates the loading state, triggers data change callbacks,
|
|
179
|
+
* and handles total item count calculation if pagination is enabled.
|
|
180
|
+
*
|
|
181
|
+
* @returns A promise resolving to the fetched page of data.
|
|
182
|
+
*/
|
|
183
|
+
async _fetchCurrentPageDataDebounced() {
|
|
184
|
+
this._isLoading = true;
|
|
185
|
+
this._dataChangedCallback(this._isLoading, undefined, undefined);
|
|
186
|
+
let abortController;
|
|
187
|
+
let caughtError;
|
|
188
|
+
try {
|
|
189
|
+
// Cancel any in-flight request before starting a new one
|
|
190
|
+
if (this._currentAbortController) {
|
|
191
|
+
try {
|
|
192
|
+
this._currentAbortController.abort();
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Ignore abort errors as they are expected when debouncing
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
abortController = new AbortController();
|
|
199
|
+
this._currentAbortController = abortController;
|
|
200
|
+
const definition = this._parseOptionsToDefinition();
|
|
201
|
+
const { result, metadata } = await this._readClient.readSelectedAsync(definition, abortController?.signal);
|
|
202
|
+
this._pageData = result;
|
|
203
|
+
if (this._currentOptions.pagination.useTotalItemCount && metadata.totalCount !== undefined) {
|
|
204
|
+
this._totalItemCount = metadata.totalCount;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const pageSize = this._currentOptions.pagination.pageSize;
|
|
208
|
+
const currentPageNumber = this._currentOptions.pagination.pageNumber;
|
|
209
|
+
const itemsOnCurrentPageCount = this._pageData.length;
|
|
210
|
+
this._totalItemCount = this._calculateTotal(pageSize, currentPageNumber, itemsOnCurrentPageCount, this._totalItemCount);
|
|
211
|
+
}
|
|
212
|
+
return this._pageData;
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
caughtError = error;
|
|
216
|
+
// Do not log abort-related errors as they are expected when debouncing
|
|
217
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
218
|
+
return Promise.reject(error);
|
|
219
|
+
}
|
|
220
|
+
console.error('Error fetching data:', error);
|
|
221
|
+
return Promise.reject(error);
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
this._isLoading = false;
|
|
225
|
+
let errorName;
|
|
226
|
+
if (caughtError && !(caughtError instanceof DOMException && caughtError.name === 'AbortError')) {
|
|
227
|
+
errorName = caughtError instanceof Error ? caughtError.name : undefined;
|
|
228
|
+
}
|
|
229
|
+
this._dataChangedCallback(this._isLoading, this._pageData, errorName);
|
|
230
|
+
// Clear the abort controller only if it belongs to this request (avoid racing with a newer one)
|
|
231
|
+
if (abortController && this._currentAbortController === abortController) {
|
|
232
|
+
this._currentAbortController = undefined;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Takes current `CollectionViewAdapterOptions` and parses to `ReadSelectedDefinitionDto` which the cornerstone API expects.
|
|
238
|
+
*
|
|
239
|
+
* @returns - `ReadSelectedDefinitionDto` object
|
|
240
|
+
*/
|
|
241
|
+
_parseOptionsToDefinition() {
|
|
242
|
+
const definition = {
|
|
243
|
+
paginationDefinition: {
|
|
244
|
+
skip: (this._currentOptions.pagination.pageNumber - 1) * this._currentOptions.pagination.pageSize,
|
|
245
|
+
limit: this._currentOptions.pagination.pageSize,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
// Apply sorting
|
|
249
|
+
const sortPaths = this._currentOptions.sorting.sortByPath;
|
|
250
|
+
const sortDirection = this._currentOptions.sorting.sortDirection;
|
|
251
|
+
if (sortPaths && sortPaths.length > 0) {
|
|
252
|
+
definition.orderingDefinition = {
|
|
253
|
+
order: [{ propertyPath: sortPaths.map(path => toPascalCase(path)), direction: sortDirection ?? ReadSelectedOrderingDirection.Ascending }],
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// Apply search
|
|
257
|
+
definition.searchDefinition = this._currentOptions.searching.searchDefinition;
|
|
258
|
+
return definition;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Calculates the total item count given pagination inputs.
|
|
262
|
+
*
|
|
263
|
+
* For example, with 10 items per page, on page 2 with 3 items, total = 13.
|
|
264
|
+
* If the total cannot be inferred (e.g., a full page is returned), the previous total is returned unchanged.
|
|
265
|
+
*
|
|
266
|
+
* @param pageSize - Number of items per page
|
|
267
|
+
* @param currentPageNumber - Current page number (1-based)
|
|
268
|
+
* @param itemsOnCurrentPageCount - Number of items in the fetched page
|
|
269
|
+
* @param previousTotal - Previous known total to preserve when indeterminate
|
|
270
|
+
* @returns The computed total item count
|
|
271
|
+
*/
|
|
272
|
+
_calculateTotal(pageSize, currentPageNumber, itemsOnCurrentPageCount, previousTotal) {
|
|
273
|
+
if (itemsOnCurrentPageCount < pageSize && itemsOnCurrentPageCount !== 0) {
|
|
274
|
+
return pageSize * (currentPageNumber - 1) + itemsOnCurrentPageCount;
|
|
275
|
+
}
|
|
276
|
+
else if (itemsOnCurrentPageCount === 0 && pageSize === 0) {
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
return previousTotal;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CollectionViewAdapter';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CollectionViewAdapter';
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReadResultMetadataDto } from './ReadResultMetadataDto';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a read result containing a collection of results and metadata.
|
|
4
|
+
*
|
|
5
|
+
* @template T The type of result in the collection.
|
|
6
|
+
* @property {T[]} items - The collection of results returned.
|
|
7
|
+
* @property {ReadResultMetadata} metadata - Metadata about the paginated result.
|
|
8
|
+
*/
|
|
9
|
+
export type ReadResultDto<T> = {
|
|
10
|
+
results: T[];
|
|
11
|
+
metadata: ReadResultMetadataDto;
|
|
12
|
+
};
|
|
File without changes
|