@intellegens/cornerstone-client 0.0.9999-alpha-8 → 0.0.9999-alpha-10

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 (227) hide show
  1. package/demo/index.ts +29 -0
  2. package/demo/public_html/favicon.ico +0 -0
  3. package/demo/public_html/index.html +106 -0
  4. package/demo/public_html/websettings.json +3 -0
  5. package/{adapters → dist/adapters}/CollectionViewAdapter/index.d.ts +0 -0
  6. package/{adapters → dist/adapters}/CollectionViewAdapter/index.js +0 -0
  7. package/{adapters → dist/adapters}/index.d.ts +0 -0
  8. package/{adapters → dist/adapters}/index.js +0 -0
  9. package/{data → dist/data}/api/dto/PropertyPathDto.d.ts +0 -0
  10. package/{data → dist/data}/api/dto/PropertyPathDto.js +0 -0
  11. package/{data → dist/data}/api/dto/ReadOptionsDto.d.ts +0 -0
  12. package/{data → dist/data}/api/dto/ReadOptionsDto.js +0 -0
  13. package/{data → dist/data}/api/dto/ReadResultDto.d.ts +0 -0
  14. package/{data → dist/data}/api/dto/ReadResultDto.js +0 -0
  15. package/{data → dist/data}/api/dto/ReadResultMetadataDto.d.ts +0 -0
  16. package/{data → dist/data}/api/dto/ReadResultMetadataDto.js +0 -0
  17. package/{data → dist/data}/api/dto/crud/CrudMetadataDto.d.ts +0 -0
  18. package/{data → dist/data}/api/dto/crud/CrudMetadataDto.js +0 -0
  19. package/{data → dist/data}/api/dto/crud/index.d.ts +0 -0
  20. package/{data → dist/data}/api/dto/crud/index.js +0 -0
  21. package/{data → dist/data}/api/dto/index.d.ts +0 -0
  22. package/{data → dist/data}/api/dto/index.js +0 -0
  23. package/{data → dist/data}/api/dto/read/ReadMetadataDto.d.ts +0 -0
  24. package/{data → dist/data}/api/dto/read/ReadMetadataDto.js +0 -0
  25. package/{data → dist/data}/api/dto/read/ReadSelectedDefinitionDto.d.ts +0 -0
  26. package/{data → dist/data}/api/dto/read/ReadSelectedDefinitionDto.js +0 -0
  27. package/{data → dist/data}/api/dto/read/ReadSelectedOrderingDefinitionDto.d.ts +0 -0
  28. package/{data → dist/data}/api/dto/read/ReadSelectedOrderingDefinitionDto.js +0 -0
  29. package/{data → dist/data}/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.d.ts +0 -0
  30. package/{data → dist/data}/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.js +0 -0
  31. package/{data → dist/data}/api/dto/read/ReadSelectedPaginationDefinitionDto.d.ts +0 -0
  32. package/{data → dist/data}/api/dto/read/ReadSelectedPaginationDefinitionDto.js +0 -0
  33. package/{data → dist/data}/api/dto/read/ReadSelectedSearchDefinitionDto.d.ts +0 -0
  34. package/{data → dist/data}/api/dto/read/ReadSelectedSearchDefinitionDto.js +0 -0
  35. package/{data → dist/data}/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.d.ts +0 -0
  36. package/{data → dist/data}/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.js +0 -0
  37. package/{data → dist/data}/api/dto/read/index.d.ts +0 -0
  38. package/{data → dist/data}/api/dto/read/index.js +0 -0
  39. package/{data → dist/data}/api/dto/response/ApiErrorDto.d.ts +0 -0
  40. package/{data → dist/data}/api/dto/response/ApiErrorDto.js +0 -0
  41. package/{data → dist/data}/api/dto/response/ApiResponseDto.d.ts +0 -0
  42. package/{data → dist/data}/api/dto/response/ApiResponseDto.js +0 -0
  43. package/{data → dist/data}/api/dto/response/ApiSuccessResponseDto.d.ts +0 -0
  44. package/{data → dist/data}/api/dto/response/ApiSuccessResponseDto.js +0 -0
  45. package/{data → dist/data}/api/dto/response/EmptyMetadataDto.d.ts +0 -0
  46. package/{data → dist/data}/api/dto/response/EmptyMetadataDto.js +0 -0
  47. package/{data → dist/data}/api/dto/response/index.d.ts +0 -0
  48. package/{data → dist/data}/api/dto/response/index.js +0 -0
  49. package/{data → dist/data}/api/enum/index.d.ts +0 -0
  50. package/{data → dist/data}/api/enum/index.js +0 -0
  51. package/{data → dist/data}/api/enum/read/ReadSelectedComparisonOperator.d.ts +0 -0
  52. package/{data → dist/data}/api/enum/read/ReadSelectedComparisonOperator.js +0 -0
  53. package/{data → dist/data}/api/enum/read/ReadSelectedLogicalOperator.d.ts +0 -0
  54. package/{data → dist/data}/api/enum/read/ReadSelectedLogicalOperator.js +0 -0
  55. package/{data → dist/data}/api/enum/read/ReadSelectedOrderingDirection.d.ts +0 -0
  56. package/{data → dist/data}/api/enum/read/ReadSelectedOrderingDirection.js +0 -0
  57. package/{data → dist/data}/api/enum/read/ReadSelectedPropertyType.d.ts +0 -0
  58. package/{data → dist/data}/api/enum/read/ReadSelectedPropertyType.js +0 -0
  59. package/{data → dist/data}/api/enum/read/index.d.ts +0 -0
  60. package/{data → dist/data}/api/enum/read/index.js +0 -0
  61. package/{data → dist/data}/api/enum/response/ApiErrorCodes.d.ts +0 -0
  62. package/{data → dist/data}/api/enum/response/ApiErrorCodes.js +0 -0
  63. package/{data → dist/data}/api/enum/response/index.d.ts +0 -0
  64. package/{data → dist/data}/api/enum/response/index.js +0 -0
  65. package/{data → dist/data}/api/index.d.ts +0 -0
  66. package/{data → dist/data}/api/index.js +0 -0
  67. package/{data → dist/data}/api/interface/IConcurrencySafe.d.ts +0 -0
  68. package/{data → dist/data}/api/interface/IConcurrencySafe.js +0 -0
  69. package/{data → dist/data}/api/interface/IIdentifiable.d.ts +0 -0
  70. package/{data → dist/data}/api/interface/IIdentifiable.js +0 -0
  71. package/{data → dist/data}/api/interface/IIdentifiableSecondary.d.ts +0 -0
  72. package/{data → dist/data}/api/interface/IIdentifiableSecondary.js +0 -0
  73. package/{data → dist/data}/api/interface/index.d.ts +0 -0
  74. package/{data → dist/data}/api/interface/index.js +0 -0
  75. package/{data → dist/data}/auth/dto/ClaimDto.d.ts +0 -0
  76. package/{data → dist/data}/auth/dto/ClaimDto.js +0 -0
  77. package/{data → dist/data}/auth/dto/RegisterRequestDto.d.ts +0 -0
  78. package/{data → dist/data}/auth/dto/RegisterRequestDto.js +0 -0
  79. package/{data → dist/data}/auth/dto/RoleDto.d.ts +0 -0
  80. package/{data → dist/data}/auth/dto/RoleDto.js +0 -0
  81. package/{data → dist/data}/auth/dto/SignInRequestDto.d.ts +0 -0
  82. package/{data → dist/data}/auth/dto/SignInRequestDto.js +0 -0
  83. package/{data → dist/data}/auth/dto/TokensDto.d.ts +0 -0
  84. package/{data → dist/data}/auth/dto/TokensDto.js +0 -0
  85. package/{data → dist/data}/auth/dto/UserDto.d.ts +0 -0
  86. package/{data → dist/data}/auth/dto/UserDto.js +0 -0
  87. package/{data → dist/data}/auth/dto/UserInfoDto.d.ts +0 -0
  88. package/{data → dist/data}/auth/dto/UserInfoDto.js +0 -0
  89. package/{data → dist/data}/auth/dto/index.d.ts +0 -0
  90. package/{data → dist/data}/auth/dto/index.js +0 -0
  91. package/{data → dist/data}/auth/index.d.ts +0 -0
  92. package/{data → dist/data}/auth/index.js +0 -0
  93. package/{data → dist/data}/index.d.ts +0 -0
  94. package/{data → dist/data}/index.js +0 -0
  95. package/{index.d.ts → dist/index.d.ts} +0 -0
  96. package/{index.js → dist/index.js} +0 -0
  97. package/{services → dist/services}/api/ApiCrudControllerClient/index.d.ts +0 -0
  98. package/{services → dist/services}/api/ApiCrudControllerClient/index.js +0 -0
  99. package/{services → dist/services}/api/ApiInitializationService/index.d.ts +0 -0
  100. package/{services → dist/services}/api/ApiInitializationService/index.js +0 -0
  101. package/{services → dist/services}/api/ApiReadControllerClient/index.d.ts +0 -0
  102. package/{services → dist/services}/api/ApiReadControllerClient/index.js +0 -0
  103. package/{services → dist/services}/api/HttpService/FetchHttpService.d.ts +0 -0
  104. package/{services → dist/services}/api/HttpService/FetchHttpService.js +0 -0
  105. package/{services → dist/services}/api/HttpService/HttpRequestConfig.d.ts +0 -0
  106. package/{services → dist/services}/api/HttpService/HttpRequestConfig.js +0 -0
  107. package/{services → dist/services}/api/HttpService/HttpResponse.d.ts +0 -0
  108. package/{services → dist/services}/api/HttpService/HttpResponse.js +0 -0
  109. package/{services → dist/services}/api/HttpService/IHttpService.d.ts +0 -0
  110. package/{services → dist/services}/api/HttpService/IHttpService.js +0 -0
  111. package/{services → dist/services}/api/HttpService/index.d.ts +0 -0
  112. package/{services → dist/services}/api/HttpService/index.js +0 -0
  113. package/{services → dist/services}/api/UserManagementControllerClient/index.d.ts +0 -0
  114. package/{services → dist/services}/api/UserManagementControllerClient/index.js +0 -0
  115. package/{services → dist/services}/api/index.d.ts +0 -0
  116. package/{services → dist/services}/api/index.js +0 -0
  117. package/{services → dist/services}/auth/client/AuthService/index.d.ts +0 -0
  118. package/{services → dist/services}/auth/client/AuthService/index.js +0 -0
  119. package/{services → dist/services}/auth/client/AuthorizationManagementControllerClient/index.d.ts +0 -0
  120. package/{services → dist/services}/auth/client/AuthorizationManagementControllerClient/index.js +0 -0
  121. package/{services → dist/services}/auth/client/index.d.ts +0 -0
  122. package/{services → dist/services}/auth/client/index.js +0 -0
  123. package/{services → dist/services}/auth/index.d.ts +0 -0
  124. package/{services → dist/services}/auth/index.js +0 -0
  125. package/{services → dist/services}/index.d.ts +0 -0
  126. package/{services → dist/services}/index.js +0 -0
  127. package/{utils → dist/utils}/index.d.ts +0 -0
  128. package/{utils → dist/utils}/index.js +0 -0
  129. package/jest.config.js +29 -0
  130. package/package.json +24 -24
  131. package/src/adapters/CollectionViewAdapter/index.ts +390 -0
  132. package/src/adapters/index.ts +1 -0
  133. package/src/data/api/dto/PropertyPathDto.ts +4 -0
  134. package/src/data/api/dto/ReadOptionsDto.ts +8 -0
  135. package/src/data/api/dto/ReadResultDto.ts +13 -0
  136. package/src/data/api/dto/ReadResultMetadataDto.ts +8 -0
  137. package/src/data/api/dto/crud/CrudMetadataDto.ts +4 -0
  138. package/src/data/api/dto/crud/index.ts +1 -0
  139. package/src/data/api/dto/index.ts +4 -0
  140. package/src/data/api/dto/read/ReadMetadataDto.ts +8 -0
  141. package/src/data/api/dto/read/ReadSelectedDefinitionDto.ts +21 -0
  142. package/src/data/api/dto/read/ReadSelectedNestedCollectionCriteriaDto.ts +25 -0
  143. package/src/data/api/dto/read/ReadSelectedNestedCriteriaDto.ts +20 -0
  144. package/src/data/api/dto/read/ReadSelectedOrderingDefinitionDto.ts +8 -0
  145. package/src/data/api/dto/read/ReadSelectedOrderingPropertyDefinitionDto.ts +16 -0
  146. package/src/data/api/dto/read/ReadSelectedPaginationDefinitionDto.ts +13 -0
  147. package/src/data/api/dto/read/ReadSelectedSearchDefinitionDto.ts +43 -0
  148. package/src/data/api/dto/read/ReadSelectedSearchPropertyDefinitionDto.ts +186 -0
  149. package/src/data/api/dto/read/index.ts +9 -0
  150. package/src/data/api/dto/response/ApiErrorDto.ts +21 -0
  151. package/src/data/api/dto/response/ApiErrorResponseDto.ts +13 -0
  152. package/src/data/api/dto/response/ApiResponseDto.ts +7 -0
  153. package/src/data/api/dto/response/ApiSuccessResponseDto.ts +13 -0
  154. package/src/data/api/dto/response/MetadataDto.ts +24 -0
  155. package/src/data/api/dto/response/index.ts +5 -0
  156. package/src/data/api/enum/index.ts +2 -0
  157. package/src/data/api/enum/read/ReadSelectedCollectionOperator.ts +17 -0
  158. package/src/data/api/enum/read/ReadSelectedComparisonOperator.ts +96 -0
  159. package/src/data/api/enum/read/ReadSelectedLogicalOperator.ts +16 -0
  160. package/src/data/api/enum/read/ReadSelectedOrderingDirection.ts +13 -0
  161. package/src/data/api/enum/read/ReadSelectedPropertyType.ts +86 -0
  162. package/src/data/api/enum/read/index.ts +5 -0
  163. package/src/data/api/enum/response/ErrorCode.ts +13 -0
  164. package/src/data/api/enum/response/index.ts +1 -0
  165. package/src/data/api/index.ts +3 -0
  166. package/src/data/api/interface/IConcurrencySafe.ts +9 -0
  167. package/src/data/api/interface/IIdentifiable.ts +12 -0
  168. package/src/data/api/interface/IIdentifiableSecondary.ts +9 -0
  169. package/src/data/api/interface/index.ts +3 -0
  170. package/src/data/auth/dto/ClaimDto.ts +4 -0
  171. package/src/data/auth/dto/RegisterRequestDto.ts +4 -0
  172. package/src/data/auth/dto/RoleDto.ts +6 -0
  173. package/src/data/auth/dto/SignInRequestDto.ts +4 -0
  174. package/src/data/auth/dto/TokensDto.ts +4 -0
  175. package/src/data/auth/dto/UserDto.ts +18 -0
  176. package/src/data/auth/dto/UserInfoDto.ts +15 -0
  177. package/src/data/auth/dto/index.ts +4 -0
  178. package/src/data/auth/index.ts +2 -0
  179. package/src/data/auth/policy.ts +63 -0
  180. package/src/data/index.ts +2 -0
  181. package/src/index.ts +4 -0
  182. package/src/services/api/ApiCrudControllerClient/index.ts +129 -0
  183. package/src/services/api/ApiInitializationService/index.ts +254 -0
  184. package/src/services/api/ApiReadControllerClient/index.ts +137 -0
  185. package/src/services/api/HttpService/FetchHttpService.ts +34 -0
  186. package/src/services/api/HttpService/HttpRequestConfig.ts +10 -0
  187. package/src/services/api/HttpService/HttpResponse.ts +14 -0
  188. package/src/services/api/HttpService/IHttpService.ts +17 -0
  189. package/src/services/api/HttpService/README.md +106 -0
  190. package/src/services/api/HttpService/index.ts +12 -0
  191. package/src/services/api/UserManagementControllerClient/index.ts +160 -0
  192. package/src/services/api/index.ts +5 -0
  193. package/src/services/auth/client/AuthService/index.ts +187 -0
  194. package/src/services/auth/client/AuthorizationManagementControllerClient/index.ts +165 -0
  195. package/src/services/auth/client/index.ts +2 -0
  196. package/src/services/auth/index.ts +1 -0
  197. package/src/services/index.ts +2 -0
  198. package/src/utils/authorization/index.ts +47 -0
  199. package/src/utils/index.ts +2 -0
  200. package/src/utils/result/index.ts +25 -0
  201. package/src/utils/search/index.ts +150 -0
  202. package/tests/ApiClients.test.ts +284 -0
  203. package/tests/CollectionViewAdapter.test.ts +392 -0
  204. package/tests/HttpService.test.ts +303 -0
  205. package/tests/setup.ts +76 -0
  206. package/tsconfig.json +19 -0
  207. package/LICENSE.md +0 -7
  208. /package/{data → dist/data}/api/dto/read/ReadSelectedNestedCollectionCriteriaDto.d.ts +0 -0
  209. /package/{data → dist/data}/api/dto/read/ReadSelectedNestedCollectionCriteriaDto.js +0 -0
  210. /package/{data → dist/data}/api/dto/read/ReadSelectedNestedCriteriaDto.d.ts +0 -0
  211. /package/{data → dist/data}/api/dto/read/ReadSelectedNestedCriteriaDto.js +0 -0
  212. /package/{data → dist/data}/api/dto/response/ApiErrorResponseDto.d.ts +0 -0
  213. /package/{data → dist/data}/api/dto/response/ApiErrorResponseDto.js +0 -0
  214. /package/{data → dist/data}/api/dto/response/MetadataDto.d.ts +0 -0
  215. /package/{data → dist/data}/api/dto/response/MetadataDto.js +0 -0
  216. /package/{data → dist/data}/api/enum/read/ReadSelectedCollectionOperator.d.ts +0 -0
  217. /package/{data → dist/data}/api/enum/read/ReadSelectedCollectionOperator.js +0 -0
  218. /package/{data → dist/data}/api/enum/response/ErrorCode.d.ts +0 -0
  219. /package/{data → dist/data}/api/enum/response/ErrorCode.js +0 -0
  220. /package/{data → dist/data}/auth/policy.d.ts +0 -0
  221. /package/{data → dist/data}/auth/policy.js +0 -0
  222. /package/{utils → dist/utils}/authorization/index.d.ts +0 -0
  223. /package/{utils → dist/utils}/authorization/index.js +0 -0
  224. /package/{utils → dist/utils}/result/index.d.ts +0 -0
  225. /package/{utils → dist/utils}/result/index.js +0 -0
  226. /package/{utils → dist/utils}/search/index.d.ts +0 -0
  227. /package/{utils → dist/utils}/search/index.js +0 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Interface for policies that can be checked.
