pryv 2.4.7 → 3.0.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.
package/src/Connection.js CHANGED
@@ -4,7 +4,9 @@
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');
9
+ const buildSearchParams = require('./lib/buildSearchParams');
8
10
 
9
11
  /**
10
12
  * @class Connection
@@ -51,19 +53,26 @@ class Connection {
51
53
  }
52
54
 
53
55
  /**
54
- * get username.
55
- * It's async as in it constructed from access info
56
- * @param {*} arrayOfAPICalls
57
- * @param {*} progress
56
+ * Get username for this connection.
57
+ * It's async as it's constructed from access info.
58
+ * @returns {Promise<string>} Promise resolving to the username
58
59
  */
59
60
  async username () {
60
61
  const accessInfo = await this.accessInfo();
62
+ if (accessInfo.error) {
63
+ throw new PryvError(
64
+ 'Failed fetching accessinfo: ' + accessInfo.error.message,
65
+ accessInfo.error
66
+ );
67
+ }
68
+ // @ts-ignore - username is always a string
61
69
  return accessInfo.user.username;
62
70
  }
63
71
 
64
72
  /**
65
- * get access info
66
- * It's async as it is constructed with get function.
73
+ * Get access info for this connection.
74
+ * It's async as it is fetched from the API.
75
+ * @returns {Promise<AccessInfo>} Promise resolving to the access info
67
76
  */
