feat(schedules): schedules support timezones (#363)
Fixes vatesfr/xo-web#1258
This commit is contained in:
parent
1d1a597b22
commit
7d993e8319
@ -79,6 +79,7 @@
|
|||||||
"make-error": "^1",
|
"make-error": "^1",
|
||||||
"micromatch": "^2.3.2",
|
"micromatch": "^2.3.2",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
|
"moment-timezone": "^0.5.4",
|
||||||
"ms": "^0.7.1",
|
"ms": "^0.7.1",
|
||||||
"multikey-hash": "^1.0.1",
|
"multikey-hash": "^1.0.1",
|
||||||
"ndjson": "^1.4.3",
|
"ndjson": "^1.4.3",
|
||||||
|
@ -3,6 +3,7 @@ const debug = createDebug('xo:api')
|
|||||||
|
|
||||||
import getKeys from 'lodash/keys'
|
import getKeys from 'lodash/keys'
|
||||||
import kindOf from 'kindof'
|
import kindOf from 'kindof'
|
||||||
|
import moment from 'moment-timezone'
|
||||||
import ms from 'ms'
|
import ms from 'ms'
|
||||||
import schemaInspector from 'schema-inspector'
|
import schemaInspector from 'schema-inspector'
|
||||||
|
|
||||||
@ -188,6 +189,11 @@ methodSignature.description = 'returns the signature of an API method'
|
|||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
|
const getServerTimezone = (tz => () => tz)(moment.tz.guess())
|
||||||
|
getServerTimezone.description = 'return the timezone server'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
export default class Api {
|
export default class Api {
|
||||||
constructor ({
|
constructor ({
|
||||||
context,
|
context,
|
||||||
@ -201,6 +207,7 @@ export default class Api {
|
|||||||
system: {
|
system: {
|
||||||
getMethodsInfo,
|
getMethodsInfo,
|
||||||
getServerVersion,
|
getServerVersion,
|
||||||
|
getServerTimezone,
|
||||||
getVersion,
|
getVersion,
|
||||||
listMethods,
|
listMethods,
|
||||||
methodSignature
|
methodSignature
|
||||||
|
@ -17,8 +17,8 @@ get.params = {
|
|||||||
id: {type: 'string'}
|
id: {type: 'string'}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create ({jobId, cron, enabled, name}) {
|
export async function create ({ jobId, cron, enabled, name, timezone }) {
|
||||||
return /* await */ this.createSchedule(this.session.get('user_id'), {job: jobId, cron, enabled, name})
|
return /* await */ this.createSchedule(this.session.get('user_id'), { job: jobId, cron, enabled, name, timezone })
|
||||||
}
|
}
|
||||||
|
|
||||||
create.permission = 'admin'
|
create.permission = 'admin'
|
||||||
@ -30,8 +30,8 @@ create.params = {
|
|||||||
name: {type: 'string', optional: true}
|
name: {type: 'string', optional: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function set ({id, jobId, cron, enabled, name}) {
|
export async function set ({ id, jobId, cron, enabled, name, timezone }) {
|
||||||
await this.updateSchedule(id, {job: jobId, cron, enabled, name})
|
await this.updateSchedule(id, { job: jobId, cron, enabled, name, timezone })
|
||||||
}
|
}
|
||||||
|
|
||||||
set.permission = 'admin'
|
set.permission = 'admin'
|
||||||
|
@ -15,13 +15,14 @@ export class Schedules extends Collection {
|
|||||||
return 'schedule:'
|
return 'schedule:'
|
||||||
}
|
}
|
||||||
|
|
||||||
create (userId, job, cron, enabled, name = undefined) {
|
create (userId, job, cron, enabled, name = undefined, timezone = undefined) {
|
||||||
return this.add(new Schedule({
|
return this.add(new Schedule({
|
||||||
userId,
|
userId,
|
||||||
job,
|
job,
|
||||||
cron,
|
cron,
|
||||||
enabled,
|
enabled,
|
||||||
name
|
name,
|
||||||
|
timezone
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
src/utils.js
17
src/utils.js
@ -12,6 +12,12 @@ import keys from 'lodash/keys'
|
|||||||
import kindOf from 'kindof'
|
import kindOf from 'kindof'
|
||||||
import multiKeyHashInt from 'multikey-hash'
|
import multiKeyHashInt from 'multikey-hash'
|
||||||
import xml2js from 'xml2js'
|
import xml2js from 'xml2js'
|
||||||
|
|
||||||
|
// Moment timezone can be loaded only one time, it's a workaround to load
|
||||||
|
// the latest version because cron module uses an old version of moment which
|
||||||
|
// does not implement `guess` function for example.
|
||||||
|
import 'moment-timezone'
|
||||||
|
|
||||||
import { CronJob } from 'cron'
|
import { CronJob } from 'cron'
|
||||||
import {
|
import {
|
||||||
all as pAll,
|
all as pAll,
|
||||||
@ -439,10 +445,12 @@ export const streamToArray = (stream, {
|
|||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
export const scheduleFn = (cronPattern, fn) => {
|
export const scheduleFn = (cronTime, fn, timeZone) => {
|
||||||
let running = false
|
let running = false
|
||||||
|
|
||||||
const job = new CronJob(cronPattern, async () => {
|
const job = new CronJob({
|
||||||
|
cronTime,
|
||||||
|
onTick: async () => {
|
||||||
if (running) {
|
if (running) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -456,10 +464,11 @@ export const scheduleFn = (cronPattern, fn) => {
|
|||||||
} finally {
|
} finally {
|
||||||
running = false
|
running = false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
start: true,
|
||||||
|
timeZone
|
||||||
})
|
})
|
||||||
|
|
||||||
job.start()
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
job.stop()
|
job.stop()
|
||||||
}
|
}
|
||||||
|
@ -74,8 +74,10 @@ export default class {
|
|||||||
_enable (schedule) {
|
_enable (schedule) {
|
||||||
const { id } = schedule
|
const { id } = schedule
|
||||||
|
|
||||||
const stopSchedule = scheduleFn(schedule.cron, () =>
|
const stopSchedule = scheduleFn(
|
||||||
this.xo.runJobSequence([ schedule.job ])
|
schedule.cron,
|
||||||
|
() => this.xo.runJobSequence([ schedule.job ]),
|
||||||
|
schedule.timezone
|
||||||
)
|
)
|
||||||
|
|
||||||
this._cronJobs[id] = stopSchedule
|
this._cronJobs[id] = stopSchedule
|
||||||
@ -137,8 +139,8 @@ export default class {
|
|||||||
return /* await */ this._redisSchedules.get()
|
return /* await */ this._redisSchedules.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSchedule (userId, {job, cron, enabled, name}) {
|
async createSchedule (userId, { job, cron, enabled, name, timezone }) {
|
||||||
const schedule_ = await this._redisSchedules.create(userId, job, cron, enabled, name)
|
const schedule_ = await this._redisSchedules.create(userId, job, cron, enabled, name, timezone)
|
||||||
const schedule = schedule_.properties
|
const schedule = schedule_.properties
|
||||||
|
|
||||||
this._add(schedule)
|
this._add(schedule)
|
||||||
@ -146,13 +148,14 @@ export default class {
|
|||||||
return schedule
|
return schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSchedule (id, {job, cron, enabled, name}) {
|
async updateSchedule (id, { job, cron, enabled, name, timezone }) {
|
||||||
const schedule = await this._getSchedule(id)
|
const schedule = await this._getSchedule(id)
|
||||||
|
|
||||||
if (job) schedule.set('job', job)
|
if (job) schedule.set('job', job)
|
||||||
if (cron) schedule.set('cron', cron)
|
if (cron) schedule.set('cron', cron)
|
||||||
if (enabled !== undefined) schedule.set('enabled', enabled)
|
if (enabled !== undefined) schedule.set('enabled', enabled)
|
||||||
if (name !== undefined) schedule.set('name', name)
|
if (name !== undefined) schedule.set('name', name)
|
||||||
|
if (timezone !== undefined) schedule.set('timezone', timezone)
|
||||||
|
|
||||||
await this._redisSchedules.save(schedule)
|
await this._redisSchedules.save(schedule)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user