alchemymvc 1.3.16 → 1.3.18

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.
@@ -63,7 +63,7 @@ var Session = Function.inherits('Alchemy.Base', function ClientSession(conduit)
63
63
  *
64
64
  * @author Jelle De Loecker <jelle@develry.be>
65
65
  * @since 1.1.0
66
- * @version 1.1.0
66
+ * @version 1.3.17
67
67
  *
68
68
  * @type {Number}
69
69
  */
@@ -75,6 +75,10 @@ Session.enforceProperty(function request_count(amount) {
75
75
 
76
76
  this.last_activity_date = new Date();
77
77
 
78
+ if (this.menu_item) {
79
+ this.menu_item.setWeight(this.last_activity_date.getTime());
80
+ }
81
+
78
82
  return amount;
79
83
  });
80
84
 
@@ -167,7 +171,7 @@ Session.setMethod(function hasAlreadyQueued() {
167
171
  *
168
172
  * @author Jelle De Loecker <jelle@develry.be>
169
173
  * @since 0.2.0
170
- * @version 0.4.0
174
+ * @version 1.3.17
171
175
  *
172
176
  * @param {Conduit} conduit The conduit to postpone
173
177
  *
@@ -187,7 +191,14 @@ Session.setMethod(function createMenuItem(conduit) {
187
191
 
188
192
  browser += ': ' + conduit.ip;
189
193
 
190
- this.menu_item = alchemy.Janeway.session_menu.addItem(browser);
194
+ let options = {
195
+ title : browser,
196
+ weight : this.last_activity_date.getTime(),
197
+ };
198
+
199
+ this.menu_item = alchemy.Janeway.session_menu.addItem(options, () => {
200
+ console.log('Clicked on session', this, this.id);
201
+ });
191
202
  });
192
203
 
193
204
  /**
package/lib/class/task.js CHANGED
@@ -1,19 +1,36 @@
1
- var running = alchemy.shared('Task.running', 'Array');
1
+ const running = alchemy.shared('Task.running', 'Array'),
2
+ HISTORY_DOC = Symbol('history_document'),
3
+ PAUSE_PLEDGE = Symbol('pause_pledge'),
4
+ RUNNING_PLEDGE = Symbol('running_pledge'),
5
+ STATUS = Symbol('status'),
6
+ STARTING = 0,
7
+ STARTED = 1,
8
+ PAUSED = 2,
9
+ STOPPED = 3;
2
10
 
3
11
  /**
4
12
  * The base "Task" class
5
13
  *
6
- * @author Jelle De Loecker <jelle@develry.be>
14
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
15
  * @since 0.3.0
8
16
  * @version 0.5.0
9
17
  */
10
- var Task = Function.inherits('Alchemy.Base', function Task() {
18
+ const Task = Function.inherits('Alchemy.Base', 'Alchemy.Task', function Task() {
11
19
 
12
20
  // When this command started executing
13
- this.started = null;
21
+ this.started_at = null;
14
22
 
15
23
  // When this command stopped executing
16
- this.stopped = null;
24
+ this.stopped_at = null;
25
+
26
+ // The current status
27
+ this[STATUS] = STARTING;
28
+
29
+ // Optional pause pledge
30
+ this[PAUSE_PLEDGE] = null;
31
+
32
+ // The main running pledge
33
+ this[RUNNING_PLEDGE] = null;
17
34
 
18
35
  // Caught error
19
36
  this.error = null;
@@ -24,12 +41,65 @@ var Task = Function.inherits('Alchemy.Base', function Task() {
24
41
 
25
42
  // Status reports
26
43
  this.reports = [];
44
+
45
+ // The payload/settings
46
+ this.payload = null;
47
+
48
+ // The origin AlchemyTask document
49
+ this.alchemy_task_document = null;
50
+
51
+ // The AlchemyTaskHistory document
52
+ this[HISTORY_DOC] = null;
53
+ });
54
+
55
+ /**
56
+ * Add a forced cron schedule:
57
+ * this task will always run at the given time
58
+ *
59
+ * @author Jelle De Loecker <jelle@elevenways.be>
60
+ * @since 1.3.17
61
+ * @version 1.3.17
62
+ *
63
+ * @param {String} cron_schedule
64
+ * @param {Object} settings
65
+ */
66
+ Task.setStatic(function addForcedCronSchedule(cron_schedule, settings) {
67
+
68
+ cron_schedule = new Classes.Alchemy.Cron(cron_schedule);
69
+
70
+ if (!this.forced_cron_schedules) {
71
+ this.forced_cron_schedules = [];
72
+ }
73
+
74
+ this.forced_cron_schedules.push({cron_schedule, settings});
75
+ });
76
+
77
+ /**
78
+ * Add a fallback cron schedule:
79
+ * this task will run at the given time if no other schedule is found
80
+ *
81
+ * @author Jelle De Loecker <jelle@elevenways.be>
82
+ * @since 1.3.17
83
+ * @version 1.3.17
84
+ *
85
+ * @param {String} cron_schedule
86
+ * @param {Object} settings
87
+ */
88
+ Task.setStatic(function addFallbackCronSchedule(cron_schedule, settings) {
89
+
90
+ cron_schedule = new Classes.Alchemy.Cron(cron_schedule);
91
+
92
+ if (!this.fallback_cron_schedules) {
93
+ this.fallback_cron_schedules = [];
94
+ }
95
+
96
+ this.fallback_cron_schedules.push({cron_schedule, settings});
27
97
  });
28
98
 
29
99
  /**
30
100
  * Each command has a configuration schema
31
101
  *
32
- * @author Jelle De Loecker <jelle@develry.be>
102
+ * @author Jelle De Loecker <jelle@elevenways.be>
33
103
  * @since 0.3.0
34
104
  * @version 0.5.0
35
105
  */
@@ -89,10 +159,49 @@ Task.setProperty('static_description', '');
89
159
  */
90
160
  Task.setProperty('force_description_callback', false);
91
161
 
162
+ /**
163
+ * Has this task started?
164
+ *
165
+ * @author Jelle De Loecker <jelle@elevenways.be>
166
+ * @since 1.3.17
167
+ * @version 1.3.17
168
+ *
169
+ * @type {Boolean}
170
+ */
171
+ Task.setProperty(function has_started() {
172
+ return this[STATUS] > STARTING;
173
+ });
174
+
175
+ /**
176
+ * Has this task been paused?
177
+ *
178
+ * @author Jelle De Loecker <jelle@elevenways.be>
179
+ * @since 1.3.17
180
+ * @version 1.3.17
181
+ *
182
+ * @type {Boolean}
183
+ */
184
+ Task.setProperty(function is_paused() {
185
+ return this[STATUS] == PAUSED;
186
+ });
187
+
188
+ /**
189
+ * Has this task stopped?
190
+ *
191
+ * @author Jelle De Loecker <jelle@elevenways.be>
192
+ * @since 1.3.17
193
+ * @version 1.3.17
194
+ *
195
+ * @type {Boolean}
196
+ */
197
+ Task.setProperty(function has_stopped() {
198
+ return this[STATUS] == STOPPED;
199
+ });
200
+
92
201
  /**
93
202
  * Return the basic record for JSON
94
203
  *
95
- * @author Jelle De Loecker <jelle@develry.be>
204
+ * @author Jelle De Loecker <jelle@elevenways.be>
96
205
  * @since 0.3.0
97
206
  * @version 1.0.0
98
207
  */
@@ -117,250 +226,288 @@ Task.setMethod(function toJSON() {
117
226
  /**
118
227
  * Callback with a nice description
119
228
  *
120
- * @author Jelle De Loecker <jelle@develry.be>
229
+ * @author Jelle De Loecker <jelle@elevenways.be>
121
230
  * @since 0.3.0
122
- * @version 0.3.0
231
+ * @version 1.3.17
123
232
  *
124
- * @param {Function} callback
233
+ * @return {Promise<String>}
125
234
  */
126
- Task.setMethod(function getDescription(callback) {
127
-
128
- var that = this;
235
+ Task.setMethod(async function getDescription() {
129
236
 
130
237
  // If there is a static description, that should be returned
131
238
  if (this.static_description && !this.force_description_callback) {
132
- return callback(null, this.static_description);
239
+ return this.static_description;
133
240
  }
134
241
 
135
- return this._getDescription(function gotDescription(err, description) {
136
-
137
- if (err) {
138
- return callback(err);
139
- }
242
+ let description = await this._getDescription();
243
+ this.description = description;
140
244
 
141
- that.description = description;
142
- return callback(null, description);
143
- });
245
+ return description;
144
246
  });
145
247
 
146
248
  /**
147
249
  * Callback with a nice description,
148
250
  * should be modified upon extension
149
251
  *
150
- * @author Jelle De Loecker <jelle@develry.be>
252
+ * @author Jelle De Loecker <jelle@elevenways.be>
151
253
  * @since 0.3.0
152
254
  * @version 0.3.0
153
255
  *
154
- * @param {Function} callback
256
+ * @return {Promise<String>}
155
257
  */
156
- Task.setMethod(function _getDescription(callback) {
157
- callback(null, this.constructor.title || this.name);
258
+ Task.setMethod(async function _getDescription() {
259
+ return this.constructor.title || this.name;
158
260
  });
159
261
 
160
262
  /**
161
- * The function to execute, needs to be overridden
263
+ * The main function to execute. Should not be called directly.
264
+ * Needs to be overridden by child classes.
162
265
  *
163
- * @author Jelle De Loecker <jelle@develry.be>
266
+ * @author Jelle De Loecker <jelle@elevenways.be>
164
267
  * @since 0.5.0
165
- * @version 0.5.0
268
+ * @version 1.3.17
269
+ */
270
+ Task.setMethod(async function executor() {
271
+ throw new Error('Task ' + this.constructor.title + ' has no executor function!');
272
+ });
273
+
274
+ /**
275
+ * Set the payload/settings
276
+ *
277
+ * @author Jelle De Loecker <jelle@elevenways.be>
278
+ * @since 1.3.17
279
+ * @version 1.3.17
166
280
  *
167
281
  * @param {Object} payload User provided data
168
- * @param {Object} callback
169
282
  */
170
- Task.setMethod(function execute(payload, callback) {
171
- if (!callback) {
172
- callback = Function.thrower;
173
- }
283
+ Task.setMethod(function setPayload(payload) {
284
+ this.payload = payload;
285
+ });
174
286
 
175
- callback(new Error('Task ' + this.constructor.title + ' has no executable function!'));
287
+ /**
288
+ * Set the original AlchemyTask document
289
+ *
290
+ * @author Jelle De Loecker <jelle@elevenways.be>
291
+ * @since 1.3.17
292
+ * @version 1.3.17
293
+ *
294
+ * @param {Document.AlchemyTask} doc
295
+ */
296
+ Task.setMethod(function setAlchemyTaskDocument(doc) {
297
+ this.alchemy_task_document = doc;
298
+ });
299
+
300
+ /**
301
+ * Set the AlchemyTaskHistory document
302
+ *
303
+ * @author Jelle De Loecker <jelle@elevenways.be>
304
+ * @since 1.3.17
305
+ * @version 1.3.17
306
+ *
307
+ * @param {Document.AlchemyTaskHistory} doc
308
+ */
309
+ Task.setMethod(function setAlchemyTaskHistoryDocument(doc) {
310
+ this[HISTORY_DOC] = doc;
176
311
  });
177
312
 
178
313
  /**
179
314
  * Start executing the command
180
315
  *
181
- * @author Jelle De Loecker <jelle@develry.be>
316
+ * @author Jelle De Loecker <jelle@elevenways.be>
182
317
  * @since 0.3.0
183
- * @version 0.5.0
318
+ * @version 1.3.17
184
319
  *
185
320
  * @param {Object} payload User provided data
186
- * @param {Object} callback
187
321
  */
188
- Task.setMethod(function start(payload, callback) {
189
-
190
- var that = this,
191
- run_id = String(alchemy.ObjectId());
322
+ Task.setMethod(async function start(payload) {
192
323
 
193
- if (typeof payload == 'function') {
194
- callback = payload;
195
- payload = {};
324
+ if (this.has_started) {
325
+ throw new Error('Task ' + this.constructor.title + ' has already started!');
196
326
  }
197
327
 
198
- if (typeof callback != 'function') {
199
- callback = Function.thrower;
328
+ if (payload) {
329
+ this.setPayload(payload);
200
330
  }
201
331
 
202
- if (!payload) {
203
- payload = {};
332
+ const History = Model.get('AlchemyTaskHistory');
333
+
334
+ this[STATUS] = STARTED;
335
+ this[RUNNING_PLEDGE] = new Pledge();
336
+
337
+ let document = this[HISTORY_DOC];
338
+
339
+ if (!document) {
340
+ document = History.createDocument();
341
+ document.type = this.constructor.type_path || this.constructor.type_name;
342
+ document.alchemy_task_id = this.alchemy_task_document?.$pk;
343
+ document.process_id = process.pid;
344
+ this[HISTORY_DOC] = document;
204
345
  }
205
346
 
206
- // Store the payload
207
- this.payload = payload;
347
+ let started_at = new Date();
348
+ document.settings = this.payload;
349
+ document.started_at = started_at;
350
+ document.is_running = true;
208
351
 
209
352
  // Register this command as running
210
353
  running.push(this);
211
354
 
355
+ await document.save();
356
+
212
357
  // Set the id
213
- this.id = run_id;
358
+ this.id = String(document.$pk);
214
359
 
215
360
  // Set the start time
216
- this.started = Date.now();
361
+ this.started = started_at.getTime();
217
362
 
218
- // Actually execute the command
219
- this.execute(payload, function done(err, result) {
363
+ let result;
220
364
 
221
- var report;
222
-
223
- // Catch manua stop requests
365
+ try {
366
+ result = await this.executor();
367
+ } catch (err) {
224
368
  if (err == 'stopped') {
225
- that.report('stopped', 'Stopped');
226
- return callback(null);
369
+ this.report('stopped', 'Stopped');
370
+ return;
227
371
  }
228
372
 
229
- if (err) {
373
+ // Set the error
374
+ this.error = err;
230
375
 
231
- // Set the error
232
- that.error = err;
376
+ // Report failed
377
+ let report = this.report('failed');
378
+ report.error = err;
233
379
 
234
- // Report failed
235
- report = that.report('failed');
236
- report.error = err;
380
+ throw err;
381
+ }
237
382
 
238
- return callback(err);
239
- }
383
+ // If the command hasn't been manually stopped,
384
+ // report it as done
385
+ if (!this.manual_stop_end) {
386
+ this.report('done');
387
+ }
240
388
 
241
- // If the command hasn't been manually stopped,
242
- // report it as done
243
- if (!that.manual_stop_end) {
244
- that.report('done');
245
- }
389
+ document.ended_at = new Date();
390
+ document.is_running = false;
391
+ await document.save();
246
392
 
247
- callback(null, result);
248
- });
393
+ this[RUNNING_PLEDGE].resolve(result);
394
+
395
+ return result;
249
396
  });
250
397
 
251
398
  /**
252
399
  * Stop the running command
253
400
  *
254
- * @author Jelle De Loecker <jelle@develry.be>
401
+ * @author Jelle De Loecker <jelle@elevenways.be>
255
402
  * @since 0.3.0
256
- * @version 0.3.0
257
- *
258
- * @param {Object} callback
403
+ * @version 1.3.17
259
404
  */
260
- Task.setMethod(function stop(callback) {
261
-
262
- var that = this;
263
-
264
- if (!callback) {
265
- callback = Function.thrower;
266
- }
405
+ Task.setMethod(async function stop() {
267
406
 
268
407
  // Set time when manual stop was requested
269
408
  this.manual_stop_start = Date.now();
270
409
  this.need_stop = true;
271
410
 
272
411
  if (typeof this.doStop == 'function') {
273
- return this.doStop(function stopped(err) {
274
-
275
- if (err) {
276
- return callback(err);
277
- }
278
-
279
- that.manual_stop_end = Date.now();
280
- callback(null);
281
- });
282
- } else {
283
- callback();
412
+ let result = await this.doStop();
413
+ this.manual_stop_end = Date.now();
414
+ return result;
284
415
  }
285
416
  });
286
417
 
287
418
  /**
288
419
  * Pause the running command
289
420
  *
290
- * @author Jelle De Loecker <jelle@develry.be>
421
+ * @author Jelle De Loecker <jelle@elevenways.be>
291
422
  * @since 0.3.0
292
- * @version 0.3.0
293
- *
294
- * @param {Object} callback
423
+ * @version 1.3.17
295
424
  */
296
- Task.setMethod(function pause(callback) {
425
+ Task.setMethod(function pause() {
297
426
 
298
- var that = this;
427
+ // Do nothing if already paused
428
+ if (this.is_paused) {
429
+ return;
430
+ }
299
431
 
300
- if (!callback) {
301
- callback = Function.thrower;
432
+ // If this has stopped, throw an error
433
+ if (this.has_stopped) {
434
+ throw new Error('Unable to pause a task that has already stopped');
302
435
  }
303
436
 
304
- // Indicate this command needs to be paused
305
- this.pause_requested = true;
437
+ this[PAUSE_PLEDGE] = new Pledge();
438
+ this[STATUS] = PAUSED;
306
439
 
307
440
  if (typeof this.doPause == 'function') {
308
- return this.doPause(function paused(err) {
309
-
310
- if (err) {
311
- return callback(err);
312
- }
313
-
314
- callback(null);
315
- });
316
- } else {
317
- callback();
441
+ return this.doPause();
318
442
  }
319
443
  });
320
444
 
321
445
  /**
322
- * Resule the paused command
446
+ * Resume this task if it has been paused
323
447
  *
324
- * @author Jelle De Loecker <jelle@develry.be>
448
+ * @author Jelle De Loecker <jelle@elevenways.be>
325
449
  * @since 0.3.0
326
- * @version 0.3.0
327
- *
328
- * @param {Object} callback
450
+ * @version 1.3.17
329
451
  */
330
- Task.setMethod(function resume(callback) {
331
-
332
- var that = this;
452
+ Task.setMethod(function resume() {
333
453
 
334
- if (!callback) {
335
- callback = Function.thrower;
454
+ if (!this.is_paused) {
455
+ return;
336
456
  }
337
457
 
338
- // Indicate this command needs to be resumed
339
- this.pause_requested = false;
340
- this.resume_requested = true;
458
+ if (this.has_stopped) {
459
+ throw new Error('Unable to resume a task that has already stopped');
460
+ }
341
461
 
342
462
  if (typeof this.doResume == 'function') {
343
- return this.doResume(function resumed(err) {
463
+ this.doResume();
464
+ }
344
465
 
345
- if (err) {
346
- return callback(err);
347
- }
466
+ // If there is a pause pledge, resolve it
467
+ if (this[PAUSE_PLEDGE]) {
468
+ this[PAUSE_PLEDGE].resolve();
469
+ this[PAUSE_PLEDGE] = null;
470
+ }
348
471
 
349
- callback(null);
350
- });
351
- } else {
352
- callback();
472
+ // Set the status back to `started`
473
+ this[STATUS] = STARTED;
474
+ });
475
+
476
+ /**
477
+ * Get a parameter from the payload by the given name
478
+ *
479
+ * @author Jelle De Loecker <jelle@elevenways.be>
480
+ * @since 1.3.17
481
+ * @version 1.3.17
482
+ */
483
+ Task.setMethod(function getParam(name) {
484
+ return this.payload?.[name];
485
+ });
486
+
487
+ /**
488
+ * Wait for the pause to resolve
489
+ *
490
+ * @author Jelle De Loecker <jelle@elevenways.be>
491
+ * @since 1.3.17
492
+ * @version 1.3.17
493
+ */
494
+ Task.setMethod(function waitUntilResumed() {
495
+
496
+ if (!this.is_paused || this.has_stopped) {
497
+ return false;
353
498
  }
499
+
500
+ return this[PAUSE_PLEDGE];
354
501
  });
355
502
 
356
503
  /**
357
504
  * Report command progress
358
505
  *
359
- * @author Jelle De Loecker <jelle@develry.be>
506
+ * @author Jelle De Loecker <jelle@elevenways.be>
360
507
  * @since 0.3.0
361
- * @version 0.3.0
508
+ * @version 1.3.17
362
509
  *
363
- * @param {Number} percentage Percentage that is done
510
+ * @param {Number} percentage Percentage that is done as a decimal (between 0 & 1)
364
511
  * @param {String} type The type of status
365
512
  *
366
513
  * @return {Object}
@@ -381,6 +528,10 @@ Task.setMethod(function report(percentage, type) {
381
528
  this.paused = false;
382
529
  }
383
530
 
531
+ if (typeof percentage == 'number') {
532
+ percentage = Math.round(percentage * 10000) / 100;
533
+ }
534
+
384
535
  report = this.reports.last();
385
536
 
386
537
  if (type == null) {
@@ -436,9 +587,10 @@ Task.setMethod(function report(percentage, type) {
436
587
  });
437
588
 
438
589
  /**
439
- * Report progress, Janeway hooks into this
590
+ * Report progress, Janeway monkey-patches this method
591
+ * and uses it to display the progress bar.
440
592
  *
441
- * @author Jelle De Loecker <jelle@develry.be>
593
+ * @author Jelle De Loecker <jelle@elevenways.be>
442
594
  * @since 0.5.0
443
595
  * @version 0.5.0
444
596
  *
@@ -452,7 +604,7 @@ Task.setMethod(function reportProgress(value, label) {
452
604
  /**
453
605
  * Log command messages
454
606
  *
455
- * @author Jelle De Loecker <jelle@develry.be>
607
+ * @author Jelle De Loecker <jelle@elevenways.be>
456
608
  * @since 0.3.0
457
609
  * @version 0.3.0
458
610
  */
@@ -475,7 +627,7 @@ Task.setMethod(function log() {
475
627
  /**
476
628
  * Start executing a task
477
629
  *
478
- * @author Jelle De Loecker <jelle@develry.be>
630
+ * @author Jelle De Loecker <jelle@elevenways.be>
479
631
  * @since 0.3.0
480
632
  * @version 0.5.0
481
633
  *