pryv 2.4.6 → 3.0.0

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/src/Connection.js CHANGED
@@ -4,7 +4,8 @@
4
4
  */
5
5
  const utils = require('./utils.js');
6
6
  const jsonParser = require('./lib/json-parser');
7
- const browserGetEventStreamed = require('./lib/browser-getEventStreamed');
7
+ const libGetEventStreamed = require('./lib/getEventStreamed');
8
+ const PryvError = require('./lib/PryvError');
8
9
 
9
10
  /**
10
11
  * @class Connection
@@ -51,19 +52,26 @@ class Connection {
51
52
  }
52
53
 
53
54
  /**
54
- * get username.
55
- * It's async as in it constructed from access info
56
- * @param {*} arrayOfAPICalls
57
- * @param {*} progress
55
+ * Get username for this connection.
56
+ * It's async as it's constructed from access info.
57
+ * @returns {Promise<string>} Promise resolving to the username
58
58
  */
59
59
  async username () {
60
60
  const accessInfo = await this.accessInfo();
61
+ if (accessInfo.error) {
62
+ throw new PryvError(
63
+ 'Failed fetching accessinfo: ' + accessInfo.error.message,
64
+ accessInfo.error
65
+ );
66
+ }
67
+ // @ts-ignore - username is always a string
61
68
  return accessInfo.user.username;
62
69
  }
63
70
 
64
71
  /**
65
- * get access info
66
- * It's async as it is constructed with get function.
72
+ * Get access info for this connection.
73
+ * It's async as it is fetched from the API.
74
+ * @returns {Promise<AccessInfo>} Promise resolving to the access info
67
75
  */
