1 |
|
2 |
|
3 |
|
4 | 'use strict';
|
5 |
|
6 | const Base = require('../base/Component');
|
7 |
|
8 | module.exports = class Task extends Base {
|
9 |
|
10 | static getConstants () {
|
11 | return {
|
12 | EVENT_BEFORE_RUN: 'beforeRun',
|
13 | EVENT_DONE: 'done',
|
14 | EVENT_FAIL: 'fail',
|
15 | DAY_PERIOD: 24 * 3600 * 1000
|
16 | };
|
17 | }
|
18 |
|
19 | constructor (config) {
|
20 | super({
|
21 | active: true,
|
22 | startup: false,
|
23 | startDate: null,
|
24 | startTime: null,
|
25 | period: 0,
|
26 | repeats: 0,
|
27 | stopOnFail: true,
|
28 | ...config
|
29 | });
|
30 | if (this.startup) {
|
31 | this.startDate = new Date;
|
32 | }
|
33 | this.period = DateHelper.parseDuration(this.period);
|
34 | this._counter = 0;
|
35 | }
|
36 |
|
37 | init () {
|
38 | if (this.active) {
|
39 | this.start();
|
40 | }
|
41 | }
|
42 |
|
43 | stop () {
|
44 | this._nextDate = null;
|
45 | this.cancelJob();
|
46 | }
|
47 |
|
48 | start () {
|
49 | this.stop();
|
50 | if (this.isRunning()) {
|
51 | return this.fail('Skip task start. Job in progress');
|
52 | }
|
53 | this._counter = 0;
|
54 | this._lastStartDate = null;
|
55 | this._lastEndtDate = null;
|
56 | this._lastError = null;
|
57 | this.setNextDate(this.startDate || this.getPeriodTime());
|
58 | return true;
|
59 | }
|
60 |
|
61 | isActive () {
|
62 | return this.active;
|
63 | }
|
64 |
|
65 | isRunning () {
|
66 | return !!this._job;
|
67 | }
|
68 |
|
69 | canRepeat () {
|
70 | return this.repeats === 0 || this._counter < this.repeats;
|
71 | }
|
72 |
|
73 | getLastError () {
|
74 | return this._lastError;
|
75 | }
|
76 |
|
77 | getLastStartDate () {
|
78 | return this._lastStartDate;
|
79 | }
|
80 |
|
81 | getLastEndDate () {
|
82 | return this._lastEndDate;
|
83 | }
|
84 |
|
85 | getNextDate () {
|
86 | return this._nextDate;
|
87 | }
|
88 |
|
89 | setNextDate (date) {
|
90 | if (!date) {
|
91 | return this._nextDate = null;
|
92 | }
|
93 | if (DateHelper.isValid(date)) {
|
94 | return this._nextDate = new Date(date);
|
95 | }
|
96 | this.log('error', `Invalid next date: ${date}`);
|
97 | return this._nextDate = null;
|
98 | }
|
99 |
|
100 | getPeriodTime () {
|
101 | if (this.canRepeat()) {
|
102 | if (this.startTime) {
|
103 | return this.formatStartTime();
|
104 | }
|
105 | if (this.period) {
|
106 | return Date.now() + this.period;
|
107 | }
|
108 | }
|
109 | }
|
110 |
|
111 | formatStartTime () {
|
112 | let date = `${moment().format('YYYY-MM-DD')} ${this.startTime}`;
|
113 | if (!DateHelper.isValid(date)) {
|
114 | return this.log('error', `Invalid start time: ${date}`);
|
115 | }
|
116 | date = (new Date(date)).getTime();
|
117 | return date < Date.now() ? (date + this.DAY_PERIOD) : date;
|
118 | }
|
119 |
|
120 | refresh () {
|
121 | if (this._nextDate && Date.now() >= this._nextDate) {
|
122 | this.setNextDate(this.getPeriodTime());
|
123 | if (this.active) {
|
124 | return this.execute();
|
125 | }
|
126 | }
|
127 | }
|
128 |
|
129 | async execute (data) {
|
130 | if (this.isRunning()) {
|
131 | return this.fail('Job not started. Previous one in progress');
|
132 | }
|
133 | try {
|
134 | this._job = this.createJob();
|
135 | await this.beforeRun();
|
136 | this.processInternal(data);
|
137 | } catch (err) {
|
138 | this._job = null;
|
139 | return this.fail(err);
|
140 | }
|
141 | }
|
142 |
|
143 | createJob () {
|
144 | return this.spawn(this.job, {task: this});
|
145 | }
|
146 |
|
147 | cancelJob () {
|
148 | if (!this.isRunning()) {
|
149 | return false;
|
150 | }
|
151 | try {
|
152 | this._job.cancel();
|
153 | } catch (err) {
|
154 | return this.fail(err);
|
155 | }
|
156 | }
|
157 |
|
158 | async processInternal (data) {
|
159 | if (!this.isRunning()) {
|
160 | return false;
|
161 | }
|
162 | try {
|
163 | this.log('info', `Job started: ${this._job.constructor.name}`);
|
164 | this._lastStartDate = new Date;
|
165 | const result = await this._job.execute(data);
|
166 | if (this._job.isCanceled()) {
|
167 | await this.fail('Job canceled');
|
168 | } else {
|
169 | this._counter += 1;
|
170 | this._lastEndDate = new Date;
|
171 | await this.done(result);
|
172 | }
|
173 | } catch (err) {
|
174 | try {
|
175 | await this.fail(err);
|
176 | } catch (err) {
|
177 | this.log('error', 'Failed', err);
|
178 | }
|
179 | }
|
180 | this._job = null;
|
181 | }
|
182 |
|
183 | beforeRun () {
|
184 | return this.trigger(this.EVENT_BEFORE_RUN);
|
185 | }
|
186 |
|
187 | done (result) {
|
188 | return this.trigger(this.EVENT_DONE, new Event({result}));
|
189 | }
|
190 |
|
191 | fail (error) {
|
192 | if (this.stopOnFail) {
|
193 | this._nextDate = null;
|
194 | }
|
195 | this._lastError = error;
|
196 | return this.trigger(this.EVENT_FAIL, new Event({error}));
|
197 | }
|
198 |
|
199 | log (type, message, data) {
|
200 | this.scheduler.log(type, `${this.constructor.name}: ${this.id}: ${message}`, data);
|
201 | }
|
202 | };
|
203 | module.exports.init();
|
204 |
|
205 | const moment = require('moment');
|
206 | const DateHelper = require('../helper/DateHelper');
|
207 | const Event = require('../base/Event'); |
\ | No newline at end of file |