ticketman
Version:
A simple pull-based job/ticket system contians a centeral ticket dispatcher and distributed workers. This system is written in NodeJS, runing on MongoDB
217 lines (193 loc) • 7.46 kB
JavaScript
// Generated by CoffeeScript 1.12.5
(function() {
var DEFAULT_BASIC_AUTH, DEFAULT_TIMEOUT, DEFAULT_WATCH_INTERVAL, EventEmitter, PATH_FOR_REQUIRE_TICKET, TicketWorker, assert, debuglog, env, oauth, request,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
assert = require("assert");
oauth = require("./utils/oauth");
env = process.env.NODE_ENV || 'development';
DEFAULT_BASIC_AUTH = require('./config/config')[env]['basicAuth'];
debuglog = require("debug")("ticketman:TicketWorker#");
EventEmitter = require('events').EventEmitter;
request = require("request");
PATH_FOR_REQUIRE_TICKET = "/api/tickets/assign";
DEFAULT_TIMEOUT = 20 * 60 * 1000;
DEFAULT_WATCH_INTERVAL = 1000;
TicketWorker = (function(superClass) {
extend(TicketWorker, superClass);
function TicketWorker(options) {
if (options == null) {
options = {};
}
assert((this.name = options.name), "missing id");
assert((this.id = options.id), "missing id");
assert((this.consumerSecret = options.consumer_secret || options.consumerSecret), "missing consumer secret");
assert((this.watchCategory = options.category), "missing category to watch");
assert((this.host = options.host), "missing host");
this.oauth = {
consumer_key: this.id,
consumer_secret: this.consumerSecret
};
this._isBusy = false;
this.timeout = options.timeout || DEFAULT_TIMEOUT;
this.interval = options.interval || DEFAULT_WATCH_INTERVAL;
this.basicAuth = options.basicAuth || DEFAULT_BASIC_AUTH;
if (this.timeout < this.interval * 3) {
this.timeout = this.interval * 3;
}
this.ticket = null;
this.commenceAt = 0;
debuglog("constructor, @name:" + this.name + ", @watchCategory:" + this.watchCategory + ", @timeout:" + this.timeout + ", @interval:" + this.interval);
setInterval(((function(_this) {
return function() {
return _this.watch();
};
})(this)), this.interval);
debuglog("[TicketWorker:constructor] @:%j", this);
}
TicketWorker.prototype.isBusy = function() {
return this._isBusy;
};
TicketWorker.prototype.watch = function() {
debuglog("watch: isBusy:" + (this.isBusy()));
if (this.isBusy()) {
if (Date.now() > this.timeout + this.commenceAt) {
this.giveup("ticket timeout");
}
} else {
this.requireTicket();
}
};
TicketWorker.prototype.setBusy = function(val) {
this._isBusy = Boolean(val);
if (this._isBusy) {
return this.commenceAt = Date.now();
}
};
TicketWorker.prototype.requireTicket = function() {
var body, options;
debuglog("requireTicket");
if (this.isBusy()) {
return;
}
this.setBusy(true);
body = {
category: this.watchCategory
};
options = {
method: 'PUT',
auth: this.basicAuth,
url: "" + this.host + PATH_FOR_REQUIRE_TICKET,
headers: oauth.makeSignatureHeader(this.id, 'PUT', PATH_FOR_REQUIRE_TICKET, body, this.consumerSecret),
json: body
};
request(options, (function(_this) {
return function(err, res, result) {
debuglog("requireTicket: err:" + err + ", res.statusCode:" + (res != null ? res.statusCode : "n/a") + ", ticket:%j", (typeof ticket !== "undefined" && ticket !== null ? ticket.title + "(" + ticket._id + ")" : "n/a"));
if (err != null) {
return debuglog("requireTicket: err: " + err);
}
if (res.statusCode !== 200) {
return debuglog("requireTicket: request failed, server status: " + res.statusCode);
}
if (result.success == null) {
_this.setBusy(false);
debuglog("requireTicket: request failed, " + result.error);
return;
}
if (result.ticket == null) {
_this.setBusy(false);
debuglog("requireTicket: no more ticket");
return;
}
_this.ticket = result.ticket;
if (_this.ticket._id) {
_this.ticket.id = _this.ticket._id;
}
_this.emit("new ticket", _this.ticket);
};
})(this));
return;
};
TicketWorker.prototype.complete = function() {
var _ticket, options, path;
if (!this.isBusy()) {
return;
}
path = "/api/tickets/" + this.ticket.id + "/complete";
options = {
method: 'PUT',
auth: this.basicAuth,
headers: oauth.makeSignatureHeader(this.id, 'PUT', path, {}, this.consumerSecret),
json: {},
url: "" + this.host + path
};
request(options, function(err, res, ticket) {
debuglog("complete: err:" + err + ", res.statusCode:" + (res != null ? res.statusCode : "n/a") + ", ticket:%j", (ticket != null ? ticket.title + "(" + ticket._id + ")" : "n/a"));
});
_ticket = this.ticket;
this.ticket = null;
this.emit("complete", _ticket);
this.setBusy(false);
};
TicketWorker.prototype.update = function(message, kind) {
var body, options, path;
if (kind == null) {
kind = 'default';
}
if (this.ticket == null) {
return debuglog("update: ERROR: current has no ticket. message:" + message);
}
body = {
kind: kind,
content: message
};
path = "/api/tickets/" + this.ticket._id + "/comment";
options = {
method: 'PUT',
auth: this.basicAuth,
headers: oauth.makeSignatureHeader(this.id, 'PUT', path, body, this.consumerSecret),
url: "" + this.host + path,
json: body
};
request(options, function(err, res, ticket) {
debuglog("update: err:" + err + ", res.statusCode:" + (res != null ? res.statusCode : "n/a") + ", ticket:%j", (ticket != null ? ticket.title + "(" + ticket._id + ")" : "n/a"));
});
};
TicketWorker.prototype.giveup = function(reason) {
var body, options, path;
debuglog("giveup");
if (!this.isBusy()) {
return;
}
if (this.ticket == null) {
debuglog("ERROR: busy but not ticket!!!!");
this.setBusy(false);
return;
}
path = "/api/tickets/" + this.ticket.id + "/giveup";
body = {
reason: reason
};
options = {
method: 'PUT',
auth: this.basicAuth,
headers: oauth.makeSignatureHeader(this.id, 'PUT', path, body, this.consumerSecret),
url: "" + this.host + path,
json: body
};
request(options, (function(_this) {
return function(err, res, ticket) {
var _ticket;
debuglog("giveup: err:" + err + ", res.statusCode:" + (res != null ? res.statusCode : "n/a") + ", ticket:%j", (ticket != null ? ticket.title + "(" + ticket._id + ")" : "n/a"));
_ticket = _this.ticket;
_this.ticket = null;
_this.emit("giveup", _ticket);
_this.setBusy(false);
};
})(this));
};
return TicketWorker;
})(EventEmitter);
module.exports = TicketWorker;
}).call(this);