@intellegens/cornerstone-client 0.0.0-experimental-upgrade-20260302-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/README.md +256 -0
- package/demo/index.ts +29 -0
- package/demo/public_html/favicon.ico +0 -0
- package/demo/public_html/index.html +106 -0
- package/demo/public_html/websettings.json +3 -0
- package/dist/adapters/CollectionViewAdapter/index.d.ts +198 -0
- package/dist/adapters/CollectionViewAdapter/index.integration.test.d.ts +1 -0
- package/dist/adapters/CollectionViewAdapter/index.integration.test.js +163 -0
- package/dist/adapters/CollectionViewAdapter/index.js +381 -0
- package/dist/adapters/SearchAdapter/index.d.ts +55 -0
- package/dist/adapters/SearchAdapter/index.js +233 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.js +2 -0
- package/dist/data/api/dto/PropertyPathDto.d.ts +4 -0
- package/dist/data/api/dto/PropertyPathDto.js +1 -0
- package/dist/data/api/dto/ReadOptionsDto.d.ts +8 -0
- package/dist/data/api/dto/ReadOptionsDto.js +1 -0
- package/dist/data/api/dto/ReadResultDto.d.ts +12 -0
- package/dist/data/api/dto/ReadResultDto.js +1 -0
- package/dist/data/api/dto/ReadResultMetadataDto.d.ts +8 -0
- package/dist/data/api/dto/ReadResultMetadataDto.js +1 -0
- package/dist/data/api/dto/crud/CrudMetadataDto.d.ts +4 -0
- package/dist/data/api/dto/crud/CrudMetadataDto.js +1 -0
- package/dist/data/api/dto/crud/index.d.ts +1 -0
- package/dist/data/api/dto/crud/index.js +1 -0
- package/dist/data/api/dto/index.d.ts +4 -0
- package/dist/data/api/dto/index.js +4 -0
- package/dist/data/api/dto/read/ReadMetadataDto.d.ts +8 -0
- package/dist/data/api/dto/read/ReadMetadataDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedDefinitionDto.d.ts +18 -0
- package/dist/data/api/dto/read/ReadSelectedDefinitionDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedNestedCollectionCriteriaDto.d.ts +22 -0
- package/dist/data/api/dto/read/ReadSelectedNestedCollectionCriteriaDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedNestedCriteriaDto.d.ts +18 -0
- package/dist/data/api/dto/read/ReadSelectedNestedCriteriaDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedOrderingDefinitionDto.d.ts +7 -0
- package/dist/data/api/dto/read/ReadSelectedOrderingDefinitionDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.d.ts +14 -0
- package/dist/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedPaginationDefinitionDto.d.ts +13 -0
- package/dist/data/api/dto/read/ReadSelectedPaginationDefinitionDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedSearchDefinitionBuilder.d.ts +167 -0
- package/dist/data/api/dto/read/ReadSelectedSearchDefinitionBuilder.js +267 -0
- package/dist/data/api/dto/read/ReadSelectedSearchDefinitionDto.d.ts +33 -0
- package/dist/data/api/dto/read/ReadSelectedSearchDefinitionDto.js +1 -0
- package/dist/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.d.ts +114 -0
- package/dist/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.js +1 -0
- package/dist/data/api/dto/read/index.d.ts +10 -0
- package/dist/data/api/dto/read/index.js +10 -0
- package/dist/data/api/dto/response/ApiErrorDto.d.ts +17 -0
- package/dist/data/api/dto/response/ApiErrorDto.js +1 -0
- package/dist/data/api/dto/response/ApiErrorResponseDto.d.ts +11 -0
- package/dist/data/api/dto/response/ApiErrorResponseDto.js +1 -0
- package/dist/data/api/dto/response/ApiResponseDto.d.ts +3 -0
- package/dist/data/api/dto/response/ApiResponseDto.js +1 -0
- package/dist/data/api/dto/response/ApiSuccessResponseDto.d.ts +13 -0
- package/dist/data/api/dto/response/ApiSuccessResponseDto.js +1 -0
- package/dist/data/api/dto/response/EmptyMetadataDto.d.ts +4 -0
- package/dist/data/api/dto/response/EmptyMetadataDto.js +1 -0
- package/dist/data/api/dto/response/MetadataDto.d.ts +25 -0
- package/dist/data/api/dto/response/MetadataDto.js +1 -0
- package/dist/data/api/dto/response/index.d.ts +5 -0
- package/dist/data/api/dto/response/index.js +5 -0
- package/dist/data/api/enum/index.d.ts +2 -0
- package/dist/data/api/enum/index.js +2 -0
- package/dist/data/api/enum/read/ReadSelectedCollectionOperator.d.ts +16 -0
- package/dist/data/api/enum/read/ReadSelectedCollectionOperator.js +17 -0
- package/dist/data/api/enum/read/ReadSelectedComparisonOperator.d.ts +69 -0
- package/dist/data/api/enum/read/ReadSelectedComparisonOperator.js +76 -0
- package/dist/data/api/enum/read/ReadSelectedLogicalOperator.d.ts +15 -0
- package/dist/data/api/enum/read/ReadSelectedLogicalOperator.js +16 -0
- package/dist/data/api/enum/read/ReadSelectedOrderingDirection.d.ts +13 -0
- package/dist/data/api/enum/read/ReadSelectedOrderingDirection.js +14 -0
- package/dist/data/api/enum/read/ReadSelectedPropertyType.d.ts +66 -0
- package/dist/data/api/enum/read/ReadSelectedPropertyType.js +75 -0
- package/dist/data/api/enum/read/index.d.ts +5 -0
- package/dist/data/api/enum/read/index.js +5 -0
- package/dist/data/api/enum/response/ApiErrorCodes.d.ts +7 -0
- package/dist/data/api/enum/response/ApiErrorCodes.js +8 -0
- package/dist/data/api/enum/response/ErrorCode.d.ts +13 -0
- package/dist/data/api/enum/response/ErrorCode.js +14 -0
- package/dist/data/api/enum/response/index.d.ts +1 -0
- package/dist/data/api/enum/response/index.js +1 -0
- package/dist/data/api/index.d.ts +3 -0
- package/dist/data/api/index.js +3 -0
- package/dist/data/api/interface/ICommonIdentifiable.d.ts +7 -0
- package/dist/data/api/interface/ICommonIdentifiable.js +1 -0
- package/dist/data/api/interface/IConcurrencySafe.d.ts +9 -0
- package/dist/data/api/interface/IConcurrencySafe.js +2 -0
- package/dist/data/api/interface/IIdentifiable.d.ts +11 -0
- package/dist/data/api/interface/IIdentifiable.js +1 -0
- package/dist/data/api/interface/IIdentifiableSecondary.d.ts +9 -0
- package/dist/data/api/interface/IIdentifiableSecondary.js +1 -0
- package/dist/data/api/interface/index.d.ts +3 -0
- package/dist/data/api/interface/index.js +3 -0
- package/dist/data/auth/dto/ClaimDto.d.ts +4 -0
- package/dist/data/auth/dto/ClaimDto.js +1 -0
- package/dist/data/auth/dto/RegisterRequestDto.d.ts +4 -0
- package/dist/data/auth/dto/RegisterRequestDto.js +1 -0
- package/dist/data/auth/dto/RoleDto.d.ts +5 -0
- package/dist/data/auth/dto/RoleDto.js +1 -0
- package/dist/data/auth/dto/SignInRequestDto.d.ts +4 -0
- package/dist/data/auth/dto/SignInRequestDto.js +1 -0
- package/dist/data/auth/dto/TokensDto.d.ts +4 -0
- package/dist/data/auth/dto/TokensDto.js +1 -0
- package/dist/data/auth/dto/UserDto.d.ts +17 -0
- package/dist/data/auth/dto/UserDto.js +1 -0
- package/dist/data/auth/dto/UserInfoDto.d.ts +14 -0
- package/dist/data/auth/dto/UserInfoDto.js +1 -0
- package/dist/data/auth/dto/index.d.ts +5 -0
- package/dist/data/auth/dto/index.js +5 -0
- package/dist/data/auth/index.d.ts +2 -0
- package/dist/data/auth/index.js +2 -0
- package/dist/data/auth/policy.d.ts +52 -0
- package/dist/data/auth/policy.js +44 -0
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/services/api/ApiCrudControllerClient/index.d.ts +41 -0
- package/dist/services/api/ApiCrudControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/api/ApiCrudControllerClient/index.integration.test.js +34 -0
- package/dist/services/api/ApiCrudControllerClient/index.js +116 -0
- package/dist/services/api/ApiInitializationService/index.d.ts +106 -0
- package/dist/services/api/ApiInitializationService/index.js +208 -0
- package/dist/services/api/ApiReadControllerClient/index.d.ts +40 -0
- package/dist/services/api/ApiReadControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/api/ApiReadControllerClient/index.integration.test.js +59 -0
- package/dist/services/api/ApiReadControllerClient/index.js +111 -0
- package/dist/services/api/HttpService/FetchHttpService.d.ts +7 -0
- package/dist/services/api/HttpService/FetchHttpService.integration.test.d.ts +1 -0
- package/dist/services/api/HttpService/FetchHttpService.integration.test.js +52 -0
- package/dist/services/api/HttpService/FetchHttpService.js +29 -0
- package/dist/services/api/HttpService/HttpRequestConfig.d.ts +10 -0
- package/dist/services/api/HttpService/HttpRequestConfig.js +1 -0
- package/dist/services/api/HttpService/HttpResponse.d.ts +11 -0
- package/dist/services/api/HttpService/HttpResponse.js +3 -0
- package/dist/services/api/HttpService/IHttpService.d.ts +13 -0
- package/dist/services/api/HttpService/IHttpService.js +3 -0
- package/dist/services/api/HttpService/index.d.ts +9 -0
- package/dist/services/api/HttpService/index.js +10 -0
- package/dist/services/api/UserManagementControllerClient/index.d.ts +41 -0
- package/dist/services/api/UserManagementControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/api/UserManagementControllerClient/index.integration.test.js +60 -0
- package/dist/services/api/UserManagementControllerClient/index.js +117 -0
- package/dist/services/api/index.d.ts +5 -0
- package/dist/services/api/index.js +5 -0
- package/dist/services/auth/client/AuthService/index.d.ts +75 -0
- package/dist/services/auth/client/AuthService/index.js +200 -0
- package/dist/services/auth/client/AuthorizationManagementControllerClient/index.d.ts +48 -0
- package/dist/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.d.ts +1 -0
- package/dist/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.js +89 -0
- package/dist/services/auth/client/AuthorizationManagementControllerClient/index.js +148 -0
- package/dist/services/auth/client/index.d.ts +2 -0
- package/dist/services/auth/client/index.js +2 -0
- package/dist/services/auth/index.d.ts +1 -0
- package/dist/services/auth/index.js +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +2 -0
- package/dist/utils/authorization/index.d.ts +17 -0
- package/dist/utils/authorization/index.js +45 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/result/index.d.ts +21 -0
- package/dist/utils/result/index.js +16 -0
- package/dist/utils/search/index.d.ts +34 -0
- package/dist/utils/search/index.js +106 -0
- package/package.json +45 -0
- package/src/adapters/CollectionViewAdapter/index.integration.test.ts +197 -0
- package/src/adapters/CollectionViewAdapter/index.ts +477 -0
- package/src/adapters/SearchAdapter/index.ts +302 -0
- package/src/adapters/index.ts +2 -0
- package/src/data/api/dto/PropertyPathDto.ts +4 -0
- package/src/data/api/dto/ReadOptionsDto.ts +8 -0
- package/src/data/api/dto/ReadResultDto.ts +13 -0
- package/src/data/api/dto/ReadResultMetadataDto.ts +8 -0
- package/src/data/api/dto/crud/CrudMetadataDto.ts +4 -0
- package/src/data/api/dto/crud/index.ts +1 -0
- package/src/data/api/dto/index.ts +4 -0
- package/src/data/api/dto/read/ReadMetadataDto.ts +8 -0
- package/src/data/api/dto/read/ReadSelectedDefinitionDto.ts +21 -0
- package/src/data/api/dto/read/ReadSelectedNestedCollectionCriteriaDto.ts +25 -0
- package/src/data/api/dto/read/ReadSelectedNestedCriteriaDto.ts +20 -0
- package/src/data/api/dto/read/ReadSelectedOrderingDefinitionDto.ts +8 -0
- package/src/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.ts +16 -0
- package/src/data/api/dto/read/ReadSelectedPaginationDefinitionDto.ts +13 -0
- package/src/data/api/dto/read/ReadSelectedSearchDefinitionBuilder.ts +348 -0
- package/src/data/api/dto/read/ReadSelectedSearchDefinitionDto.ts +43 -0
- package/src/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.ts +186 -0
- package/src/data/api/dto/read/index.ts +10 -0
- package/src/data/api/dto/response/ApiErrorDto.ts +21 -0
- package/src/data/api/dto/response/ApiErrorResponseDto.ts +13 -0
- package/src/data/api/dto/response/ApiResponseDto.ts +7 -0
- package/src/data/api/dto/response/ApiSuccessResponseDto.ts +13 -0
- package/src/data/api/dto/response/MetadataDto.ts +24 -0
- package/src/data/api/dto/response/index.ts +5 -0
- package/src/data/api/enum/index.ts +2 -0
- package/src/data/api/enum/read/ReadSelectedCollectionOperator.ts +17 -0
- package/src/data/api/enum/read/ReadSelectedComparisonOperator.ts +96 -0
- package/src/data/api/enum/read/ReadSelectedLogicalOperator.ts +16 -0
- package/src/data/api/enum/read/ReadSelectedOrderingDirection.ts +13 -0
- package/src/data/api/enum/read/ReadSelectedPropertyType.ts +96 -0
- package/src/data/api/enum/read/index.ts +5 -0
- package/src/data/api/enum/response/ErrorCode.ts +13 -0
- package/src/data/api/enum/response/index.ts +1 -0
- package/src/data/api/index.ts +3 -0
- package/src/data/api/interface/ICommonIdentifiable.ts +9 -0
- package/src/data/api/interface/IConcurrencySafe.ts +9 -0
- package/src/data/api/interface/IIdentifiable.ts +12 -0
- package/src/data/api/interface/IIdentifiableSecondary.ts +9 -0
- package/src/data/api/interface/index.ts +3 -0
- package/src/data/auth/dto/ClaimDto.ts +4 -0
- package/src/data/auth/dto/RegisterRequestDto.ts +4 -0
- package/src/data/auth/dto/RoleDto.ts +6 -0
- package/src/data/auth/dto/SignInRequestDto.ts +4 -0
- package/src/data/auth/dto/TokensDto.ts +4 -0
- package/src/data/auth/dto/UserDto.ts +18 -0
- package/src/data/auth/dto/UserInfoDto.ts +15 -0
- package/src/data/auth/dto/index.ts +5 -0
- package/src/data/auth/index.ts +2 -0
- package/src/data/auth/policy.ts +63 -0
- package/src/data/index.ts +2 -0
- package/src/index.ts +4 -0
- package/src/services/api/ApiCrudControllerClient/index.integration.test.ts +46 -0
- package/src/services/api/ApiCrudControllerClient/index.ts +135 -0
- package/src/services/api/ApiInitializationService/index.ts +254 -0
- package/src/services/api/ApiReadControllerClient/index.integration.test.ts +71 -0
- package/src/services/api/ApiReadControllerClient/index.ts +137 -0
- package/src/services/api/HttpService/FetchHttpService.integration.test.ts +65 -0
- package/src/services/api/HttpService/FetchHttpService.ts +34 -0
- package/src/services/api/HttpService/HttpRequestConfig.ts +10 -0
- package/src/services/api/HttpService/HttpResponse.ts +14 -0
- package/src/services/api/HttpService/IHttpService.ts +17 -0
- package/src/services/api/HttpService/README.md +106 -0
- package/src/services/api/HttpService/index.ts +12 -0
- package/src/services/api/UserManagementControllerClient/index.integration.test.ts +69 -0
- package/src/services/api/UserManagementControllerClient/index.ts +134 -0
- package/src/services/api/index.ts +5 -0
- package/src/services/auth/client/AuthService/index.ts +233 -0
- package/src/services/auth/client/AuthorizationManagementControllerClient/index.integration.test.ts +110 -0
- package/src/services/auth/client/AuthorizationManagementControllerClient/index.ts +165 -0
- package/src/services/auth/client/index.ts +2 -0
- package/src/services/auth/index.ts +1 -0
- package/src/services/index.ts +2 -0
- package/src/utils/authorization/index.ts +47 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/result/index.ts +25 -0
- package/src/utils/search/index.ts +150 -0
- package/tsconfig.json +19 -0
- package/vitest-setup.ts +43 -0
- package/vitest.config.ts +59 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// websettings.json configuration file contents type
|
|
2
|
+
import { fail, ok } from '@utils';
|
|
3
|
+
|
|
4
|
+
type WebSettings = {
|
|
5
|
+
api: string | undefined;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generic shared settings interface - can represent any user-configured settings structure
|
|
10
|
+
* The actual properties depend on what users configure in their appsettings.json or environment variables
|
|
11
|
+
*/
|
|
12
|
+
type ISharedSettings = {
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Initialization info DTO that matches the server-side structure
|
|
18
|
+
*/
|
|
19
|
+
type IInitializationInfoDto<TSharedSettings = ISharedSettings> = {
|
|
20
|
+
appSettings: TSharedSettings;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
import { ApiErrorResponseDto, ApiSuccessResponseDto, EmptyMetadataDto } from '@/data';
|
|
24
|
+
import { defaultHttpService, IHttpService } from '../HttpService';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Central API configuration service, fetches and exposes Cornerstone API configuration
|
|
28
|
+
*
|
|
29
|
+
* @export
|
|
30
|
+
* @class ApiInitializationService
|
|
31
|
+
*/
|
|
32
|
+
export class ApiInitializationService {
|
|
33
|
+
/**
|
|
34
|
+
* If API base URL detection was completed
|
|
35
|
+
* IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
|
|
36
|
+
*/
|
|
37
|
+
public _apiBaseUrlDetected = false;
|
|
38
|
+
|
|
39
|
+
// #region HTTP service
|
|
40
|
+
/**
|
|
41
|
+
* If API base URL detection was completed, this property will hold the method by which detection was performed
|
|
42
|
+
* IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
|
|
43
|
+
*/
|
|
44
|
+
public _apiBaseUrlDetectionMethod: string | undefined = undefined;
|
|
45
|
+
/**
|
|
46
|
+
* If API base URL detection was completed, this property will hold the detected API base URL
|
|
47
|
+
* IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
|
|
48
|
+
*/
|
|
49
|
+
public _apiBaseUrl: string | undefined = undefined;
|
|
50
|
+
|
|
51
|
+
// #endregion
|
|
52
|
+
|
|
53
|
+
// #region API Base URL detection
|
|
54
|
+
/**
|
|
55
|
+
* If API base URL detection has failed, this property will hold the error with which it has failed
|
|
56
|
+
* IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
|
|
57
|
+
*/
|
|
58
|
+
public _apiBaseError: Error | undefined = undefined;
|
|
59
|
+
// #region config App
|
|
60
|
+
public appConfig?: ISharedSettings | undefined;
|
|
61
|
+
private _httpService: IHttpService = defaultHttpService;
|
|
62
|
+
private _apiBaseUrlPromise: Promise<string | undefined> | undefined = undefined;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Initializes the API configuration
|
|
66
|
+
*
|
|
67
|
+
* This method calls all methods that need to be called during the API configuration phase:
|
|
68
|
+
* - Starts detection of API base URL.
|
|
69
|
+
* - Fetches shared settings from the server.
|
|
70
|
+
*
|
|
71
|
+
* @async
|
|
72
|
+
* @param httpService - HTTP service implementation to use for requests
|
|
73
|
+
* @param {AbortSignal} [signal] - Optional cancellation signal
|
|
74
|
+
* @return {Promise<void>} Resolves when initialization is complete.
|
|
75
|
+
*/
|
|
76
|
+
public async initialize({
|
|
77
|
+
httpService,
|
|
78
|
+
signal,
|
|
79
|
+
}: {
|
|
80
|
+
httpService?: IHttpService;
|
|
81
|
+
signal?: AbortSignal;
|
|
82
|
+
} = {}): Promise<void> {
|
|
83
|
+
// Store configuration
|
|
84
|
+
if (httpService !== undefined) this._httpService = httpService;
|
|
85
|
+
// (Pre)detect API base URL
|
|
86
|
+
await this._getApiBaseUrl(signal);
|
|
87
|
+
// Initialize app config
|
|
88
|
+
await this._getAppConfig();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Gets configured HTTP service
|
|
93
|
+
* @returns
|
|
94
|
+
*/
|
|
95
|
+
public _getHttpService(): IHttpService {
|
|
96
|
+
return this._httpService;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Tries detecting API base URL by multiple methods
|
|
101
|
+
*
|
|
102
|
+
* - Checks the response headers of the current URL for the `CORNERSTONE-API-BASEURL` header.
|
|
103
|
+
* - Attempts to load the `websettings.json` file and looks for an `api` field.
|
|
104
|
+
* - Defaults to `undefined`.
|
|
105
|
+
*
|
|
106
|
+
* IMPORTANT: This method is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
|
|
107
|
+
*
|
|
108
|
+
* @async
|
|
109
|
+
* @param {AbortSignal} [signal] - Optional cancellation signal
|
|
110
|
+
* @return {Promise<string | undefined>} Returns detected API base URL
|
|
111
|
+
* @throws {Error} Throws error if either of the detection methods fails
|
|
112
|
+
*/
|
|
113
|
+
public async _getApiBaseUrl(signal?: AbortSignal): Promise<string | undefined> {
|
|
114
|
+
// Check if API already detected; don't reattempt detection
|
|
115
|
+
if (this._apiBaseUrlDetected) return this._apiBaseUrl;
|
|
116
|
+
|
|
117
|
+
// Check if detection already in progress; don't allow multiple detections in parallel
|
|
118
|
+
if (this._apiBaseUrlPromise) return this._apiBaseUrlPromise;
|
|
119
|
+
|
|
120
|
+
// Detect API base URL
|
|
121
|
+
return (this._apiBaseUrlPromise = new Promise<string | undefined>((resolve, reject) =>
|
|
122
|
+
(async () => {
|
|
123
|
+
// Reset
|
|
124
|
+
this._apiBaseUrlDetected = false;
|
|
125
|
+
this._apiBaseUrlDetectionMethod = undefined;
|
|
126
|
+
this._apiBaseUrl = undefined;
|
|
127
|
+
this._apiBaseError = undefined;
|
|
128
|
+
|
|
129
|
+
// Check CORNERSTONE-API-BASEURL header for API base URL
|
|
130
|
+
try {
|
|
131
|
+
const currentUrlResponse = await this._httpService.request(window.location.href, {
|
|
132
|
+
method: 'GET',
|
|
133
|
+
signal,
|
|
134
|
+
});
|
|
135
|
+
const apiHeader: string | null =
|
|
136
|
+
currentUrlResponse.headers['CORNERSTONE-API-BASEURL'] || currentUrlResponse.headers['cornerstone-api-baseurl'] || null;
|
|
137
|
+
if (apiHeader) {
|
|
138
|
+
this._apiBaseUrlDetected = true;
|
|
139
|
+
this._apiBaseUrlDetectionMethod = 'GET / Header:CORNERSTONE-API-BASEURL';
|
|
140
|
+
this._apiBaseUrl = apiHeader;
|
|
141
|
+
this._apiBaseError = undefined;
|
|
142
|
+
this._apiBaseUrlPromise = undefined;
|
|
143
|
+
return resolve(this._apiBaseUrl);
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from response header!');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check ./websettings.json header for API base URL
|
|
150
|
+
try {
|
|
151
|
+
const webSettingsResponse = await this._httpService.request('./websettings.json', {
|
|
152
|
+
method: 'GET',
|
|
153
|
+
signal,
|
|
154
|
+
});
|
|
155
|
+
if (webSettingsResponse.status === 404) return (this._apiBaseUrl = undefined);
|
|
156
|
+
const webSettings: WebSettings = await webSettingsResponse.json();
|
|
157
|
+
if (webSettings?.api) {
|
|
158
|
+
this._apiBaseUrlDetected = true;
|
|
159
|
+
this._apiBaseUrlDetectionMethod = 'GET websettings.json';
|
|
160
|
+
this._apiBaseUrl = webSettings.api;
|
|
161
|
+
this._apiBaseError = undefined;
|
|
162
|
+
this._apiBaseUrlPromise = undefined;
|
|
163
|
+
return resolve(this._apiBaseUrl);
|
|
164
|
+
}
|
|
165
|
+
} catch (err) {
|
|
166
|
+
this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from settings file!');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check if error caught during any of the detection methods
|
|
170
|
+
if (this._apiBaseError !== undefined) {
|
|
171
|
+
this._apiBaseUrlDetected = false;
|
|
172
|
+
this._apiBaseUrlDetectionMethod = undefined;
|
|
173
|
+
this._apiBaseUrl = undefined;
|
|
174
|
+
this._apiBaseUrlPromise = undefined;
|
|
175
|
+
return reject(this._apiBaseError);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Default to no API found
|
|
179
|
+
this._apiBaseUrlDetected = true;
|
|
180
|
+
this._apiBaseUrlDetectionMethod = undefined;
|
|
181
|
+
this._apiBaseUrl = undefined;
|
|
182
|
+
this._apiBaseError = undefined;
|
|
183
|
+
this._apiBaseUrlPromise = undefined;
|
|
184
|
+
return resolve(this._apiBaseUrl);
|
|
185
|
+
})(),
|
|
186
|
+
));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Composes a full API URL for a provided endpoint path.
|
|
191
|
+
*
|
|
192
|
+
* @async
|
|
193
|
+
* @param relativeEndpointPathSections Relative endpoint path sections
|
|
194
|
+
* @return {Promise<string | undefined>} Returns the API endpoint's URL
|
|
195
|
+
* @throws {Error} Throws error if API base URL detection fails
|
|
196
|
+
*/
|
|
197
|
+
public async getApiUrl(...relativeEndpointPathSections: string[]): Promise<string> {
|
|
198
|
+
const apiBaseUrl = await this._getApiBaseUrl();
|
|
199
|
+
const domain = !apiBaseUrl ? '/' : `${this.removeEndsWithSlashChar(apiBaseUrl)}/`;
|
|
200
|
+
const path = relativeEndpointPathSections.map(section => this.removeStartsWithSlashChar(this.removeEndsWithSlashChar(section))).join('/');
|
|
201
|
+
return `${domain}${path}`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Removes starting '/' character
|
|
206
|
+
* @param str String to remove the starting '/' character from
|
|
207
|
+
* @returns String with starting '/' character removed
|
|
208
|
+
*/
|
|
209
|
+
private removeStartsWithSlashChar(str: string): string {
|
|
210
|
+
return !str.startsWith('/') ? str : str.substring(1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// #endregion
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Removes ending '/' character
|
|
217
|
+
* @param str String to remove the ending '/' character from
|
|
218
|
+
* @returns String with ending '/' character removed
|
|
219
|
+
*/
|
|
220
|
+
private removeEndsWithSlashChar(str: string): string {
|
|
221
|
+
return !str.endsWith('/') ? str : str.substring(0, str.length - 1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Fetches initialization info (shared settings) from the server
|
|
226
|
+
* @returns Promise that resolves to shared settings or undefined if the request fails
|
|
227
|
+
*/
|
|
228
|
+
private async _getAppConfig(): Promise<void> {
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch(`${this._apiBaseUrl}/system/init`, { credentials: 'include' });
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
throw fail(await response.json()) as ApiErrorResponseDto;
|
|
233
|
+
}
|
|
234
|
+
const apiResponse = ok<ApiSuccessResponseDto<IInitializationInfoDto, EmptyMetadataDto>, ApiErrorResponseDto>(await response.json());
|
|
235
|
+
|
|
236
|
+
if (!apiResponse.ok) {
|
|
237
|
+
throw apiResponse.error;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Return the settings from the nested structure
|
|
241
|
+
this.appConfig = apiResponse.result.appSettings;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error('Failed to get app config', error);
|
|
244
|
+
this.appConfig = undefined;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// #endregion
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Singleton instance of ApiInitializationService
|
|
253
|
+
*/
|
|
254
|
+
export const apiInitializationService = new ApiInitializationService();
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { ApiReadControllerClient } from '.';
|
|
3
|
+
import { FetchHttpService } from '../HttpService';
|
|
4
|
+
import { ReadSelectedComparisonOperator, ReadSelectedDefinitionDto, ReadSelectedLogicalOperator, ReadSelectedPropertyType } from '@/data';
|
|
5
|
+
|
|
6
|
+
type VehicleSlimDto = { Id: number; Brand: string; Model: string; Year: number; RowVersion: string };
|
|
7
|
+
|
|
8
|
+
describe('ApiReadControllerClient', () => {
|
|
9
|
+
let client: ApiReadControllerClient<number, any, any>;
|
|
10
|
+
let fetchService: FetchHttpService;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
fetchService = new FetchHttpService();
|
|
14
|
+
client = new ApiReadControllerClient('/VehiclesSlim', fetchService);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should use injected HTTP service for readAll', async () => {
|
|
18
|
+
const result = (await client.readAll()) as { ok: boolean; result: any };
|
|
19
|
+
|
|
20
|
+
expect(result.ok).toBe(true);
|
|
21
|
+
expect(result.result).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should use injected HTTP service for readSingle', async () => {
|
|
25
|
+
const testId = 1;
|
|
26
|
+
const result = (await client.readSingle(testId)) as { ok: boolean; result: any };
|
|
27
|
+
|
|
28
|
+
expect(result.ok).toBe(true);
|
|
29
|
+
expect(result.result).toBeDefined();
|
|
30
|
+
expect(typeof result.result).toBe('object');
|
|
31
|
+
expect(result.result.id).toBe(testId);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should use injected HTTP service for readSelected', async () => {
|
|
35
|
+
const definition: ReadSelectedDefinitionDto<VehicleSlimDto> = {
|
|
36
|
+
searchDefinition: {
|
|
37
|
+
searches: [
|
|
38
|
+
{
|
|
39
|
+
propertyCriteria: [
|
|
40
|
+
{
|
|
41
|
+
propertyName: 'Brand',
|
|
42
|
+
comparisonOperator: ReadSelectedComparisonOperator.Equal,
|
|
43
|
+
valueType: ReadSelectedPropertyType.String,
|
|
44
|
+
value: 'Audi',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
propertyName: 'Brand',
|
|
48
|
+
comparisonOperator: ReadSelectedComparisonOperator.Equal,
|
|
49
|
+
valueType: ReadSelectedPropertyType.String,
|
|
50
|
+
value: 'BMW',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
logicalOperator: ReadSelectedLogicalOperator.Or,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
logicalOperator: ReadSelectedLogicalOperator.And,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const result = (await client.readSelected(definition)) as { ok: boolean; result: any };
|
|
61
|
+
|
|
62
|
+
expect(result.ok).toBe(true);
|
|
63
|
+
expect(result.result).toBeDefined();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should handle HTTP errors properly', async () => {
|
|
67
|
+
const invalidClient = new ApiReadControllerClient('/InvalidPath', fetchService);
|
|
68
|
+
const result = await invalidClient.readAll();
|
|
69
|
+
expect(result.ok).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { apiInitializationService } from '../ApiInitializationService';
|
|
2
|
+
import {
|
|
3
|
+
ApiErrorResponseDto,
|
|
4
|
+
ApiResponseDto,
|
|
5
|
+
ApiSuccessResponseDto,
|
|
6
|
+
EmptyMetadataDto,
|
|
7
|
+
ReadMetadataDto,
|
|
8
|
+
ReadSelectedDefinitionDto,
|
|
9
|
+
} from '../../../data/api/dto';
|
|
10
|
+
import { IIdentifiable } from '../../../data/api/interface';
|
|
11
|
+
import { IHttpService } from '../HttpService';
|
|
12
|
+
import { fail, ok } from '@utils/result';
|
|
13
|
+
import { ErrorCode } from '@data';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Base client for read-only API controllers
|
|
17
|
+
*/
|
|
18
|
+
export class ApiReadControllerClient<TKey, TResultDto extends IIdentifiable<TKey>, TSingleResultDto extends IIdentifiable<TKey>> {
|
|
19
|
+
/**
|
|
20
|
+
* Constructor
|
|
21
|
+
* @param baseControllerPath Base path to API controller
|
|
22
|
+
* @param httpService HTTP service implementation to use for requests
|
|
23
|
+
*/
|
|
24
|
+
constructor(
|
|
25
|
+
protected readonly baseControllerPath: string,
|
|
26
|
+
httpService?: IHttpService,
|
|
27
|
+
) {
|
|
28
|
+
this._httpService = httpService;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// #region HTTP service
|
|
32
|
+
private readonly _httpService?: IHttpService = undefined;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gets globally selected HTTP service
|
|
36
|
+
*/
|
|
37
|
+
protected get httpService(): IHttpService {
|
|
38
|
+
return this._httpService || apiInitializationService._getHttpService();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// #endregion
|
|
42
|
+
|
|
43
|
+
// #region API
|
|
44
|
+
/**
|
|
45
|
+
* Fetches all entities from the read controller.
|
|
46
|
+
* @param {AbortSignal} [signal] - Optional cancellation signal
|
|
47
|
+
* @returns {Promise<ApiSuccessResponseDto<TDto>[], ReadMetadataDto>>} List of all entities with metadata
|
|
48
|
+
*/
|
|
49
|
+
public async readAll(signal?: AbortSignal): Promise<ApiResponseDto<TResultDto[], ReadMetadataDto>> {
|
|
50
|
+
try {
|
|
51
|
+
const url = await apiInitializationService.getApiUrl(this.baseControllerPath, `/ReadAll`);
|
|
52
|
+
const res = await this.httpService.request(url, { method: 'GET', credentials: 'include', signal });
|
|
53
|
+
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
return fail<ApiSuccessResponseDto<TResultDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return ok<ApiSuccessResponseDto<TResultDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(err);
|
|
61
|
+
|
|
62
|
+
return fail<ApiSuccessResponseDto<TResultDto[], ReadMetadataDto>, ApiErrorResponseDto>({
|
|
63
|
+
error: {
|
|
64
|
+
code: ErrorCode.UnknownError,
|
|
65
|
+
message: 'Unknown error fetching all records',
|
|
66
|
+
metadata: {} as EmptyMetadataDto,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Fetches selected entities based on a filter definition.
|
|
74
|
+
* @param {any} definition - The filter definition object
|
|
75
|
+
* @param {AbortSignal} [signal] - Optional cancellation signal
|
|
76
|
+
* @returns {Promise<ApiSuccessResponseDto<TDto>[], ReadMetadataDto>>} The result of the read operation with metadata
|
|
77
|
+
*/
|
|
78
|
+
public async readSelected(definition: ReadSelectedDefinitionDto<TResultDto>, signal?: AbortSignal): Promise<ApiResponseDto<TResultDto[], ReadMetadataDto>> {
|
|
79
|
+
try {
|
|
80
|
+
const url = await apiInitializationService.getApiUrl(this.baseControllerPath, `/ReadSelected`);
|
|
81
|
+
const res = await this.httpService.request(url, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: { 'Content-Type': 'application/json' },
|
|
84
|
+
body: JSON.stringify(definition),
|
|
85
|
+
credentials: 'include',
|
|
86
|
+
signal,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (!res.ok) {
|
|
90
|
+
return fail<ApiSuccessResponseDto<TResultDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return ok<ApiSuccessResponseDto<TResultDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error(err);
|
|
96
|
+
|
|
97
|
+
return fail<ApiSuccessResponseDto<TResultDto[], ReadMetadataDto>, ApiErrorResponseDto>({
|
|
98
|
+
error: {
|
|
99
|
+
code: ErrorCode.UnknownError,
|
|
100
|
+
message: 'Unknown error fetching selected records',
|
|
101
|
+
metadata: {} as EmptyMetadataDto,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fetches a single entity by its ID.
|
|
109
|
+
* @param {TKey} id - The ID of the entity
|
|
110
|
+
* @param {AbortSignal} [signal] - Optional cancellation signal
|
|
111
|
+
* @returns {Promise<ApiSuccessResponseDto<TDto, EmptyMetadataDto>>} The requested entity
|
|
112
|
+
*/
|
|
113
|
+
public async readSingle(id: TKey, signal?: AbortSignal): Promise<ApiResponseDto<TSingleResultDto, EmptyMetadataDto>> {
|
|
114
|
+
try {
|
|
115
|
+
const url = await apiInitializationService.getApiUrl(this.baseControllerPath, `/ReadSingle?id=${encodeURIComponent(String(id))}`);
|
|
116
|
+
const res = await this.httpService.request(url, { method: 'GET', credentials: 'include', signal });
|
|
117
|
+
|
|
118
|
+
if (!res.ok) {
|
|
119
|
+
return fail<ApiSuccessResponseDto<TSingleResultDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return ok<ApiSuccessResponseDto<TSingleResultDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.error(err);
|
|
125
|
+
|
|
126
|
+
return fail<ApiSuccessResponseDto<TSingleResultDto, EmptyMetadataDto>, ApiErrorResponseDto>({
|
|
127
|
+
error: {
|
|
128
|
+
code: ErrorCode.UnknownError,
|
|
129
|
+
message: `Unknown error fetching record with id ${id}`,
|
|
130
|
+
metadata: {} as EmptyMetadataDto,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// #endregion
|
|
137
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { FetchHttpService } from './FetchHttpService';
|
|
3
|
+
|
|
4
|
+
const baseUrl = 'http://localhost:5000/api';
|
|
5
|
+
const credentials = {
|
|
6
|
+
username: 'admin@test.com',
|
|
7
|
+
password: 'Password1234!',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe('FetchHttpService', () => {
|
|
11
|
+
let fetchService: FetchHttpService;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
fetchService = new FetchHttpService();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should make a GET request successfully', async () => {
|
|
18
|
+
const result = await fetchService.request(`${baseUrl}/AnythingManuallyMappedRead/ReadAll`);
|
|
19
|
+
|
|
20
|
+
expect(result.ok).toBe(true);
|
|
21
|
+
expect(result.status).toBe(200);
|
|
22
|
+
expect(result.statusText).toBe('OK');
|
|
23
|
+
|
|
24
|
+
const jsonData = await result.json();
|
|
25
|
+
expect(jsonData.result).toBeDefined();
|
|
26
|
+
expect(jsonData.error).toBeNull();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should make a POST request with body', async () => {
|
|
30
|
+
const result = await fetchService.request(`${baseUrl}/auth/signin`, {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: { 'Content-Type': 'application/json' },
|
|
33
|
+
body: JSON.stringify(credentials),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(result.ok).toBe(true);
|
|
37
|
+
expect(result.status).toBe(204);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should handle non-ok responses', async () => {
|
|
41
|
+
const result = await fetchService.request(`${baseUrl}/auth/signin`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: { 'Content-Type': 'application/json' },
|
|
44
|
+
body: JSON.stringify({ username: credentials.username, password: `${credentials.password}wrong` }),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(result.ok).toBe(false);
|
|
48
|
+
expect(result.status).toBe(400);
|
|
49
|
+
expect(result.statusText).toBe('Bad Request');
|
|
50
|
+
|
|
51
|
+
const jsonData = await result.json();
|
|
52
|
+
expect(jsonData.result).toBeNull();
|
|
53
|
+
expect(jsonData.error).toBeDefined();
|
|
54
|
+
expect(jsonData.error.code).toBe(400);
|
|
55
|
+
expect(jsonData.error.message).toBe('Invalid email or password');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should handle fetch errors from backend', async () => {
|
|
59
|
+
const result = await fetchService.request('http://localhost:5000/api/error');
|
|
60
|
+
|
|
61
|
+
expect(result.ok).toBe(false);
|
|
62
|
+
expect(result.status).toBe(404);
|
|
63
|
+
expect(result.statusText).toBe('Not Found');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Ignore eslint any error in this file
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
|
|
4
|
+
import { HttpRequestConfig, HttpResponse, IHttpService } from '@/services';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default HTTP service implementation using the Fetch API
|
|
8
|
+
*/
|
|
9
|
+
export class FetchHttpService implements IHttpService {
|
|
10
|
+
public async request<T = any>(url: string, config?: HttpRequestConfig): Promise<HttpResponse<T>> {
|
|
11
|
+
const response = await fetch(url, {
|
|
12
|
+
method: config?.method || 'GET',
|
|
13
|
+
headers: config?.headers,
|
|
14
|
+
body: config?.body,
|
|
15
|
+
credentials: config?.credentials || 'include',
|
|
16
|
+
signal: config?.signal,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Convert Headers to a plain object
|
|
20
|
+
const headers: Record<string, string> = {};
|
|
21
|
+
response.headers.forEach((value, key) => {
|
|
22
|
+
headers[key] = value;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
ok: response.ok,
|
|
27
|
+
status: response.status,
|
|
28
|
+
statusText: response.statusText,
|
|
29
|
+
headers,
|
|
30
|
+
json: () => response.json(),
|
|
31
|
+
text: () => response.text(),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP request configuration interface
|
|
3
|
+
*/
|
|
4
|
+
export interface HttpRequestConfig {
|
|
5
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
body?: string;
|
|
8
|
+
credentials?: RequestCredentials;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Ignore eslint any error in this file
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HTTP response interface
|
|
6
|
+
*/
|
|
7
|
+
export interface HttpResponse<T = any> {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
status: number;
|
|
10
|
+
statusText: string;
|
|
11
|
+
headers: Record<string, string>;
|
|
12
|
+
json(): Promise<T>;
|
|
13
|
+
text(): Promise<string>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Ignore eslint any error in this file
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
|
|
4
|
+
import { HttpRequestConfig, HttpResponse } from '@/services';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Abstract HTTP service interface that can be implemented by different HTTP clients
|
|
8
|
+
*/
|
|
9
|
+
export interface IHttpService {
|
|
10
|
+
/**
|
|
11
|
+
* Performs an HTTP request
|
|
12
|
+
* @param url - The URL to make the request to
|
|
13
|
+
* @param config - Request configuration
|
|
14
|
+
* @returns Promise that resolves to the HTTP response
|
|
15
|
+
*/
|
|
16
|
+
request<T = any>(url: string, config?: HttpRequestConfig): Promise<HttpResponse<T>>;
|
|
17
|
+
}
|