@openeo/js-client 2.3.1 → 2.5.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
@@ -51,21 +51,21 @@ class Connection {
51
51
  * Auth Provider cache
52
52
  *
53
53
  * @protected
54
- * @type {?Array.<AuthProvider>}
54
+ * @type {Array.<AuthProvider> | null}
55
55
  */
56
56
  this.authProviderList = null;
57
57
  /**
58
58
  * Current auth provider
59
59
  *
60
60
  * @protected
61
- * @type {?AuthProvider}
61
+ * @type {AuthProvider | null}
62
62
  */
63
63
  this.authProvider = null;
64
64
  /**
65
65
  * Capability cache
66
66
  *
67
67
  * @protected
68
- * @type {?Capabilities}
68
+ * @type {Capabilities | null}
69
69
  */
70
70
  this.capabilitiesObject = null;
71
71
  /**
@@ -119,11 +119,12 @@ class Connection {
119
119
  let promises = this.processes.namespaces().map(namespace => {
120
120
  let fn = () => Promise.resolve();
121
121
  if (namespace === 'user') {
122
+ let userProcesses = this.processes.namespace('user');
122
123
  if (!this.isAuthenticated()) {
123
124
  fn = () => (this.processes.remove(null, 'user') ? Promise.resolve() : Promise.reject(new Error("Can't clear user processes")));
124
125
  }
125
126
  else if (this.capabilities().hasFeature('listUserProcesses')) {
126
- fn = () => this.listUserProcesses();
127
+ fn = () => this.listUserProcesses(userProcesses);
127
128
  }
128
129
  }
129
130
  else if (this.capabilities().hasFeature('listProcesses')) {
@@ -244,7 +245,7 @@ class Connection {
244
245
  * 2. Lower left corner, coordinate axis 2
245
246
  * 3. Upper right corner, coordinate axis 1
246
247
  * 4. Upper right corner, coordinate axis 2
247
- * @param {?Array.<*>} [temporalExtent=null] - Limits the items to the specified temporal interval.
248
+ * @param {?Array} [temporalExtent=null] - Limits the items to the specified temporal interval.
248
249
  * The interval has to be specified as an array with exactly two elements (start, end) and
249
250
  * each must be either an RFC 3339 compatible string or a Date object.
250
251
  * Also supports open intervals by setting one of the boundaries to `null`, but never both.
@@ -290,6 +291,25 @@ class Connection {
290
291
  }
291
292
  }
292
293
 
294
+ /**
295
+ * Normalisation of the namespace to a value that is compatible with the OpenEO specs - EXPERIMENTAL.
296
+ *
297
+ * This is required to support UDP that are shared as public. These can only be executed with providing the full URL
298
+ * (e.g. https://<backend>/processes/<namespace>/<process_id>) as the namespace value in the processing graph. For other
299
+ * parts of the API (such as the listing of the processes, only the name of the namespace is required.
300
+ *
301
+ * This function will extract the short name of the namespace from a shareable URL.
302
+ *
303
+ * @protected
304
+ * @param {?string} namespace - Namespace of the process
305
+ * @returns {?string}
306
+ */
307
+ normalizeNamespace(namespace) {
308
+ // The pattern in https://github.com/Open-EO/openeo-api/pull/348 doesn't include the double colon yet - the regexp may change in the future
309
+ const matches = namespace.match( /^https?:\/\/.*\/processes\/(@?[\w\-.~:]+)\/?/i);
310
+ return matches && matches.length > 1 ? matches[1] : namespace;
311
+ }
312
+
293
313
  /**
294
314
  * List processes available on the back-end.
295
315
  *
@@ -308,7 +328,7 @@ class Connection {
308
328
  if (!namespace) {
309
329
  namespace = 'backend';
310
330
  }
311
- let path = (namespace === 'backend') ? '/processes' : `/processes/${namespace}`;
331
+ let path = (namespace === 'backend') ? '/processes' : `/processes/${this.normalizeNamespace(namespace)}`;
312
332
  let response = await this._get(path);
313
333
 
314
334
  if (!Utils.isObject(response.data) || !Array.isArray(response.data.processes)) {
@@ -340,7 +360,7 @@ class Connection {
340
360
  await this.listProcesses();
341
361
  }
342
362
  else {
343
- let response = await this._get(`/processes/${namespace}/${processId}`);
363
+ let response = await this._get(`/processes/${this.normalizeNamespace(namespace)}/${processId}`);
344
364
  if (!Utils.isObject(response.data) || typeof response.data.id !== 'string') {
345
365
  throw new Error('Invalid response received for process');
346
366
  }
@@ -588,14 +608,15 @@ class Connection {
588
608
  * Lists all files from the user workspace.
589
609
  *
590
610
  * @async
591
- * @returns {Promise<Array.<UserFile>>} A list of files.
611
+ * @returns {Promise<ResponseArray.<UserFile>>} A list of files.
592
612
  * @throws {Error}
593
613
  */
594
614
  async listFiles() {
595
615
  let response = await this._get('/files');
596
- return response.data.files.map(
616
+ let files = response.data.files.map(
597
617
  f => new UserFile(this, f.path).setAll(f)
598
618
  );
619
+ return this._toResponseArray(files, response.data);
599
620
  }
600
621
 
601
622
  /**
@@ -689,23 +710,34 @@ class Connection {
689
710
  * Lists all user-defined processes of the authenticated user.
690
711
  *
691
712
  * @async
692
- * @returns {Promise<Array.<UserProcess>>} A list of user-defined processes.
713
+ * @param {Array.<UserProcess>} [oldProcesses=[]] - A list of existing user-defined processes to update.
714
+ * @returns {Promise<ResponseArray.<UserProcess>>} A list of user-defined processes.
693
715
  * @throws {Error}
694
716
  */
695
- async listUserProcesses() {
717
+ async listUserProcesses(oldProcesses = []) {
696
718
  let response = await this._get('/process_graphs');
697
719
 
698
720
  if (!Utils.isObject(response.data) || !Array.isArray(response.data.processes)) {
699
721
  throw new Error('Invalid response received for processes');
700
722
  }
701
723
 
702
- // Store processes in cache
724
+ // Remove existing processes from cache
703
725
  this.processes.remove(null, 'user');
704
- this.processes.addAll(response.data.processes, 'user');
705
726
 
706
- return response.data.processes.map(
707
- pg => new UserProcess(this, pg.id).setAll(pg)
708
- );
727
+ // Update existing processes if needed
728
+ let newProcesses = response.data.processes.map(newProcess => {
729
+ let process = oldProcesses.find(oldProcess => oldProcess.id === newProcess.id);
730
+ if (!process) {
731
+ process = new UserProcess(this, newProcess.id);
732
+ }
733
+ return process.setAll(newProcess);
734
+ });
735
+
736
+ // Store plain JS variant (i.e. no Job objects involved) of processes in cache
737
+ let jsonProcesses = oldProcesses.length > 0 ? newProcesses.map(p => p.toJSON()) : response.data.processes;
738
+ this.processes.addAll(jsonProcesses, 'user');
739
+
740
+ return this._toResponseArray(newProcesses, response.data);
709
741
  }
710
742
 
711
743
  /**
@@ -819,14 +851,21 @@ class Connection {
819
851
  * Lists all batch jobs of the authenticated user.
820
852
  *
821
853
  * @async
822
- * @returns {Promise<Array.<Job>>} A list of jobs.
854
+ * @param {Array.<Job>} [oldJobs=[]] - A list of existing jobs to update.
855
+ * @returns {Promise<ResponseArray.<Job>>} A list of jobs.
823
856
  * @throws {Error}
824
857
  */
825
- async listJobs() {
858
+ async listJobs(oldJobs = []) {
826
859
  let response = await this._get('/jobs');
827
- return response.data.jobs.map(
828
- j => new Job(this, j.id).setAll(j)
829
- );
860
+ let newJobs = response.data.jobs.map(newJob => {
861
+ delete newJob.status;
862
+ let job = oldJobs.find(oldJob => oldJob.id === newJob.id);
863
+ if (!job) {
864
+ job = new Job(this, newJob.id);
865
+ }
866
+ return job.setAll(newJob);
867
+ });
868
+ return this._toResponseArray(newJobs, response.data);
830
869
  }
831
870
 
832
871
  /**
@@ -880,14 +919,20 @@ class Connection {
880
919
  * Lists all secondary web services of the authenticated user.
881
920
  *
882
921
  * @async
883
- * @returns {Promise<Array.<Job>>} A list of services.
922
+ * @param {Array.<Service>} [oldServices=[]] - A list of existing services to update.
923
+ * @returns {Promise<ResponseArray.<Job>>} A list of services.
884
924
  * @throws {Error}
885
925
  */
886
- async listServices() {
926
+ async listServices(oldServices = []) {
887
927
  let response = await this._get('/services');
888
- return response.data.services.map(
889
- s => new Service(this, s.id).setAll(s)
890
- );
928
+ let newServices = response.data.services.map(newService => {
929
+ let service = oldServices.find(oldService => oldService.id === newService.id);
930
+ if (!service) {
931
+ service = new Service(this, newService.id);
932
+ }
933
+ return service.setAll(newService);
934
+ });
935
+ return this._toResponseArray(newServices, response.data);
891
936
  }
892
937
 
893
938
  /**
@@ -942,9 +987,26 @@ class Connection {
942
987
  return await service.describeService();
943
988
  }
944
989
 
990
+ /**
991
+ * Adds additional response details to the array.
992
+ *
993
+ * Adds links and federation:missing.
994
+ *
995
+ * @protected
996
+ * @param {Array.<*>} arr
997
+ * @param {object.<string, *>} response
998
+ * @returns {ResponseArray}
999
+ */
1000
+ _toResponseArray(arr, response) {
1001
+ arr.links = Array.isArray(response.links) ? response.links : [];
1002
+ arr['federation:missing'] = Array.isArray(response['federation:missing']) ? response['federation:missing'] : [];
1003
+ return arr;
1004
+ }
1005
+
945
1006
  /**
946
1007
  * Get the a link with the given rel type.
947
1008
  *
1009
+ * @protected
948
1010
  * @param {Array.<Link>} links - An array of links.
949
1011
  * @param {string} rel - Relation type to find, defaults to `next`.
950
1012
  * @returns {string | null}
@@ -963,6 +1025,7 @@ class Connection {
963
1025
  /**
964
1026
  * Sends a GET request.
965
1027
  *
1028
+ * @protected
966
1029
  * @async
967
1030
  * @param {string} path
968
1031
  * @param {object.<string, *>} query
@@ -986,6 +1049,7 @@ class Connection {
986
1049
  /**
987
1050
  * Sends a POST request.
988
1051
  *
1052
+ * @protected
989
1053
  * @async
990
1054
  * @param {string} path
991
1055
  * @param {*} body
@@ -1008,6 +1072,7 @@ class Connection {
1008
1072
  /**
1009
1073
  * Sends a PUT request.
1010
1074
  *
1075
+ * @protected
1011
1076
  * @async
1012
1077
  * @param {string} path
1013
1078
  * @param {*} body
@@ -1025,6 +1090,7 @@ class Connection {
1025
1090
  /**
1026
1091
  * Sends a PATCH request.
1027
1092
  *
1093
+ * @protected
1028
1094
  * @async
1029
1095
  * @param {string} path
1030
1096
  * @param {*} body
@@ -1042,6 +1108,7 @@ class Connection {
1042
1108
  /**
1043
1109
  * Sends a DELETE request.
1044
1110
  *
1111
+ * @protected
1045
1112
  * @async
1046
1113
  * @param {string} path
1047
1114
  * @returns {Promise<AxiosResponse>}
@@ -1086,6 +1153,7 @@ class Connection {
1086
1153
  * Tries to smoothly handle error responses by providing an object for all response types,
1087
1154
  * instead of Streams or Blobs for non-JSON response types.
1088
1155
  *
1156
+ * @protected
1089
1157
  * @async
1090
1158
  * @param {object.<string, *>} options
1091
1159
  * @param {?AbortController} [abortController=null] - An AbortController object that can be used to cancel the request.
package/src/filetypes.js CHANGED
@@ -12,6 +12,7 @@ class FileTypes {
12
12
  */
13
13
  constructor(data) {
14
14
  /**
15
+ * @protected
15
16
  * @type {FileTypesAPI}
16
17
  */
17
18
  this.data = {
@@ -29,6 +30,13 @@ class FileTypes {
29
30
  this.data[io][type.toUpperCase()] = data[io][type];
30
31
  }
31
32
  }
33
+ /**
34
+ * A list of backends from the federation that are missing in the response data.
35
+ *
36
+ * @public
37
+ * @type {Array.<string>}
38
+ */
39
+ this['federation:missing'] = data['federation:missing'];
32
40
  }
33
41
 
34
42
  /**
package/src/job.js CHANGED
@@ -44,7 +44,7 @@ class Job extends BaseEntity {
44
44
  * The process chain to be executed.
45
45
  * @public
46
46
  * @readonly
47
- * @type {Process}
47
+ * @type {?Process}
48
48
  */
49
49
  this.process = undefined;
50
50
  /**
@@ -52,35 +52,35 @@ class Job extends BaseEntity {
52
52
  * One of "created", "queued", "running", "canceled", "finished" or "error".
53
53
  * @public
54
54
  * @readonly
55
- * @type {string}
55
+ * @type {?string}
56
56
  */
57
57
  this.status = undefined;
58
58
  /**
59
59
  * Indicates the process of a running batch job in percent.
60
60
  * @public
61
61
  * @readonly
62
- * @type {number}
62
+ * @type {?number}
63
63
  */
64
64
  this.progress = undefined;
65
65
  /**
66
66
  * Date and time of creation, formatted as a RFC 3339 date-time.
67
67
  * @public
68
68
  * @readonly
69
- * @type {string}
69
+ * @type {?string}
70
70
  */
71
71
  this.created = undefined;
72
72
  /**
73
73
  * Date and time of the last status change, formatted as a RFC 3339 date-time.
74
74
  * @public
75
75
  * @readonly
76
- * @type {string}
76
+ * @type {?string}
77
77
  */
78
78
  this.updated = undefined;
79
79
  /**
80
80
  * The billing plan to process and charge the batch job with.
81
81
  * @public
82
82
  * @readonly
83
- * @type {string}
83
+ * @type {?string}
84
84
  */
85
85
  this.plan = undefined;
86
86
  /**
@@ -78,7 +78,7 @@ class OidcProvider extends AuthProvider {
78
78
  /**
79
79
  * The client ID to use for authentication.
80
80
  *
81
- * @type {?string}
81
+ * @type {string | null}
82
82
  */
83
83
  this.clientId = null;
84
84
 
@@ -105,6 +105,13 @@ class OidcProvider extends AuthProvider {
105
105
  */
106
106
  this.scopes = Array.isArray(options.scopes) && options.scopes.length > 0 ? options.scopes : ['openid'];
107
107
 
108
+ /**
109
+ * The scope that is used to request a refresh token.
110
+ *
111
+ * @type {string}
112
+ */
113
+ this.refreshTokenScope = "offline_access";
114
+
108
115
  /**
109
116
  * Any additional links.
110
117
  *
@@ -161,17 +168,20 @@ class OidcProvider extends AuthProvider {
161
168
  *
162
169
  * Supported only in Browser environments.
163
170
  *
171
+ * @async
164
172
  * @param {object.<string, *>} [options={}] - Object with authentication options.
173
+ * @param {boolean} [requestRefreshToken=false] - If set to `true`, adds a scope to request a refresh token.
165
174
  * @returns {Promise<void>}
166
175
  * @throws {Error}
167
176
  * @see https://github.com/IdentityModel/oidc-client-js/wiki#other-optional-settings
177
+ * @see {OidcProvider#refreshTokenScope}
168
178
  */
169
- async login(options = {}) {
179
+ async login(options = {}, requestRefreshToken = false) {
170
180
  if (!this.issuer || typeof this.issuer !== 'string') {
171
181
  throw new Error("No Issuer URL available for OpenID Connect");
172
182
  }
173
183
 
174
- this.manager = new Oidc.UserManager(this.getOptions(options));
184
+ this.manager = new Oidc.UserManager(this.getOptions(options, requestRefreshToken));
175
185
  this.addListener('UserLoaded', async () => this.setUser(await this.manager.getUser()), 'js-client');
176
186
  this.addListener('AccessTokenExpired', () => this.setUser(null), 'js-client');
177
187
  if (OidcProvider.uiMethod === 'popup') {
@@ -216,15 +226,22 @@ class OidcProvider extends AuthProvider {
216
226
  *
217
227
  * @protected
218
228
  * @param {object.<string, *>} options
229
+ * @param {boolean} [requestRefreshToken=false] - If set to `true`, adds a scope to request a refresh token.
219
230
  * @returns {object.<string, *>}
231
+ * @see {OidcProvider#refreshTokenScope}
220
232
  */
221
- getOptions(options = {}) {
233
+ getOptions(options = {}, requestRefreshToken = false) {
222
234
  let response_type = this.getResponseType();
235
+ let scope = this.scopes.slice(0);
236
+ if (requestRefreshToken && !scope.includes(this.refreshTokenScope)) {
237
+ scope.push(this.refreshTokenScope);
238
+ }
239
+
223
240
  return Object.assign({
224
241
  client_id: this.clientId,
225
242
  redirect_uri: OidcProvider.redirectUrl,
226
243
  authority: this.issuer.replace('/.well-known/openid-configuration', ''),
227
- scope: this.scopes.join(' '),
244
+ scope: scope.join(' '),
228
245
  validateSubOnSilentRenew: true,
229
246
  response_type,
230
247
  response_mode: response_type.includes('code') ? 'query' : 'fragment'
package/src/openeo.js CHANGED
@@ -110,7 +110,7 @@ class OpenEO {
110
110
  * @returns {string} Version number (according to SemVer).
111
111
  */
112
112
  static clientVersion() {
113
- return "2.3.1";
113
+ return "2.5.0";
114
114
  }
115
115
 
116
116
  }
package/src/service.js CHANGED
@@ -39,55 +39,55 @@ class Service extends BaseEntity {
39
39
  * The process chain to be executed.
40
40
  * @public
41
41
  * @readonly
42
- * @type {Process}
42
+ * @type {?Process}
43
43
  */
44
44
  this.process = undefined;
45
45
  /**
46
46
  * URL at which the secondary web service is accessible
47
47
  * @public
48
48
  * @readonly
49
- * @type {string}
49
+ * @type {?string}
50
50
  */
51
51
  this.url = undefined;
52
52
  /**
53
53
  * Web service type (protocol / standard) that is exposed.
54
54
  * @public
55
55
  * @readonly
56
- * @type {string}
56
+ * @type {?string}
57
57
  */
58
58
  this.type = undefined;
59
59
  /**
60
60
  * @public
61
61
  * @readonly
62
- * @type {boolean}
62
+ * @type {?boolean}
63
63
  */
64
64
  this.enabled = undefined;
65
65
  /**
66
66
  * Map of configuration settings, i.e. the setting names supported by the secondary web service combined with actual values.
67
67
  * @public
68
68
  * @readonly
69
- * @type {object.<string, *>}
69
+ * @type {?object.<string, *>}
70
70
  */
71
71
  this.configuration = undefined;
72
72
  /**
73
73
  * Additional attributes of the secondary web service, e.g. available layers for a WMS based on the bands in the underlying GeoTiff.
74
74
  * @public
75
75
  * @readonly
76
- * @type {object.<string, *>}
76
+ * @type {?object.<string, *>}
77
77
  */
78
78
  this.attributes = undefined;
79
79
  /**
80
80
  * Date and time of creation, formatted as a RFC 3339 date-time.
81
81
  * @public
82
82
  * @readonly
83
- * @type {string}
83
+ * @type {?string}
84
84
  */
85
85
  this.created = undefined;
86
86
  /**
87
87
  * The billing plan to process and charge the service with.
88
88
  * @public
89
89
  * @readonly
90
- * @type {string}
90
+ * @type {?string}
91
91
  */
92
92
  this.plan = undefined;
93
93
  /**
package/src/typedefs.js CHANGED
@@ -193,6 +193,18 @@
193
193
  * @type {object.<string, *>}
194
194
  */
195
195
 
196
+ /**
197
+ * An array, but enriched with additional details from an openEO API response.
198
+ *
199
+ * Adds two properties: `links` and `federation:missing`.
200
+ *
201
+ * @typedef ResponseArray
202
+ * @augments Array
203
+ * @type {Array.<*>}
204
+ * @property {Array.<Link>} links A list of related links.
205
+ * @property {Array.<string>} federation:missing A list of backends from the federation that are missing in the response data.
206
+ */
207
+
196
208
  /**
197
209
  * @typedef ServiceType
198
210
  * @type {object.<string, *>}
@@ -52,7 +52,7 @@ class UserProcess extends BaseEntity {
52
52
  * A list of categories.
53
53
  * @public
54
54
  * @readonly
55
- * @type {Array.<string>}
55
+ * @type {?Array.<string>}
56
56
  */
57
57
  this.categories = undefined;
58
58
  /**
@@ -74,40 +74,40 @@ class UserProcess extends BaseEntity {
74
74
  * Specifies that the process or parameter is deprecated with the potential to be removed in any of the next versions.
75
75
  * @public
76
76
  * @readonly
77
- * @type {boolean}
77
+ * @type {?boolean}
78
78
  */
79
79
  this.deprecated = undefined;
80
80
  /**
81
81
  * Declares the process or parameter to be experimental, which means that it is likely to change or may produce unpredictable behaviour.
82
82
  * @public
83
83
  * @readonly
84
- * @type {boolean}
84
+ * @type {?boolean}
85
85
  */
86
86
  this.experimental = undefined;
87
87
  /**
88
88
  * Declares any exceptions (errors) that might occur during execution of this process.
89
89
  * @public
90
90
  * @readonly
91
- * @type {object.<string, *>}
91
+ * @type {?object.<string, *>}
92
92
  */
93
93
  this.exceptions = undefined;
94
94
  /**
95
95
  * @public
96
96
  * @readonly
97
- * @type {Array.<object.<string, *>>}
97
+ * @type {?Array.<object.<string, *>>}
98
98
  */
99
99
  this.examples = undefined;
100
100
  /**
101
101
  * Links related to this process.
102
102
  * @public
103
103
  * @readonly
104
- * @type {Array.<Link>}
104
+ * @type {?Array.<Link>}
105
105
  */
106
106
  this.links = undefined;
107
107
  /**
108
108
  * @public
109
109
  * @readonly
110
- * @type {object.<string, *>}
110
+ * @type {?object.<string, *>}
111
111
  */
112
112
  this.processGraph = undefined;
113
113
  }