blob: 5ba11f36ad30853568f6f4c062e7d9e77ac509a8 [file] [log] [blame]
var crypto = require('crypto');
var https = require('https');
var querystring = require('querystring');
var utils = require('./utils');
var HttpsProxyAgent = require('https-proxy-agent');
var url = require('url');
var extend = utils.extend;
var replace = utils.replace;
var DEFAULTS = {
username: null,
password: null,
proxy: null,
hostname: 'saucelabs.com',
base: '/rest/v1/',
port: '443'
};
function SauceLabs(options) {
this.options = extend({}, DEFAULTS, options);
this.options.auth = this.options.username + ':' + this.options.password;
}
module.exports = SauceLabs;
// API
SauceLabs.prototype.getAccountDetails = function (callback) {
this.send({
method: 'GET',
path: 'users/:username'
}, callback);
};
SauceLabs.prototype.getAccountLimits = function (callback) {
this.send({
method: 'GET',
path: ':username/limits'
}, callback);
};
SauceLabs.prototype.getUserActivity = function (start, end, callback) {
if (typeof start === 'function') {
callback = start;
start = null;
end = null;
} else if (typeof end === 'function') {
callback = end;
end = null;
}
var dates = (start != null || end != null) ? {} : null;
if (start != null) {
dates.start = formatDate(start);
}
if (end != null) {
dates.end = formatDate(end);
}
this.send({
method: 'GET',
path: ':username/activity',
query: dates
}, callback);
};
SauceLabs.prototype.getUserConcurrency = function (callback) {
this.send({
method: 'GET',
path: 'users/:username/concurrency'
}, callback);
};
SauceLabs.prototype.getAccountUsage = function (callback) {
this.send({
method: 'GET',
path: 'users/:username/usage'
}, callback);
};
SauceLabs.prototype.getJobs = function (callback) {
this.send({
method: 'GET',
path: ':username/jobs',
query: { full: true }
}, callback);
};
SauceLabs.prototype.showJob = function (id, callback) {
this.send({
method: 'GET',
path: ':username/jobs/:id',
args: { id: id }
}, callback);
};
SauceLabs.prototype.showJobAssets = function (id, callback) {
this.send({
method: 'GET',
path: ':username/jobs/:id/assets',
args: { id: id }
}, callback);
};
SauceLabs.prototype.updateJob = function (id, data, callback) {
this.send({
method: 'PUT',
path: ':username/jobs/:id',
args: { id: id },
data: data
}, callback);
};
SauceLabs.prototype.stopJob = function (id, data, callback) {
this.send({
method: 'PUT',
path: ':username/jobs/:id/stop',
args: { id: id },
data: data
}, callback);
};
SauceLabs.prototype.deleteJob = function (id, callback) {
this.send({
method: 'DELETE',
path: ':username/jobs/:id',
args: { id: id }
}, callback);
};
SauceLabs.prototype.getActiveTunnels = function (callback) {
this.send({
method: 'GET',
path: ':username/tunnels'
}, callback);
};
SauceLabs.prototype.getTunnel = function (id, callback){
this.send({
method: 'GET',
path: ':username/tunnels/:id',
args: { id: id }
}, callback);
};
SauceLabs.prototype.deleteTunnel = function (id, callback){
this.send({
method: 'DELETE',
path: ':username/tunnels/:id',
args: { id: id }
}, callback);
};
SauceLabs.prototype.getServiceStatus = function (callback) {
this.send({
method: 'GET',
path: 'info/status'
}, callback);
};
SauceLabs.prototype.getBrowsers = function (callback) {
this.send({
method: 'GET',
path: 'info/browsers'
}, callback);
};
SauceLabs.prototype.getAllBrowsers = function (callback) {
this.send({
method: 'GET',
path: 'info/browsers/all'
}, callback);
};
SauceLabs.prototype.getSeleniumBrowsers = function (callback) {
this.send({
method: 'GET',
path: 'info/browsers/selenium-rc'
}, callback);
};
SauceLabs.prototype.getWebDriverBrowsers = function (callback) {
this.send({
method: 'GET',
path: 'info/browsers/webdriver'
}, callback);
};
SauceLabs.prototype.getTestCounter = function (callback) {
this.send({
method: 'GET',
path: 'info/counter'
}, callback);
};
SauceLabs.prototype.updateSubAccount = function (data, callback) {
this.send({
method: 'POST',
path: 'users/:username/subscription',
data: data
}, callback);
};
SauceLabs.prototype.deleteSubAccount = function (callback) {
this.send({
method: 'DELETE',
path: 'users/:username/subscription'
}, callback);
};
SauceLabs.prototype.createSubAccount = function (data, callback) {
this.send({
method: 'POST',
path: 'users/:username',
data: data
}, callback);
};
SauceLabs.prototype.createPublicLink = function (id, date, useHour, callback) {
if (typeof date === 'function') {
callback = date;
date = null;
useHour = false;
} else if (typeof useHour === 'function') {
callback = useHour;
useHour = false;
}
if (date != null) {
date = formatDate(date, useHour);
}
var link = generateLink(this.options.hostname, this.options.auth, date, id);
callback(null, link);
};
SauceLabs.prototype.send = function (message, callback) {
var method = message.method,
path = message.path,
args = message.args,
query = message.query,
data = message.data,
body = JSON.stringify(data);
// Build path with base, placeholders, and query.
path = this.options.base + replace(path, extend({}, this.options, args));
if (query != null) {
path += '?' + querystring.stringify(query);
}
// Make the request.
var options = extend({}, this.options, {
method: method,
path: path,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Content-Length': body != null ? Buffer.byteLength(body) : 0
}
});
makeRequest(options, body, callback);
};
// Helpers
function formatDate(date, useHour) {
return date.toISOString().replace(/T(\d+).*/, (useHour ? '-$1' : ''));
}
function generateToken(auth, date, job) {
var key = auth + (date ? ':' + date : '');
return crypto
.createHmac('md5', key)
.update(job)
.digest('hex');
}
function generateLink(hostname, auth, date, job) {
return replace('https://:hostname/jobs/:id?auth=:token', {
hostname: hostname,
id: job,
token: generateToken(auth, date, job)
});
}
function makeRequest(options, body, callback) {
if(options.proxy){
options.agent = new HttpsProxyAgent(url.parse(options.proxy));
}
var request = https.request(options, function (response) {
var result = '';
if (callback) {
response
.on('data', function (chunk) {
result += chunk;
})
.on('end', function () {
var res;
try {
res = JSON.parse(result);
} catch (e) {
callback('Could not parse response: ' + result);
return;
}
if (response.statusCode === 200) {
callback(null, res);
} else {
callback(res);
}
});
}
});
request
.on('error', function (err) {
callback('Could not send request: ' + err.message);
});
if (body != null) {
request.write(body);
}
request.end();
}