feat(schedules): schedules support timezones (#363)

Fixes vatesfr/xo-web#1258
This commit is contained in:
ABHAMON Ronan 2016-07-19 13:32:27 +02:00 committed by Julien Fontanet
parent 1d1a597b22
commit 7d993e8319
6 changed files with 47 additions and 26 deletions

View File

@ -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",

View File

@ -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

View File

@ -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'

View File

@ -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
})) }))
} }

View File

@ -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()
} }

View File

@ -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)