@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 +6 -14
- package/package.json +1 -1
- package/services/api/client/crud/index.d.ts +9 -0
- package/services/api/client/crud/index.js +9 -0
- package/services/api/client/index.d.ts +2 -0
- package/services/api/client/index.js +2 -0
- package/services/api/client/read/index.d.ts +8 -0
- package/services/api/client/read/index.js +8 -0
- package/services/api/detection/index.d.ts +66 -0
- package/services/api/detection/index.js +139 -0
- package/services/api/index.d.ts +2 -66
- package/services/api/index.js +2 -139
- package/services/api/initialization/index.d.ts +66 -0
- package/services/api/initialization/index.js +139 -0
- package/services/auth/index.d.ts +6 -5
- package/services/auth/index.js +22 -9
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 {
|
|
37
|
-
const
|
|
38
|
-
await
|
|
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 {
|
|
45
|
-
await
|
|
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
|
|
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
|
@@ -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,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();
|
package/services/api/index.d.ts
CHANGED
|
@@ -1,66 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
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';
|
package/services/api/index.js
CHANGED
|
@@ -1,139 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
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();
|
package/services/auth/index.d.ts
CHANGED
|
@@ -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>>;
|
package/services/auth/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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();
|