@intellegens/cornerstone-client 0.0.5 → 0.0.6

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 CHANGED
@@ -33,16 +33,16 @@ http://localhost:3000
33
33
  This service provides way to load API configuration for a Cornerstone client application.
34
34
 
35
35
  ```ts
36
- import { ApiService } from '@intellegens/cornerstone-client';
37
- const apiService = new ApiService();
38
- await apiService.initialize();
36
+ import { ApiInitializationService } from '@intellegens/cornerstone-client';
37
+ const apiInitializationService = new ApiInitializationService();
38
+ await apiInitializationService.initialize();
39
39
  ```
40
40
 
41
41
  or use the existing singleton instance
42
42
 
43
43
  ```ts
44
- import { apiService } from '@intellegens/cornerstone-client';
45
- await apiService.initialize();
44
+ import { apiInitializationService } from '@intellegens/cornerstone-client';
45
+ await apiInitializationService.initialize();
46
46
  ```
47
47
 
48
48
  #### API base URL detection
@@ -54,7 +54,7 @@ Detected by checking the `CORNERSTONE-API-BASEURL` response header and the `webs
54
54
  - If no API base URL is found, it defaults to `undefined`.
55
55
 
56
56
  ```ts
57
- const fetchUrl = `${await apiService.getApiUrl('/my/endpoint')}`;
57
+ const fetchUrl = `${await apiInitializationService.getApiUrl('/my/endpoint')}`;
58
58
  ```
59
59
 
60
60
  ### Auth Service
@@ -68,14 +68,6 @@ await authService.initialize();
68
68
  const user = authService.user;
69
69
  ```
70
70
 
71
- or use the existing singleton instance
72
-
73
- ```ts
74
- import { authService } from '@intellegens/cornerstone-client';
75
- await authService.initialize();
76
- const user = authService.user;
77
- ```
78
-
79
71
  #### Methods
80
72
 
