@dronedeploy/rocos-js-sdk 4.4.11 → 4.4.13

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.
@@ -6,6 +6,7 @@ export declare class AuthService extends BaseServiceAbstract implements IBaseSer
6
6
  private token?;
7
7
  private tokenRefreshTimeoutId?;
8
8
  private tokenSubject$;
9
+ private pendingTokenRequest?;
9
10
  constructor(config: IRocosSDKConfig);
10
11
  /** Observable for token updates
11
12
  *
@@ -54,6 +55,8 @@ export declare class AuthService extends BaseServiceAbstract implements IBaseSer
54
55
  * This will return the current token if it is not expired, otherwise it will get a new token based on your config.
55
56
  */
56
57
  getToken(): Promise<Token>;
58
+ private getOrRequestToken;
59
+ private fetchNewToken;
57
60
  /**
58
61
  * Refresh the authentication token
59
62
  *
@@ -50,25 +50,27 @@ class AuthService extends BaseServiceAbstract_1.BaseServiceAbstract {
50
50
  if (expiresIn !== undefined) {
51
51
  this.logger.warn('setToken(value, expiresIn) is deprecated. Use setToken(value) instead');
52
52
  }
53
- if (typeof value === 'string') {
54
- this.token = new models_1.Token(value);
55
- }
56
- else {
57
- this.token = value;
53
+ const token = typeof value === 'string' ? new models_1.Token(value) : value;
54
+ if (token.value !== this.token?.value) {
55
+ this.pendingTokenRequest = undefined;
58
56
  }
57
+ this.token = token;
59
58
  this.config.token = this.token.value;
60
59
  // output a message for token change
61
60
  if (this.isTokenValid(this.token)) {
62
61
  this.tokenSubject$.next(this.token);
63
62
  }
64
63
  else {
65
- void this.getToken();
64
+ void this.getToken().catch((error) => {
65
+ this.logger.error('Failed to initialize auth token', error);
66
+ });
66
67
  }
67
68
  }
68
69
  /**
69
70
  * Clear token
70
71
  */
71
72
  clearToken() {
73
+ this.pendingTokenRequest = undefined;
72
74
  this.token = undefined;
73
75
  this.config.token = undefined;
74
76
  }
