@openeo/js-client 2.5.1 → 2.7.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/job.js CHANGED
@@ -1,322 +1,323 @@
1
- const Environment = require('./env');
2
- const BaseEntity = require('./baseentity');
3
- const Logs = require('./logs');
4
- const Utils = require('@openeo/js-commons/src/utils');
5
- const StacMigrate = require('@radiantearth/stac-migrate');
6
-
7
- const STOP_STATUS = ['finished', 'canceled', 'error'];
8
-
9
- /**
10
- * A Batch Job.
11
- *
12
- * @augments BaseEntity
13
- */
14
- class Job extends BaseEntity {
15
-
16
- /**
17
- * Creates an object representing a batch job stored at the back-end.
18
- *
19
- * @param {Connection} connection - A Connection object representing an established connection to an openEO back-end.
20
- * @param {string} jobId - The batch job ID.
21
- */
22
- constructor(connection, jobId) {
23
- super(connection, ["id", "title", "description", "process", "status", "progress", "created", "updated", "plan", "costs", "budget"]);
24
- /**
25
- * The identifier of the batch job.
26
- * @public
27
- * @readonly
28
- * @type {string}
29
- */
30
- this.id = jobId;
31
- /**
32
- * @public
33
- * @readonly
34
- * @type {?string}
35
- */
36
- this.title = undefined;
37
- /**
38
- * @public
39
- * @readonly
40
- * @type {?string}
41
- */
42
- this.description = undefined;
43
- /**
44
- * The process chain to be executed.
45
- * @public
46
- * @readonly
47
- * @type {?Process}
48
- */
49
- this.process = undefined;
50
- /**
51
- * The current status of a batch job.
52
- * One of "created", "queued", "running", "canceled", "finished" or "error".
53
- * @public
54
- * @readonly
55
- * @type {?string}
56
- */
57
- this.status = undefined;
58
- /**
59
- * Indicates the process of a running batch job in percent.
60
- * @public
61
- * @readonly
62
- * @type {?number}
63
- */
64
- this.progress = undefined;
65
- /**
66
- * Date and time of creation, formatted as a RFC 3339 date-time.
67
- * @public
68
- * @readonly
69
- * @type {?string}
70
- */
71
- this.created = undefined;
72
- /**
73
- * Date and time of the last status change, formatted as a RFC 3339 date-time.
74
- * @public
75
- * @readonly
76
- * @type {?string}
77
- */
78
- this.updated = undefined;
79
- /**
80
- * The billing plan to process and charge the batch job with.
81
- * @public
82
- * @readonly
83
- * @type {?string}
84
- */
85
- this.plan = undefined;
86
- /**
87
- * An amount of money or credits in the currency specified by the back-end.
88
- * @public
89
- * @readonly
90
- * @type {?number}
91
- */
92
- this.costs = undefined;
93
- /**
94
- * Maximum amount of costs the request is allowed to produce in the currency specified by the back-end.
95
- * @public
96
- * @readonly
97
- * @type {?number}
98
- */
99
- this.budget = undefined;
100
- }
101
-
102
- /**
103
- * Updates the batch job data stored in this object by requesting the metadata from the back-end.
104
- *
105
- * @async
106
- * @returns {Promise<Job>} The update job object (this).
107
- * @throws {Error}
108
- */
109
- async describeJob() {
110
- let response = await this.connection._get('/jobs/' + this.id);
111
- return this.setAll(response.data);
112
- }
113
-
114
- /**
115
- * Modifies the batch job at the back-end and afterwards updates this object, too.
116
- *
117
- * @async
118
- * @param {object} parameters - An object with properties to update, each of them is optional, but at least one of them must be specified. Additional properties can be set if the server supports them.
119
- * @param {Process} parameters.process - A new process.
120
- * @param {string} parameters.title - A new title.
121
- * @param {string} parameters.description - A new description.
122
- * @param {string} parameters.plan - A new plan.
123
- * @param {number} parameters.budget - A new budget.
124
- * @returns {Promise<Job>} The updated job object (this).
125
- * @throws {Error}
126
- */
127
- async updateJob(parameters) {
128
- await this.connection._patch('/jobs/' + this.id, this._convertToRequest(parameters));
129
- if (this._supports('describeJob')) {
130
- return await this.describeJob();
131
- }
132
- else {
133
- return this.setAll(parameters);
134
- }
135
- }
136
-
137
- /**
138
- * Deletes the batch job from the back-end.
139
- *
140
- * @async
141
- * @throws {Error}
142
- */
143
- async deleteJob() {
144
- await this.connection._delete('/jobs/' + this.id);
145
- }
146
-
147
- /**
148
- * Calculate an estimate (potentially time/costs/volume) for a batch job.
149
- *
150
- * @async
151
- * @returns {Promise<JobEstimate>} A response compatible to the API specification.
152
- * @throws {Error}
153
- */
154
- async estimateJob() {
155
- let response = await this.connection._get('/jobs/' + this.id + '/estimate');
156
- return response.data;
157
- }
158
-
159
- /**
160
- * Get logs for the batch job from the back-end.
161
- *
162
- * @returns {Logs}
163
- */
164
- debugJob() {
165
- return new Logs(this.connection, '/jobs/' + this.id + '/logs');
166
- }
167
-
168
- /**
169
- * Checks for status changes and new log entries every x seconds.
170
- *
171
- * On every status change observed or on new log entries (if supported by the
172
- * back-end and not disabled via `requestLogs`), the callback is executed.
173
- * It may also be executed once at the beginning.
174
- * The callback receives the updated job (this object) and the logs (array) passed.
175
- *
176
- * The monitoring stops once the job has finished, was canceled or errored out.
177
- *
178
- * This is only supported if describeJob is supported by the back-end.
179
- *
180
- * Returns a function that can be called to stop monitoring the job manually.
181
- *
182
- * @param {Function} callback
183
- * @param {number} [interval=60] - Interval between update requests, in seconds as integer.
184
- * @param {boolean} [requestLogs=true] - Enables/Disables requesting logs
185
- * @returns {Function}
186
- * @throws {Error}
187
- */
188
- monitorJob(callback, interval = 60, requestLogs = true) {
189
- if (typeof callback !== 'function' || interval < 1) {
190
- return;
191
- }
192
- let capabilities = this.connection.capabilities();
193
- if (!capabilities.hasFeature('describeJob')) {
194
- throw new Error('Monitoring Jobs not supported by the back-end.');
195
- }
196
-
197
- let lastStatus = this.status;
198
- let intervalId = null;
199
- let logIterator = null;
200
- if (capabilities.hasFeature('debugJob') && requestLogs) {
201
- logIterator = this.debugJob();
202
- }
203
- let monitorFn = async () => {
204
- if (this.getDataAge() > 1) {
205
- await this.describeJob();
206
- }
207
- let logs = logIterator ? await logIterator.nextLogs() : [];
208
- if (lastStatus !== this.status || logs.length > 0) {
209
- callback(this, logs);
210
- }
211
- lastStatus = this.status;
212
- if (STOP_STATUS.includes(this.status)) {
213
- stopFn(); // eslint-disable-line no-use-before-define
214
- }
215
- };
216
- setTimeout(monitorFn, 0);
217
- intervalId = setInterval(monitorFn, interval * 1000);
218
- let stopFn = () => {
219
- if (intervalId) {
220
- clearInterval(intervalId);
221
- intervalId = null;
222
- }
223
- };
224
- return stopFn;
225
- }
226
-
227
- /**
228
- * Starts / queues the batch job for processing at the back-end.
229
- *
230
- * @async
231
- * @returns {Promise<Job>} The updated job object (this).
232
- * @throws {Error}
233
- */
234
- async startJob() {
235
- await this.connection._post('/jobs/' + this.id + '/results', {});
236
- if (this._supports('describeJob')) {
237
- return await this.describeJob();
238
- }
239
- return this;
240
- }
241
-
242
- /**
243
- * Stops / cancels the batch job processing at the back-end.
244
- *
245
- * @async
246
- * @returns {Promise<Job>} The updated job object (this).
247
- * @throws {Error}
248
- */
249
- async stopJob() {
250
- await this.connection._delete('/jobs/' + this.id + '/results');
251
- if (this._supports('describeJob')) {
252
- return await this.describeJob();
253
- }
254
- return this;
255
- }
256
-
257
- /**
258
- * Retrieves the STAC Item or Collection produced for the job results.
259
- *
260
- * The Item or Collection returned always complies to the latest STAC version (currently 1.0.0).
261
- *
262
- * @async
263
- * @returns {Promise<object.<string, *>>} The JSON-based response compatible to the API specification, but also including a `costs` property if present in the headers.
264
- * @throws {Error}
265
- */
266
- async getResultsAsStac() {
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
- }
271
- let data = StacMigrate.stac(response.data);
272
- if (!Utils.isObject(data.assets)) {
273
- data.assets = {};
274
- }
275
- if (data.type === 'Feature') { // Item
276
- if (typeof response.headers['openeo-costs'] === 'number') {
277
- data.properties.costs = response.headers['openeo-costs'];
278
- }
279
- }
280
- else { // Collection
281
- if (typeof response.headers['openeo-costs'] === 'number') {
282
- data.costs = response.headers['openeo-costs'];
283
- }
284
- }
285
-
286
- return data;
287
- }
288
-
289
- /**
290
- * Retrieves download links.
291
- *
292
- * @async
293
- * @returns {Promise<Array.<Link>>} A list of links (object with href, rel, title, type and roles).
294
- * @throws {Error}
295
- */
296
- async listResults() {
297
- let item = await this.getResultsAsStac();
298
- if (Utils.isObject(item.assets)) {
299
- return Object.values(item.assets);
300
- }
301
- else {
302
- return [];
303
- }
304
- }
305
-
306
- /**
307
- * Downloads the results to the specified target folder. The specified target folder must already exist!
308
- *
309
- * NOTE: This method is only supported in a NodeJS environment. In a browser environment this method throws an exception!
310
- *
311
- * @async
312
- * @param {string} targetFolder - A target folder to store the file to, which must already exist.
313
- * @returns {Promise<Array.<string>|void>} Depending on the environment: A list of file paths of the newly created files (Node), throws in Browsers.
314
- * @throws {Error}
315
- */
316
- async downloadResults(targetFolder) {
317
- let list = await this.listResults();
318
- return await Environment.downloadResults(this.connection, list, targetFolder);
319
- }
320
- }
321
-
322
- module.exports = Job;
1
+ const Environment = require('./env');
2
+ const BaseEntity = require('./baseentity');
3
+ const Logs = require('./logs');
4
+ const Utils = require('@openeo/js-commons/src/utils');
5
+ const StacMigrate = require('@radiantearth/stac-migrate');
6
+
7
+ const STOP_STATUS = ['finished', 'canceled', 'error'];
8
+
9
+ /**
10
+ * A Batch Job.
11
+ *
12
+ * @augments BaseEntity
13
+ */
14
+ class Job extends BaseEntity {
15
+
16
+ /**
17
+ * Creates an object representing a batch job stored at the back-end.
18
+ *
19
+ * @param {Connection} connection - A Connection object representing an established connection to an openEO back-end.
20
+ * @param {string} jobId - The batch job ID.
21
+ */
22
+ constructor(connection, jobId) {
23
+ super(connection, ["id", "title", "description", "process", "status", "progress", "created", "updated", "plan", "costs", "budget", "usage", ["log_level", "logLevel"], "links"]);
24
+ /**
25
+ * The identifier of the batch job.
26
+ * @public
27
+ * @readonly
28
+ * @type {string}
29
+ */
30
+ this.id = jobId;
31
+ /**
32
+ * @public
33
+ * @readonly
34
+ * @type {?string}
35
+ */
36
+ this.title = undefined;
37
+ /**
38
+ * @public
39
+ * @readonly
40
+ * @type {?string}
41
+ */
42
+ this.description = undefined;
43
+ /**
44
+ * The process chain to be executed.
45
+ * @public
46
+ * @readonly
47
+ * @type {?Process}
48
+ */
49
+ this.process = undefined;
50
+ /**
51
+ * The current status of a batch job.
52
+ * One of "created", "queued", "running", "canceled", "finished" or "error".
53
+ * @public
54
+ * @readonly
55
+ * @type {?string}
56
+ */
57
+ this.status = undefined;
58
+ /**
59
+ * Indicates the process of a running batch job in percent.
60
+ * @public
61
+ * @readonly
62
+ * @type {?number}
63
+ */
64
+ this.progress = undefined;
65
+ /**
66
+ * Date and time of creation, formatted as a RFC 3339 date-time.
67
+ * @public
68
+ * @readonly
69
+ * @type {?string}
70
+ */
71
+ this.created = undefined;
72
+ /**
73
+ * Date and time of the last status change, formatted as a RFC 3339 date-time.
74
+ * @public
75
+ * @readonly
76
+ * @type {?string}
77
+ */
78
+ this.updated = undefined;
79
+ /**
80
+ * The billing plan to process and charge the batch job with.
81
+ * @public
82
+ * @readonly
83
+ * @type {?string}
84
+ */
85
+ this.plan = undefined;
86
+ /**
87
+ * An amount of money or credits in the currency specified by the back-end.
88
+ * @public
89
+ * @readonly
90
+ * @type {?number}
91
+ */
92
+ this.costs = undefined;
93
+ /**
94
+ * Maximum amount of costs the request is allowed to produce in the currency specified by the back-end.
95
+ * @public
96
+ * @readonly
97
+ * @type {?number}
98
+ */
99
+ this.budget = undefined;
100
+ }
101
+
102
+ /**
103
+ * Updates the batch job data stored in this object by requesting the metadata from the back-end.
104
+ *
105
+ * @async
106
+ * @returns {Promise<Job>} The update job object (this).
107
+ * @throws {Error}
108
+ */
109
+ async describeJob() {
110
+ let response = await this.connection._get('/jobs/' + this.id);
111
+ return this.setAll(response.data);
112
+ }
113
+
114
+ /**
115
+ * Modifies the batch job at the back-end and afterwards updates this object, too.
116
+ *
117
+ * @async
118
+ * @param {object} parameters - An object with properties to update, each of them is optional, but at least one of them must be specified. Additional properties can be set if the server supports them.
119
+ * @param {Process} parameters.process - A new process.
120
+ * @param {string} parameters.title - A new title.
121
+ * @param {string} parameters.description - A new description.
122
+ * @param {string} parameters.plan - A new plan.
123
+ * @param {number} parameters.budget - A new budget.
124
+ * @returns {Promise<Job>} The updated job object (this).
125
+ * @throws {Error}
126
+ */
127
+ async updateJob(parameters) {
128
+ await this.connection._patch('/jobs/' + this.id, this._convertToRequest(parameters));
129
+ if (this._supports('describeJob')) {
130
+ return await this.describeJob();
131
+ }
132
+ else {
133
+ return this.setAll(parameters);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Deletes the batch job from the back-end.
139
+ *
140
+ * @async
141
+ * @throws {Error}
142
+ */
143
+ async deleteJob() {
144
+ await this.connection._delete('/jobs/' + this.id);
145
+ }
146
+
147
+ /**
148
+ * Calculate an estimate (potentially time/costs/volume) for a batch job.
149
+ *
150
+ * @async
151
+ * @returns {Promise<JobEstimate>} A response compatible to the API specification.
152
+ * @throws {Error}
153
+ */
154
+ async estimateJob() {
155
+ let response = await this.connection._get('/jobs/' + this.id + '/estimate');
156
+ return response.data;
157
+ }
158
+
159
+ /**
160
+ * Get logs for the batch job from the back-end.
161
+ *
162
+ * @param {?string} [level=null] - Minimum level of logs to return.
163
+ * @returns {Logs}
164
+ */
165
+ debugJob(level = null) {
166
+ return new Logs(this.connection, '/jobs/' + this.id + '/logs', level);
167
+ }
168
+
169
+ /**
170
+ * Checks for status changes and new log entries every x seconds.
171
+ *
172
+ * On every status change observed or on new log entries (if supported by the
173
+ * back-end and not disabled via `requestLogs`), the callback is executed.
174
+ * It may also be executed once at the beginning.
175
+ * The callback receives the updated job (this object) and the logs (array) passed.
176
+ *
177
+ * The monitoring stops once the job has finished, was canceled or errored out.
178
+ *
179
+ * This is only supported if describeJob is supported by the back-end.
180
+ *
181
+ * Returns a function that can be called to stop monitoring the job manually.
182
+ *
183
+ * @param {Function} callback
184
+ * @param {number} [interval=60] - Interval between update requests, in seconds as integer.
185
+ * @param {boolean} [requestLogs=true] - Enables/Disables requesting logs
186
+ * @returns {Function}
187
+ * @throws {Error}
188
+ */
189
+ monitorJob(callback, interval = 60, requestLogs = true) {
190
+ if (typeof callback !== 'function' || interval < 1) {
191
+ return;
192
+ }
193
+ let capabilities = this.connection.capabilities();
194
+ if (!capabilities.hasFeature('describeJob')) {
195
+ throw new Error('Monitoring Jobs not supported by the back-end.');
196
+ }
197
+
198
+ let lastStatus = this.status;
199
+ let intervalId = null;
200
+ let logIterator = null;
201
+ if (capabilities.hasFeature('debugJob') && requestLogs) {
202
+ logIterator = this.debugJob();
203
+ }
204
+ let monitorFn = async () => {
205
+ if (this.getDataAge() > 1) {
206
+ await this.describeJob();
207
+ }
208
+ let logs = logIterator ? await logIterator.nextLogs() : [];
209
+ if (lastStatus !== this.status || logs.length > 0) {
210
+ callback(this, logs);
211
+ }
212
+ lastStatus = this.status;
213
+ if (STOP_STATUS.includes(this.status)) {
214
+ stopFn(); // eslint-disable-line no-use-before-define
215
+ }
216
+ };
217
+ setTimeout(monitorFn, 0);
218
+ intervalId = setInterval(monitorFn, interval * 1000);
219
+ let stopFn = () => {
220
+ if (intervalId) {
221
+ clearInterval(intervalId);
222
+ intervalId = null;
223
+ }
224
+ };
225
+ return stopFn;
226
+ }
227
+
228
+ /**
229
+ * Starts / queues the batch job for processing at the back-end.
230
+ *
231
+ * @async
232
+ * @returns {Promise<Job>} The updated job object (this).
233
+ * @throws {Error}
234
+ */
235
+ async startJob() {
236
+ await this.connection._post('/jobs/' + this.id + '/results', {});
237
+ if (this._supports('describeJob')) {
238
+ return await this.describeJob();
239
+ }
240
+ return this;
241
+ }
242
+
243
+ /**
244
+ * Stops / cancels the batch job processing at the back-end.
245
+ *
246
+ * @async
247
+ * @returns {Promise<Job>} The updated job object (this).
248
+ * @throws {Error}
249
+ */
250
+ async stopJob() {
251
+ await this.connection._delete('/jobs/' + this.id + '/results');
252
+ if (this._supports('describeJob')) {
253
+ return await this.describeJob();
254
+ }
255
+ return this;
256
+ }
257
+
258
+ /**
259
+ * Retrieves the STAC Item or Collection produced for the job results.
260
+ *
261
+ * The Item or Collection returned always complies to the latest STAC version (currently 1.0.0).
262
+ *
263
+ * @async
264
+ * @returns {Promise<object.<string, *>>} The JSON-based response compatible to the API specification, but also including a `costs` property if present in the headers.
265
+ * @throws {Error}
266
+ */
267
+ async getResultsAsStac() {
268
+ let response = await this.connection._get('/jobs/' + this.id + '/results');
269
+ if (!Utils.isObject(response) || !Utils.isObject(response.data)) {
270
+ throw new Error("Results received from the back-end are invalid");
271
+ }
272
+ let data = StacMigrate.stac(response.data);
273
+ if (!Utils.isObject(data.assets)) {
274
+ data.assets = {};
275
+ }
276
+ if (data.type === 'Feature') { // Item
277
+ if (typeof response.headers['openeo-costs'] === 'number') {
278
+ data.properties.costs = response.headers['openeo-costs'];
279
+ }
280
+ }
281
+ else { // Collection
282
+ if (typeof response.headers['openeo-costs'] === 'number') {
283
+ data.costs = response.headers['openeo-costs'];
284
+ }
285
+ }
286
+
287
+ return data;
288
+ }
289
+
290
+ /**
291
+ * Retrieves download links.
292
+ *
293
+ * @async
294
+ * @returns {Promise<Array.<Link>>} A list of links (object with href, rel, title, type and roles).
295
+ * @throws {Error}
296
+ */
297
+ async listResults() {
298
+ let item = await this.getResultsAsStac();
299
+ if (Utils.isObject(item.assets)) {
300
+ return Object.values(item.assets);
301
+ }
302
+ else {
303
+ return [];
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Downloads the results to the specified target folder. The specified target folder must already exist!
309
+ *
310
+ * NOTE: This method is only supported in a NodeJS environment. In a browser environment this method throws an exception!
311
+ *
312
+ * @async
313
+ * @param {string} targetFolder - A target folder to store the file to, which must already exist.
314
+ * @returns {Promise<Array.<string>|void>} Depending on the environment: A list of file paths of the newly created files (Node), throws in Browsers.
315
+ * @throws {Error}
316
+ */
317
+ async downloadResults(targetFolder) {
318
+ let list = await this.listResults();
319
+ return await Environment.downloadResults(this.connection, list, targetFolder);
320
+ }
321
+ }
322
+
323
+ module.exports = Job;