@intellegens/cornerstone-client 0.0.44 → 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.
Files changed (132) hide show
  1. package/LICENSE.md +0 -0
  2. package/README.md +133 -12
  3. package/adapters/CollectionViewAdapter/index.d.ts +154 -0
  4. package/adapters/CollectionViewAdapter/index.js +281 -0
  5. package/adapters/index.d.ts +1 -0
  6. package/adapters/index.js +1 -0
  7. package/data/api/dto/PropertyPathDto.d.ts +4 -0
  8. package/data/api/dto/{crud/CrudMetadata.js → PropertyPathDto.js} +0 -0
  9. package/data/api/dto/ReadOptionsDto.d.ts +8 -0
  10. package/data/api/dto/{read/ReadMetadata.js → ReadOptionsDto.js} +0 -0
  11. package/data/api/dto/ReadResultDto.d.ts +12 -0
  12. package/data/api/dto/{response/ApiError.js → ReadResultDto.js} +0 -0
  13. package/data/api/dto/ReadResultMetadataDto.d.ts +8 -0
  14. package/data/api/dto/{response/ApiResponse.js → ReadResultMetadataDto.js} +0 -0
  15. package/data/api/dto/crud/{CrudMetadata.d.ts → CrudMetadataDto.d.ts} +1 -1
  16. package/data/api/dto/{response/ApiSuccessResponse.js → crud/CrudMetadataDto.js} +0 -0
  17. package/data/api/dto/crud/index.d.ts +1 -1
  18. package/data/api/dto/crud/index.js +1 -1
  19. package/data/api/dto/index.d.ts +1 -0
  20. package/data/api/dto/index.js +1 -0
  21. package/data/api/dto/read/{ReadMetadata.d.ts → ReadMetadataDto.d.ts} +1 -1
  22. package/data/api/dto/{response/EmptyMetadata.js → read/ReadMetadataDto.js} +0 -0
  23. package/data/api/dto/read/ReadSelectedDefinitionDto.d.ts +0 -0
  24. package/data/api/dto/read/ReadSelectedDefinitionDto.js +0 -0
  25. package/data/api/dto/read/ReadSelectedOrderingDefinitionDto.d.ts +0 -0
  26. package/data/api/dto/read/ReadSelectedOrderingDefinitionDto.js +0 -0
  27. package/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.d.ts +2 -2
  28. package/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.js +0 -0
  29. package/data/api/dto/read/ReadSelectedPaginationDefinitionDto.d.ts +0 -0
  30. package/data/api/dto/read/ReadSelectedPaginationDefinitionDto.js +0 -0
  31. package/data/api/dto/read/ReadSelectedSearchDefinitionDto.d.ts +0 -0
  32. package/data/api/dto/read/ReadSelectedSearchDefinitionDto.js +0 -0
  33. package/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.d.ts +2 -2
  34. package/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.js +0 -0
  35. package/data/api/dto/read/index.d.ts +1 -1
  36. package/data/api/dto/read/index.js +1 -1
  37. package/data/api/dto/response/{ApiError.d.ts → ApiErrorDto.d.ts} +1 -1
  38. package/data/api/dto/response/ApiErrorDto.js +1 -0
  39. package/data/api/dto/response/{ApiResponse.d.ts → ApiResponseDto.d.ts} +3 -3
  40. package/data/api/dto/response/ApiResponseDto.js +1 -0
  41. package/data/api/dto/response/{ApiSuccessResponse.d.ts → ApiSuccessResponseDto.d.ts} +1 -1
  42. package/data/api/dto/response/ApiSuccessResponseDto.js +1 -0
  43. package/data/api/dto/response/EmptyMetadataDto.d.ts +4 -0
  44. package/data/api/dto/response/EmptyMetadataDto.js +1 -0
  45. package/data/api/dto/response/index.d.ts +4 -4
  46. package/data/api/dto/response/index.js +4 -4
  47. package/data/api/enum/index.d.ts +0 -0
  48. package/data/api/enum/index.js +0 -0
  49. package/data/api/enum/read/ReadSelectedComparisonOperator.d.ts +0 -0
  50. package/data/api/enum/read/ReadSelectedComparisonOperator.js +0 -0
  51. package/data/api/enum/read/ReadSelectedLogicalOperator.d.ts +0 -0
  52. package/data/api/enum/read/ReadSelectedLogicalOperator.js +0 -0
  53. package/data/api/enum/read/ReadSelectedOrderingDirection.d.ts +0 -0
  54. package/data/api/enum/read/ReadSelectedOrderingDirection.js +0 -0
  55. package/data/api/enum/read/ReadSelectedPropertyType.d.ts +0 -0
  56. package/data/api/enum/read/ReadSelectedPropertyType.js +0 -0
  57. package/data/api/enum/read/index.d.ts +0 -0
  58. package/data/api/enum/read/index.js +0 -0
  59. package/data/api/enum/response/ApiErrorCodes.d.ts +0 -0
  60. package/data/api/enum/response/ApiErrorCodes.js +0 -0
  61. package/data/api/enum/response/index.d.ts +0 -0
  62. package/data/api/enum/response/index.js +0 -0
  63. package/data/api/index.d.ts +0 -0
  64. package/data/api/index.js +0 -0
  65. package/data/api/interface/IConcurrencySafe.d.ts +9 -0
  66. package/data/api/interface/IConcurrencySafe.js +2 -0
  67. package/data/api/interface/IIdentifiable.d.ts +4 -3
  68. package/data/api/interface/IIdentifiable.js +0 -0
  69. package/data/api/interface/IIdentifiableSecondary.d.ts +3 -3
  70. package/data/api/interface/IIdentifiableSecondary.js +0 -0
  71. package/data/api/interface/index.d.ts +1 -0
  72. package/data/api/interface/index.js +1 -0
  73. package/data/auth/dto/ClaimDto.d.ts +0 -0
  74. package/data/auth/dto/ClaimDto.js +0 -0
  75. package/data/auth/dto/RegisterRequestDto.d.ts +0 -0
  76. package/data/auth/dto/RegisterRequestDto.js +0 -0
  77. package/data/auth/dto/RoleDto.d.ts +0 -0
  78. package/data/auth/dto/RoleDto.js +0 -0
  79. package/data/auth/dto/SignInRequestDto.d.ts +0 -0
  80. package/data/auth/dto/SignInRequestDto.js +0 -0
  81. package/data/auth/dto/TokensDto.d.ts +0 -0
  82. package/data/auth/dto/TokensDto.js +0 -0
  83. package/data/auth/dto/UserDto.d.ts +0 -0
  84. package/data/auth/dto/UserDto.js +0 -0
  85. package/data/auth/dto/UserInfoDto.d.ts +0 -0
  86. package/data/auth/dto/UserInfoDto.js +0 -0
  87. package/data/auth/dto/index.d.ts +0 -0
  88. package/data/auth/dto/index.js +0 -0
  89. package/data/auth/index.d.ts +0 -0
  90. package/data/auth/index.js +0 -0
  91. package/data/index.d.ts +0 -0
  92. package/data/index.js +0 -0
  93. package/index.d.ts +3 -0
  94. package/index.js +3 -0
  95. package/package.json +15 -10
  96. package/services/api/ApiCrudControllerClient/index.d.ts +9 -5
  97. package/services/api/ApiCrudControllerClient/index.js +15 -9
  98. package/services/api/ApiInitializationService/index.d.ts +21 -3
  99. package/services/api/ApiInitializationService/index.js +36 -7
  100. package/services/api/ApiReadControllerClient/index.d.ts +12 -13
  101. package/services/api/ApiReadControllerClient/index.js +17 -18
  102. package/services/api/HttpService/FetchHttpService.d.ts +1 -1
  103. package/services/api/HttpService/FetchHttpService.js +2 -1
  104. package/services/api/HttpService/HttpRequestConfig.d.ts +1 -0
  105. package/services/api/HttpService/HttpRequestConfig.js +0 -0
  106. package/services/api/HttpService/HttpResponse.d.ts +0 -0
  107. package/services/api/HttpService/HttpResponse.js +0 -0
  108. package/services/api/HttpService/IHttpService.d.ts +1 -1
  109. package/services/api/HttpService/IHttpService.js +0 -0
  110. package/services/api/HttpService/index.d.ts +0 -2
  111. package/services/api/HttpService/index.js +0 -2
  112. package/services/api/UserManagementControllerClient/index.d.ts +9 -5
  113. package/services/api/UserManagementControllerClient/index.js +20 -12
  114. package/services/api/index.d.ts +0 -0
  115. package/services/api/index.js +0 -0
  116. package/services/auth/client/AuthService/index.d.ts +5 -5
  117. package/services/auth/client/AuthService/index.js +9 -9
  118. package/services/auth/client/AuthorizationManagementControllerClient/index.d.ts +5 -5
  119. package/services/auth/client/AuthorizationManagementControllerClient/index.js +8 -8
  120. package/services/auth/client/index.d.ts +0 -0
  121. package/services/auth/client/index.js +0 -0
  122. package/services/auth/index.d.ts +0 -0
  123. package/services/auth/index.js +0 -0
  124. package/services/index.d.ts +0 -0
  125. package/services/index.js +0 -0
  126. package/utils/index.d.ts +37 -0
  127. package/utils/index.js +106 -0
  128. package/data/api/dto/response/EmptyMetadata.d.ts +0 -4
  129. package/services/api/HttpService/AngularHttpService.d.ts +0 -9
  130. package/services/api/HttpService/AngularHttpService.js +0 -86
  131. package/services/api/HttpService/AxiosHttpService.d.ts +0 -10
  132. 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 load API configuration for a Cornerstone client application:
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.initialize();
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.initialize();
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.initialize({
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.getApiUrl('/my/endpoint')}`;
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, Axios, Angular HttpClient) with the same API clients. This abstraction provides consistent behavior across different HTTP implementations while maintaining framework independence.
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, Axios, and Angular HttpClient
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, AxiosHttpService } from '@intellegens/cornerstone-client';
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';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Array of properties that when combined form a path to a property.
3
+ */
4
+ export type PropertyPathDto = string[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Options for configuring read operations.
3
+ *
4
+ * @property {boolean} includeTotalCount - Whether to include the total count of items in the response.
5
+ */
6
+ export type ReadOptionsDto = {
7
+ includeTotalCount: boolean;
8
+ };
@@ -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
+ };