81
73
  ##### Who Am I
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellegens/cornerstone-client",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -0,0 +1,9 @@
1
+ import { ApiReadControllerClient } from '../read';
2
+ /**
3
+ * Generic API client for consuming any Cornerstone ReadController
4
+ *
5
+ * @export
6
+ * @class ApiService
7
+ */
8
+ export declare class ApiCrudControllerClient<TDto> extends ApiReadControllerClient<TDto> {
9
+ }
@@ -0,0 +1,9 @@
1
+ import { ApiReadControllerClient } from '../read';
2
+ /**
3
+ * Generic API client for consuming any Cornerstone ReadController
4
+ *
5
+ * @export
6
+ * @class ApiService
7
+ */
8
+ export class ApiCrudControllerClient extends ApiReadControllerClient {
9
+ }
@@ -0,0 +1,2 @@
1
+ export * from './read';
2
+ export * from './crud';
@@ -0,0 +1,2 @@
1
+ export * from './read';
2
+ export * from './crud';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Generic API client for consuming any Cornerstone ReadController
3
+ *
4
+ * @export
5
+ * @class ApiService
6
+ */
7
+ export declare class ApiReadControllerClient<TDto> {
8
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Generic API client for consuming any Cornerstone ReadController
3
+ *
4
+ * @export
5
+ * @class ApiService
6
+ */
7
+ export class ApiReadControllerClient {
8
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Central API configuration service, fetches and exposes Cornerstone API configuration
3
+ *
4
+ * @export
5
+ * @class ApiService
6
+ */
7
+ export declare class ApiService {
8
+ /**
9
+ * Initializes the API configuration
10
+ *
11
+ * This method calls all methods that need to be called during the API configuration phase:
12
+ * - Starts detection of API base URL.
13
+ *
14
+ * @async
15
+ * @return {Promise<void>} Resolves when initialization is complete.
16
+ */
17
+ initialize(): Promise<void>;
18
+ private _apiBaseUrlPromise;
19
+ /**
20
+ * If API base URL detection was completed
21
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
22
+ */
23
+ _apiBaseUrlDetected: boolean;
24
+ /**
25
+ * If API base URL detection was completed, this property will hold the method by which detection was performed
26
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
27
+ */
28
+ _apiBaseUrlDetectionMethod: string | undefined;
29
+ /**
30
+ * If API base URL detection was completed, this property will hold the detected API base URL
31
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
32
+ */
33
+ _apiBaseUrl: string | undefined;
34
+ /**
35
+ * If API base URL detection has failed, this property will hold the error with which it has failed
36
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
37
+ */
38
+ _apiBaseError: Error | undefined;
39
+ /**
40
+ * Tries detecting API base URL by multiple methods
41
+ *
42
+ * - Checks the response headers of the current URL for the `CORNERSTONE-API-BASEURL` header.
43
+ * - Attempts to load the `websettings.json` file and looks for an `api` field.
44
+ * - Defaults to `undefined`.
45
+ *
46
+ * IMPORTANT: This method is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
47
+ *
48
+ * @async
49
+ * @return {Promise<string | undefined>} Returns detected API base URL
50
+ * @throws {Error} Throws error if either of the detection methods fails
51
+ */
52
+ _getApiBaseUrl(): Promise<string | undefined>;
53
+ /**
54
+ * Composes a full API URL for a provided endpoint path.
55
+ *
56
+ * @async
57
+ * @param relativeEndpointPath Relative endpoint path
58
+ * @return {Promise<string | undefined>} Returns the API endpoint's URL
59
+ * @throws {Error} Throws error if API base URL detection fails
60
+ */
61
+ getApiUrl(relativeEndpointPath: string): Promise<string>;
62
+ }
63
+ /**
64
+ * Singleton instance of ApiService
65
+ */
66
+ export declare const apiService: ApiService;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Central API configuration service, fetches and exposes Cornerstone API configuration
3
+ *
4
+ * @export
5
+ * @class ApiService
6
+ */
7
+ export class ApiService {
8
+ /**
9
+ * Initializes the API configuration
10
+ *
11
+ * This method calls all methods that need to be called during the API configuration phase:
12
+ * - Starts detection of API base URL.
13
+ *
14
+ * @async
15
+ * @return {Promise<void>} Resolves when initialization is complete.
16
+ */
17
+ async initialize() {
18
+ // (Pre)detect API base URL
19
+ await this._getApiBaseUrl();
20
+ }
21
+ // #region API Base URL detection
22
+ _apiBaseUrlPromise = undefined;
23
+ /**
24
+ * If API base URL detection was completed
25
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
26
+ */
27
+ _apiBaseUrlDetected = false;
28
+ /**
29
+ * If API base URL detection was completed, this property will hold the method by which detection was performed
30
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
31
+ */
32
+ _apiBaseUrlDetectionMethod = undefined;
33
+ /**
34
+ * If API base URL detection was completed, this property will hold the detected API base URL
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
+ _apiBaseUrl = undefined;
38
+ /**
39
+ * If API base URL detection has failed, this property will hold the error with which it has failed
40
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
41
+ */
42
+ _apiBaseError = undefined;
43
+ /**
44
+ * Tries detecting API base URL by multiple methods
45
+ *
46
+ * - Checks the response headers of the current URL for the `CORNERSTONE-API-BASEURL` header.
47
+ * - Attempts to load the `websettings.json` file and looks for an `api` field.
48
+ * - Defaults to `undefined`.
49
+ *
50
+ * IMPORTANT: This method is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
51
+ *
52
+ * @async
53
+ * @return {Promise<string | undefined>} Returns detected API base URL
54
+ * @throws {Error} Throws error if either of the detection methods fails
55
+ */
56
+ async _getApiBaseUrl() {
57
+ // Check if API already detected; don't reattempt detection
58
+ if (this._apiBaseUrlDetected)
59
+ return this._apiBaseUrl;
60
+ // Check if detection already in progress; don't allow multiple detections in parallel
61
+ if (this._apiBaseUrlPromise)
62
+ return this._apiBaseUrlPromise;
63
+ // Detect API base URL
64
+ return (this._apiBaseUrlPromise = new Promise((resolve, reject) => (async () => {
65
+ // Reset
66
+ this._apiBaseUrlDetected = false;
67
+ this._apiBaseUrlDetectionMethod = undefined;
68
+ this._apiBaseUrl = undefined;
69
+ this._apiBaseError = undefined;
70
+ // Check CORNERSTONE-API-BASEURL header for API base URL
71
+ try {
72
+ const currentUrlResponse = await fetch(window.location.href, { method: 'GET' });
73
+ const apiHeader = currentUrlResponse.headers.get('CORNERSTONE-API-BASEURL');
74
+ if (apiHeader) {
75
+ this._apiBaseUrlDetected = true;
76
+ this._apiBaseUrlDetectionMethod = 'GET / Header:CORNERSTONE-API-BASEURL';
77
+ this._apiBaseUrl = apiHeader;
78
+ this._apiBaseError = undefined;
79
+ this._apiBaseUrlPromise = undefined;
80
+ return resolve(this._apiBaseUrl);
81
+ }
82
+ }
83
+ catch (err) {
84
+ this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from response header!');
85
+ }
86
+ // Check ./websettings.json header for API base URL
87
+ try {
88
+ const webSettingsResponse = await fetch('./websettings.json', { method: 'GET' });
89
+ if (webSettingsResponse.status === 404)
90
+ return (this._apiBaseUrl = undefined);
91
+ const webSettings = await webSettingsResponse.json();
92
+ if (webSettings?.api) {
93
+ this._apiBaseUrlDetected = true;
94
+ this._apiBaseUrlDetectionMethod = 'GET websettings.json';
95
+ this._apiBaseUrl = webSettings.api;
96
+ this._apiBaseError = undefined;
97
+ this._apiBaseUrlPromise = undefined;
98
+ return resolve(this._apiBaseUrl);
99
+ }
100
+ }
101
+ catch (err) {
102
+ this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from settings file!');
103
+ }
104
+ // Check if error caught during any of the detection methods
105
+ if (this._apiBaseError !== undefined) {
106
+ this._apiBaseUrlDetected = false;
107
+ this._apiBaseUrlDetectionMethod = undefined;
108
+ this._apiBaseUrl = undefined;
109
+ this._apiBaseUrlPromise = undefined;
110
+ return reject(this._apiBaseError);
111
+ }
112
+ // Default to no API found
113
+ this._apiBaseUrlDetected = true;
114
+ this._apiBaseUrlDetectionMethod = undefined;
115
+ this._apiBaseUrl = undefined;
116
+ this._apiBaseError = undefined;
117
+ this._apiBaseUrlPromise = undefined;
118
+ return resolve(this._apiBaseUrl);
119
+ })()));
120
+ }
121
+ /**
122
+ * Composes a full API URL for a provided endpoint path.
123
+ *
124
+ * @async
125
+ * @param relativeEndpointPath Relative endpoint path
126
+ * @return {Promise<string | undefined>} Returns the API endpoint's URL
127
+ * @throws {Error} Throws error if API base URL detection fails
128
+ */
129
+ async getApiUrl(relativeEndpointPath) {
130
+ const apiBaseUrl = await this._getApiBaseUrl();
131
+ const domain = !apiBaseUrl ? `` : apiBaseUrl?.endsWith('/') ? apiBaseUrl : `${apiBaseUrl}/`;
132
+ const path = !relativeEndpointPath.startsWith('/') ? relativeEndpointPath : relativeEndpointPath.substring(1);
133
+ return `${domain}${path}`;
134
+ }
135
+ }
136
+ /**
137
+ * Singleton instance of ApiService
138
+ */
139
+ export const apiService = new ApiService();
@@ -1,66 +1,2 @@
1
- /**
2
- * Central API configuration service, fetches and exposes Cornerstone API configuration
3
- *
4
- * @export
5
- * @class ApiService
6
- */
7
- export declare class ApiService {
8
- /**
9
- * Initializes the API configuration
10
- *
11
- * This method calls all methods that need to be called during the API configuration phase:
12
- * - Starts detection of API base URL.
13
- *
14
- * @async
15
- * @return {Promise<void>} Resolves when initialization is complete.
16
- */
17
- initialize(): Promise<void>;
18
- private _apiBaseUrlPromise;
19
- /**
20
- * If API base URL detection was completed
21
- * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
22
- */
23
- _apiBaseUrlDetected: boolean;
24
- /**
25
- * If API base URL detection was completed, this property will hold the method by which detection was performed
26
- * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
27
- */
28
- _apiBaseUrlDetectionMethod: string | undefined;
29
- /**
30
- * If API base URL detection was completed, this property will hold the detected API base URL
31
- * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
32
- */
33
- _apiBaseUrl: string | undefined;
34
- /**
35
- * If API base URL detection has failed, this property will hold the error with which it has failed
36
- * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
37
- */
38
- _apiBaseError: Error | undefined;
39
- /**
40
- * Tries detecting API base URL by multiple methods
41
- *
42
- * - Checks the response headers of the current URL for the `CORNERSTONE-API-BASEURL` header.
43
- * - Attempts to load the `websettings.json` file and looks for an `api` field.
44
- * - Defaults to `undefined`.
45
- *
46
- * IMPORTANT: This method is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
47
- *
48
- * @async
49
- * @return {Promise<string | undefined>} Returns detected API base URL
50
- * @throws {Error} Throws error if either of the detection methods fails
51
- */
52
- _getApiBaseUrl(): Promise<string | undefined>;
53
- /**
54
- * Composes a full API URL for a provided endpoint path.
55
- *
56
- * @async
57
- * @param relativeEndpointPath Relative endpoint path
58
- * @return {Promise<string | undefined>} Returns the API endpoint's URL
59
- * @throws {Error} Throws error if API base URL detection fails
60
- */
61
- getApiUrl(relativeEndpointPath: string): Promise<string>;
62
- }
63
- /**
64
- * Singleton instance of ApiService
65
- */
66
- export declare const apiService: ApiService;
1
+ export * from './initialization';
2
+ export * from './client';
@@ -1,139 +1,2 @@
1
- /**
2
- * Central API configuration service, fetches and exposes Cornerstone API configuration
3
- *
4
- * @export
5
- * @class ApiService
6
- */
7
- export class ApiService {
8
- /**
9
- * Initializes the API configuration
10
- *
11
- * This method calls all methods that need to be called during the API configuration phase:
12
- * - Starts detection of API base URL.
13
- *
14
- * @async
15
- * @return {Promise<void>} Resolves when initialization is complete.
16
- */
17
- async initialize() {
18
- // (Pre)detect API base URL
19
- await this._getApiBaseUrl();
20
- }
21
- // #region API Base URL detection
22
- _apiBaseUrlPromise = undefined;
23
- /**
24
- * If API base URL detection was completed
25
- * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
26
- */
27
- _apiBaseUrlDetected = false;
28
- /**
29
- * If API base URL detection was completed, this property will hold the method by which detection was performed
30
- * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
31
- */
32
- _apiBaseUrlDetectionMethod = undefined;
33
- /**
34
- * If API base URL detection was completed, this property will hold the detected API base URL
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
- _apiBaseUrl = undefined;
38
- /**
39
- * If API base URL detection has failed, this property will hold the error with which it has failed
40
- * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
41
- */
42
- _apiBaseError = undefined;
43
- /**
44
- * Tries detecting API base URL by multiple methods
45
- *
46
- * - Checks the response headers of the current URL for the `CORNERSTONE-API-BASEURL` header.
47
- * - Attempts to load the `websettings.json` file and looks for an `api` field.
48
- * - Defaults to `undefined`.
49
- *
50
- * IMPORTANT: This method is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
51
- *
52
- * @async
53
- * @return {Promise<string | undefined>} Returns detected API base URL
54
- * @throws {Error} Throws error if either of the detection methods fails
55
- */
56
- async _getApiBaseUrl() {
57
- // Check if API already detected; don't reattempt detection
58
- if (this._apiBaseUrlDetected)
59
- return this._apiBaseUrl;
60
- // Check if detection already in progress; don't allow multiple detections in parallel
61
- if (this._apiBaseUrlPromise)
62
- return this._apiBaseUrlPromise;
63
- // Detect API base URL
64
- return (this._apiBaseUrlPromise = new Promise((resolve, reject) => (async () => {
65
- // Reset
66
- this._apiBaseUrlDetected = false;
67
- this._apiBaseUrlDetectionMethod = undefined;
68
- this._apiBaseUrl = undefined;
69
- this._apiBaseError = undefined;
70
- // Check CORNERSTONE-API-BASEURL header for API base URL
71
- try {
72
- const currentUrlResponse = await fetch(window.location.href, { method: 'GET' });
73
- const apiHeader = currentUrlResponse.headers.get('CORNERSTONE-API-BASEURL');
74
- if (apiHeader) {
75
- this._apiBaseUrlDetected = true;
76
- this._apiBaseUrlDetectionMethod = 'GET / Header:CORNERSTONE-API-BASEURL';
77
- this._apiBaseUrl = apiHeader;
78
- this._apiBaseError = undefined;
79
- this._apiBaseUrlPromise = undefined;
80
- return resolve(this._apiBaseUrl);
81
- }
82
- }
83
- catch (err) {
84
- this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from response header!');
85
- }
86
- // Check ./websettings.json header for API base URL
87
- try {
88
- const webSettingsResponse = await fetch('./websettings.json', { method: 'GET' });
89
- if (webSettingsResponse.status === 404)
90
- return (this._apiBaseUrl = undefined);
91
- const webSettings = await webSettingsResponse.json();
92
- if (webSettings?.api) {
93
- this._apiBaseUrlDetected = true;
94
- this._apiBaseUrlDetectionMethod = 'GET websettings.json';
95
- this._apiBaseUrl = webSettings.api;
96
- this._apiBaseError = undefined;
97
- this._apiBaseUrlPromise = undefined;
98
- return resolve(this._apiBaseUrl);
99
- }
100
- }
101
- catch (err) {
102
- this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from settings file!');
103
- }
104
- // Check if error caught during any of the detection methods
105
- if (this._apiBaseError !== undefined) {
106
- this._apiBaseUrlDetected = false;
107
- this._apiBaseUrlDetectionMethod = undefined;
108
- this._apiBaseUrl = undefined;
109
- this._apiBaseUrlPromise = undefined;
110
- return reject(this._apiBaseError);
111
- }
112
- // Default to no API found
113
- this._apiBaseUrlDetected = true;
114
- this._apiBaseUrlDetectionMethod = undefined;
115
- this._apiBaseUrl = undefined;
116
- this._apiBaseError = undefined;
117
- this._apiBaseUrlPromise = undefined;
118
- return resolve(this._apiBaseUrl);
119
- })()));
120
- }
121
- /**
122
- * Composes a full API URL for a provided endpoint path.
123
- *
124
- * @async
125
- * @param relativeEndpointPath Relative endpoint path
126
- * @return {Promise<string | undefined>} Returns the API endpoint's URL
127
- * @throws {Error} Throws error if API base URL detection fails
128
- */
129
- async getApiUrl(relativeEndpointPath) {
130
- const apiBaseUrl = await this._getApiBaseUrl();
131
- const domain = !apiBaseUrl ? `` : apiBaseUrl?.endsWith('/') ? apiBaseUrl : `${apiBaseUrl}/`;
132
- const path = !relativeEndpointPath.startsWith('/') ? relativeEndpointPath : relativeEndpointPath.substring(1);
133
- return `${domain}${path}`;
134
- }
135
- }
136
- /**
137
- * Singleton instance of ApiService
138
- */
139
- export const apiService = new ApiService();
1
+ export * from './initialization';
2
+ export * from './client';
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Central API configuration service, fetches and exposes Cornerstone API configuration
3
+ *
4
+ * @export
5
+ * @class ApiInitializationService
6
+ */
7
+ export declare class ApiInitializationService {
8
+ /**
9
+ * Initializes the API configuration
10
+ *
11
+ * This method calls all methods that need to be called during the API configuration phase:
12
+ * - Starts detection of API base URL.
13
+ *
14
+ * @async
15
+ * @return {Promise<void>} Resolves when initialization is complete.
16
+ */
17
+ initialize(): Promise<void>;
18
+ private _apiBaseUrlPromise;
19
+ /**
20
+ * If API base URL detection was completed
21
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
22
+ */
23
+ _apiBaseUrlDetected: boolean;
24
+ /**
25
+ * If API base URL detection was completed, this property will hold the method by which detection was performed
26
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
27
+ */
28
+ _apiBaseUrlDetectionMethod: string | undefined;
29
+ /**
30
+ * If API base URL detection was completed, this property will hold the detected API base URL
31
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
32
+ */
33
+ _apiBaseUrl: string | undefined;
34
+ /**
35
+ * If API base URL detection has failed, this property will hold the error with which it has failed
36
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
37
+ */
38
+ _apiBaseError: Error | undefined;
39
+ /**
40
+ * Tries detecting API base URL by multiple methods
41
+ *
42
+ * - Checks the response headers of the current URL for the `CORNERSTONE-API-BASEURL` header.
43
+ * - Attempts to load the `websettings.json` file and looks for an `api` field.
44
+ * - Defaults to `undefined`.
45
+ *
46
+ * IMPORTANT: This method is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
47
+ *
48
+ * @async
49
+ * @return {Promise<string | undefined>} Returns detected API base URL
50
+ * @throws {Error} Throws error if either of the detection methods fails
51
+ */
52
+ _getApiBaseUrl(): Promise<string | undefined>;
53
+ /**
54
+ * Composes a full API URL for a provided endpoint path.
55
+ *
56
+ * @async
57
+ * @param relativeEndpointPath Relative endpoint path
58
+ * @return {Promise<string | undefined>} Returns the API endpoint's URL
59
+ * @throws {Error} Throws error if API base URL detection fails
60
+ */
61
+ getApiUrl(relativeEndpointPath: string): Promise<string>;
62
+ }
63
+ /**
64
+ * Singleton instance of ApiInitializationService
65
+ */
66
+ export declare const apiInitializationService: ApiInitializationService;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Central API configuration service, fetches and exposes Cornerstone API configuration
3
+ *
4
+ * @export
5
+ * @class ApiInitializationService
6
+ */
7
+ export class ApiInitializationService {
8
+ /**
9
+ * Initializes the API configuration
10
+ *
11
+ * This method calls all methods that need to be called during the API configuration phase:
12
+ * - Starts detection of API base URL.
13
+ *
14
+ * @async
15
+ * @return {Promise<void>} Resolves when initialization is complete.
16
+ */
17
+ async initialize() {
18
+ // (Pre)detect API base URL
19
+ await this._getApiBaseUrl();
20
+ }
21
+ // #region API Base URL detection
22
+ _apiBaseUrlPromise = undefined;
23
+ /**
24
+ * If API base URL detection was completed
25
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
26
+ */
27
+ _apiBaseUrlDetected = false;
28
+ /**
29
+ * If API base URL detection was completed, this property will hold the method by which detection was performed
30
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
31
+ */
32
+ _apiBaseUrlDetectionMethod = undefined;
33
+ /**
34
+ * If API base URL detection was completed, this property will hold the detected API base URL
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
+ _apiBaseUrl = undefined;
38
+ /**
39
+ * If API base URL detection has failed, this property will hold the error with which it has failed
40
+ * IMPORTANT: This property is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
41
+ */
42
+ _apiBaseError = undefined;
43
+ /**
44
+ * Tries detecting API base URL by multiple methods
45
+ *
46
+ * - Checks the response headers of the current URL for the `CORNERSTONE-API-BASEURL` header.
47
+ * - Attempts to load the `websettings.json` file and looks for an `api` field.
48
+ * - Defaults to `undefined`.
49
+ *
50
+ * IMPORTANT: This method is not meant to be used in normal cases - when ever possible use the .getApiUrl(relativeEndpointPath: string) method instead.
51
+ *
52
+ * @async
53
+ * @return {Promise<string | undefined>} Returns detected API base URL
54
+ * @throws {Error} Throws error if either of the detection methods fails
55
+ */
56
+ async _getApiBaseUrl() {
57
+ // Check if API already detected; don't reattempt detection
58
+ if (this._apiBaseUrlDetected)
59
+ return this._apiBaseUrl;
60
+ // Check if detection already in progress; don't allow multiple detections in parallel
61
+ if (this._apiBaseUrlPromise)
62
+ return this._apiBaseUrlPromise;
63
+ // Detect API base URL
64
+ return (this._apiBaseUrlPromise = new Promise((resolve, reject) => (async () => {
65
+ // Reset
66
+ this._apiBaseUrlDetected = false;
67
+ this._apiBaseUrlDetectionMethod = undefined;
68
+ this._apiBaseUrl = undefined;
69
+ this._apiBaseError = undefined;
70
+ // Check CORNERSTONE-API-BASEURL header for API base URL
71
+ try {
72
+ const currentUrlResponse = await fetch(window.location.href, { method: 'GET' });
73
+ const apiHeader = currentUrlResponse.headers.get('CORNERSTONE-API-BASEURL');
74
+ if (apiHeader) {
75
+ this._apiBaseUrlDetected = true;
76
+ this._apiBaseUrlDetectionMethod = 'GET / Header:CORNERSTONE-API-BASEURL';
77
+ this._apiBaseUrl = apiHeader;
78
+ this._apiBaseError = undefined;
79
+ this._apiBaseUrlPromise = undefined;
80
+ return resolve(this._apiBaseUrl);
81
+ }
82
+ }
83
+ catch (err) {
84
+ this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from response header!');
85
+ }
86
+ // Check ./websettings.json header for API base URL
87
+ try {
88
+ const webSettingsResponse = await fetch('./websettings.json', { method: 'GET' });
89
+ if (webSettingsResponse.status === 404)
90
+ return (this._apiBaseUrl = undefined);
91
+ const webSettings = await webSettingsResponse.json();
92
+ if (webSettings?.api) {
93
+ this._apiBaseUrlDetected = true;
94
+ this._apiBaseUrlDetectionMethod = 'GET websettings.json';
95
+ this._apiBaseUrl = webSettings.api;
96
+ this._apiBaseError = undefined;
97
+ this._apiBaseUrlPromise = undefined;
98
+ return resolve(this._apiBaseUrl);
99
+ }
100
+ }
101
+ catch (err) {
102
+ this._apiBaseError = err instanceof Error ? err : new Error('Failed loading API base URL from settings file!');
103
+ }
104
+ // Check if error caught during any of the detection methods
105
+ if (this._apiBaseError !== undefined) {
106
+ this._apiBaseUrlDetected = false;
107
+ this._apiBaseUrlDetectionMethod = undefined;
108
+ this._apiBaseUrl = undefined;
109
+ this._apiBaseUrlPromise = undefined;
110
+ return reject(this._apiBaseError);
111
+ }
112
+ // Default to no API found
113
+ this._apiBaseUrlDetected = true;
114
+ this._apiBaseUrlDetectionMethod = undefined;
115
+ this._apiBaseUrl = undefined;
116
+ this._apiBaseError = undefined;
117
+ this._apiBaseUrlPromise = undefined;
118
+ return resolve(this._apiBaseUrl);
119
+ })()));
120
+ }
121
+ /**
122
+ * Composes a full API URL for a provided endpoint path.
123
+ *
124
+ * @async
125
+ * @param relativeEndpointPath Relative endpoint path
126
+ * @return {Promise<string | undefined>} Returns the API endpoint's URL
127
+ * @throws {Error} Throws error if API base URL detection fails
128
+ */
129
+ async getApiUrl(relativeEndpointPath) {
130
+ const apiBaseUrl = await this._getApiBaseUrl();
131
+ const domain = !apiBaseUrl ? `` : apiBaseUrl?.endsWith('/') ? apiBaseUrl : `${apiBaseUrl}/`;
132
+ const path = !relativeEndpointPath.startsWith('/') ? relativeEndpointPath : relativeEndpointPath.substring(1);
133
+ return `${domain}${path}`;
134
+ }
135
+ }
136
+ /**
137
+ * Singleton instance of ApiInitializationService
138
+ */
139
+ export const apiInitializationService = new ApiInitializationService();
@@ -25,7 +25,7 @@ export declare class AuthService<TId, TUser = User<TId>> {
25
25
  * @return {Promise<TUser>} Currently logged in user's details
26
26
  * @throws {Error} Error if fetch fails
27
27
  */
28
- whoAmI(): Promise<TUser>;
28
+ whoAmI(): Promise<TUser | undefined>;
29
29
  /**
30
30
  * Authenticates a user using their username and password, and retrieves user's details
31
31
  *
@@ -42,8 +42,9 @@ export declare class AuthService<TId, TUser = User<TId>> {
42
42
  * @throws {Error} Error if fetch fails
43
43
  */
44
44
  signout(): Promise<void>;
45
+ /**
46
+ * If any API response returns an "Unauthenticated" response, call this method
47
+ * to update local authentication state to unauthenticated
48
+ */
49
+ _handleResponse401(): void;
45
50
  }
46
- /**
47
- * Singleton instance of AuthService
48
- */
49
- export declare const authService: AuthService<unknown, User<unknown>>;
@@ -1,4 +1,4 @@
1
- import { apiService } from '../api';
1
+ import { apiInitializationService } from '../api';
2
2
  /**
3
3
  * AuthService class is responsible for managing user authentication operations.
4
4
  * It supports login, logout, and checking the current user's details.
@@ -29,13 +29,17 @@ export class AuthService {
29
29
  */
30
30
  async whoAmI() {
31
31
  try {
32
- const url = await apiService.getApiUrl('/auth/whoami');
32
+ const url = await apiInitializationService.getApiUrl('/auth/whoami');
33
33
  const response = await fetch(url, { credentials: 'include' });
34
34
  if (response.ok) {
35
35
  const info = await response.json();
36
36
  return (this.user = info.user);
37
37
  }
38
+ else if (response.status === 401) {
39
+ return (this.user = undefined);
40
+ }
38
41
  else {
42
+ this.user = undefined;
39
43
  throw new Error('Failed checking authentication status');
40
44
  }
41
45
  }
@@ -53,7 +57,7 @@ export class AuthService {
53
57
  */
54
58
  async signin(username, password) {
55
59
  try {
56
- const url = await apiService.getApiUrl('/auth/signin');
60
+ const url = await apiInitializationService.getApiUrl('/auth/signin');
57
61
  const response = await fetch(url, {
58
62
  method: 'POST',
59
63
  headers: { 'Content-Type': 'application/json' },
@@ -61,7 +65,13 @@ export class AuthService {
61
65
  credentials: 'include',
62
66
  });
63
67
  if (response.ok) {
64
- return (this.user = await this.whoAmI());
68
+ const user = await this.whoAmI();
69
+ if (user !== undefined) {
70
+ return (this.user = user);
71
+ }
72
+ else {
73
+ throw new Error('Failed signing in');
74
+ }
65
75
  }
66
76
  else {
67
77
  throw new Error('Failed signing in');
@@ -79,7 +89,7 @@ export class AuthService {
79
89
  */
80
90
  async signout() {
81
91
  try {
82
- const url = await apiService.getApiUrl('/auth/signout');
92
+ const url = await apiInitializationService.getApiUrl('/auth/signout');
83
93
  const response = await fetch(url, { credentials: 'include' });
84
94
  if (response.ok) {
85
95
  this.user = undefined;
@@ -93,8 +103,11 @@ export class AuthService {
93
103
  throw error instanceof Error ? error : new Error('Failed signing out');
94
104
  }
95
105
  }
106
+ /**
107
+ * If any API response returns an "Unauthenticated" response, call this method
108
+ * to update local authentication state to unauthenticated
109
+ */
110
+ _handleResponse401() {
111
+ this.user = undefined;
112
+ }
96
113
  }
97
- /**
98
- * Singleton instance of AuthService
99
- */
100
- export const authService = new AuthService();