68
77
  async accessInfo () {
69
78
  return this.get('access-info', null);
@@ -89,11 +98,12 @@ class Connection {
89
98
  }
90
99
 
91
100
  /**
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
101
+ * Make one API call
102
+ * @param {string} method - Method ID (e.g., 'events.get', 'streams.create')
103
+ * @param {Object|Array} [params={}] - The params associated with this method
104
+ * @param {string} [expectedKey] - If given, returns the value of this key or throws an error if not present
105
+ * @returns {Promise<Object>} Promise resolving to the API result or the value of expectedKey
106
+ * @throws {Error} If .error is present in the response or expectedKey is missing
97
107
  */
98
108
  async apiOne (method, params = {}, expectedKey) {
99
109
  const result = await this.api([{ method, params }]);
@@ -103,13 +113,12 @@ class Connection {
103
113
  (expectedKey != null && result[0][expectedKey] == null)
104
114
  ) {
105
115
  const innerObject = result[0]?.error || result;
106
- const error = new Error(
116
+ throw new PryvError(
107
117
  `Error for api method: "${method}" with params: ${JSON.stringify(
108
118
  params
109
- )} >> Result: ${JSON.stringify(innerObject)}"`
119
+ )} >> Result: ${JSON.stringify(innerObject)}"`,
120
+ innerObject
110
121
  );
111
- error.innerObject = innerObject;
112
- throw error;
113
122
  }
114
123
  if (expectedKey != null) return result[0][expectedKey];
115
124
  return result[0];
@@ -117,9 +126,10 @@ class Connection {
117
126
 
118
127
  /**
119
128
  * 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
129
+ * - Do not throw error if access is already revoked, just return null;
130
+ * @param {boolean} [throwOnFail=true] - if set to false do not throw Error on failure
131
+ * @param {Connection} [usingConnection] - specify which connection issues the revoke, might be necessary when selfRevoke
132
+ * @returns {Promise<Object|null>} Promise resolving to deletion result or null if already revoked/failed
123
133
  */
124
134
  async revoke (throwOnFail = true, usingConnection) {
125
135
  usingConnection = usingConnection || this;
@@ -174,7 +184,6 @@ class Connection {
174
184
  });
175
185
  }
176
186
  const resRequest = await callHandler(thisBatch);
177
-
178
187
  // result checks
179
188
  if (!resRequest || !Array.isArray(resRequest.results)) {
180
189
  throw new Error(
@@ -208,76 +217,107 @@ class Connection {
208
217
  }
209
218
 
210
219
  /**
211
- * Post to API return results
212
- * @param {(Array | Object)} data
213
- * @param {Object} queryParams
214
- * @param {string} path
215
- * @returns {Promise<Array|Object>} Promise to result.body
220
+ * Post to API and return results
221
+ * @param {string} path - API path
222
+ * @param {(Array | Object)} data - Data to post
223
+ * @returns {Promise<Object|Object[]>} Promise to result.body
216
224
  */
217
- async post (path, data, queryParams) {
225
+ async post (path, data) {
218
226
  const now = getTimestamp();
219
- const res = await this.postRaw(path, data, queryParams);
227
+ const res = await this._postFetch(path, data);
220
228
  this._handleMeta(res.body, now);
221
229
  return res.body;
222
230
  }
223
231
 
224
232
  /**
225
- * Raw Post to API return superagent object
226
- * @param {Array | Object} data
227
- * @param {Object} queryParams
228
- * @param {string} path
229
- * @returns {request.superagent} Promise from superagent's post request
233
+ * @private
234
+ * Post object as JSON to API
235
+ * @param {string} path - API path
236
+ * @param {Array | Object} data - Data to post as JSON
237
+ * @returns {Promise<{response: Response, body: Object|Object[]}>} Promise to response and body
230
238
  */
231
- async postRaw (path, data, queryParams) {
232
- return this._post(path).query(queryParams).send(data);
239
+ async _postFetch (path, data) {
240
+ return this._postFetchRaw(path, JSON.stringify(data), 'application/json');
233
241
  }
234
242
 
235
- _post (path) {
236
- return utils.superagent
237
- .post(this.endpoint + path)
238
- .set('Authorization', this.token)
239
- .set('accept', 'json');
243
+ /**
244
+ * @private
245
+ * Raw Post to API
246
+ * @param {string} path - API path
247
+ * @param {any} data - Raw data to post
248
+ * @param {string} [contentType] - Content-Type header (optional, allows fetch to set it for FormData)
249
+ * @returns {Promise<{response: Response, body: Object|Object[]}>} Promise to response and body
250
+ */
251
+ async _postFetchRaw (path, data, contentType) {
252
+ const headers = {
253
+ Authorization: this.token,
254
+ Accept: 'application/json'
255
+ };
256
+ // optional for form-data llowing fetch to
257
+ // automatically set multipart/form-data with the correct boundary
258
+ if (contentType) {
259
+ headers['Content-Type'] = contentType;
260
+ }
261
+ const response = await fetch(this.endpoint + path, {
262
+ method: 'POST',
263
+ headers,
264
+ body: data
265
+ });
266
+ const body = await response.json();
267
+ return { response, body };
240
268
  }
241
269
 
242
270
  /**
243
- * Post to API return results
244
- * @param {Object} queryParams
245
- * @param {string} path
246
- * @returns {Promise<Array|Object>} Promise to result.body
271
+ * GET from API and return results
272
+ * @param {string} path - API path
273
+ * @param {Object} [queryParams] - Query parameters
274
+ * @returns {Promise<Object|Object[]>} Promise to result.body
247
275
  */
248
276
  async get (path, queryParams) {
249
277
  const now = getTimestamp();
250
- const res = await this.getRaw(path, queryParams);
278
+ const res = await this._getFetchRaw(path, queryParams);
251
279
  this._handleMeta(res.body, now);
252
280
  return res.body;
253
281
  }
254
282
 
255
283
  /**
256
- * Raw Get to API return superagent object
257
- * @param {Object} queryParams
258
- * @param {string} path
259
- * @returns {request.superagent} Promise from superagent's get request
284
+ * @private
285
+ * Raw GET from API
286
+ * @param {string} path - API path
287
+ * @param {Object} [queryParams={}] - Query parameters
288
+ * @returns {Promise<{response: Response, body: Object|Object[]}>} Promise to response and body
260
289
  */
261
- getRaw (path, queryParams) {
290
+ async _getFetchRaw (path, queryParams = {}) {
262
291
  path = path || '';
263
- return utils.superagent
264
- .get(this.endpoint + path)
265
- .set('Authorization', this.token)
266
- .set('accept', 'json')
267
- .query(queryParams);
292
+ let queryStr = '';
293
+ if (queryParams && Object.keys(queryParams).length > 0) {
294
+ queryStr = '?' + buildSearchParams(queryParams);
295
+ }
296
+ const response = await fetch(this.endpoint + path + queryStr, {
297
+ headers: {
298
+ Authorization: this.token,
299
+ Accept: 'application/json'
300
+ }
301
+ });
302
+ const body = await response.json();
303
+ return { response, body };
268
304
  }
269
305
 
270
306
  /**
271
- * ADD Data Points to HFEvent (flatJSON format)
272
- * https://api.pryv.com/reference/#add-hf-series-data-points
307
+ * Add data points to an HF (High Frequency) series event (flatJSON format)
308
+ * @param {string} eventId - The HF event ID
309
+ * @param {string[]} fields - Array of field names for the series
310
+ * @param {Array<Array<number|string>>} points - Array of data points, each point is an array of values
311
+ * @returns {Promise<HFSeriesAddResult>} Promise resolving to status response
312
+ * @see https://api.pryv.com/reference/#add-hf-series-data-points
273
313
  */
274
314
  async addPointsToHFEvent (eventId, fields, points) {
275
315
  const res = await this.post('events/' + eventId + '/series', {
276
316
  format: 'flatJSON',
277
- fields: fields,
278
- points: points
317
+ fields,
318
+ points
279
319
  });
280
- if (!res.status === 'ok') {
320
+ if (res.status !== 'ok') {
281
321
  throw new Error('Failed loading serie: ' + JSON.stringify(res.status));
282
322
  }
283
323
  return res;
@@ -285,7 +325,6 @@ class Connection {
285
325
 
286
326
  /**
287
327
  * Streamed get Event.
288
- * Fallbacks to not streamed, for browsers that does not support `fetch()` API
289
328
  * @see https://api.pryv.com/reference/#get-events
290
329
  * @param {Object} queryParams See `events.get` parameters
291
330
  * @param {Function} forEachEvent Function taking one event as parameter. Will be called for each event
@@ -293,38 +332,7 @@ class Connection {
293
332
  */
294
333
  async getEventsStreamed (queryParams, forEachEvent) {
295
334
  const myParser = jsonParser(forEachEvent, queryParams.includeDeletions);
296
- let res = null;
297
- if (typeof window === 'undefined') {
298
- // node
299
- res = await this.getRaw('events', queryParams)
300
- .buffer(false)
301
- .parse(myParser);
302
- } else if (
303
- typeof fetch !== 'undefined' &&
304
- !(typeof navigator !== 'undefined' && navigator.product === 'ReactNative')
305
- ) {
306
- // browser supports fetch and it is not react native
307
- res = await browserGetEventStreamed(this, queryParams, myParser);
308
- } else {
309
- // browser no fetch supports
310
- console.log(
311
- 'WARNING: Browser does not support fetch() required by pryv.Connection.getEventsStreamed()'
312
- );
313
- res = await this.getRaw('events', queryParams);
314
- res.body.eventsCount = 0;
315
- if (res.body.events) {
316
- res.body.events.forEach(forEachEvent);
317
- res.body.eventsCount += res.body.events.length;
318
- delete res.body.events;
319
- }
320
- if (res.body.eventDeletions) {
321
- // deletions are in a seprated Array
322
- res.body.eventDeletions.forEach(forEachEvent);
323
- res.body.eventsCount += res.body.eventDeletions.length;
324
- delete res.body.eventDeletions;
325
- }
326
- }
327
-
335
+ const res = await libGetEventStreamed(this, queryParams, myParser);
328
336
  const now = getTimestamp();
329
337
  this._handleMeta(res.body, now);
330
338
  return res.body;
@@ -337,38 +345,44 @@ class Connection {
337
345
  * @param {string} filePath
338
346
  */
339
347
  async createEventWithFile (event, filePath) {
340
- const res = await this._post('events')
341
- .field('event', JSON.stringify(event))
342
- .attach('file', filePath);
348
+ const fs = require('fs');
349
+ const path = require('path');
350
+
351
+ if (!fs || !path) {
352
+ throw new Error('createEventWithFile is only available in Node.js. Use createEventWithFormData in browser.');
353
+ }
354
+
355
+ const fileName = path.basename(filePath);
356
+ const mimeType = getMimeType(path.extname(filePath));
357
+ const fileBlob = await fs.openAsBlob(filePath, { type: mimeType });
358
+
359
+ const formData = new FormData();
360
+ formData.append('event', JSON.stringify(event));
361
+ formData.append('file', fileBlob, fileName);
343
362
 
344
363
  const now = getTimestamp();
345
- this._handleMeta(res.body, now);
346
- return res.body;
364
+ const { body } = await this._postFetchRaw('events', formData);
365
+ this._handleMeta(body, now);
366
+ return body;
347
367
  }
348
368
 
349
369
  /**
350
370
  * Create an event from a Buffer
351
- * @param {Event} event
371
+ * @param {Object} event
352
372
  * @param {Buffer|Blob} bufferData - Buffer for node, Blob for browser
353
- * @param {string} fileName
373
+ * @param {string} filename
354
374
  */
355
375
  async createEventWithFileFromBuffer (event, bufferData, filename) {
356
- if (typeof window === 'undefined') {
357
- // node
358
- const res = await this._post('events')
359
- .field('event', JSON.stringify(event))
360
- .attach('file', bufferData, filename);
361
-
362
- const now = getTimestamp();
363
- this._handleMeta(res.body, now);
364
- return res.body;
365
- } else {
366
- /* global FormData */
367
- const formData = new FormData();
368
- formData.append('file', bufferData, filename);
369
- const body = await this.createEventWithFormData(event, formData);
370
- return body;
371
- }
376
+ const mimeType = getMimeType(getExtname(filename));
377
+ const fileBlob = bufferData instanceof Blob
378
+ ? bufferData
379
+ // @ts-ignore - Buffer is valid for Blob in Node.js
380
+ : new Blob([bufferData], { type: mimeType });
381
+
382
+ const formData = new FormData();
383
+ formData.append('file', fileBlob, filename);
384
+ const body = await this.createEventWithFormData(event, formData);
385
+ return body;
372
386
  }
373
387
 
374
388
  /**
@@ -379,8 +393,8 @@ class Connection {
379
393
  */
380
394
  async createEventWithFormData (event, formData) {
381
395
  formData.append('event', JSON.stringify(event));
382
- const res = await this._post('events').send(formData);
383
- return res.body;
396
+ const { body } = await this._postFetchRaw('events', formData);
397
+ return body;
384
398
  }
385
399
 
386
400
  /**
@@ -394,9 +408,9 @@ class Connection {
394
408
  }
395
409
 
396
410
  /**
397
- * API endpoint of this connection
411
+ * API endpoint of this connection (includes token if present)
398
412
  * @readonly
399
- * @property {APIEndpoint} deltaTime
413
+ * @property {APIEndpoint} apiEndpoint
400
414
  */
401
415
  get apiEndpoint () {
402
416
  return utils.buildAPIEndpoint(this);
@@ -422,6 +436,45 @@ function getTimestamp () {
422
436
  return Date.now() / 1000;
423
437
  }
424
438
 
439
+ const MIME_TYPES = {
440
+ '.png': 'image/png',
441
+ '.jpg': 'image/jpeg',
442
+ '.jpeg': 'image/jpeg',
443
+ '.gif': 'image/gif',
444
+ '.webp': 'image/webp',
445
+ '.svg': 'image/svg+xml',
446
+ '.bmp': 'image/bmp',
447
+ '.ico': 'image/x-icon',
448
+ '.pdf': 'application/pdf',
449
+ '.json': 'application/json',
450
+ '.xml': 'application/xml',
451
+ '.zip': 'application/zip',
452
+ '.gz': 'application/gzip',
453
+ '.tar': 'application/x-tar',
454
+ '.txt': 'text/plain',
455
+ '.html': 'text/html',
456
+ '.htm': 'text/html',
457
+ '.css': 'text/css',
458
+ '.js': 'application/javascript',
459
+ '.mjs': 'application/javascript',
460
+ '.mp3': 'audio/mpeg',
461
+ '.wav': 'audio/wav',
462
+ '.ogg': 'audio/ogg',
463
+ '.mp4': 'video/mp4',
464
+ '.webm': 'video/webm',
465
+ '.avi': 'video/x-msvideo',
466
+ '.mov': 'video/quicktime'
467
+ };
468
+
469
+ function getMimeType (ext) {
470
+ return MIME_TYPES[ext.toLowerCase()] || 'application/octet-stream';
471
+ }
472
+
473
+ function getExtname (filename) {
474
+ const lastDot = filename.lastIndexOf('.');
475
+ return lastDot >= 0 ? filename.slice(lastDot) : '';
476
+ }
477
+
425
478
  // service is require "after" to allow circular require
426
479
  const Service = require('./Service');
427
480
 
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
  }