@openeo/js-client 2.0.1 → 2.3.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
@@ -1,5 +1,6 @@
1
1
  const Environment = require('./env');
2
2
  const Utils = require('@openeo/js-commons/src/utils');
3
+ const ProcessRegistry = require('@openeo/js-commons/src/processRegistry');
3
4
  const axios = require('axios').default;
4
5
  const StacMigrate = require('@radiantearth/stac-migrate');
5
6
 
@@ -25,33 +26,77 @@ class Connection {
25
26
  /**
26
27
  * Creates a new Connection.
27
28
  *
28
- * @param {string} baseUrl - URL to the back-end
29
+ * @param {string} baseUrl - The versioned URL or the back-end instance.
30
+ * @param {Options} [options={}] - Additional options for the connection.
31
+ * @param {?string} [url=null] - User-provided URL of the backend connected to.
29
32
  */
30
- constructor(baseUrl) {
33
+ constructor(baseUrl, options = {}, url = null) {
31
34
  /**
35
+ * User-provided URL of the backend connected to.
36
+ *
37
+ * `null` if not given and the connection was directly made to a versioned instance of the back-end.
38
+ *
39
+ * @protected
40
+ * @type {string | null}
41
+ */
42
+ this.url = url;
43
+ /**
44
+ * The versioned URL or the back-end instance.
45
+ *
46
+ * @protected
32
47
  * @type {string}
33
48
  */
34
49
  this.baseUrl = Utils.normalizeUrl(baseUrl);
35
50
  /**
51
+ * Auth Provider cache
52
+ *
53
+ * @protected
36
54
  * @type {?Array.<AuthProvider>}
37
55
  */
38
56
  this.authProviderList = null;
39
57
  /**
58
+ * Current auth provider
59
+ *
60
+ * @protected
40
61
  * @type {?AuthProvider}
41
62
  */
42
63
  this.authProvider = null;
43
64
  /**
65
+ * Capability cache
66
+ *
67
+ * @protected
44
68
  * @type {?Capabilities}
45
69
  */
46
70
  this.capabilitiesObject = null;
47
- this.processes = null;
71
+ /**
72
+ * Listeners for events.
73
+ *
74
+ * @protected
75
+ * @type {object.<string|Function>}
76
+ */
48
77
  this.listeners = {};
78
+ /**
79
+ * Additional options for the connection.
80
+ *
81
+ * @protected
82
+ * @type {Options}
83
+ */
84
+ this.options = options;
85
+ /**
86
+ * Process cache
87
+ *
88
+ * @protected
89
+ * @type {ProcessRegistry}
90
+ */
91
+ this.processes = new ProcessRegistry([], Boolean(options.addNamespaceToProcess));
92
+ this.processes.listeners.push((...args) => this.emit('processesChanged', ...args));
49
93
  }
50
94
 
51
95
  /**
52
96
  * Initializes the connection by requesting the capabilities.
53
97
  *
54
98
  * @async
99
+ * @protected
55
100
  * @returns {Promise<Capabilities>} Capabilities
56
101
  */
57
102
  async init() {
@@ -61,14 +106,52 @@ class Connection {
61
106
  }
62
107
 
63
108
  /**
64
- * Returns the URL of the back-end currently connected to.
109
+ * Refresh the cache for processes.
65
110
  *
66
- * @returns {string} The URL or the back-end.
111
+ * @async
112
+ * @protected
113
+ * @returns {Promise}
114
+ */
115
+ async refreshProcessCache() {
116
+ if (this.processes.count() === 0) {
117
+ return;
118
+ }
119
+ let promises = this.processes.namespaces().map(namespace => {
120
+ let fn = () => Promise.resolve();
121
+ if (namespace === 'user') {
122
+ if (!this.isAuthenticated()) {
123
+ fn = () => (this.processes.remove(null, 'user') ? Promise.resolve() : Promise.reject(new Error("Can't clear user processes")));
124
+ }
125
+ else if (this.capabilities().hasFeature('listUserProcesses')) {
126
+ fn = () => this.listUserProcesses();
127
+ }
128
+ }
129
+ else if (this.capabilities().hasFeature('listProcesses')) {
130
+ fn = () => this.listProcesses(namespace);
131
+ }
132
+ return fn().catch(error => console.warn(`Could not update processes for namespace '${namespace}' due to an error: ${error.message}`));
133
+ });
134
+ return await Promise.all(promises);
135
+ }
136
+
137
+ /**
138
+ * Returns the URL of the versioned back-end instance currently connected to.
139
+ *
140
+ * @returns {string} The versioned URL or the back-end instance.
67
141
  */
68
142
  getBaseUrl() {
69
143
  return this.baseUrl;
70
144
  }
71
145
 
146
+ /**
147
+ * Returns the user-provided URL of the back-end currently connected to.
148
+ *
149
+ * @returns {string} The URL or the back-end.
150
+ */
151
+ getUrl() {
152
+ return this.url || this.baseUrl;
153
+ }
154
+
72
155
  /**
73
156
  * Returns the capabilities of the back-end.
74
157
  *
@@ -156,16 +239,16 @@ class Connection {
156
239
  *
157
240
  * @async
158
241
  * @param {string} collectionId - Collection ID to request items for.
159
- * @param {?Array.<number>} spatialExtent - Limits the items to the given bounding box in WGS84:
242
+ * @param {?Array.<number>} [spatialExtent=null] - Limits the items to the given bounding box in WGS84:
160
243
  * 1. Lower left corner, coordinate axis 1
161
244
  * 2. Lower left corner, coordinate axis 2
162
245
  * 3. Upper right corner, coordinate axis 1
163
246
  * 4. Upper right corner, coordinate axis 2
164
- * @param {?Array.<*>} temporalExtent - Limits the items to the specified temporal interval.
247
+ * @param {?Array.<*>} [temporalExtent=null] - Limits the items to the specified temporal interval.
165
248
  * The interval has to be specified as an array with exactly two elements (start, end) and
166
249
  * each must be either an RFC 3339 compatible string or a Date object.
167
250
  * Also supports open intervals by setting one of the boundaries to `null`, but never both.
168
- * @param {?number} limit - The amount of items per request/page as integer. If `null` (default), the back-end decides.
251
+ * @param {?number} [limit=null] - The amount of items per request/page as integer. If `null` (default), the back-end decides.
169
252
  * @yields {Promise<ItemCollection>} A response compatible to the API specification.
170
253
  * @throws {Error}
171
254
  */
@@ -208,20 +291,35 @@ class Connection {
208
291
  }
209
292
 
210
293
  /**
211
- * List all processes available on the back-end.
294
+ * List processes available on the back-end.
295
+ *
296
+ * Requests pre-defined processes by default.
297
+ * Set the namespace parameter to request processes from a specific namespace.
212
298
  *
213
- * Data is cached in memory.
299
+ * Note: The list of namespaces can be retrieved by calling `listProcesses` without a namespace given.
300
+ * The namespaces are then listed in the property `namespaces`.
214
301
  *
215
302
  * @async
303
+ * @param {?string} [namespace=null] - Namespace of the processes (default to `null`, i.e. pre-defined processes). EXPERIMENTAL!
216
304
  * @returns {Promise<Processes>} - A response compatible to the API specification.
217
305
  * @throws {Error}
218
306
  */
219
- async listProcesses() {
220
- if (this.processes === null) {
221
- let response = await this._get('/processes');
222
- this.processes = response.data;
307
+ async listProcesses(namespace = null) {
308
+ if (!namespace) {
309
+ namespace = 'backend';
223
310
  }
224
- return this.processes;
311
+ let path = (namespace === 'backend') ? '/processes' : `/processes/${namespace}`;
312
+ let response = await this._get(path);
313
+
314
+ if (!Utils.isObject(response.data) || !Array.isArray(response.data.processes)) {
315
+ throw new Error('Invalid response received for processes');
316
+ }
317
+
318
+ // Store processes in cache
319
+ this.processes.remove(null, namespace);
320
+ this.processes.addAll(response.data.processes, namespace);
321
+
322
+ return Object.assign(response.data, {processes: this.processes.namespace(namespace)});
225
323
  }
226
324
 
227
325
  /**
@@ -229,20 +327,30 @@ class Connection {
229
327
  *
230
328
  * @async
231
329
  * @param {string} processId - Collection ID to request further metadata for.
330
+ * @param {?string} [namespace=null] - Namespace of the process (default to `null`, i.e. pre-defined processes). EXPERIMENTAL!
232
331
  * @returns {Promise<?Process>} - A single process as object, or `null` if none is found.
233
332
  * @throws {Error}
234
333
  * @see Connection#listProcesses
235
334
  */
236
- async describeProcess(processId) {
237
- let response = await this.listProcesses();
238
- if (Array.isArray(response.processes)) {
239
- return response.processes.find(process => Utils.isObject(process) && process.id === processId) || null;
335
+ async describeProcess(processId, namespace = null) {
336
+ if (!namespace) {
337
+ namespace = 'backend';
240
338
  }
241
- return null;
339
+ if (namespace === 'backend') {
340
+ await this.listProcesses();
341
+ }
342
+ else {
343
+ let response = await this._get(`/processes/${namespace}/${processId}`);
344
+ if (!Utils.isObject(response.data) || typeof response.data.id !== 'string') {
345
+ throw new Error('Invalid response received for process');
346
+ }
347
+ this.processes.add(response.data, namespace);
348
+ }
349
+ return this.processes.get(processId, namespace);
242
350
  }
243
351
 
244
352
  /**
245
- * Returns an object to simply build user-defined processes.
353
+ * Returns an object to simply build user-defined processes based upon pre-defined processes.
246
354
  *
247
355
  * @async
248
356
  * @param {string} id - A name for the process.
@@ -251,8 +359,8 @@ class Connection {
251
359
  * @see Connection#listProcesses
252
360
  */
253
361
  async buildProcess(id) {
254
- let response = await this.listProcesses();
255
- return new Builder(response.processes, null, id);
362
+ await this.listProcesses();
363
+ return new Builder(this.processes, null, id);
256
364
  }
257
365
 
258
366
  /**
@@ -305,7 +413,7 @@ class Connection {
305
413
  *
306
414
  * @callback oidcProviderFactoryFunction
307
415
  * @param {object.<string, *>} providerInfo - The provider information as provided by the API, having the properties `id`, `issuer`, `title` etc.
308
- * @returns {?AuthProvider}
416
+ * @returns {AuthProvider | null}
309
417
  */
310
418
 
311
419
  /**
@@ -315,7 +423,7 @@ class Connection {
315
423
  * on the AuthProvider interface (or OIDCProvider class), e.g. to use a
316
424
  * OIDC library other than oidc-client-js.
317
425
  *
318
- * @param {?oidcProviderFactoryFunction} providerFactoryFunc
426
+ * @param {?oidcProviderFactoryFunction} [providerFactoryFunc=null]
319
427
  * @see AuthProvider
320
428
  */
321
429
  setOidcProviderFactory(providerFactoryFunc) {
@@ -328,7 +436,7 @@ class Connection {
328
436
  * Returns `null` if OIDC is not supported by the client or an instance
329
437
  * can't be created for whatever reason.
330
438
  *
331
- * @returns {?oidcProviderFactoryFunction}
439
+ * @returns {oidcProviderFactoryFunction | null}
332
440
  * @see AuthProvider
333
441
  */
334
442
  getOidcProviderFactory() {
@@ -390,6 +498,7 @@ class Connection {
390
498
  * Currently supported:
391
499
  * - authProviderChanged(provider): Raised when the auth provider has changed.
392
500
  * - tokenChanged(token): Raised when the access token has changed.
501
+ * - processesChanged(type, data, namespace): Raised when the process registry has changed (i.e. a process was added, updated or deleted).
393
502
  *
394
503
  * @param {string} event
395
504
  * @param {Function} callback
@@ -410,7 +519,7 @@ class Connection {
410
519
  /**
411
520
  * Returns the AuthProvider.
412
521
  *
413
- * @returns {?AuthProvider}
522
+ * @returns {AuthProvider | null}
414
523
  */
415
524
  getAuthProvider() {
416
525
  return this.authProvider;
@@ -432,6 +541,8 @@ class Connection {
432
541
  this.authProvider = null;
433
542
  }
434
543
  this.emit('authProviderChanged', this.authProvider);
544
+ // Update process cache on auth changes: https://github.com/Open-EO/openeo-js-client/issues/55
545
+ this.refreshProcessCache();
435
546
  }
436
547
 
437
548
  /**
@@ -487,12 +598,12 @@ class Connection {
487
598
  );
488
599
  }
489
600
 
490
-
491
601
  /**
492
602
  * A callback that is executed on upload progress updates.
493
603
  *
494
604
  * @callback uploadStatusCallback
495
605
  * @param {number} percentCompleted - The percent (0-100) completed.
606
+ * @param {UserFile} file - The file object corresponding to the callback.
496
607
  */
497
608
 
498
609
  /**
@@ -507,15 +618,16 @@ class Connection {
507
618
  * @param {*} source - The source, see method description for details.
508
619
  * @param {?string} [targetPath=null] - The target path on the server, relative to the user workspace. Defaults to the file name of the source file.
509
620
  * @param {?uploadStatusCallback} [statusCallback=null] - Optionally, a callback that is executed on upload progress updates.
621
+ * @param {?AbortController} [abortController=null] - An AbortController object that can be used to cancel the upload process.
510
622
  * @returns {Promise<UserFile>}
511
623
  * @throws {Error}
512
624
  */
513
- async uploadFile(source, targetPath = null, statusCallback = null) {
625
+ async uploadFile(source, targetPath = null, statusCallback = null, abortController = null) {
514
626
  if (targetPath === null) {
515
627
  targetPath = Environment.fileNameForUpload(source);
516
628
  }
517
629
  let file = await this.getFile(targetPath);
518
- return await file.uploadFile(source, statusCallback);
630
+ return await file.uploadFile(source, statusCallback, abortController);
519
631
  }
520
632
 
521
633
  /**
@@ -582,6 +694,15 @@ class Connection {
582
694
  */
583
695
  async listUserProcesses() {
584
696
  let response = await this._get('/process_graphs');
697
+
698
+ if (!Utils.isObject(response.data) || !Array.isArray(response.data.processes)) {
699
+ throw new Error('Invalid response received for processes');
700
+ }
701
+
702
+ // Store processes in cache
703
+ this.processes.remove(null, 'user');
704
+ this.processes.addAll(response.data.processes, 'user');
705
+
585
706
  return response.data.processes.map(
586
707
  pg => new UserProcess(this, pg.id).setAll(pg)
587
708
  );
@@ -623,9 +744,10 @@ class Connection {
623
744
  * @param {Process} process - A user-defined process.
624
745
  * @param {?string} [plan=null] - The billing plan to use for this computation.
625
746
  * @param {?number} [budget=null] - The maximum budget allowed to spend for this computation.
747
+ * @param {?AbortController} [abortController=null] - An AbortController object that can be used to cancel the processing request.
626
748
  * @returns {Promise<SyncResult>} - An object with the data and some metadata.
627
749
  */
628
- async computeResult(process, plan = null, budget = null) {
750
+ async computeResult(process, plan = null, budget = null, abortController = null) {
629
751
  let requestBody = this._normalizeUserProcess(
630
752
  process,
631
753
  {
@@ -633,7 +755,7 @@ class Connection {
633
755
  budget: budget
634
756
  }
635
757
  );
636
- let response = await this._post('/result', requestBody, Environment.getResponseType());
758
+ let response = await this._post('/result', requestBody, Environment.getResponseType(), abortController);
637
759
  let syncResult = {
638
760
  data: response.data,
639
761
  costs: null,
@@ -684,10 +806,11 @@ class Connection {
684
806
  * @param {string} targetPath - The target, see method description for details.
685
807
  * @param {?string} [plan=null] - The billing plan to use for this computation.
686
808
  * @param {?number} [budget=null] - The maximum budget allowed to spend for this computation.
809
+ * @param {?AbortController} [abortController=null] - An AbortController object that can be used to cancel the processing request.
687
810
  * @throws {Error}
688
811
  */
689
- async downloadResult(process, targetPath, plan = null, budget = null) {
690
- let response = await this.computeResult(process, plan, budget);
812
+ async downloadResult(process, targetPath, plan = null, budget = null, abortController = null) {
813
+ let response = await this.computeResult(process, plan, budget, abortController);
691
814
  // @ts-ignore
692
815
  await Environment.saveToFile(response.data, targetPath);
693
816
  }
@@ -732,7 +855,7 @@ class Connection {
732
855
  throw new Error("Response did not contain a Job ID. Job has likely been created, but may not show up yet.");
733
856
  }
734
857
  let job = new Job(this, response.headers['openeo-identifier']).setAll(requestBody);
735
- if (this.capabilitiesObject.hasFeature('describeJob')) {
858
+ if (this.capabilities().hasFeature('describeJob')) {
736
859
  return await job.describeJob();
737
860
  }
738
861
  else {
@@ -798,7 +921,7 @@ class Connection {
798
921
  throw new Error("Response did not contain a Service ID. Service has likely been created, but may not show up yet.");
799
922
  }
800
923
  let service = new Service(this, response.headers['openeo-identifier']).setAll(requestBody);
801
- if (this.capabilitiesObject.hasFeature('describeService')) {
924
+ if (this.capabilities().hasFeature('describeService')) {
802
925
  return service.describeService();
803
926
  }
804
927
  else {
@@ -824,7 +947,7 @@ class Connection {
824
947
  *
825
948
  * @param {Array.<Link>} links - An array of links.
826
949
  * @param {string} rel - Relation type to find, defaults to `next`.
827
- * @returns {?string}
950
+ * @returns {string | null}
828
951
  * @throws {Error}
829
952
  */
830
953
  _getLinkHref(links, rel = 'next') {
@@ -855,7 +978,7 @@ class Connection {
855
978
  url: path,
856
979
  // Timeout for capabilities requests as they are used for a quick first discovery to check whether the server is a openEO back-end.
857
980
  // Without timeout connecting with a wrong server url may take forever.
858
- timeout: path === '/' ? 3000 : 0,
981
+ timeout: path === '/' ? 5000 : 0,
859
982
  params: query
860
983
  });
861
984
  }
@@ -867,17 +990,19 @@ class Connection {
867
990
  * @param {string} path
868
991
  * @param {*} body
869
992
  * @param {string} responseType - Response type according to axios, defaults to `json`.
993
+ * @param {?AbortController} [abortController=null] - An AbortController object that can be used to cancel the request.
870
994
  * @returns {Promise<AxiosResponse>}
871
995
  * @throws {Error}
872
996
  * @see https://github.com/axios/axios#request-config
873
997
  */
874
- async _post(path, body, responseType) {
875
- return await this._send({
998
+ async _post(path, body, responseType, abortController = null) {
999
+ let options = {
876
1000
  method: 'post',
877
1001
  responseType: responseType,
878
1002
  url: path,
879
1003
  data: body
880
- });
1004
+ };
1005
+ return await this._send(options, abortController);
881
1006
  }
882
1007
 
883
1008
  /**
@@ -963,11 +1088,12 @@ class Connection {
963
1088
  *
964
1089
  * @async
965
1090
  * @param {object.<string, *>} options
1091
+ * @param {?AbortController} [abortController=null] - An AbortController object that can be used to cancel the request.
966
1092
  * @returns {Promise<AxiosResponse>}
967
1093
  * @throws {Error}
968
1094
  * @see https://github.com/axios/axios
969
1095
  */
970
- async _send(options) {
1096
+ async _send(options, abortController = null) {
971
1097
  options.baseURL = this.baseUrl;
972
1098
  if (this.isAuthenticated() && (typeof options.authorization === 'undefined' || options.authorization === true)) {
973
1099
  if (!options.headers) {
@@ -978,6 +1104,9 @@ class Connection {
978
1104
  if (!options.responseType) {
979
1105
  options.responseType = 'json';
980
1106
  }
1107
+ if (abortController) {
1108
+ options.signal = abortController.signal;
1109
+ }
981
1110
 
982
1111
  try {
983
1112
  return await axios(options);
package/src/filetypes.js CHANGED
@@ -64,7 +64,7 @@ class FileTypes {
64
64
  * Returns null if no input file format was found for the given identifier.
65
65
  *
66
66
  * @param {string} type - Case-insensitive file format identifier
67
- * @returns {?FileType}
67
+ * @returns {FileType | null}
68
68
  */
69
69
  getInputType(type) {
70
70
  return this._findType(type, 'input');
@@ -76,7 +76,7 @@ class FileTypes {
76
76
  * Returns null if no output file format was found for the given identifier.
77
77
  *
78
78
  * @param {string} type - Case-insensitive file format identifier
79
- * @returns {?FileType}
79
+ * @returns {FileType | null}
80
80
  */
81
81
  getOutputType(type) {
82
82
  return this._findType(type, 'output');
@@ -87,7 +87,7 @@ class FileTypes {
87
87
  *
88
88
  * @param {string} type - Identifier of the file type
89
89
  * @param {string} io - Either `input` or `output`
90
- * @returns {?FileType}
90
+ * @returns {FileType | null}
91
91
  * @protected
92
92
  */
93
93
  _findType(type, io) {
package/src/job.js CHANGED
@@ -265,6 +265,9 @@ class Job extends BaseEntity {
265
265
  */
266
266
  async getResultsAsStac() {
267
267
  let response = await this.connection._get('/jobs/' + this.id + '/results');
268
+ if (!Utils.isObject(response) || !Utils.isObject(response.data)) {
269
+ throw new Error("Results received from the back-end are invalid");
270
+ }
268
271
  let data = StacMigrate.stac(response.data);
269
272
  if (!Utils.isObject(data.assets)) {
270
273
  data.assets = {};
@@ -132,8 +132,8 @@ class OidcProvider extends AuthProvider {
132
132
  * Adds a listener to one of the following events:
133
133
  *
134
134
  * - AccessTokenExpiring: Raised prior to the access token expiring.
135
- * - accessTokenExpired: Raised after the access token has expired.
136
- * - silentRenewError: Raised when the automatic silent renew has failed.
135
+ * - AccessTokenExpired: Raised after the access token has expired.
136
+ * - SilentRenewError: Raised when the automatic silent renew has failed.
137
137
  *
138
138
  * @param {string} event
139
139
  * @param {Function} callback
@@ -271,7 +271,7 @@ class OidcProvider extends AuthProvider {
271
271
  *
272
272
  * This may override a detected default client ID.
273
273
  *
274
- * @param {?string} clientId
274
+ * @param {string | null} clientId
275
275
  */
276
276
  setClientId(clientId) {
277
277
  this.clientId = clientId;
@@ -281,7 +281,7 @@ class OidcProvider extends AuthProvider {
281
281
  * Sets the OIDC User.
282
282
  *
283
283
  * @see https://github.com/IdentityModel/oidc-client-js/wiki#user
284
- * @param {?Oidc.User} user - The OIDC User. Passing `null` resets OIDC authentication details.
284
+ * @param {Oidc.User | null} user - The OIDC User. Passing `null` resets OIDC authentication details.
285
285
  */
286
286
  setUser(user) {
287
287
  if (!user) {
@@ -299,7 +299,7 @@ class OidcProvider extends AuthProvider {
299
299
  *
300
300
  * Sets the grant and client ID accordingly.
301
301
  *
302
- * @returns {?OidcClient}
302
+ * @returns {OidcClient | null}
303
303
  * @see OidcProvider#setGrant
304
304
  * @see OidcProvider#setClientId
305
305
  */
package/src/openeo.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const axios = require('axios').default;
2
+ const { AbortController } = require("node-abort-controller");
2
3
  const Utils = require('@openeo/js-commons/src/utils');
3
4
  const Versions = require('@openeo/js-commons/src/versions');
4
5
 
@@ -43,15 +44,17 @@ class OpenEO {
43
44
  *
44
45
  * @async
45
46
  * @param {string} url - The server URL to connect to.
47
+ * @param {Options} [options={}] - Additional options for the connection.
46
48
  * @returns {Promise<Connection>}
47
49
  * @throws {Error}
48
50
  * @static
49
51
  */
50
- static async connect(url) {
52
+ static async connect(url, options = {}) {
51
53
  let wellKnownUrl = Utils.normalizeUrl(url, '/.well-known/openeo');
54
+ let versionedUrl = url;
52
55
  let response = null;
53
56
  try {
54
- response = await axios.get(wellKnownUrl);
57
+ response = await axios.get(wellKnownUrl, {timeout: 5000});
55
58
 
56
59
  if (!Utils.isObject(response.data) || !Array.isArray(response.data.versions)) {
57
60
  throw new Error("Well-Known Document doesn't list any versions.");
@@ -63,14 +66,16 @@ class OpenEO {
63
66
  if (Utils.isObject(response)) {
64
67
  let version = Versions.findLatest(response.data.versions, true, MIN_API_VERSION, MAX_API_VERSION);
65
68
  if (version !== null) {
66
- url = version.url;
69
+ versionedUrl = version.url;
67
70
  }
68
71
  else {
69
72
  throw new Error("Server not supported. Client only supports the API versions between " + MIN_API_VERSION + " and " + MAX_API_VERSION);
70
73
  }
71
74
  }
72
75
 
73
- return await OpenEO.connectDirect(url);
76
+ let connection = await OpenEO.connectDirect(versionedUrl, options);
77
+ connection.url = url;
78
+ return connection;
74
79
  }
75
80
 
76
81
  /**
@@ -80,12 +85,13 @@ class OpenEO {
80
85
  *
81
86
  * @async
82
87
  * @param {string} versionedUrl - The server URL to connect to.
88
+ * @param {Options} [options={}] - Additional options for the connection.
83
89
  * @returns {Promise<Connection>}
84
90
  * @throws {Error}
85
91
  * @static
86
92
  */
87
- static async connectDirect(versionedUrl) {
88
- let connection = new Connection(versionedUrl);
93
+ static async connectDirect(versionedUrl, options = {}) {
94
+ let connection = new Connection(versionedUrl, options);
89
95
 
90
96
  // Check whether back-end is accessible and supports a compatible version.
91
97
  let capabilities = await connection.init();
@@ -104,7 +110,7 @@ class OpenEO {
104
110
  * @returns {string} Version number (according to SemVer).
105
111
  */
106
112
  static clientVersion() {
107
- return "2.0.1";
113
+ return "2.3.1";
108
114
  }
109
115
 
110
116
  }
@@ -112,6 +118,7 @@ class OpenEO {
112
118
  OpenEO.Environment = require('./env');
113
119
 
114
120
  module.exports = {
121
+ AbortController,
115
122
  AuthProvider,
116
123
  BasicProvider,
117
124
  Capabilities,
package/src/typedefs.js CHANGED
@@ -170,11 +170,20 @@
170
170
  * @property {Array.<Link>} links Links
171
171
  */
172
172
 
173
+ /**
174
+ * Connection options.
175
+ *
176
+ * @typedef Options
177
+ * @type {object}
178
+ * @property {boolean} addNamespaceToProcess Add a namespace property to processes if set to `true`. Defaults to `false`.
179
+ */
180
+
173
181
  /**
174
182
  * @typedef Processes
175
183
  * @type {object}
176
184
  * @property {Array.<Process>} processes
177
185
  * @property {Array.<Link>} links
186
+ * @property {?Array.<string>} namespaces EXPERIMENTAL!
178
187
  */
179
188
 
180
189
  /**
@@ -214,8 +223,9 @@
214
223
  * @typedef UserAccount
215
224
  * @type {object}
216
225
  * @property {string} user_id
217
- * @property {string} name
218
- * @property {UserAccountStorage} storage
226
+ * @property {?string} name
227
+ * @property {?string} default_plan
228
+ * @property {?UserAccountStorage} storage
219
229
  * @property {?number} budget
220
- * @property {Array.<Link>} links
230
+ * @property {?Array.<Link>} links
221
231
  */
package/src/userfile.js CHANGED
@@ -90,10 +90,11 @@ class UserFile extends BaseEntity {
90
90
  * @async
91
91
  * @param {*} source - The source, see method description for details.
92
92
  * @param {?uploadStatusCallback} statusCallback - Optionally, a callback that is executed on upload progress updates.
93
+ * @param {?AbortController} [abortController=null] - An AbortController object that can be used to cancel the upload process.
93
94
  * @returns {Promise<UserFile>}
94
95
  * @throws {Error}
95
96
  */
96
- async uploadFile(source, statusCallback = null) {
97
+ async uploadFile(source, statusCallback = null, abortController = null) {
97
98
  let options = {
98
99
  method: 'put',
99
100
  url: '/files/' + this.path,
@@ -109,7 +110,7 @@ class UserFile extends BaseEntity {
109
110
  };
110
111
  }
111
112
 
112
- let response = await this.connection._send(options);
113
+ let response = await this.connection._send(options, abortController);
113
114
  return this.setAll(response.data);
114
115
  }
115
116