@@ -125,15 +127,38 @@ class AuthService extends BaseServiceAbstract_1.BaseServiceAbstract {
125
127
  * This will return the current token if it is not expired, otherwise it will get a new token based on your config.
126
128
  */
127
129
  async getToken() {
128
- if (this.token && this.isTokenValid(this.token)) {
129
- if (this.token.isExpiredIn(15, 'minutes')) {
130
- this.logger.info('Token expires within 15 minutes. Refreshing.');
131
- const newToken = await this.refreshToken();
132
- this.setToken(newToken);
133
- return newToken;
134
- }
130
+ if (this.token && this.isTokenValid(this.token) && !this.token.isExpiredIn(15, 'minutes')) {
135
131
  return this.token;
136
132
  }
133
+ return this.getOrRequestToken(() => this.fetchNewToken());
134
+ }
135
+ getOrRequestToken(fetchToken) {
136
+ if (this.pendingTokenRequest) {
137
+ return this.pendingTokenRequest;
138
+ }
139
+ const request = fetchToken()
140
+ .then((token) => {
141
+ if (this.pendingTokenRequest === request) {
142
+ this.setToken(token);
143
+ }
144
+ else if (this.token) {
145
+ return this.getToken();
146
+ }
147
+ return token;
148
+ })
149
+ .finally(() => {
150
+ if (this.pendingTokenRequest === request) {
151
+ this.pendingTokenRequest = undefined;
152
+ }
153
+ });
154
+ this.pendingTokenRequest = request;
155
+ return request;
156
+ }
157
+ async fetchNewToken() {
158
+ if (this.token && this.isTokenValid(this.token)) {
159
+ this.logger.info('Token expires within 15 minutes. Refreshing.');
160
+ return this.refreshToken();
161
+ }
137
162
  let token;
138
163
  try {
139
164
  if ('appId' in this.config && 'appKey' in this.config) {
@@ -159,7 +184,6 @@ class AuthService extends BaseServiceAbstract_1.BaseServiceAbstract {
159
184
  this.logger.error(message);
160
185
  throw new models_1.RocosError(message, models_1.errorCodes.AUTH_ERROR);
161
186
  }
162
- this.setToken(token);
163
187
  return token;
164
188
  }
165
189
  /**
@@ -192,9 +216,8 @@ class AuthService extends BaseServiceAbstract_1.BaseServiceAbstract {
192
216
  */
193
217
  async refreshTokenIfExpired(minutes) {
194
218
  if (this.token?.isExpiredIn(minutes, 'minutes')) {
195
- this.logger.info('Token expires in 15 minutes. Refreshing.');
196
- const newToken = await this.refreshToken();
197
- this.setToken(newToken);
219
+ this.logger.info(`Token expires in ${minutes} minutes. Refreshing.`);
220
+ await this.getOrRequestToken(() => this.refreshToken());
198
221
  return true;
199
222
  }
200
223
  return false;
@@ -247,16 +247,17 @@ export declare class ProfileService extends BaseServiceAbstract implements IBase
247
247
  * @param profileData - Robot definition data
248
248
  * @param newName - Optional new name for the robot definition. If not provided, the name from the imported definition will be used.
249
249
  * @param newDescription - Optional new description for the robot definition. If not provided, the description from the imported definition will be used.
250
+ * @param replaceExisting - Move robots from same-named definitions to the import and delete those definitions.
250
251
  *
251
252
  * @see export
252
253
  */
253
- import(projectId: string, profileData: Blob | File, newName?: string, newDescription?: string): Promise<{
254
+ import(projectId: string, profileData: Blob | File, newName?: string, newDescription?: string, replaceExisting?: boolean): Promise<{
254
255
  profileId: string;
255
256
  }>;
256
257
  getLibraryProfiles(): Promise<(RobotProfile & {
257
258
  last_modified: Date;
258
259
  })[]>;
259
- importFromLibrary(projectId: string, profileId: string, name?: string, description?: string): Promise<{
260
+ importFromLibrary(projectId: string, profileId: string, name?: string, description?: string, replaceExisting?: boolean): Promise<{
260
261
  profileId: string;
261
262
  }>;
262
263
  /**
@@ -343,10 +343,11 @@ class ProfileService extends BaseServiceAbstract_1.BaseServiceAbstract {
343
343
  * @param profileData - Robot definition data
344
344
  * @param newName - Optional new name for the robot definition. If not provided, the name from the imported definition will be used.
345
345
  * @param newDescription - Optional new description for the robot definition. If not provided, the description from the imported definition will be used.
346
+ * @param replaceExisting - Move robots from same-named definitions to the import and delete those definitions.
346
347
  *
347
348
  * @see export
348
349
  */
349
- async import(projectId, profileData, newName, newDescription) {
350
+ async import(projectId, profileData, newName, newDescription, replaceExisting) {
350
351
  const formData = new FormData();
351
352
  if (newName) {
352
353
  formData.append('name', newName);
@@ -354,6 +355,9 @@ class ProfileService extends BaseServiceAbstract_1.BaseServiceAbstract {
354
355
  if (newDescription) {
355
356
  formData.append('description', newDescription);
356
357
  }
358
+ if (replaceExisting) {
359
+ formData.append('replaceExisting', 'true');
360
+ }
357
361
  formData.append('file', profileData);
358
362
  return this.callPost((0, formatServiceUrl_1.formatServiceUrl)(api_1.API_PROJECT_DEFINITION_IMPORT, { url: this.config.url, projectId }, this.config.insecure), formData, `Failed to import robot definition for ${projectId}.`);
359
363
  }
@@ -364,8 +368,8 @@ class ProfileService extends BaseServiceAbstract_1.BaseServiceAbstract {
364
368
  last_modified: new Date(profile.last_modified),
365
369
  }));
366
370
  }
367
- async importFromLibrary(projectId, profileId, name, description) {
368
- return this.callPost((0, formatServiceUrl_1.formatServiceUrl)(api_1.API_PROJECT_DEFINITION_IMPORT_LIBRARY, { url: this.config.url, projectId, profileId }, this.config.insecure), { name, description }, `Failed to import library profile ${profileId} into project ${projectId}.`);
371
+ async importFromLibrary(projectId, profileId, name, description, replaceExisting) {
372
+ return this.callPost((0, formatServiceUrl_1.formatServiceUrl)(api_1.API_PROJECT_DEFINITION_IMPORT_LIBRARY, { url: this.config.url, projectId, profileId }, this.config.insecure), { name, description, ...(replaceExisting ? { replaceExisting } : {}) }, `Failed to import library profile ${profileId} into project ${projectId}.`);
369
373
  }
370
374
  /**
371
375
  * Update gamepads for a robot definition
@@ -6,6 +6,7 @@ export declare class AuthService extends BaseServiceAbstract implements IBaseSer
6
6
  private token?;
7
7
  private tokenRefreshTimeoutId?;
8
8
  private tokenSubject$;
9
+ private pendingTokenRequest?;
9
10
  constructor(config: IRocosSDKConfig);
10
11
  /** Observable for token updates
11
12
  *
@@ -54,6 +55,8 @@ export declare class AuthService extends BaseServiceAbstract implements IBaseSer
54
55
  * This will return the current token if it is not expired, otherwise it will get a new token based on your config.
55
56
  */
56
57
  getToken(): Promise<Token>;
58
+ private getOrRequestToken;
59
+ private fetchNewToken;
57
60
  /**
58
61
  * Refresh the authentication token
59
62
  *
@@ -47,25 +47,27 @@ export class AuthService extends BaseServiceAbstract {
47
47
  if (expiresIn !== undefined) {
48
48
  this.logger.warn('setToken(value, expiresIn) is deprecated. Use setToken(value) instead');
49
49
  }
50
- if (typeof value === 'string') {
51
- this.token = new Token(value);
52
- }
53
- else {
54
- this.token = value;
50
+ const token = typeof value === 'string' ? new Token(value) : value;
51
+ if (token.value !== this.token?.value) {
52
+ this.pendingTokenRequest = undefined;
55
53
  }
54
+ this.token = token;
56
55
  this.config.token = this.token.value;
57
56
  // output a message for token change
58
57
  if (this.isTokenValid(this.token)) {
59
58
  this.tokenSubject$.next(this.token);
60
59
  }
61
60
  else {
62
- void this.getToken();
61
+ void this.getToken().catch((error) => {
62
+ this.logger.error('Failed to initialize auth token', error);
63
+ });
63
64
  }
64
65
  }
65
66
  /**
66
67
  * Clear token
67
68
  */
68
69
  clearToken() {
70
+ this.pendingTokenRequest = undefined;
69
71
  this.token = undefined;
70
72
  this.config.token = undefined;
71
73
  }
@@ -122,15 +124,38 @@ export class AuthService extends BaseServiceAbstract {
122
124
  * This will return the current token if it is not expired, otherwise it will get a new token based on your config.
123
125
  */
124
126
  async getToken() {
125
- if (this.token && this.isTokenValid(this.token)) {
126
- if (this.token.isExpiredIn(15, 'minutes')) {
127
- this.logger.info('Token expires within 15 minutes. Refreshing.');
128
- const newToken = await this.refreshToken();
129
- this.setToken(newToken);
130
- return newToken;
131
- }
127
+ if (this.token && this.isTokenValid(this.token) && !this.token.isExpiredIn(15, 'minutes')) {
132
128
  return this.token;
133
129
  }
130
+ return this.getOrRequestToken(() => this.fetchNewToken());
131
+ }
132
+ getOrRequestToken(fetchToken) {
133
+ if (this.pendingTokenRequest) {
134
+ return this.pendingTokenRequest;
135
+ }
136
+ const request = fetchToken()
137
+ .then((token) => {
138
+ if (this.pendingTokenRequest === request) {
139
+ this.setToken(token);
140
+ }
141
+ else if (this.token) {
142
+ return this.getToken();
143
+ }
144
+ return token;
145
+ })
146
+ .finally(() => {
147
+ if (this.pendingTokenRequest === request) {
148
+ this.pendingTokenRequest = undefined;
149
+ }
150
+ });
151
+ this.pendingTokenRequest = request;
152
+ return request;
153
+ }
154
+ async fetchNewToken() {
155
+ if (this.token && this.isTokenValid(this.token)) {
156
+ this.logger.info('Token expires within 15 minutes. Refreshing.');
157
+ return this.refreshToken();
158
+ }
134
159
  let token;
135
160
  try {
136
161
  if ('appId' in this.config && 'appKey' in this.config) {
@@ -156,7 +181,6 @@ export class AuthService extends BaseServiceAbstract {
156
181
  this.logger.error(message);
157
182
  throw new RocosError(message, errorCodes.AUTH_ERROR);
158
183
  }
159
- this.setToken(token);
160
184
  return token;
161
185
  }
162
186
  /**
@@ -189,9 +213,8 @@ export class AuthService extends BaseServiceAbstract {
189
213
  */
190
214
  async refreshTokenIfExpired(minutes) {
191
215
  if (this.token?.isExpiredIn(minutes, 'minutes')) {
192
- this.logger.info('Token expires in 15 minutes. Refreshing.');
193
- const newToken = await this.refreshToken();
194
- this.setToken(newToken);
216
+ this.logger.info(`Token expires in ${minutes} minutes. Refreshing.`);
217
+ await this.getOrRequestToken(() => this.refreshToken());
195
218
  return true;
196
219
  }
197
220
  return false;
@@ -247,16 +247,17 @@ export declare class ProfileService extends BaseServiceAbstract implements IBase
247
247
  * @param profileData - Robot definition data
248
248
  * @param newName - Optional new name for the robot definition. If not provided, the name from the imported definition will be used.
249
249
  * @param newDescription - Optional new description for the robot definition. If not provided, the description from the imported definition will be used.
250
+ * @param replaceExisting - Move robots from same-named definitions to the import and delete those definitions.
250
251
  *
251
252
  * @see export
252
253
  */
253
- import(projectId: string, profileData: Blob | File, newName?: string, newDescription?: string): Promise<{
254
+ import(projectId: string, profileData: Blob | File, newName?: string, newDescription?: string, replaceExisting?: boolean): Promise<{
254
255
  profileId: string;
255
256
  }>;
256
257
  getLibraryProfiles(): Promise<(RobotProfile & {
257
258
  last_modified: Date;
258
259
  })[]>;
259
- importFromLibrary(projectId: string, profileId: string, name?: string, description?: string): Promise<{
260
+ importFromLibrary(projectId: string, profileId: string, name?: string, description?: string, replaceExisting?: boolean): Promise<{
260
261
  profileId: string;
261
262
  }>;
262
263
  /**
@@ -340,10 +340,11 @@ export class ProfileService extends BaseServiceAbstract {
340
340
  * @param profileData - Robot definition data
341
341
  * @param newName - Optional new name for the robot definition. If not provided, the name from the imported definition will be used.
342
342
  * @param newDescription - Optional new description for the robot definition. If not provided, the description from the imported definition will be used.
343
+ * @param replaceExisting - Move robots from same-named definitions to the import and delete those definitions.
343
344
  *
344
345
  * @see export
345
346
  */
346
- async import(projectId, profileData, newName, newDescription) {
347
+ async import(projectId, profileData, newName, newDescription, replaceExisting) {
347
348
  const formData = new FormData();
348
349
  if (newName) {
349
350
  formData.append('name', newName);
@@ -351,6 +352,9 @@ export class ProfileService extends BaseServiceAbstract {
351
352
  if (newDescription) {
352
353
  formData.append('description', newDescription);
353
354
  }
355
+ if (replaceExisting) {
356
+ formData.append('replaceExisting', 'true');
357
+ }
354
358
  formData.append('file', profileData);
355
359
  return this.callPost(formatServiceUrl(API_PROJECT_DEFINITION_IMPORT, { url: this.config.url, projectId }, this.config.insecure), formData, `Failed to import robot definition for ${projectId}.`);
356
360
  }
@@ -361,8 +365,8 @@ export class ProfileService extends BaseServiceAbstract {
361
365
  last_modified: new Date(profile.last_modified),
362
366
  }));
363
367
  }
364
- async importFromLibrary(projectId, profileId, name, description) {
365
- return this.callPost(formatServiceUrl(API_PROJECT_DEFINITION_IMPORT_LIBRARY, { url: this.config.url, projectId, profileId }, this.config.insecure), { name, description }, `Failed to import library profile ${profileId} into project ${projectId}.`);
368
+ async importFromLibrary(projectId, profileId, name, description, replaceExisting) {
369
+ return this.callPost(formatServiceUrl(API_PROJECT_DEFINITION_IMPORT_LIBRARY, { url: this.config.url, projectId, profileId }, this.config.insecure), { name, description, ...(replaceExisting ? { replaceExisting } : {}) }, `Failed to import library profile ${profileId} into project ${projectId}.`);
366
370
  }
367
371
  /**
368
372
  * Update gamepads for a robot definition
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dronedeploy/rocos-js-sdk",
3
- "version": "4.4.11",
3
+ "version": "4.4.13",
4
4
  "description": "Javascript SDK for rocos",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",