68
76
  async accessInfo () {
69
77
  return this.get('access-info', null);
@@ -89,11 +97,12 @@ class Connection {
89
97
  }
90
98
 
91
99
  /**
92
- * Make one api Api call
93
- * @param {string} method - methodId
94
- * @param {Object|Array} [params] - the params associated with this methodId
95
- * @param {string} [resultKey] - if given, returns the value or throws an error if not present
96
- * @throws {Error} if .error is present the response
100
+ * Make one API call
101
+ * @param {string} method - Method ID (e.g., 'events.get', 'streams.create')
102
+ * @param {Object|Array} [params={}] - The params associated with this method
103
+ * @param {string} [expectedKey] - If given, returns the value of this key or throws an error if not present
104
+ * @returns {Promise<Object>} Promise resolving to the API result or the value of expectedKey
105
+ * @throws {Error} If .error is present in the response or expectedKey is missing
97
106
  */
98
107
  async apiOne (method, params = {}, expectedKey) {
99
108
  const result = await this.api([{ method, params }]);
@@ -103,13 +112,12 @@ class Connection {
103
112
  (expectedKey != null && result[0][expectedKey] == null)
104
113
  ) {
105
114
  const innerObject = result[0]?.error || result;
106
- const error = new Error(
115
+ throw new PryvError(
107
116
  `Error for api method: "${method}" with params: ${JSON.stringify(
108
117
  params
109
- )} >> Result: ${JSON.stringify(innerObject)}"`
118
+ )} >> Result: ${JSON.stringify(innerObject)}"`,
119
+ innerObject
110
120
  );
111
- error.innerObject = innerObject;
112
- throw error;
113
121
  }
114
122
  if (expectedKey != null) return result[0][expectedKey];
115
123
  return result[0];
@@ -117,9 +125,10 @@ class Connection {
117
125
 
118
126
  /**
119
127
  * Revoke : Delete the accessId
120
- * - Do not thow error if access is already revoked, just return null;
121
- * @param {boolean} [throwOnFail = true] - if set to false do not throw Error on failure
122
- * @param {Connection} [usingConnection] - specify which connection issues the revoke, might be necessary when selfRovke
128
+ * - Do not throw error if access is already revoked, just return null;
129
+ * @param {boolean} [throwOnFail=true] - if set to false do not throw Error on failure
130
+ * @param {Connection} [usingConnection] - specify which connection issues the revoke, might be necessary when selfRevoke
131
+ * @returns {Promise<Object|null>} Promise resolving to deletion result or null if already revoked/failed
123
132
  */
124
133
  async revoke (throwOnFail = true, usingConnection) {
125
134
  usingConnection = usingConnection || this;
@@ -174,7 +183,6 @@ class Connection {
174
183
  });
175
184
  }
176
185
  const resRequest = await callHandler(thisBatch);
177
-
178
186
  // result checks
179
187
  if (!resRequest || !Array.isArray(resRequest.results)) {
180
188
  throw new Error(
@@ -193,7 +201,8 @@ class Connection {
193
201
  if (arrayOfAPICalls[i + cursor].handleResult) {
194
202
  await arrayOfAPICalls[i + cursor].handleResult.call(
195
203
  null,
196
- resRequest.results[i]
204
+ resRequest.results[i],
205
+ thisBatch[i] // request
197
206
  );
198
207
  }
199
208
  }
@@ -207,76 +216,107 @@ class Connection {
207
216
  }
208
217
 
209
218
  /**
210
- * Post to API return results
211
- * @param {(Array | Object)} data
212
- * @param {Object} queryParams
213
- * @param {string} path
214
- * @returns {Promise<Array|Object>} Promise to result.body
219
+ * Post to API and return results
220
+ * @param {string} path - API path
221
+ * @param {(Array | Object)} data - Data to post
222
+ * @returns {Promise<Object|Object[]>} Promise to result.body
215
223
  */
216
- async post (path, data, queryParams) {
224
+ async post (path, data) {
217
225
  const now = getTimestamp();
218
- const res = await this.postRaw(path, data, queryParams);
226
+ const res = await this._postFetch(path, data);
219
227
  this._handleMeta(res.body, now);
220
228
  return res.body;
221
229
  }
222
230
 
223
231
  /**
224
- * Raw Post to API return superagent object
225
- * @param {Array | Object} data
226
- * @param {Object} queryParams
227
- * @param {string} path
228
- * @returns {request.superagent} Promise from superagent's post request
232
+ * @private
233
+ * Post object as JSON to API
234
+ * @param {string} path - API path
235
+ * @param {Array | Object} data - Data to post as JSON
236
+ * @returns {Promise<{response: Response, body: Object|Object[]}>} Promise to response and body
229
237
  */
230
- async postRaw (path, data, queryParams) {
231
- return this._post(path).query(queryParams).send(data);
238
+ async _postFetch (path, data) {
239
+ return this._postFetchRaw(path, JSON.stringify(data), 'application/json');
232
240
  }
233
241
 
234
- _post (path) {
235
- return utils.superagent
236
- .post(this.endpoint + path)
237
- .set('Authorization', this.token)
238
- .set('accept', 'json');
242
+ /**
243
+ * @private
244
+ * Raw Post to API
245
+ * @param {string} path - API path
246
+ * @param {any} data - Raw data to post
247
+ * @param {string} [contentType] - Content-Type header (optional, allows fetch to set it for FormData)
248
+ * @returns {Promise<{response: Response, body: Object|Object[]}>} Promise to response and body
249
+ */
250
+ async _postFetchRaw (path, data, contentType) {
251
+ const headers = {
252
+ Authorization: this.token,
253
+ Accept: 'application/json'
254
+ };
255
+ // optional for form-data llowing fetch to
256
+ // automatically set multipart/form-data with the correct boundary
257
+ if (contentType) {
258
+ headers['Content-Type'] = contentType;
259
+ }
260
+ const response = await fetch(this.endpoint + path, {
261
+ method: 'POST',
262
+ headers,
263
+ body: data
264
+ });
265
+ const body = await response.json();
266
+ return { response, body };
239
267
  }
240
268
 
241
269
  /**
242
- * Post to API return results
243
- * @param {Object} queryParams
244
- * @param {string} path
245
- * @returns {Promise<Array|Object>} Promise to result.body
270
+ * GET from API and return results
271
+ * @param {string} path - API path
272
+ * @param {Object} [queryParams] - Query parameters
273
+ * @returns {Promise<Object|Object[]>} Promise to result.body
246
274
  */
247
275
  async get (path, queryParams) {
248
276
  const now = getTimestamp();
249
- const res = await this.getRaw(path, queryParams);
277
+ const res = await this._getFetchRaw(path, queryParams);
250
278
  this._handleMeta(res.body, now);
251
279
  return res.body;
252
280
  }
253
281
 
254
282
  /**
255
- * Raw Get to API return superagent object
256
- * @param {Object} queryParams
257
- * @param {string} path
258
- * @returns {request.superagent} Promise from superagent's get request
283
+ * @private
284
+ * Raw GET from API
285
+ * @param {string} path - API path
286
+ * @param {Object} [queryParams={}] - Query parameters
287
+ * @returns {Promise<{response: Response, body: Object|Object[]}>} Promise to response and body
259
288
  */
260
- getRaw (path, queryParams) {
289
+ async _getFetchRaw (path, queryParams = {}) {
261
290
  path = path || '';
262
- return utils.superagent
263
- .get(this.endpoint + path)
264
- .set('Authorization', this.token)
265
- .set('accept', 'json')
266
- .query(queryParams);
291
+ let queryStr = '';
292
+ if (queryParams && Object.keys(queryParams).length > 0) {
293
+ queryStr = '?' + new URLSearchParams(queryParams).toString();
294
+ }
295
+ const response = await fetch(this.endpoint + path + queryStr, {
296
+ headers: {
297
+ Authorization: this.token,
298
+ Accept: 'application/json'
299
+ }
300
+ });
301
+ const body = await response.json();
302
+ return { response, body };
267
303
  }
268
304
 
269
305
  /**
270
- * ADD Data Points to HFEvent (flatJSON format)
271
- * https://api.pryv.com/reference/#add-hf-series-data-points
306
+ * Add data points to an HF (High Frequency) series event (flatJSON format)
307
+ * @param {string} eventId - The HF event ID
308
+ * @param {string[]} fields - Array of field names for the series
309
+ * @param {Array<Array<number|string>>} points - Array of data points, each point is an array of values
310
+ * @returns {Promise<HFSeriesAddResult>} Promise resolving to status response
311
+ * @see https://api.pryv.com/reference/#add-hf-series-data-points
272
312
  */
273
313
  async addPointsToHFEvent (eventId, fields, points) {
274
314
  const res = await this.post('events/' + eventId + '/series', {
275
315
  format: 'flatJSON',
276
- fields: fields,
277
- points: points
316
+ fields,
317
+ points
278
318
  });
279
- if (!res.status === 'ok') {
319
+ if (res.status !== 'ok') {
280
320
  throw new Error('Failed loading serie: ' + JSON.stringify(res.status));
281
321
  }
282
322
  return res;
@@ -284,7 +324,6 @@ class Connection {
284
324
 
285
325
  /**
286
326
  * Streamed get Event.
287
- * Fallbacks to not streamed, for browsers that does not support `fetch()` API
288
327
  * @see https://api.pryv.com/reference/#get-events
289
328
  * @param {Object} queryParams See `events.get` parameters
290
329
  * @param {Function} forEachEvent Function taking one event as parameter. Will be called for each event
@@ -292,38 +331,7 @@ class Connection {
292
331
  */
293
332
  async getEventsStreamed (queryParams, forEachEvent) {
294
333
  const myParser = jsonParser(forEachEvent, queryParams.includeDeletions);
295
- let res = null;
296
- if (typeof window === 'undefined') {
297
- // node
298
- res = await this.getRaw('events', queryParams)
299
- .buffer(false)
300
- .parse(myParser);
301
- } else if (
302
- typeof fetch !== 'undefined' &&
303
- !(typeof navigator !== 'undefined' && navigator.product === 'ReactNative')
304
- ) {
305
- // browser supports fetch and it is not react native
306
- res = await browserGetEventStreamed(this, queryParams, myParser);
307
- } else {
308
- // browser no fetch supports
309
- console.log(
310
- 'WARNING: Browser does not support fetch() required by pryv.Connection.getEventsStreamed()'
311
- );
312
- res = await this.getRaw('events', queryParams);
313
- res.body.eventsCount = 0;
314
- if (res.body.events) {
315
- res.body.events.forEach(forEachEvent);
316
- res.body.eventsCount += res.body.events.length;
317
- delete res.body.events;
318
- }
319
- if (res.body.eventDeletions) {
320
- // deletions are in a seprated Array
321
- res.body.eventDeletions.forEach(forEachEvent);
322
- res.body.eventsCount += res.body.eventDeletions.length;
323
- delete res.body.eventDeletions;
324
- }
325
- }
326
-
334
+ const res = await libGetEventStreamed(this, queryParams, myParser);
327
335
  const now = getTimestamp();
328
336
  this._handleMeta(res.body, now);
329
337
  return res.body;
@@ -336,38 +344,44 @@ class Connection {
336
344
  * @param {string} filePath
337
345
  */
338
346
  async createEventWithFile (event, filePath) {
339
- const res = await this._post('events')
340
- .field('event', JSON.stringify(event))
341
- .attach('file', filePath);
347
+ const fs = require('fs');
348
+ const path = require('path');
349
+
350
+ if (!fs || !path) {
351
+ throw new Error('createEventWithFile is only available in Node.js. Use createEventWithFormData in browser.');
352
+ }
353
+
354
+ const fileName = path.basename(filePath);
355
+ const mimeType = getMimeType(path.extname(filePath));
356
+ const fileBlob = await fs.openAsBlob(filePath, { type: mimeType });
357
+
358
+ const formData = new FormData();
359
+ formData.append('event', JSON.stringify(event));
360
+ formData.append('file', fileBlob, fileName);
342
361
 
343
362
  const now = getTimestamp();
344
- this._handleMeta(res.body, now);
345
- return res.body;
363
+ const { body } = await this._postFetchRaw('events', formData);
364
+ this._handleMeta(body, now);
365
+ return body;
346
366
  }
347
367
 
348
368
  /**
349
369
  * Create an event from a Buffer
350
- * @param {Event} event
370
+ * @param {Object} event
351
371
  * @param {Buffer|Blob} bufferData - Buffer for node, Blob for browser
352
- * @param {string} fileName
372
+ * @param {string} filename
353
373
  */
354
374
  async createEventWithFileFromBuffer (event, bufferData, filename) {
355
- if (typeof window === 'undefined') {
356
- // node
357
- const res = await this._post('events')
358
- .field('event', JSON.stringify(event))
359
- .attach('file', bufferData, filename);
360
-
361
- const now = getTimestamp();
362
- this._handleMeta(res.body, now);
363
- return res.body;
364
- } else {
365
- /* global FormData */
366
- const formData = new FormData();
367
- formData.append('file', bufferData, filename);
368
- const body = await this.createEventWithFormData(event, formData);
369
- return body;
370
- }
375
+ const mimeType = getMimeType(getExtname(filename));
376
+ const fileBlob = bufferData instanceof Blob
377
+ ? bufferData
378
+ // @ts-ignore - Buffer is valid for Blob in Node.js
379
+ : new Blob([bufferData], { type: mimeType });
380
+
381
+ const formData = new FormData();
382
+ formData.append('file', fileBlob, filename);
383
+ const body = await this.createEventWithFormData(event, formData);
384
+ return body;
371
385
  }
372
386
 
373
387
  /**
@@ -378,8 +392,8 @@ class Connection {
378
392
  */
379
393
  async createEventWithFormData (event, formData) {
380
394
  formData.append('event', JSON.stringify(event));
381
- const res = await this._post('events').send(formData);
382
- return res.body;
395
+ const { body } = await this._postFetchRaw('events', formData);
396
+ return body;
383
397
  }
384
398
 
385
399
  /**
@@ -393,9 +407,9 @@ class Connection {
393
407
  }
394
408
 
395
409
  /**
396
- * API endpoint of this connection
410
+ * API endpoint of this connection (includes token if present)
397
411
  * @readonly
398
- * @property {APIEndpoint} deltaTime
412
+ * @property {APIEndpoint} apiEndpoint
399
413
  */
400
414
  get apiEndpoint () {
401
415
  return utils.buildAPIEndpoint(this);
@@ -421,6 +435,45 @@ function getTimestamp () {
421
435
  return Date.now() / 1000;
422
436
  }
423
437
 
438
+ const MIME_TYPES = {
439
+ '.png': 'image/png',
440
+ '.jpg': 'image/jpeg',
441
+ '.jpeg': 'image/jpeg',
442
+ '.gif': 'image/gif',
443
+ '.webp': 'image/webp',
444
+ '.svg': 'image/svg+xml',
445
+ '.bmp': 'image/bmp',
446
+ '.ico': 'image/x-icon',
447
+ '.pdf': 'application/pdf',
448
+ '.json': 'application/json',
449
+ '.xml': 'application/xml',
450
+ '.zip': 'application/zip',
451
+ '.gz': 'application/gzip',
452
+ '.tar': 'application/x-tar',
453
+ '.txt': 'text/plain',
454
+ '.html': 'text/html',
455
+ '.htm': 'text/html',
456
+ '.css': 'text/css',
457
+ '.js': 'application/javascript',
458
+ '.mjs': 'application/javascript',
459
+ '.mp3': 'audio/mpeg',
460
+ '.wav': 'audio/wav',
461
+ '.ogg': 'audio/ogg',
462
+ '.mp4': 'video/mp4',
463
+ '.webm': 'video/webm',
464
+ '.avi': 'video/x-msvideo',
465
+ '.mov': 'video/quicktime'
466
+ };
467
+
468
+ function getMimeType (ext) {
469
+ return MIME_TYPES[ext.toLowerCase()] || 'application/octet-stream';
470
+ }
471
+
472
+ function getExtname (filename) {
473
+ const lastDot = filename.lastIndexOf('.');
474
+ return lastDot >= 0 ? filename.slice(lastDot) : '';
475
+ }
476
+
424
477
  // service is require "after" to allow circular require
425
478
  const Service = require('./Service');
426
479
 
package/src/Service.js CHANGED
@@ -52,17 +52,18 @@ class Service {
52
52
  * - name of a platform
53
53
  * `const serviceName = await service.info().name`
54
54
  * @see ServiceInfo For details on available properties.
55
- * @param {boolean?} forceFetch If true, will force fetching service info.
55
+ * @param {boolean} [forceFetch] If true, will force fetching service info.
56
56
  * @returns {Promise<ServiceInfo>} Promise to Service info Object
57
57
  */
58
58
  async info (forceFetch) {
59
59
  if (forceFetch || !this._serviceInfo) {
60
60
  let baseServiceInfo = {};
61
61
  if (this._serviceInfoUrl) {
62
- const res = await utils.superagent.get(this._serviceInfoUrl).set('Access-Control-Allow-Origin', '*').set('accept', 'json');
63
- baseServiceInfo = res.body;
62
+ const { body } = await utils.fetchGet(this._serviceInfoUrl);
63
+ baseServiceInfo = body;
64
64
  }
65
65
  Object.assign(baseServiceInfo, this._pryvServiceCustomizations);
66
+ // @ts-ignore - baseServiceInfo is populated from body or customizations
66
67
  this.setServiceInfo(baseServiceInfo);
67
68
  }
68
69
  return this._serviceInfo;
@@ -70,7 +71,7 @@ class Service {
70
71
 
71
72
  /**
72
73
  * Check if a service supports High Frequency Data Sets
73
- * @return true if yes
74
+ * @returns {Promise<boolean>} Promise resolving to true if HF is supported
74
75
  */
75
76
  async supportsHF () {
76
77
  const infos = await this.info();
@@ -78,8 +79,8 @@ class Service {
78
79
  }
79
80
 
80
81
  /**
81
- * Check if a service has username in the hostname or in the path of the api
82
- * @return true if the service does not rely on DNS to find a host related to a username
82
+ * Check if a service has username in the hostname or in the path of the API.
83
+ * @returns {Promise<boolean>} Promise resolving to true if the service does not rely on DNS to find a host related to a username
83
84
  */
84
85
  async isDnsLess () {
85
86
  const infos = await this.info();
@@ -107,8 +108,8 @@ class Service {
107
108
 
108
109
  /**
109
110
  * Return assets property content
110
- * @param {boolean?} forceFetch If true, will force fetching service info.
111
- * @returns {Promise<ServiceAssets>} Promise to ServiceAssets
111
+ * @param {boolean} [forceFetch] If true, will force fetching service info.
112
+ * @returns {Promise<ServiceAssets|null>} Promise to ServiceAssets
112
113
  */
113
114
  async assets (forceFetch) {
114
115
  if (!forceFetch && this._assets) {
@@ -134,9 +135,9 @@ class Service {
134
135
 
135
136
  /**
136
137
  * Return an API endpoint from a username and token
137
- * @param {string} username
138
- * @param {string} [token]
139
- * @return {APIEndpoint}
138
+ * @param {string} username - The username
139
+ * @param {string} [token] - Optional authorization token
140
+ * @returns {Promise<APIEndpoint>} Promise resolving to the API endpoint URL
140
141
  */
141
142
  async apiEndpointFor (username, token) {
142
143
  const serviceInfo = await this.info();
@@ -144,16 +145,16 @@ class Service {
144
145
  }
145
146
 
146
147
  /**
147
- * Return an API endpoint from a username and token and a ServiceInfo.
148
- * This is method is rarely used. See **apiEndpointFor** as an alternative.
149
- * @param {ServiceInfo} serviceInfo
150
- * @param {string} username
151
- * @param {string} [token]
152
- * @return {APIEndpoint}
148
+ * Return an API endpoint from a username, token and ServiceInfo.
149
+ * This method is rarely used. See **apiEndpointFor** as an alternative.
150
+ * @param {ServiceInfo} serviceInfo - The service info object containing API URL template
151
+ * @param {string} username - The username
152
+ * @param {string} [token] - Optional authorization token
153
+ * @returns {APIEndpoint} The constructed API endpoint URL
153
154
  */
154
155
  static buildAPIEndpoint (serviceInfo, username, token) {
155
156
  const endpoint = serviceInfo.api.replace('{username}', username);
156
- return utils.buildAPIEndpoint({ endpoint: endpoint, token: token });
157
+ return utils.buildAPIEndpoint({ endpoint, token });
157
158
  }
158
159
 
159
160
  /**
@@ -169,32 +170,31 @@ class Service {
169
170
  async login (username, password, appId, originHeader) {
170
171
  const apiEndpoint = await this.apiEndpointFor(username);
171
172
 
172
- try {
173
- const headers = { accept: 'json' };
174
- originHeader = originHeader || (await this.info()).register;
175
- if (!utils.isBrowser()) {
176
- headers.Origin = originHeader;
173
+ const headers = {};
174
+ originHeader = originHeader || (await this.info()).register;
175
+ if (!utils.isBrowser()) {
176
+ headers.Origin = originHeader;
177
+ }
178
+ const { response, body } = await utils.fetchPost(
179
+ apiEndpoint + 'auth/login',
180
+ { username, password, appId },
181
+ headers
182
+ );
183
+
184
+ if (!response.ok) {
185
+ if (body?.error?.message) {
186
+ throw new Error(body.error.message);
177
187
  }
178
- const res = await utils.superagent.post(apiEndpoint + 'auth/login')
179
- .set(headers)
180
- .send({ username: username, password: password, appId: appId });
188
+ throw new Error('Login failed: ' + JSON.stringify(body));
189
+ }
181
190
 
182
- if (!res.body.token) {
183
- throw new Error('Invalid login response: ' + res.body);
184
- }
185
- return new Connection(
186
- Service.buildAPIEndpoint(await this.info(), username, res.body.token),
187
- this // Pre load Connection with service
188
- );
189
- } catch (e) {
190
- if (e.response &&
191
- e.response.body &&
192
- e.response.body.error &&
193
- e.response.body.error.message) {
194
- throw new Error(e.response.body.error.message);
195
- }
196
- throw (e);
191
+ if (!body.token) {
192
+ throw new Error('Invalid login response: ' + JSON.stringify(body));
197
193
  }
194
+ return new Connection(
195
+ Service.buildAPIEndpoint(await this.info(), username, body.token),
196
+ this // Pre load Connection with service
197
+ );
198
198
  }
199
199
  }
200
200
 
@@ -215,5 +215,7 @@ const Connection = require('./Connection');
215
215
  * @property {string} terms The terms and conditions, in plain text or the URL displaying them.
216
216
  * @property {string} eventTypes The URL of the list of validated event types.
217
217
  * @property {Object} [assets] Holder for service specific Assets (icons, css, ...)
218
- * @property {String} [assets.definitions] URL to json object with assets definitions
218
+ * @property {string} [assets.definitions] URL to json object with assets definitions
219
+ * @property {Object} [features] Platform feature flags
220
+ * @property {boolean} [features.noHF] True if HF data is not supported
219
221
  */
@@ -16,8 +16,8 @@ const utils = require('./utils.js');
16
16
  class ServiceAssets {
17
17
  /**
18
18
  * Private => use ServiceAssets.setup()
19
- * @param { object} assets The content of service/info.assets properties.
20
- * @param { string } pryvServiceAssetsSourceUrl Url point to assets of the service of a Pryv platform: https://api.pryv.com/reference/#service-info property `assets.src`
19
+ * @param {Object} assets The content of service/info.assets properties.
20
+ * @param {string} assetsURL Url point to assets of the service of a Pryv platform
21
21
  */
22
22
  constructor (assets, assetsURL) {
23
23
  this._assets = assets;
@@ -25,13 +25,13 @@ class ServiceAssets {
25
25
  }
26
26
 
27
27
  /**
28
- * Load Assets definition
29
- * @param {string} pryvServiceAssetsSourceUrl
30
- * @returns {ServiceAssets}
28
+ * Load Assets definition from URL
29
+ * @param {string} pryvServiceAssetsSourceUrl - URL to the assets definition JSON
30
+ * @returns {Promise<ServiceAssets>} Promise resolving to ServiceAssets instance
31
31
  */
32
32
  static async setup (pryvServiceAssetsSourceUrl) {
33
- const res = await utils.superagent.get(pryvServiceAssetsSourceUrl).set('accept', 'json');
34
- return new ServiceAssets(res.body, pryvServiceAssetsSourceUrl);
33
+ const { body } = await utils.fetchGet(pryvServiceAssetsSourceUrl);
34
+ return new ServiceAssets(body, pryvServiceAssetsSourceUrl);
35
35
  }
36
36
 
37
37
  /**
@@ -85,6 +85,8 @@ class ServiceAssets {
85
85
  * Set service Favicon to Web Page
86
86
  */
87
87
  setFavicon () {
88
+ /** @type {HTMLLinkElement} */
89
+ // @ts-ignore - querySelector returns Element but we know it's HTMLLinkElement
88
90
  const link = document.querySelector("link[rel*='icon']") || document.createElement('link');
89
91
  link.type = 'image/x-icon';
90
92
  link.rel = 'shortcut icon';
@@ -110,18 +112,20 @@ class ServiceAssets {
110
112
 
111
113
  /**
112
114
  * Get HTML for Login Button
115
+ * @returns {Promise<string>} Promise resolving to HTML string
113
116
  */
114
117
  async loginButtonGetHTML () {
115
- const res = await utils.superagent.get(this.relativeURL(this._assets['lib-js'].buttonSignIn.html)).set('accept', 'html');
116
- return res.text;
118
+ const { text } = await utils.fetchGetText(this.relativeURL(this._assets['lib-js'].buttonSignIn.html));
119
+ return text;
117
120
  }
118
121
 
119
122
  /**
120
123
  * Get Messages strings for Login Button
124
+ * @returns {Promise<Object.<string, string>>} Promise resolving to messages object
121
125
  */
122
126
  async loginButtonGetMessages () {
123
- const res = await utils.superagent.get(this.relativeURL(this._assets['lib-js'].buttonSignIn.messages)).set('accept', 'json');
124
- return res.body;
127
+ const { body } = await utils.fetchGet(this.relativeURL(this._assets['lib-js'].buttonSignIn.messages));
128
+ return body;
125
129
  }
126
130
  }
127
131
 
@@ -153,8 +157,10 @@ function loadCSS (url) {
153
157
  \*/
154
158
 
155
159
  function relPathToAbs (baseUrlString, sRelPath) {
160
+ /** @type {Location|HTMLAnchorElement} */
156
161
  var baseLocation = location;
157
162
  if (baseUrlString) {
163
+ // @ts-ignore - HTMLAnchorElement has compatible URL properties
158
164
  baseLocation = document.createElement('a');
159
165
  baseLocation.href = baseUrlString;
160
166
  }