3
+ *
4
+ * @interface IPolicy<TParams extends unknown[]>
5
+ * @template TParams - The type of the parameters that will be passed to the policy check function.
6
+ */
7
+ export interface IPolicy<TParams extends unknown[]> {
8
+ /**
9
+ * Checks if the policy is valid for the given parameters.
10
+ *
11
+ * @param params - Parameters to pass to the policy check function.
12
+ * @return A promise that resolves if the policy is valid, rejects otherwise.
13
+ */
14
+ check(...params: TParams): Promise<boolean>;
15
+ }
16
+
17
+ /**
18
+ * Base class for policies. It implements the IPolicy interface.
19
+ *
20
+ * @class PolicyBase
21
+ * @template TParams - The type of the parameters that will be passed to the policy check function.
22
+ */
23
+ export class PolicyBase<TParams extends unknown[]> implements IPolicy<TParams> {
24
+ /**
25
+ * Checks if the policy is valid for the given parameters.
26
+ *
27
+ * @return A promise that resolves if the policy is valid, rejects otherwise.
28
+ * @throws {Error} Error if not implemented.
29
+ */
30
+ public async check(): Promise<boolean> {
31
+ throw new Error('Not implemented');
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Class for policies that can be checked.
37
+ *
38
+ * @class Policy
39
+ * @template TParams - The type of the parameters that will be passed to the policy check function.
40
+ */
41
+ export class Policy<TParams extends unknown[]> extends PolicyBase<TParams> {
42
+ private readonly _verificationFn: (...params: TParams) => Promise<boolean>;
43
+
44
+ /**
45
+ * Creates a new policy.
46
+ *
47
+ * @param verificationFn - The function that will be used to verify the policy.
48
+ */
49
+ constructor(verificationFn: (...params: TParams) => Promise<boolean>) {
50
+ super();
51
+ this._verificationFn = verificationFn;
52
+ }
53
+
54
+ /**
55
+ * Checks if the policy is valid for the given parameters.
56
+ *
57
+ * @param params - Parameters to pass to the policy check function.
58
+ * @return A promise that resolves if the policy is valid, rejects otherwise.
59
+ */
60
+ public async check(...params: TParams): Promise<boolean> {
61
+ return this._verificationFn(...params);
62
+ }
63
+ }
@@ -0,0 +1,2 @@
1
+ export * from './auth';
2
+ export * from './api';
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './services';
2
+ export * from './data';
3
+ export * from './adapters';
4
+ export * from './utils';
@@ -0,0 +1,129 @@
1
+ import { apiInitializationService } from '../ApiInitializationService';
2
+ import { ApiErrorResponseDto, ApiResponseDto, ApiSuccessResponseDto, EmptyMetadataDto } from '../../../data/api/dto';
3
+ import { IIdentifiable } from '../../../data/api/interface';
4
+ import { IHttpService } from '../HttpService';
5
+ import { ApiReadControllerClient } from '../ApiReadControllerClient';
6
+ import { ErrorCode } from '@data';
7
+ import { fail, ok } from '@utils/result';
8
+
9
+ /**
10
+ * Generic API client for consuming any Cornerstone CrudController
11
+ *
12
+ * @export
13
+ * @class ApiCrudControllerClient
14
+ * @template TKey - Type of the entity key (e.g., number, string, GUID, etc.)
15
+ * @template TDto - Data Transfer Object representing the entity
16
+ */
17
+
18
+ export class ApiCrudControllerClient<TKey, TDto extends IIdentifiable<TKey>> extends ApiReadControllerClient<TKey, TDto> {
19
+ /**
20
+ * Constructor
21
+ * @param baseControllerPath Base path to API controller
22
+ * @param httpService HTTP service implementation to use for requests
23
+ */
24
+ constructor(baseControllerPath: string, httpService?: IHttpService) {
25
+ super(baseControllerPath, httpService);
26
+ }
27
+
28
+ /**
29
+ * Creates a new entity.
30
+ * @param dto - Insert request DTO
31
+ * @param {AbortSignal} [signal] - Optional cancellation signal
32
+ * @returns The created entity DTO
33
+ */
34
+ public async create(dto: TDto, signal?: AbortSignal): Promise<ApiResponseDto<TDto, EmptyMetadataDto>> {
35
+ try {
36
+ const url = await apiInitializationService.getApiUrl(this.baseControllerPath, `/Create`);
37
+ const res = await this.httpService.request(url, {
38
+ method: 'POST',
39
+ headers: { 'Content-Type': 'application/json' },
40
+ body: JSON.stringify(dto),
41
+ credentials: 'include',
42
+ signal,
43
+ });
44
+ if (!res.ok) {
45
+ return fail<ApiSuccessResponseDto<TDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
46
+ }
47
+
48
+ return ok<ApiSuccessResponseDto<TDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
49
+ } catch (err) {
50
+ console.error(err);
51
+
52
+ return fail<ApiSuccessResponseDto<TDto, EmptyMetadataDto>, ApiErrorResponseDto>({
53
+ error: {
54
+ code: ErrorCode.UnknownError,
55
+ message: 'Unknown error occurred while creating the record.',
56
+ metadata: {} as EmptyMetadataDto,
57
+ },
58
+ });
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Updates an existing entity.
64
+ * @param id - The ID of the entity to update
65
+ * @param dto - Update request DTO
66
+ * @param {AbortSignal} [signal] - Optional cancellation signal
67
+ * @returns The updated entity DTO
68
+ */
69
+ public async update(id: TKey, dto: TDto, signal?: AbortSignal): Promise<ApiResponseDto<TDto, EmptyMetadataDto>> {
70
+ try {
71
+ const url = await apiInitializationService.getApiUrl(this.baseControllerPath, `/Update?id=${encodeURIComponent(String(id))}`);
72
+ const res = await this.httpService.request(url, {
73
+ method: 'PUT',
74
+ headers: { 'Content-Type': 'application/json' },
75
+ body: JSON.stringify(dto),
76
+ credentials: 'include',
77
+ signal,
78
+ });
79
+
80
+ if (!res.ok) {
81
+ return fail<ApiSuccessResponseDto<TDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
82
+ }
83
+
84
+ return ok<ApiSuccessResponseDto<TDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
85
+ } catch (err) {
86
+ console.error(err);
87
+
88
+ return fail<ApiSuccessResponseDto<TDto, EmptyMetadataDto>, ApiErrorResponseDto>({
89
+ error: {
90
+ code: ErrorCode.UnknownError,
91
+ message: `Unknown error occurred while updating the record with id ${id}`,
92
+ metadata: {} as EmptyMetadataDto,
93
+ },
94
+ });
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Deletes an entity by ID.
100
+ * @param id - The ID of the entity to delete
101
+ * @param {AbortSignal} [signal] - Optional cancellation signal
102
+ */
103
+ public async delete(id: TKey, signal?: AbortSignal): Promise<ApiResponseDto<undefined, EmptyMetadataDto>> {
104
+ try {
105
+ const url = await apiInitializationService.getApiUrl(this.baseControllerPath, `/Delete?id=${encodeURIComponent(String(id))}`);
106
+ const res = await this.httpService.request(url, {
107
+ method: 'DELETE',
108
+ credentials: 'include',
109
+ signal,
110
+ });
111
+
112
+ if (!res.ok) {
113
+ return fail<ApiSuccessResponseDto<undefined, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
114
+ }
115
+
116
+ return ok<ApiSuccessResponseDto<undefined, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
117
+ } catch (err) {
118
+ console.error(err);
119
+
120
+ return fail<ApiSuccessResponseDto<undefined, EmptyMetadataDto>, ApiErrorResponseDto>({
121
+ error: {
122
+ code: ErrorCode.UnknownError,
123
+ message: `Unknown error occurred while deleting the record with id ${id}`,
124
+ metadata: {} as EmptyMetadataDto,
125
+ },
126
+ });
127
+ }
128
+ }
129
+ }
@@ -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
+ settings: 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.settings;
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,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, TDto 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<TDto[], 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<TDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
56
+ }
57
+
58
+ return ok<ApiSuccessResponseDto<TDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
59
+ } catch (err) {
60
+ console.error(err);
61
+
62
+ return fail<ApiSuccessResponseDto<TDto[], 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<TDto>, signal?: AbortSignal): Promise<ApiResponseDto<TDto[], 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<TDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
91
+ }
92
+
93
+ return ok<ApiSuccessResponseDto<TDto[], ReadMetadataDto>, ApiErrorResponseDto>(await res.json());
94
+ } catch (err) {
95
+ console.error(err);
96
+
97
+ return fail<ApiSuccessResponseDto<TDto[], 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<TDto, 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<TDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
120
+ }
121
+
122
+ return ok<ApiSuccessResponseDto<TDto, EmptyMetadataDto>, ApiErrorResponseDto>(await res.json());
123
+ } catch (err) {
124
+ console.error(err);
125
+
126
+ return fail<ApiSuccessResponseDto<TDto, 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,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
+ }