@webex/byods 0.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +66 -0
  2. package/dist/Errors/catalog/ExtendedError.js +24 -0
  3. package/dist/Errors/types.js +32 -0
  4. package/dist/Logger/index.js +202 -0
  5. package/dist/Logger/types.js +31 -0
  6. package/dist/base-client/index.js +182 -0
  7. package/dist/byods/index.js +127 -0
  8. package/dist/constants.js +25 -0
  9. package/dist/data-source-client/constants.js +7 -0
  10. package/dist/data-source-client/index.js +205 -0
  11. package/dist/data-source-client/types.js +5 -0
  12. package/dist/http-client/types.js +5 -0
  13. package/dist/http-utils/index.js +140 -0
  14. package/dist/index.js +48 -0
  15. package/dist/token-manager/index.js +232 -0
  16. package/dist/token-storage-adapter/index.js +80 -0
  17. package/dist/token-storage-adapter/types.js +5 -0
  18. package/dist/types/Errors/catalog/ExtendedError.d.ts +14 -0
  19. package/dist/types/Errors/types.d.ts +32 -0
  20. package/dist/types/Logger/index.d.ts +71 -0
  21. package/dist/types/Logger/types.d.ts +27 -0
  22. package/dist/types/base-client/index.d.ts +91 -0
  23. package/dist/types/byods/index.d.ts +43 -0
  24. package/dist/types/constants.d.ts +17 -0
  25. package/dist/types/data-source-client/constants.d.ts +1 -0
  26. package/dist/types/data-source-client/index.d.ts +87 -0
  27. package/dist/types/data-source-client/types.d.ts +108 -0
  28. package/dist/types/http-client/types.d.ts +53 -0
  29. package/dist/types/http-utils/index.d.ts +70 -0
  30. package/dist/types/index.d.ts +8 -0
  31. package/dist/types/token-manager/index.d.ts +101 -0
  32. package/dist/types/token-storage-adapter/index.d.ts +54 -0
  33. package/dist/types/token-storage-adapter/types.d.ts +46 -0
  34. package/dist/types/types.d.ts +112 -0
  35. package/dist/types.js +5 -0
  36. package/package.json +121 -0
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DATASOURCE_ENDPOINT = void 0;
7
+ const DATASOURCE_ENDPOINT = exports.DATASOURCE_ENDPOINT = '/dataSources';
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _jose = require("jose");
8
+ var _types = require("../Errors/types");
9
+ var _ExtendedError = _interopRequireDefault(require("../Errors/catalog/ExtendedError"));
10
+ var _constants = require("./constants");
11
+ var _constants2 = require("../constants");
12
+ var _Logger = _interopRequireDefault(require("../Logger"));
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+ /**
15
+ * Client for interacting with the /dataSource API.
16
+ */
17
+ class DataSourceClient {
18
+ /**
19
+ * Creates an instance of DataSourceClient.
20
+ * @param {HttpClient} httpClient - The HttpClient instance to use for API requests.
21
+ * @example
22
+ * const httpClient = new HttpClient();
23
+ * const client = new DataSourceClient(httpClient);
24
+ */
25
+ constructor(httpClient, loggerConfig = _constants2.DEFAULT_LOGGER_CONFIG) {
26
+ this.httpClient = httpClient;
27
+ this.loggerConfig = loggerConfig;
28
+ _Logger.default.setLogger(this.loggerConfig.level, _constants2.BYODS_DATA_SOURCE_CLIENT_MODULE);
29
+ }
30
+
31
+ /**
32
+ * Creates a new data source.
33
+ * @param {DataSourceRequest} createDataSourceRequest - The request object for creating a data source.
34
+ * @returns {Promise<ApiResponse<DataSourceResponse>>} - A promise that resolves to the API response containing the created data source.
35
+ * @example
36
+ * const request: DataSourceRequest = { name: 'New DataSource', url: 'https://mydatasource.com', schemaId: '123', audience: 'myaudience', subject: 'mysubject', nonce: 'uniqueNonce' };
37
+ * const response = await client.create(request);
38
+ */
39
+ async create(dataSourcePayload) {
40
+ return this.httpClient.post(_constants.DATASOURCE_ENDPOINT, dataSourcePayload); // TODO: Move /dataSources to constants
41
+ }
42
+
43
+ /**
44
+ * Retrieves a data source by ID.
45
+ * @param {string} id - The ID of the data source to retrieve.
46
+ * @returns {Promise<ApiResponse<DataSourceResponse>>} - A promise that resolves to the API response containing the data source.
47
+ * @example
48
+ * const id = '123';
49
+ * const response = await client.get(id);
50
+ */
51
+ async get(id) {
52
+ return this.httpClient.get(`${_constants.DATASOURCE_ENDPOINT}/${id}`);
53
+ }
54
+
55
+ /**
56
+ * Lists all data sources.
57
+ * @returns {Promise<ApiResponse<DataSourceResponse[]>>} - A promise that resolves to the API response containing the list of data sources.
58
+ * @example
59
+ * const response = await client.list();
60
+ */
61
+ async list() {
62
+ return this.httpClient.get(_constants.DATASOURCE_ENDPOINT).then(response => ({
63
+ ...response,
64
+ data: response.data.items
65
+ }));
66
+ }
67
+
68
+ /**
69
+ * Updates a data source by ID.
70
+ * @param {string} id - The ID of the data source to update.
71
+ * @param {DataSourceRequest} updateDataSourceRequest - The request object for updating a data source.
72
+ * @returns {Promise<ApiResponse<DataSourceResponse>>} - A promise that resolves to the API response containing the updated data source.
73
+ * @example
74
+ * const id = '123';
75
+ * const request: DataSourceRequest = { name: 'Updated DataSource', url: 'https://mydatasource.com', schemaId: '123', audience: 'myaudience', subject: 'mysubject', nonce: 'uniqueNonce' };
76
+ * const response = await client.update(id, request);
77
+ */
78
+ async update(id, dataSourcePayload) {
79
+ return this.httpClient.put(`${_constants.DATASOURCE_ENDPOINT}/${id}`, dataSourcePayload);
80
+ }
81
+
82
+ /**
83
+ * Deletes a data source by ID.
84
+ * @param {string} id - The ID of the data source to delete.
85
+ * @returns {Promise<ApiResponse<void>>} - A promise that resolves to the API response confirming the deletion.
86
+ * @example
87
+ * const id = '123';
88
+ * const response = await client.delete(id);
89
+ */
90
+ async delete(id) {
91
+ return this.httpClient.delete(`${_constants.DATASOURCE_ENDPOINT}/${id}`);
92
+ }
93
+
94
+ /**
95
+ * This method refreshes the DataSource token using dataSourceId, tokenLifetimeMinutes & nonceGenerator.
96
+ * @param {string} dataSourceId The id of the data source.
97
+ * @param {number} tokenLifetimeMinutes The refresh interval in minutes for the data source. Defaults to 60 mins. Should be an integer.
98
+ * @param {string} nonceGenerator Accepts an nonceGenerator, developer can provide their own nonceGenerator, defaults to randomUUID.
99
+ * @returns {Promise<Cancelable>} A promise that resolves to an object containing a cancel method that cancels the timer.
100
+ * @example
101
+ * try {
102
+ const result = await dataSourceClient.scheduleJWSTokenRefresh('myDataSourceId', 'uniqueNonce', 'myTokenLifeTimeMinutes');
103
+ // Use the cancel function if needed!
104
+ result.cancel();
105
+ } catch (error) {
106
+ console.error('Error scheduling JWS refresh for data source:', error);
107
+ }
108
+ */
109
+
110
+ async scheduleJWSTokenRefresh(dataSourceId, tokenLifetimeMinutes = 60, nonceGenerator = crypto.randomUUID) {
111
+ try {
112
+ const timer = await this.startAutoRefresh(dataSourceId, tokenLifetimeMinutes, nonceGenerator);
113
+ const cancel = () => {
114
+ if (timer) {
115
+ try {
116
+ clearInterval(timer);
117
+ _Logger.default.info(`JWS Auto-refresh has been cancelled successfully for dataSource: ${dataSourceId}`, {
118
+ file: _constants2.BYODS_DATA_SOURCE_CLIENT_MODULE,
119
+ method: 'scheduleJWSTokenRefresh'
120
+ });
121
+ } catch (error) {
122
+ _Logger.default.error(new _ExtendedError.default(`Failed to cancel the timer, the timer might have expired or is invalid ${error}`, _types.ERROR_TYPE.SCHEDULE_JWS_TOKEN_REFRESH_ERROR), {
123
+ file: _constants2.BYODS_DATA_SOURCE_CLIENT_MODULE,
124
+ method: 'scheduleJWSTokenRefresh'
125
+ });
126
+ throw new Error('Failed to cancel the timer, the timer might have expired or is invalid');
127
+ }
128
+ }
129
+ };
130
+ return Promise.resolve({
131
+ cancel
132
+ });
133
+ } catch (error) {
134
+ _Logger.default.error(new _ExtendedError.default(`Encountered error while trying to setup JWS refresh schedule ${error}`, _types.ERROR_TYPE.SCHEDULE_JWS_TOKEN_REFRESH_ERROR), {
135
+ file: _constants2.BYODS_DATA_SOURCE_CLIENT_MODULE,
136
+ method: 'scheduleJWSTokenRefresh'
137
+ });
138
+ return Promise.reject(error);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Sets up a timer that will auto refresh the DataSource JWS token for given intervals
144
+ * @param {string} dataSourceId The id of the data source
145
+ * @param {number} tokenLifetimeMinutes The refresh interval in minutes for the data source. Defaults to 60 mins. Should be <= 1440 & >=1.
146
+ * @param {string} nonceGenerator Accepts an nonceGenerator that will generate nonce for the data source request.
147
+ * @returns {Promise<NodeJS.Timeout>} A promise that resolves to the API response containing NodeJS.Timeout.
148
+ */
149
+ async startAutoRefresh(dataSourceId, tokenLifetimeMinutes, nonceGenerator) {
150
+ try {
151
+ // Generate a random percentage between 5% and 10%
152
+ const randomPercentage = Math.random() * 5 + 5;
153
+ // Then calculate the reducedTokenLifetimeMinutes
154
+ const reducedTokenLifetimeMinutes = Math.ceil(tokenLifetimeMinutes * (1 - randomPercentage / 100));
155
+ const interval = setInterval(async () => {
156
+ try {
157
+ // Fetch the dataSource details before each update
158
+ const getMethodResponse = await this.get(dataSourceId);
159
+ if (!getMethodResponse || !getMethodResponse.data || !getMethodResponse.data.jwsToken) {
160
+ throw new Error('Invalid response from get method.');
161
+ }
162
+ const jwsToken = getMethodResponse?.data?.jwsToken;
163
+ const jwsTokenPayload = (0, _jose.decodeJwt)(jwsToken);
164
+ if (!jwsTokenPayload || !jwsToken) {
165
+ throw new Error('jwsTokenPayload or jwsToken is undefined.');
166
+ }
167
+ const payloadForDataSourceUpdateMethod = {
168
+ schemaId: getMethodResponse?.data?.schemaId,
169
+ tokenLifetimeMinutes,
170
+ url: jwsTokenPayload['com.cisco.datasource.url'],
171
+ subject: jwsTokenPayload.sub,
172
+ audience: jwsTokenPayload.aud,
173
+ nonce: nonceGenerator ? nonceGenerator() : crypto.randomUUID(),
174
+ status: getMethodResponse?.data?.status
175
+ };
176
+ await this.update(dataSourceId, payloadForDataSourceUpdateMethod);
177
+ _Logger.default.info('dataSource has been refreshed successfully', {
178
+ file: _constants2.BYODS_DATA_SOURCE_CLIENT_MODULE,
179
+ method: 'startAutoRefresh'
180
+ });
181
+ return Promise.resolve();
182
+ } catch (updateError) {
183
+ // If there is some error then clear the Interval only if interval is active
184
+ if (interval) {
185
+ clearInterval(interval);
186
+ }
187
+ _Logger.default.error(new _ExtendedError.default(`Error while updating dataSource token ${updateError}`, _types.ERROR_TYPE.START_AUTO_REFRESH_ERROR), {
188
+ file: _constants2.BYODS_DATA_SOURCE_CLIENT_MODULE,
189
+ method: 'startAutoRefresh'
190
+ });
191
+ return Promise.reject(new Error(`Error while starting auto-refresh for dataSource token: ${updateError}`));
192
+ }
193
+ }, reducedTokenLifetimeMinutes * 60 * 1000); // Converts minutes to milliseconds.
194
+
195
+ return Promise.resolve(interval);
196
+ } catch (error) {
197
+ _Logger.default.error(new _ExtendedError.default(`Error while starting auto-refresh for dataSource token: ${error}`, _types.ERROR_TYPE.START_AUTO_REFRESH_ERROR), {
198
+ file: _constants2.BYODS_DATA_SOURCE_CLIENT_MODULE,
199
+ method: 'startAutoRefresh'
200
+ });
201
+ return Promise.reject(new Error(`Error while starting auto-refresh for dataSource token: ${error}`));
202
+ }
203
+ }
204
+ }
205
+ exports.default = DataSourceClient;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.httpUtils = void 0;
7
+ exports.put = put;
8
+ var _crypto = require("crypto");
9
+ var _constants = require("../constants");
10
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
11
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
12
+ /**
13
+ * Makes an HTTP request.
14
+ * @param {string} url - The URL for the request.
15
+ * @param {HttpRequestInit} [options=\{\}] - The request options.
16
+ * @returns {Promise<ApiResponse<T>>} - The API response.
17
+ * @example
18
+ * const response = await request('https://webexapis.com/v1/endpoint', { method: 'GET', headers: {} });
19
+ */
20
+ async function request(url, options = {}) {
21
+ // TODO: Fix this issue (which is being tracked in node_fetch) https://github.com/node-fetch/node-fetch/issues/1809
22
+ const fetch = (await Promise.resolve().then(() => _interopRequireWildcard(require('node-fetch')))).default;
23
+ const controller = new AbortController();
24
+ const timeout = options.timeout || 30000; // Default 30s timeout
25
+
26
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
27
+ const optionsWithHeaders = {
28
+ ...options,
29
+ signal: controller.signal,
30
+ headers: {
31
+ Trackingid: `${_constants.BYODS_PACKAGE_NAME}_${(0, _crypto.randomUUID)()}`,
32
+ 'User-Agent': _constants.USER_AGENT,
33
+ ...options.headers
34
+ }
35
+ };
36
+ try {
37
+ const response = await fetch(url, optionsWithHeaders);
38
+ if (!response.ok) {
39
+ throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
40
+ }
41
+
42
+ // Handle 204 No Content responses
43
+ if (response.status === 204) {
44
+ return {
45
+ data: {},
46
+ status: response.status
47
+ };
48
+ }
49
+ const data = await response.json();
50
+ return {
51
+ data,
52
+ status: response.status
53
+ };
54
+ } finally {
55
+ clearTimeout(timeoutId);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Makes an HTTP GET request.
61
+ * @param {string} url - The URL for the request.
62
+ * @param {HttpRequestInit} [options=\{\}] - The request options.
63
+ * @returns {Promise<ApiResponse<T>>} - The API response.
64
+ * @example
65
+ * const response = await get('https://webexapis.com/v1/endpoint', { headers: {} });
66
+ */
67
+ async function get(url, options = {}) {
68
+ return httpUtils.request(url, {
69
+ ...options,
70
+ method: 'GET'
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Makes an HTTP POST request.
76
+ * @param {string} url - The URL for the request.
77
+ * @param {HttpRequestInit} [options=\{\}] - The request options.
78
+ * @returns {Promise<ApiResponse<T>>} - The API response.
79
+ * @example
80
+ * const response = await post('https://webexapis.com/v1/endpoint', { headers: {} });
81
+ */
82
+ async function post(url, options = {}) {
83
+ return httpUtils.request(url, {
84
+ ...options,
85
+ method: 'POST'
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Makes an HTTP PUT request.
91
+ * @param {string} url - The URL for the request.
92
+ * @param {HttpRequestInit} [options=\{\}] - The request options.
93
+ * @returns {Promise<ApiResponse<T>>} - The API response.
94
+ * @example
95
+ * const response = await put('https://webexapis.com/v1/endpoint', { headers: {} });
96
+ */
97
+ async function put(url, options = {}) {
98
+ return httpUtils.request(url, {
99
+ ...options,
100
+ method: 'PUT'
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Makes an HTTP DELETE request.
106
+ * @param {string} url - The URL for the request.
107
+ * @param {HttpRequestInit} [options=\{\}] - The request options.
108
+ * @returns {Promise<ApiResponse<T>>} - The API response.
109
+ * @example
110
+ * const response = await del('https://webexapis.com/v1/endpoint', { headers: {} });
111
+ */
112
+ async function del(url, options = {}) {
113
+ return httpUtils.request(url, {
114
+ ...options,
115
+ method: 'DELETE'
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Makes an HTTP PATCH request.
121
+ * @param {string} url - The URL for the request.
122
+ * @param {HttpRequestInit} [options=\{\}] - The request options.
123
+ * @returns {Promise<ApiResponse<T>>} - The API response.
124
+ * @example
125
+ * const response = await patch('https://webexapis.com/v1/endpoint', { headers: {} });
126
+ */
127
+ async function patch(url, options = {}) {
128
+ return httpUtils.request(url, {
129
+ ...options,
130
+ method: 'PATCH'
131
+ });
132
+ }
133
+ const httpUtils = exports.httpUtils = {
134
+ request,
135
+ get,
136
+ post,
137
+ put,
138
+ del,
139
+ patch
140
+ };
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "BYODS", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _byods.default;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "BaseClient", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _baseClient.default;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "DataSourceClient", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _dataSourceClient.default;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "InMemoryTokenStorageAdapter", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _tokenStorageAdapter.InMemoryTokenStorageAdapter;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "LOGGER", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _types.LOGGER;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "TokenManager", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _tokenManager.default;
40
+ }
41
+ });
42
+ var _byods = _interopRequireDefault(require("./byods"));
43
+ var _tokenManager = _interopRequireDefault(require("./token-manager"));
44
+ var _baseClient = _interopRequireDefault(require("./base-client"));
45
+ var _dataSourceClient = _interopRequireDefault(require("./data-source-client"));
46
+ var _tokenStorageAdapter = require("./token-storage-adapter");
47
+ var _types = require("./Logger/types");
48
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _constants = require("../constants");
8
+ var _httpUtils = require("../http-utils");
9
+ var _ExtendedError = _interopRequireDefault(require("../Errors/catalog/ExtendedError"));
10
+ var _types = require("../Errors/types");
11
+ var _Logger = _interopRequireDefault(require("../Logger"));
12
+ var _tokenStorageAdapter = require("../token-storage-adapter");
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+ /**
15
+ * The token manager for the BYoDS SDK.
16
+ */
17
+ class TokenManager {
18
+ /**
19
+ * Creates an instance of TokenManager.
20
+ *
21
+ * @param clientId - The client ID of the service app.
22
+ * @param clientSecret - The client secret of the service app.
23
+ * @param baseUrl - The base URL for the API. Defaults to `PRODUCTION_BASE_URL`.
24
+ * @example
25
+ * const tokenManager = new TokenManager('your-client-id', 'your-client-secret');
26
+ */
27
+ constructor(clientId, clientSecret, baseUrl = _constants.PRODUCTION_BASE_URL, tokenStorageAdapter = new _tokenStorageAdapter.InMemoryTokenStorageAdapter(), loggerConfig = _constants.DEFAULT_LOGGER_CONFIG) {
28
+ if (!clientId || !clientSecret) {
29
+ throw new Error('clientId and clientSecret are required');
30
+ }
31
+ this.clientId = clientId;
32
+ this.clientSecret = clientSecret;
33
+ this.baseUrl = baseUrl;
34
+ this.loggerConfig = loggerConfig;
35
+ this.serviceAppId = Buffer.from(`${_constants.APPLICATION_ID_PREFIX}${clientId}`).toString('base64');
36
+ this.tokenStorageAdapter = tokenStorageAdapter;
37
+ _Logger.default.setLogger(this.loggerConfig.level, _constants.BYODS_TOKEN_MANAGER_MODULE);
38
+ }
39
+
40
+ /**
41
+ * Update the tokens and their expiration times.
42
+ * @param {TokenResponse} data - The token response data.
43
+ * @param {string} orgId - The organization ID.
44
+ * @returns {void}
45
+ * @example
46
+ * await tokenManager.updateServiceAppToken(tokenResponse, 'org-id');
47
+ */
48
+ async updateServiceAppToken(data, orgId) {
49
+ const serviceAppToken = {
50
+ accessToken: data.access_token,
51
+ refreshToken: data.refresh_token,
52
+ expiresAt: new Date(Date.now() + data.expires_in * 1000),
53
+ // Adding 1000 here to represent milliseconds
54
+ refreshAccessTokenExpiresAt: new Date(Date.now() + data.refresh_token_expires_in * 1000) // Adding 1000 here to represent milliseconds
55
+ };
56
+
57
+ await this.tokenStorageAdapter.setToken(orgId, {
58
+ orgId,
59
+ serviceAppToken
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Get the service app ID.
65
+ * @returns {string}
66
+ * @example
67
+ * const serviceAppId = tokenManager.getServiceAppId();
68
+ */
69
+ getServiceAppId() {
70
+ return this.serviceAppId;
71
+ }
72
+
73
+ /**
74
+ * Get the service app authorization data for a given organization ID.
75
+ * @param {string} orgId - The organization ID.
76
+ * @returns {Promise<OrgServiceAppAuthorization>}
77
+ * @example
78
+ * const authorization = await tokenManager.getOrgServiceAppAuthorization('org-id');
79
+ */
80
+ async getOrgServiceAppAuthorization(orgId) {
81
+ try {
82
+ let token = await this.getTokenFromAdapter(orgId);
83
+ const currentTime = new Date();
84
+ if (token.serviceAppToken.expiresAt <= currentTime) {
85
+ await this.saveServiceAppRegistrationData(orgId, token.serviceAppToken.refreshToken);
86
+ token = await this.getTokenFromAdapter(orgId); // Fetch the refreshed token
87
+ }
88
+
89
+ return token;
90
+ } catch (error) {
91
+ _Logger.default.error(new _ExtendedError.default('Error fetching token', _types.ERROR_TYPE.TOKEN_ERROR), {
92
+ file: _constants.BYODS_TOKEN_MANAGER_MODULE,
93
+ method: 'getOrgServiceAppAuthorization'
94
+ });
95
+ throw error;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * List all stored tokens.
101
+ * @returns {Promise<OrgServiceAppAuthorization[]>} List of tokens
102
+ * @example
103
+ * const tokens = await tokenManager.listTokens();
104
+ */
105
+ async listTokens() {
106
+ return this.tokenStorageAdapter.listTokens();
107
+ }
108
+
109
+ /**
110
+ * Delete a token for a given orgId.
111
+ * @param {string} orgId - The organization ID.
112
+ * @returns {Promise<void>}
113
+ * @example
114
+ * await tokenManager.deleteToken('org-id');
115
+ */
116
+ async deleteToken(orgId) {
117
+ await this.tokenStorageAdapter.deleteToken(orgId);
118
+ }
119
+
120
+ /**
121
+ * Remove all tokens stored in the TokenStorageAdapter.
122
+ * @returns {Promise<void>}
123
+ * @example
124
+ * await tokenManager.resetTokens();
125
+ */
126
+ async resetTokens() {
127
+ await this.tokenStorageAdapter.resetTokens();
128
+ }
129
+
130
+ /**
131
+ * Retrieve a new service app token using the service app owner's personal access token(PAT).
132
+ * @param {string} orgId - The organization ID.
133
+ * @param {string} personalAccessToken - The service app owner's personal access token or token from an integration that has the scope `spark:applications_token`.
134
+ * @returns {Promise<void>}
135
+ * await tokenManager.getServiceAppTokenUsingPAT('org-id', 'personal-access-token');
136
+ */
137
+ async getServiceAppTokenUsingPAT(orgId, personalAccessToken, headers = {}) {
138
+ try {
139
+ const response = await _httpUtils.httpUtils.post(`${this.baseUrl}/access_token`, {
140
+ headers: {
141
+ Authorization: `Bearer ${personalAccessToken}`,
142
+ 'Content-Type': 'application/json',
143
+ ...headers
144
+ },
145
+ body: JSON.stringify({
146
+ targetOrgId: orgId,
147
+ clientId: this.clientId,
148
+ clientSecret: this.clientSecret
149
+ })
150
+ });
151
+ await this.updateServiceAppToken(response.data, orgId);
152
+ } catch (error) {
153
+ _Logger.default.error(new _ExtendedError.default('Error retrieving token after authorization', _types.ERROR_TYPE.TOKEN_ERROR), {
154
+ file: _constants.BYODS_TOKEN_MANAGER_MODULE,
155
+ method: 'getServiceAppTokenUsingPAT'
156
+ });
157
+ throw error;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Refresh the access token using the refresh token.
163
+ * @param {string} orgId - The organization ID.
164
+ * @returns {Promise<void>}
165
+ * await tokenManager.refreshServiceAppAccessToken('org-id');
166
+ */
167
+ async refreshServiceAppAccessToken(orgId, headers = {}) {
168
+ if (!orgId) {
169
+ throw new Error('orgId not provided');
170
+ }
171
+ let serviceAppAuthorization;
172
+ try {
173
+ serviceAppAuthorization = await this.getTokenFromAdapter(orgId);
174
+ } catch (error) {
175
+ _Logger.default.error(new _ExtendedError.default('Error fetching token', _types.ERROR_TYPE.TOKEN_ERROR), {
176
+ file: _constants.BYODS_TOKEN_MANAGER_MODULE,
177
+ method: 'refreshServiceAppAccessToken'
178
+ });
179
+ throw error;
180
+ }
181
+ const refreshToken = serviceAppAuthorization?.serviceAppToken.refreshToken;
182
+ if (!refreshToken) {
183
+ throw new Error(`Refresh token was not found for org:${orgId}`);
184
+ }
185
+ await this.saveServiceAppRegistrationData(orgId, refreshToken, headers);
186
+ }
187
+
188
+ /**
189
+ * Save the service app registration using the provided refresh token.
190
+ * After saving, it can be fetched by using the {@link getOrgServiceAppAuthorization} method.
191
+ * @param {string} orgId - The organization ID.
192
+ * @param {string} refreshToken - The refresh token.
193
+ * @returns {Promise<void>}
194
+ * @example
195
+ * await tokenManager.saveServiceAppRegistrationData('org-id', 'refresh-token');
196
+ */
197
+ async saveServiceAppRegistrationData(orgId, refreshToken, headers = {}) {
198
+ try {
199
+ const response = await _httpUtils.httpUtils.post(`${this.baseUrl}/access_token`, {
200
+ headers: {
201
+ 'Content-Type': 'application/x-www-form-urlencoded',
202
+ // https://developer.webex.com/docs/login-with-webex#access-token-endpoint
203
+ ...headers
204
+ },
205
+ body: new URLSearchParams({
206
+ grant_type: 'refresh_token',
207
+ client_id: this.clientId,
208
+ client_secret: this.clientSecret,
209
+ refresh_token: refreshToken
210
+ }).toString()
211
+ });
212
+ await this.updateServiceAppToken(response.data, orgId);
213
+ } catch (error) {
214
+ _Logger.default.error(new _ExtendedError.default('Error saving service app registration', _types.ERROR_TYPE.REGISTRATION_ERROR), {
215
+ file: _constants.BYODS_TOKEN_MANAGER_MODULE,
216
+ method: 'saveServiceAppRegistration'
217
+ });
218
+ throw error;
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Proxy for extracting the token from the token adapter
224
+ * @param {string} orgId - The organization ID.
225
+ * @returns {Promise<OrgServiceAppAuthorization>}
226
+ * @private
227
+ */
228
+ async getTokenFromAdapter(orgId) {
229
+ return this.tokenStorageAdapter.getToken(orgId);
230
+ }
231
+ }
232
+ exports.default = TokenManager;