@deephaven-enterprise/query-utils 1.20240723.62-alpha-fix-dependencies.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.
@@ -0,0 +1,914 @@
1
+ import Log from '@deephaven/log';
2
+ import { TimeUtils } from '@deephaven/utils';
3
+ import { parseDateTime, toCalendarDate, toTime, } from '@internationalized/date';
4
+ const log = Log.module('QueryScheduler');
5
+ const DEFAULT_CALENDAR = 'USNYSE';
6
+ const DEFAULT_EXPIRATION_TIME_MILLIS = 24 * 60 * 60 * 1000;
7
+ const DEFAULT_TIME_ZONE = 'America/New_York';
8
+ /**
9
+ * QueryScheduler is the client side equivalent of the server side class IrisScheduler.
10
+ *
11
+ * The server stores scheduling data as an array of Strings. This class converts that array
12
+ * into an object that can be manipulated by the UI. It then converts back to an array for
13
+ * storage on the server.
14
+ */
15
+ export class QueryScheduler {
16
+ static makeDefaultScheduling(restartQueryWhenRunningDefault, timeZone) {
17
+ return [
18
+ QueryScheduler.TOKENS.RESTART_WHEN_RUNNING +
19
+ QueryScheduler.TOKENS.DELIMITER +
20
+ restartQueryWhenRunningDefault,
21
+ QueryScheduler.FIELDS.TIME_ZONE +
22
+ QueryScheduler.TOKENS.DELIMITER +
23
+ timeZone,
24
+ ];
25
+ }
26
+ static setDefaultCalendar(name) {
27
+ log.info('before', QueryScheduler.defaultCalendar, name);
28
+ QueryScheduler.defaultCalendar = name;
29
+ log.info('after', QueryScheduler.defaultCalendar, name);
30
+ }
31
+ static validateSplitLength(s, splitString, length) {
32
+ if (splitString.length !== length) {
33
+ throw new Error(`Unexpected error parsing scheduler string array: ${s}`);
34
+ }
35
+ }
36
+ /**
37
+ * Creats a new QueryScheduler object.
38
+ *
39
+ * @param stringArray an array of strings representing a scheduler
40
+ */
41
+ constructor(stringArray) {
42
+ // must define fields explicitly in constructor to ensure fields are properly
43
+ // initialized even in derived classes
44
+ this.type = QueryScheduler.TYPES.DAILY;
45
+ // Base Fields
46
+ this.startTimeInternal = QueryScheduler.parseLocalTime('07:55:00');
47
+ this.stopTimeInternal = QueryScheduler.parseLocalTime('23:55:00');
48
+ this.timeZone = DEFAULT_TIME_ZONE;
49
+ this.schedulingDisabled = false;
50
+ this.overnightInternal = false;
51
+ this.repeatEnabled = false;
52
+ this.stopTimeDisabledInternal = false;
53
+ this.repeatInterval = 0;
54
+ this.skipIfUnsuccessful = false;
55
+ this.restartErrorCount = 0;
56
+ this.restartDelayMinutes = 0;
57
+ // TODO: use queryScheduler.restartWhenRunningDefault as the default, DH-11582
58
+ this.restartWhenRunning = QueryScheduler.RESTART_WHEN_RUNNING_OPTION.NO;
59
+ // Daily Fields
60
+ this.businessDays = false;
61
+ this.dailyBusinessCalendar = DEFAULT_CALENDAR;
62
+ this.dailyDays = Array(QueryScheduler.DAYS_PER_WEEK).fill(true);
63
+ // Monthly Fields
64
+ this.firstBusinessDay = false;
65
+ this.lastBusinessDay = false;
66
+ this.specificDays = true;
67
+ this.months = Array(QueryScheduler.MONTHS_PER_YEAR).fill(true);
68
+ this.monthlyDays = Array(QueryScheduler.DAYS_PER_MONTH).fill(true);
69
+ this.monthlyBusinessCalendar = DEFAULT_CALENDAR;
70
+ // Continuous Fields
71
+ this.dailyRestart = false;
72
+ // Dependent Fields
73
+ this.runOnFailure = false;
74
+ this.restartOnCondition = false;
75
+ this.dependentOnQuerySerials = [];
76
+ this.useMinStartTime = false;
77
+ this.runOnAny = false;
78
+ this.deadlineStartTime = QueryScheduler.parseLocalTime('00:00:00');
79
+ this.deadlineEndTime = QueryScheduler.parseLocalTime('23:59:59');
80
+ this.runEveryTime = false;
81
+ // Temporary Fields
82
+ this.temporaryQueueName = '';
83
+ this.expirationTimeMillis = DEFAULT_EXPIRATION_TIME_MILLIS;
84
+ this.temporaryDependentOnQuerySerials = [];
85
+ // Range Fields
86
+ // Null means not set
87
+ this.startDateTime = null;
88
+ this.stopDateTime = null;
89
+ if (stringArray != null) {
90
+ this.parseFromStringArray(stringArray);
91
+ }
92
+ }
93
+ /**
94
+ * Automatically turns the overnight flag on / off based on start and stop time.
95
+ */
96
+ checkForOvernight() {
97
+ const { overnightInternal, startTimeInternal, stopTimeInternal, type } = this;
98
+ if (type === QueryScheduler.TYPES.RANGE || this.stopTimeDisabledInternal) {
99
+ this.overnightInternal = false;
100
+ return;
101
+ }
102
+ if (!overnightInternal && stopTimeInternal <= startTimeInternal) {
103
+ this.overnightInternal = true;
104
+ }
105
+ else if (overnightInternal && startTimeInternal <= stopTimeInternal) {
106
+ this.overnightInternal = false;
107
+ }
108
+ }
109
+ get startTime() {
110
+ return this.startTimeInternal;
111
+ }
112
+ set startTime(value) {
113
+ this.startTimeInternal = value;
114
+ this.checkForOvernight();
115
+ }
116
+ get stopTime() {
117
+ return this.stopTimeInternal;
118
+ }
119
+ set stopTime(value) {
120
+ this.stopTimeInternal = value;
121
+ this.checkForOvernight();
122
+ }
123
+ get overnight() {
124
+ return this.overnightInternal;
125
+ }
126
+ set overnight(value) {
127
+ const { startTimeInternal, stopTimeInternal } = this;
128
+ this.overnightInternal = value;
129
+ if ((!value && stopTimeInternal < startTimeInternal) ||
130
+ (value && startTimeInternal < stopTimeInternal)) {
131
+ this.startTimeInternal = stopTimeInternal;
132
+ this.stopTimeInternal = startTimeInternal;
133
+ }
134
+ }
135
+ get stopTimeDisabled() {
136
+ return this.stopTimeDisabledInternal;
137
+ }
138
+ set stopTimeDisabled(value) {
139
+ const { overnightInternal } = this;
140
+ this.stopTimeDisabledInternal = value;
141
+ if (value && overnightInternal) {
142
+ this.overnight = false;
143
+ }
144
+ }
145
+ /**
146
+ * Creates a deep copy of this scheduler.
147
+ *
148
+ * @returns a copy of this scheduler
149
+ */
150
+ deepCopy() {
151
+ const copy = Object.assign(new QueryScheduler(), this);
152
+ copy.dailyDays = this.dailyDays.slice();
153
+ copy.months = this.months.slice();
154
+ copy.monthlyDays = this.monthlyDays.slice();
155
+ copy.dependentOnQuerySerials = this.dependentOnQuerySerials.slice();
156
+ copy.temporaryDependentOnQuerySerials =
157
+ this.temporaryDependentOnQuerySerials.slice();
158
+ copy.startDateTime =
159
+ this.startDateTime == null ? null : this.startDateTime.copy();
160
+ copy.stopDateTime =
161
+ this.stopDateTime == null ? null : this.stopDateTime.copy();
162
+ return copy;
163
+ }
164
+ /**
165
+ * Ensures that the scheduler has meaningful defaults for all fields. This allows
166
+ * the UI to quickly switch between scheduler types. Note that these defaults do
167
+ * not create valid schedulers for all types.
168
+ */
169
+ initDefaults() {
170
+ this.type = QueryScheduler.TYPES.DAILY;
171
+ // Base Fields
172
+ this.startTimeInternal = QueryScheduler.parseLocalTime('07:55:00');
173
+ this.stopTimeInternal = QueryScheduler.parseLocalTime('23:55:00');
174
+ this.timeZone = DEFAULT_TIME_ZONE;
175
+ this.schedulingDisabled = false;
176
+ this.overnightInternal = false;
177
+ this.repeatEnabled = false;
178
+ this.stopTimeDisabledInternal = false;
179
+ this.repeatInterval = 0;
180
+ this.skipIfUnsuccessful = false;
181
+ this.restartErrorCount = 0;
182
+ this.restartDelayMinutes = 0;
183
+ // TODO: use queryScheduler.restartWhenRunningDefault as the default, DH-11582
184
+ this.restartWhenRunning = QueryScheduler.RESTART_WHEN_RUNNING_OPTION.NO;
185
+ // Daily Fields
186
+ this.businessDays = false;
187
+ this.dailyBusinessCalendar = DEFAULT_CALENDAR;
188
+ this.dailyDays = Array(QueryScheduler.DAYS_PER_WEEK).fill(true);
189
+ // Monthly Fields
190
+ this.firstBusinessDay = false;
191
+ this.lastBusinessDay = false;
192
+ this.specificDays = true;
193
+ this.months = Array(QueryScheduler.MONTHS_PER_YEAR).fill(true);
194
+ this.monthlyDays = Array(QueryScheduler.DAYS_PER_MONTH).fill(true);
195
+ this.monthlyBusinessCalendar = DEFAULT_CALENDAR;
196
+ // Continuous Fields
197
+ this.dailyRestart = false;
198
+ // Dependent Fields
199
+ this.runOnFailure = false;
200
+ this.restartOnCondition = false;
201
+ this.dependentOnQuerySerials = [];
202
+ this.useMinStartTime = false;
203
+ this.runOnAny = false;
204
+ this.deadlineStartTime = QueryScheduler.parseLocalTime('00:00:00');
205
+ this.deadlineEndTime = QueryScheduler.parseLocalTime('23:59:59');
206
+ this.runEveryTime = false;
207
+ // Temporary Fields
208
+ this.temporaryQueueName = '';
209
+ this.expirationTimeMillis = DEFAULT_EXPIRATION_TIME_MILLIS;
210
+ this.temporaryDependentOnQuerySerials = [];
211
+ // Range Fields
212
+ // Null means not set
213
+ this.startDateTime = null;
214
+ this.stopDateTime = null;
215
+ }
216
+ /**
217
+ * Parses an array of strings into a QueryScheduler object.
218
+ *
219
+ * @param stringArray an array of strings representing a scheduler
220
+ */
221
+ parseFromStringArray(stringArray) {
222
+ const typeSpecificStrings = this.parseBaseScheduler(stringArray);
223
+ switch (this.type) {
224
+ case QueryScheduler.TYPES.DAILY:
225
+ this.parseDailyScheduler(typeSpecificStrings);
226
+ break;
227
+ case QueryScheduler.TYPES.MONTHLY:
228
+ this.parseMonthlyScheduler(typeSpecificStrings);
229
+ break;
230
+ case QueryScheduler.TYPES.CONTINUOUS:
231
+ this.parseContinuousScheduler(typeSpecificStrings);
232
+ break;
233
+ case QueryScheduler.TYPES.DEPENDENT:
234
+ this.parseDependentScheduler(typeSpecificStrings);
235
+ break;
236
+ case QueryScheduler.TYPES.TEMPORARY:
237
+ this.parseTemporaryScheduler(typeSpecificStrings);
238
+ break;
239
+ case QueryScheduler.TYPES.RANGE:
240
+ this.parseRangeScheduler(typeSpecificStrings);
241
+ break;
242
+ default:
243
+ throw new Error(`Unknown scheduler type: ${this.type}`);
244
+ }
245
+ }
246
+ /**
247
+ * Does the base parsing that is common to all scheduler types.
248
+ *
249
+ * @param stringArray an array of strings representing a scheduler
250
+ * @returns an array of strings fo type specific parsers
251
+ */
252
+ parseBaseScheduler(stringArray) {
253
+ const typeSpecificStrings = [];
254
+ stringArray.forEach(s => {
255
+ const splitString = s.split(QueryScheduler.TOKENS.DELIMITER);
256
+ switch (splitString[0]) {
257
+ case QueryScheduler.TOKENS.SCHEDULER_TYPE:
258
+ QueryScheduler.validateSplitLength(s, splitString, 2);
259
+ // eslint-disable-next-line prefer-destructuring
260
+ this.type = splitString[1];
261
+ break;
262
+ case QueryScheduler.TOKENS.START_TIME:
263
+ QueryScheduler.validateSplitLength(s, splitString, 2);
264
+ this.startTimeInternal = QueryScheduler.parseLocalTime(splitString[1]);
265
+ break;
266
+ case QueryScheduler.TOKENS.STOP_TIME:
267
+ QueryScheduler.validateSplitLength(s, splitString, 2);
268
+ this.stopTimeInternal = QueryScheduler.parseLocalTime(splitString[1]);
269
+ break;
270
+ case QueryScheduler.TOKENS.TIME_ZONE:
271
+ QueryScheduler.validateSplitLength(s, splitString, 2);
272
+ // eslint-disable-next-line prefer-destructuring
273
+ this.timeZone = splitString[1];
274
+ break;
275
+ case QueryScheduler.TOKENS.DISABLED:
276
+ QueryScheduler.validateSplitLength(s, splitString, 2);
277
+ this.schedulingDisabled = splitString[1] === 'true';
278
+ break;
279
+ case QueryScheduler.TOKENS.OVERNIGHT:
280
+ QueryScheduler.validateSplitLength(s, splitString, 2);
281
+ this.overnightInternal = splitString[1] === 'true';
282
+ break;
283
+ case QueryScheduler.TOKENS.REPEAT_ENABLED:
284
+ QueryScheduler.validateSplitLength(s, splitString, 2);
285
+ this.repeatEnabled = splitString[1] === 'true';
286
+ break;
287
+ case QueryScheduler.TOKENS.REPEAT_INTERVAL:
288
+ QueryScheduler.validateSplitLength(s, splitString, 2);
289
+ this.repeatInterval = Number(splitString[1]);
290
+ break;
291
+ case QueryScheduler.TOKENS.SKIP_IF_UNSUCCESSFUL:
292
+ QueryScheduler.validateSplitLength(s, splitString, 2);
293
+ this.skipIfUnsuccessful = splitString[1] === 'true';
294
+ break;
295
+ case QueryScheduler.TOKENS.STOP_TIME_DISABLED:
296
+ QueryScheduler.validateSplitLength(s, splitString, 2);
297
+ this.stopTimeDisabledInternal = splitString[1] === 'true';
298
+ break;
299
+ case QueryScheduler.TOKENS.RESTART_ERROR_COUNT:
300
+ QueryScheduler.validateSplitLength(s, splitString, 2);
301
+ this.restartErrorCount = Number(splitString[1]);
302
+ break;
303
+ case QueryScheduler.TOKENS.RESTART_ERROR_DELAY:
304
+ QueryScheduler.validateSplitLength(s, splitString, 2);
305
+ this.restartDelayMinutes = Number(splitString[1]);
306
+ break;
307
+ case QueryScheduler.TOKENS.RESTART_WHEN_RUNNING:
308
+ QueryScheduler.validateSplitLength(s, splitString, 2);
309
+ // eslint-disable-next-line prefer-destructuring
310
+ this.restartWhenRunning = splitString[1];
311
+ break;
312
+ default:
313
+ // Save this string for a type specific parser
314
+ typeSpecificStrings.push(s);
315
+ }
316
+ });
317
+ // In legacy queries, stopTime can be null, as some queries may not have a stop time and it was not previously saved.
318
+ // In that case stopTimeDisabled won't have been saved or set.
319
+ if (this.stopTime == null) {
320
+ this.stopTimeDisabledInternal = true;
321
+ }
322
+ return typeSpecificStrings;
323
+ }
324
+ /**
325
+ * Parses the Daily scheduler type.
326
+ *
327
+ * @param stringArray an array of strings representing a scheduler
328
+ */
329
+ parseDailyScheduler(stringArray) {
330
+ stringArray.forEach(s => {
331
+ const splitString = s.split(QueryScheduler.TOKENS.DELIMITER);
332
+ switch (splitString[0]) {
333
+ case QueryScheduler.TOKENS.BUSINESS_DAYS:
334
+ QueryScheduler.validateSplitLength(s, splitString, 2);
335
+ this.businessDays = splitString[1] === 'true';
336
+ break;
337
+ case QueryScheduler.TOKENS.CALENDAR:
338
+ QueryScheduler.validateSplitLength(s, splitString, 2);
339
+ // eslint-disable-next-line prefer-destructuring
340
+ this.dailyBusinessCalendar = splitString[1];
341
+ break;
342
+ case QueryScheduler.TOKENS.DAYS:
343
+ // Should split into the token plus seven days
344
+ QueryScheduler.validateSplitLength(s, splitString, QueryScheduler.DAYS_PER_WEEK + 1);
345
+ // Remove the token from the array and convert the strings to booleans
346
+ this.dailyDays = splitString
347
+ .filter(v => v !== QueryScheduler.TOKENS.DAYS)
348
+ .map(b => b === 'true');
349
+ break;
350
+ default:
351
+ log.warn('Found unexpected token while parsing daily schedule', s);
352
+ break;
353
+ }
354
+ });
355
+ }
356
+ /**
357
+ * Parses the Monthly scheduler type.
358
+ *
359
+ * @param stringArray an array of strings representing a scheduler
360
+ */
361
+ parseMonthlyScheduler(stringArray) {
362
+ stringArray.forEach(s => {
363
+ const splitString = s.split(QueryScheduler.TOKENS.DELIMITER);
364
+ switch (splitString[0]) {
365
+ case QueryScheduler.TOKENS.FIRST_BUSINESS_DAY:
366
+ QueryScheduler.validateSplitLength(s, splitString, 2);
367
+ this.firstBusinessDay = splitString[1] === 'true';
368
+ break;
369
+ case QueryScheduler.TOKENS.LAST_BUSINESS_DAY:
370
+ QueryScheduler.validateSplitLength(s, splitString, 2);
371
+ this.lastBusinessDay = splitString[1] === 'true';
372
+ break;
373
+ case QueryScheduler.TOKENS.CALENDAR:
374
+ QueryScheduler.validateSplitLength(s, splitString, 2);
375
+ // eslint-disable-next-line prefer-destructuring
376
+ this.monthlyBusinessCalendar = splitString[1];
377
+ break;
378
+ case QueryScheduler.TOKENS.MONTHS:
379
+ // Should split into the token plus 12 months
380
+ QueryScheduler.validateSplitLength(s, splitString, QueryScheduler.MONTHS_PER_YEAR + 1);
381
+ // Remove the token from the array and convert the strings to booleans
382
+ this.months = splitString
383
+ .filter(v => v !== QueryScheduler.TOKENS.MONTHS)
384
+ .map(b => b === 'true');
385
+ break;
386
+ case QueryScheduler.TOKENS.SPECIFIC_DAYS:
387
+ QueryScheduler.validateSplitLength(s, splitString, 2);
388
+ this.specificDays = splitString[1] === 'true';
389
+ break;
390
+ case QueryScheduler.TOKENS.DAYS:
391
+ // Should split into the token plus 31 days
392
+ QueryScheduler.validateSplitLength(s, splitString, QueryScheduler.DAYS_PER_MONTH + 1);
393
+ // Remove the token from the array and convert the strings to booleans
394
+ this.monthlyDays = splitString
395
+ .filter(v => v !== QueryScheduler.TOKENS.DAYS)
396
+ .map(b => b === 'true');
397
+ break;
398
+ default:
399
+ log.warn('Found unexpected token while parsing monthly schedule', s);
400
+ break;
401
+ }
402
+ });
403
+ }
404
+ /**
405
+ * Parses the Continuous scheduler type.
406
+ *
407
+ * @param stringArray an array of strings representing a scheduler
408
+ */
409
+ parseContinuousScheduler(stringArray) {
410
+ stringArray.forEach(s => {
411
+ const splitString = s.split(QueryScheduler.TOKENS.DELIMITER);
412
+ switch (splitString[0]) {
413
+ case QueryScheduler.TOKENS.DAILY_RESTART:
414
+ QueryScheduler.validateSplitLength(s, splitString, 2);
415
+ this.dailyRestart = splitString[1] === 'true';
416
+ break;
417
+ default:
418
+ log.warn('Found unexpected token while parsing continuous schedule', s);
419
+ break;
420
+ }
421
+ });
422
+ }
423
+ /**
424
+ * Parses the Dependent scheduler type.
425
+ *
426
+ * @param stringArray an array of strings representing a scheduler
427
+ */
428
+ parseDependentScheduler(stringArray) {
429
+ stringArray.forEach(s => {
430
+ const splitString = s.split(QueryScheduler.TOKENS.DELIMITER);
431
+ switch (splitString[0]) {
432
+ case QueryScheduler.TOKENS.RUN_ON_FAILURE:
433
+ QueryScheduler.validateSplitLength(s, splitString, 2);
434
+ this.runOnFailure = splitString[1] === 'true';
435
+ break;
436
+ case QueryScheduler.TOKENS.RESTART_ON_CONDITION:
437
+ QueryScheduler.validateSplitLength(s, splitString, 2);
438
+ this.restartOnCondition = splitString[1] === 'true';
439
+ break;
440
+ case QueryScheduler.TOKENS.DEPENDENT_QUERY_SERIAL:
441
+ QueryScheduler.validateSplitLength(s, splitString, 2);
442
+ this.dependentOnQuerySerials = splitString[1].split(QueryScheduler.TOKENS.SERIAL_DELIMITER);
443
+ break;
444
+ case QueryScheduler.TOKENS.USE_MIN_START_TIME:
445
+ QueryScheduler.validateSplitLength(s, splitString, 2);
446
+ this.useMinStartTime = splitString[1] === 'true';
447
+ break;
448
+ case QueryScheduler.TOKENS.RUN_ON_ANY:
449
+ QueryScheduler.validateSplitLength(s, splitString, 2);
450
+ this.runOnAny = splitString[1] === 'true';
451
+ break;
452
+ case QueryScheduler.TOKENS.DEADLINE_START:
453
+ QueryScheduler.validateSplitLength(s, splitString, 2);
454
+ this.deadlineStartTime = QueryScheduler.parseLocalTime(splitString[1]);
455
+ break;
456
+ case QueryScheduler.TOKENS.DEADLINE_END:
457
+ QueryScheduler.validateSplitLength(s, splitString, 2);
458
+ this.deadlineEndTime = QueryScheduler.parseLocalTime(splitString[1]);
459
+ break;
460
+ case QueryScheduler.TOKENS.RUN_EACH_TIME:
461
+ QueryScheduler.validateSplitLength(s, splitString, 2);
462
+ this.runEveryTime = splitString[1] === 'true';
463
+ break;
464
+ default:
465
+ log.warn('Found unexpected token while parsing dependent schedule', s);
466
+ break;
467
+ }
468
+ });
469
+ }
470
+ /**
471
+ * Parses the Temporary scheduler type.
472
+ *
473
+ * @param stringArray an array of strings representing a scheduler
474
+ */
475
+ parseTemporaryScheduler(stringArray) {
476
+ stringArray.forEach(s => {
477
+ const splitString = s.split(QueryScheduler.TOKENS.DELIMITER);
478
+ switch (splitString[0]) {
479
+ case QueryScheduler.TOKENS.TEMPORARY_QUEUE_NAME:
480
+ QueryScheduler.validateSplitLength(s, splitString, 2);
481
+ // eslint-disable-next-line prefer-destructuring
482
+ this.temporaryQueueName = splitString[1];
483
+ break;
484
+ case QueryScheduler.TOKENS.TEMPORARY_EXPIRATION_TIME_MILLIS:
485
+ QueryScheduler.validateSplitLength(s, splitString, 2);
486
+ // eslint-disable-next-line prefer-destructuring
487
+ this.expirationTimeMillis = Number(splitString[1]);
488
+ break;
489
+ case QueryScheduler.TOKENS.TEMPORARY_DEPENDENT_QUERY_SERIAL:
490
+ QueryScheduler.validateSplitLength(s, splitString, 2);
491
+ this.temporaryDependentOnQuerySerials = splitString[1].split(QueryScheduler.TOKENS.SERIAL_DELIMITER);
492
+ break;
493
+ default:
494
+ log.warn('Found unexpected token while parsing tempory schedule', s);
495
+ break;
496
+ }
497
+ });
498
+ }
499
+ /**
500
+ * Parses the Range scheduler type.
501
+ *
502
+ * @param stringArray an array of strings representing a scheduler
503
+ */
504
+ parseRangeScheduler(stringArray) {
505
+ // The schedule includes boolean flags for whether to use the start and stop date times.
506
+ // But the Web UI does not use them. Instead it sets dateTimes to null if they are not used.
507
+ let useStartDateTime = false;
508
+ let useStopDateTime = false;
509
+ stringArray.forEach(s => {
510
+ const splitString = s.split(QueryScheduler.TOKENS.DELIMITER);
511
+ switch (splitString[0]) {
512
+ case QueryScheduler.TOKENS.USE_START_DATE_TIME:
513
+ QueryScheduler.validateSplitLength(s, splitString, 2);
514
+ useStartDateTime = splitString[1] === 'true';
515
+ break;
516
+ case QueryScheduler.TOKENS.USE_STOP_DATE_TIME:
517
+ QueryScheduler.validateSplitLength(s, splitString, 2);
518
+ useStopDateTime = splitString[1] === 'true';
519
+ break;
520
+ case QueryScheduler.TOKENS.START_DATE:
521
+ QueryScheduler.validateSplitLength(s, splitString, 2);
522
+ // Combine the start date and start time
523
+ // Format is 'yyyy-MM-ddTHH:mm:ss'
524
+ // startTime will be set in base parser or this will use the default
525
+ this.startDateTime = parseDateTime(`${splitString[1]}T${QueryScheduler.localTimeToString(this.startTime)}`);
526
+ break;
527
+ case QueryScheduler.TOKENS.STOP_DATE:
528
+ QueryScheduler.validateSplitLength(s, splitString, 2);
529
+ // Combine the stop date and stop time
530
+ // Format is 'yyyy-MM-ddTHH:mm:ss'
531
+ // stopTime will be set in base parser or this will use the default
532
+ this.stopDateTime = parseDateTime(`${splitString[1]}T${QueryScheduler.localTimeToString(this.stopTime)}`);
533
+ break;
534
+ default:
535
+ log.warn('Found unexpected token while parsing tempory schedule', s);
536
+ break;
537
+ }
538
+ });
539
+ if (!useStartDateTime) {
540
+ this.startDateTime = null;
541
+ }
542
+ if (!useStopDateTime) {
543
+ this.stopDateTime = null;
544
+ }
545
+ }
546
+ /**
547
+ * Generates an array of strings that describes the scheduler.
548
+ *
549
+ * @returns an array of strings describing the scheduler
550
+ */
551
+ toStringArray() {
552
+ const arrayEntries = [];
553
+ switch (this.type) {
554
+ case QueryScheduler.TYPES.DAILY:
555
+ this.toStringArrayDaily(arrayEntries);
556
+ break;
557
+ case QueryScheduler.TYPES.MONTHLY:
558
+ this.toStringArrayMonthly(arrayEntries);
559
+ break;
560
+ case QueryScheduler.TYPES.CONTINUOUS:
561
+ this.toStringArrayContinuous(arrayEntries);
562
+ break;
563
+ case QueryScheduler.TYPES.DEPENDENT:
564
+ this.toStringArrayDependent(arrayEntries);
565
+ break;
566
+ case QueryScheduler.TYPES.TEMPORARY:
567
+ this.toStringArrayTemporary(arrayEntries);
568
+ break;
569
+ case QueryScheduler.TYPES.RANGE:
570
+ this.toStringArrayRange(arrayEntries);
571
+ break;
572
+ default:
573
+ throw new Error(`Unknown scheduler type: ${this.type}`);
574
+ }
575
+ this.toStringArrayBase(arrayEntries);
576
+ return arrayEntries;
577
+ }
578
+ /**
579
+ * Generates an array of strings for the Base scheduler.
580
+ */
581
+ toStringArrayBase(arrayEntries) {
582
+ arrayEntries.push(QueryScheduler.TOKENS.START_TIME +
583
+ QueryScheduler.TOKENS.DELIMITER +
584
+ QueryScheduler.localTimeToString(this.startTime));
585
+ if (this.stopTime != null) {
586
+ arrayEntries.push(QueryScheduler.TOKENS.STOP_TIME +
587
+ QueryScheduler.TOKENS.DELIMITER +
588
+ QueryScheduler.localTimeToString(this.stopTime));
589
+ }
590
+ arrayEntries.push(QueryScheduler.TOKENS.TIME_ZONE +
591
+ QueryScheduler.TOKENS.DELIMITER +
592
+ this.timeZone);
593
+ arrayEntries.push(QueryScheduler.TOKENS.DISABLED +
594
+ QueryScheduler.TOKENS.DELIMITER +
595
+ this.schedulingDisabled);
596
+ arrayEntries.push(QueryScheduler.TOKENS.OVERNIGHT +
597
+ QueryScheduler.TOKENS.DELIMITER +
598
+ this.overnight);
599
+ arrayEntries.push(QueryScheduler.TOKENS.REPEAT_ENABLED +
600
+ QueryScheduler.TOKENS.DELIMITER +
601
+ this.repeatEnabled);
602
+ arrayEntries.push(QueryScheduler.TOKENS.SKIP_IF_UNSUCCESSFUL +
603
+ QueryScheduler.TOKENS.DELIMITER +
604
+ this.skipIfUnsuccessful);
605
+ arrayEntries.push(QueryScheduler.TOKENS.STOP_TIME_DISABLED +
606
+ QueryScheduler.TOKENS.DELIMITER +
607
+ this.stopTimeDisabled);
608
+ arrayEntries.push(QueryScheduler.TOKENS.RESTART_ERROR_COUNT +
609
+ QueryScheduler.TOKENS.DELIMITER +
610
+ this.restartErrorCount);
611
+ arrayEntries.push(QueryScheduler.TOKENS.RESTART_ERROR_DELAY +
612
+ QueryScheduler.TOKENS.DELIMITER +
613
+ this.restartDelayMinutes);
614
+ arrayEntries.push(QueryScheduler.TOKENS.RESTART_WHEN_RUNNING +
615
+ QueryScheduler.TOKENS.DELIMITER +
616
+ this.restartWhenRunning);
617
+ if (this.repeatInterval > 0) {
618
+ arrayEntries.push(QueryScheduler.TOKENS.REPEAT_INTERVAL +
619
+ QueryScheduler.TOKENS.DELIMITER +
620
+ this.repeatInterval);
621
+ }
622
+ }
623
+ /**
624
+ * Generates an array of strings for a Daily scheduler.
625
+ */
626
+ toStringArrayDaily(arrayEntries) {
627
+ arrayEntries.push(QueryScheduler.TOKENS.SCHEDULER_TYPE +
628
+ QueryScheduler.TOKENS.DELIMITER +
629
+ QueryScheduler.TYPES.DAILY);
630
+ arrayEntries.push(QueryScheduler.TOKENS.CALENDAR +
631
+ QueryScheduler.TOKENS.DELIMITER +
632
+ this.dailyBusinessCalendar);
633
+ arrayEntries.push(QueryScheduler.TOKENS.BUSINESS_DAYS +
634
+ QueryScheduler.TOKENS.DELIMITER +
635
+ this.businessDays);
636
+ arrayEntries.push(QueryScheduler.arrayToDelimitedString(QueryScheduler.TOKENS.DAYS, this.dailyDays));
637
+ }
638
+ /**
639
+ * Generates an array of strings for a Monthly scheduler.
640
+ */
641
+ toStringArrayMonthly(arrayEntries) {
642
+ arrayEntries.push(QueryScheduler.TOKENS.SCHEDULER_TYPE +
643
+ QueryScheduler.TOKENS.DELIMITER +
644
+ QueryScheduler.TYPES.MONTHLY);
645
+ arrayEntries.push(QueryScheduler.TOKENS.CALENDAR +
646
+ QueryScheduler.TOKENS.DELIMITER +
647
+ this.monthlyBusinessCalendar);
648
+ arrayEntries.push(QueryScheduler.TOKENS.FIRST_BUSINESS_DAY +
649
+ QueryScheduler.TOKENS.DELIMITER +
650
+ this.firstBusinessDay);
651
+ arrayEntries.push(QueryScheduler.TOKENS.LAST_BUSINESS_DAY +
652
+ QueryScheduler.TOKENS.DELIMITER +
653
+ this.lastBusinessDay);
654
+ arrayEntries.push(QueryScheduler.arrayToDelimitedString(QueryScheduler.TOKENS.MONTHS, this.months));
655
+ arrayEntries.push(QueryScheduler.TOKENS.SPECIFIC_DAYS +
656
+ QueryScheduler.TOKENS.DELIMITER +
657
+ this.specificDays);
658
+ arrayEntries.push(QueryScheduler.arrayToDelimitedString(QueryScheduler.TOKENS.DAYS, this.monthlyDays));
659
+ }
660
+ /**
661
+ * Generates an array of strings for a Continuous scheduler.
662
+ */
663
+ toStringArrayContinuous(arrayEntries) {
664
+ arrayEntries.push(QueryScheduler.TOKENS.SCHEDULER_TYPE +
665
+ QueryScheduler.TOKENS.DELIMITER +
666
+ QueryScheduler.TYPES.CONTINUOUS);
667
+ arrayEntries.push(QueryScheduler.TOKENS.DAILY_RESTART +
668
+ QueryScheduler.TOKENS.DELIMITER +
669
+ this.dailyRestart);
670
+ }
671
+ /**
672
+ * Generates an array of strings for a Dependent scheduler.
673
+ */
674
+ toStringArrayDependent(arrayEntries) {
675
+ arrayEntries.push(QueryScheduler.TOKENS.SCHEDULER_TYPE +
676
+ QueryScheduler.TOKENS.DELIMITER +
677
+ QueryScheduler.TYPES.DEPENDENT);
678
+ arrayEntries.push(QueryScheduler.TOKENS.RUN_ON_FAILURE +
679
+ QueryScheduler.TOKENS.DELIMITER +
680
+ this.runOnFailure);
681
+ arrayEntries.push(QueryScheduler.TOKENS.RESTART_ON_CONDITION +
682
+ QueryScheduler.TOKENS.DELIMITER +
683
+ this.restartOnCondition);
684
+ arrayEntries.push(QueryScheduler.TOKENS.DEPENDENT_QUERY_SERIAL +
685
+ QueryScheduler.TOKENS.DELIMITER +
686
+ this.dependentOnQuerySerials.join(QueryScheduler.TOKENS.SERIAL_DELIMITER));
687
+ arrayEntries.push(QueryScheduler.TOKENS.USE_MIN_START_TIME +
688
+ QueryScheduler.TOKENS.DELIMITER +
689
+ this.useMinStartTime);
690
+ arrayEntries.push(QueryScheduler.TOKENS.RUN_ON_ANY +
691
+ QueryScheduler.TOKENS.DELIMITER +
692
+ this.runOnAny);
693
+ arrayEntries.push(QueryScheduler.TOKENS.DEADLINE_START +
694
+ QueryScheduler.TOKENS.DELIMITER +
695
+ QueryScheduler.localTimeToString(this.deadlineStartTime));
696
+ arrayEntries.push(QueryScheduler.TOKENS.DEADLINE_END +
697
+ QueryScheduler.TOKENS.DELIMITER +
698
+ QueryScheduler.localTimeToString(this.deadlineEndTime));
699
+ arrayEntries.push(QueryScheduler.TOKENS.RUN_EACH_TIME +
700
+ QueryScheduler.TOKENS.DELIMITER +
701
+ this.runEveryTime);
702
+ }
703
+ /**
704
+ * Generates an array of strings for a Temporary scheduler.
705
+ */
706
+ toStringArrayTemporary(arrayEntries) {
707
+ arrayEntries.push(QueryScheduler.TOKENS.SCHEDULER_TYPE +
708
+ QueryScheduler.TOKENS.DELIMITER +
709
+ QueryScheduler.TYPES.TEMPORARY);
710
+ arrayEntries.push(QueryScheduler.TOKENS.TEMPORARY_QUEUE_NAME +
711
+ QueryScheduler.TOKENS.DELIMITER +
712
+ this.temporaryQueueName);
713
+ arrayEntries.push(QueryScheduler.TOKENS.TEMPORARY_EXPIRATION_TIME_MILLIS +
714
+ QueryScheduler.TOKENS.DELIMITER +
715
+ this.expirationTimeMillis);
716
+ const { temporaryDependentOnQuerySerials } = this;
717
+ if (temporaryDependentOnQuerySerials != null &&
718
+ temporaryDependentOnQuerySerials.length > 0) {
719
+ arrayEntries.push(QueryScheduler.TOKENS.TEMPORARY_DEPENDENT_QUERY_SERIAL +
720
+ QueryScheduler.TOKENS.DELIMITER +
721
+ temporaryDependentOnQuerySerials.join(QueryScheduler.TOKENS.SERIAL_DELIMITER));
722
+ }
723
+ }
724
+ /**
725
+ * Generates an array of strings for a Range scheduler.
726
+ */
727
+ toStringArrayRange(arrayEntries) {
728
+ const useStartDateTime = this.startDateTime != null;
729
+ const useStopDateTime = this.stopDateTime != null;
730
+ arrayEntries.push(QueryScheduler.TOKENS.SCHEDULER_TYPE +
731
+ QueryScheduler.TOKENS.DELIMITER +
732
+ QueryScheduler.TYPES.RANGE);
733
+ arrayEntries.push(QueryScheduler.TOKENS.USE_START_DATE_TIME +
734
+ QueryScheduler.TOKENS.DELIMITER +
735
+ useStartDateTime);
736
+ arrayEntries.push(QueryScheduler.TOKENS.USE_STOP_DATE_TIME +
737
+ QueryScheduler.TOKENS.DELIMITER +
738
+ useStopDateTime);
739
+ if (this.startDateTime != null) {
740
+ // Extract the startTime from the starDateTime
741
+ // This will be written later in toStringArrayBase
742
+ this.startTime = QueryScheduler.parseLocalTime(toTime(this.startDateTime).set({ millisecond: 0 }).toString());
743
+ // Extract the startDate from the startDateTime
744
+ const startDate = toCalendarDate(this.startDateTime).toString();
745
+ arrayEntries.push(QueryScheduler.TOKENS.START_DATE +
746
+ QueryScheduler.TOKENS.DELIMITER +
747
+ startDate);
748
+ }
749
+ if (this.stopDateTime != null) {
750
+ // Extract the stopTime from the stopDateTime
751
+ // This will be written later in toStringArrayBase
752
+ this.stopTime = QueryScheduler.parseLocalTime(toTime(this.stopDateTime).set({ millisecond: 0 }).toString());
753
+ // Extract the stopDate from the stopDateTime
754
+ const stopDate = toCalendarDate(this.stopDateTime).toString();
755
+ arrayEntries.push(QueryScheduler.TOKENS.STOP_DATE +
756
+ QueryScheduler.TOKENS.DELIMITER +
757
+ stopDate);
758
+ }
759
+ }
760
+ }
761
+ /**
762
+ * Creates a Default Scheduler.
763
+ */
764
+ QueryScheduler.createDefault = () => new QueryScheduler();
765
+ QueryScheduler.parseLocalTime = (string) => TimeUtils.parseTime(string);
766
+ QueryScheduler.localTimeToString = (timeInSeconds) => TimeUtils.formatTime(timeInSeconds);
767
+ QueryScheduler.MAX_LOCAL_TIME = 86399;
768
+ QueryScheduler.INVALID_SERIAL = '';
769
+ // Helper method to turn arrays into delimted strings, e.g. 'Days=true=false=true'
770
+ QueryScheduler.arrayToDelimitedString = (token, array) => {
771
+ const stringArray = [token];
772
+ array.forEach(item => stringArray.push(QueryScheduler.TOKENS.DELIMITER + item));
773
+ return stringArray.join('');
774
+ };
775
+ // The server side classes set SchedulerType to the qualified classname
776
+ // if the class names or packages ever change, this will break
777
+ QueryScheduler.TYPES = Object.freeze({
778
+ DAILY: 'com.illumon.iris.controller.IrisQuerySchedulerDaily',
779
+ MONTHLY: 'com.illumon.iris.controller.IrisQuerySchedulerMonthly',
780
+ CONTINUOUS: 'com.illumon.iris.controller.IrisQuerySchedulerContinuous',
781
+ DEPENDENT: 'com.illumon.iris.controller.IrisQuerySchedulerDependent',
782
+ TEMPORARY: 'com.illumon.iris.controller.IrisQuerySchedulerTemporary',
783
+ RANGE: 'com.illumon.iris.controller.IrisQuerySchedulerRange',
784
+ });
785
+ // These are the tokens used to parse / create the String arrays
786
+ QueryScheduler.TOKENS = Object.freeze({
787
+ // From IrisScheduler
788
+ SCHEDULER_TYPE: 'SchedulerType',
789
+ DELIMITER: '=',
790
+ // From IrisQueryScheduler
791
+ START_TIME: 'StartTime',
792
+ STOP_TIME: 'StopTime',
793
+ OVERNIGHT: 'Overnight',
794
+ TIME_ZONE: 'TimeZone',
795
+ DISABLED: 'SchedulingDisabled',
796
+ REPEAT_ENABLED: 'RepeatEnabled',
797
+ REPEAT_INTERVAL: 'RepeatInterval',
798
+ SKIP_IF_UNSUCCESSFUL: 'SkipIfUnsuccessful',
799
+ STOP_TIME_DISABLED: 'StopTimeDisabled',
800
+ RESTART_ERROR_COUNT: 'RestartErrorCount',
801
+ RESTART_ERROR_DELAY: 'RestartErrorDelay',
802
+ RESTART_WHEN_RUNNING: 'RestartWhenRunning',
803
+ // From IrisQuerySchedulerDaily
804
+ BUSINESS_DAYS: 'BusinessDays',
805
+ CALENDAR: 'Calendar',
806
+ DAYS: 'Days',
807
+ // From IrisQuerySchedulerMonthly
808
+ FIRST_BUSINESS_DAY: 'FirstBusinessDay',
809
+ LAST_BUSINESS_DAY: 'LastBusinessDay',
810
+ SPECIFIC_DAYS: 'SpecificDays',
811
+ MONTHS: 'Months',
812
+ // From IrisQuerySchedulerContinuous
813
+ DAILY_RESTART: 'DailyRestart',
814
+ // From IrisQuerySchedulerDependent
815
+ RUN_ON_FAILURE: 'RunOnFailure',
816
+ RESTART_ON_CONDITION: 'RestartOnCondition',
817
+ DEPENDENT_QUERY_SERIAL: 'DependentQuerySerial',
818
+ USE_MIN_START_TIME: 'UseMinStartTime',
819
+ RUN_ON_ANY: 'RunOnAny',
820
+ DEADLINE_START: 'DeadlineStart',
821
+ DEADLINE_END: 'DeadlineEnd',
822
+ RUN_EACH_TIME: 'RunEachTime',
823
+ // From IrisQuerySchedulerTemporary
824
+ TEMPORARY_QUEUE_NAME: 'TemporaryQueueName',
825
+ TEMPORARY_EXPIRATION_TIME_MILLIS: 'TemporaryExpirationTimeMillis',
826
+ TEMPORARY_DEPENDENT_QUERY_SERIAL: 'TemporaryDependentQuerySerial',
827
+ // From IrisQuerySchedulerRange
828
+ USE_START_DATE_TIME: 'UseStartDateTime',
829
+ USE_STOP_DATE_TIME: 'UseStopDateTime',
830
+ START_DATE: 'StartDate',
831
+ STOP_DATE: 'StopDate',
832
+ // Other
833
+ SERIAL_DELIMITER: ';',
834
+ });
835
+ QueryScheduler.FIELDS = Object.freeze({
836
+ TYPE: 'type',
837
+ // From IrisQueryScheduler
838
+ START_TIME: 'startTime',
839
+ STOP_TIME: 'stopTime',
840
+ TIME_ZONE: 'timeZone',
841
+ SCHEDULING_DISABLED: 'schedulingDisabled',
842
+ OVERNIGHT: 'overnight',
843
+ REPEAT_ENABLED: 'repeatEnabled',
844
+ STOP_TIME_DISABLED: 'stopTimeDisabled',
845
+ REPEAT_INTERVAL: 'repeatInterval',
846
+ SKIP_IF_UNSUCCESSFUL: 'skipIfUnsuccessful',
847
+ RESTART_ERROR_COUNT: 'restartErrorCount',
848
+ RESTART_DELAY_MINUTES: 'restartDelayMinutes',
849
+ RESTART_WHEN_RUNNING: 'restartWhenRunning',
850
+ // From IrisQuerySchedulerDaily
851
+ BUSINESS_DAYS: 'businessDays',
852
+ DAILY_BUSINESS_CALENDAR: 'dailyBusinessCalendar',
853
+ DAILY_DAYS: 'dailyDays',
854
+ // From IrisQuerySchedulerMonthly
855
+ FIRST_BUSINESS_DAY: 'firstBusinessDay',
856
+ LAST_BUSINESS_DAY: 'lastBusinessDay',
857
+ SPECIFIC_DAYS: 'specificDays',
858
+ MONTHS: 'months',
859
+ MONTHLY_DAYS: 'monthlyDays',
860
+ MONTHLY_BUSINESS_CALENDAR: 'monthlyBusinessCalendar',
861
+ // From IrisQuerySchedulerContinuous
862
+ DAILY_RESTART: 'dailyRestart',
863
+ // From IrisQuerySchedulerDependent
864
+ RUN_ON_FAILURE: 'runOnFailure',
865
+ RESTART_ON_CONDITION: 'restartOnCondition',
866
+ DEPENDENT_ON_QUERY_SERIALS: 'dependentOnQuerySerials',
867
+ USE_MIN_START_TIME: 'useMinStartTime',
868
+ RUN_ON_ANY: 'runOnAny',
869
+ DEADLINE_START_TIME: 'deadlineStartTime',
870
+ DEADLINE_END_TIME: 'deadlineEndTime',
871
+ RUN_EVERY_TIME: 'runEveryTime',
872
+ // From IrisQuerySchedulerTemporary
873
+ TEMPORARY_QUEUE_NAME: 'temporaryQueueName',
874
+ EXPIRATION_TIME_MILLIS: 'expirationTimeMillis',
875
+ TEMPORARY_DEPENDENT_ON_QUERY_SERIALS: 'temporaryDependentOnQuerySerials',
876
+ // From IrisQuerySchedulerRange
877
+ START_DATE_TIME: 'startDateTime',
878
+ STOP_DATE_TIME: 'stopDateTime',
879
+ });
880
+ QueryScheduler.DAYS_PER_WEEK = 7;
881
+ QueryScheduler.DAYS_PER_MONTH = 31;
882
+ QueryScheduler.MONTHS_PER_YEAR = 12;
883
+ QueryScheduler.MAX_ERROR_RESTART_COUNT = 10;
884
+ QueryScheduler.UNLIMITED_ERROR_RESTART_INDEX = QueryScheduler.MAX_ERROR_RESTART_COUNT + 1;
885
+ // Per ISO-8601 the days array is Mon - Sun
886
+ // This is display order which is Sun - Sat
887
+ QueryScheduler.DAYS = Object.freeze({
888
+ SUN: { text: 'Su', value: 6 },
889
+ MON: { text: 'Mo', value: 0 },
890
+ TUE: { text: 'Tu', value: 1 },
891
+ WED: { text: 'We', value: 2 },
892
+ THU: { text: 'Th', value: 3 },
893
+ FRI: { text: 'Fr', value: 4 },
894
+ SAT: { text: 'Sa', value: 5 },
895
+ });
896
+ QueryScheduler.MONTHS = Object.freeze({
897
+ JAN: { text: 'Jan', value: 0 },
898
+ FEB: { text: 'Feb', value: 1 },
899
+ MAR: { text: 'Mar', value: 2 },
900
+ APR: { text: 'Apr', value: 3 },
901
+ MAY: { text: 'May', value: 4 },
902
+ JUN: { text: 'Jun', value: 5 },
903
+ JUL: { text: 'Jul', value: 6 },
904
+ AUG: { text: 'Aug', value: 7 },
905
+ SEP: { text: 'Sep', value: 8 },
906
+ OCT: { text: 'Oct', value: 9 },
907
+ NOV: { text: 'Nov', value: 10 },
908
+ DEC: { text: 'Dec', value: 11 },
909
+ });
910
+ QueryScheduler.RESTART_WHEN_RUNNING_OPTION = Object.freeze({
911
+ YES: 'Yes',
912
+ NO: 'No',
913
+ });
914
+ export default QueryScheduler;