API is starting to run

This commit is contained in:
Gaël Métais
2014-12-05 16:14:08 +01:00
parent e18cd87a07
commit 906f0876a0
18 changed files with 423 additions and 108 deletions
+110 -44
View File
@@ -1,86 +1,152 @@
var debug = require('debug')('ylt:server');
var debug = require('debug')('ylt:server');
var runsQueue = require('../datastores/runsQueue');
var runsDatastore = require('../datastores/runsDatastore');
var YellowLabTools = require('../../yellowlabtools');
var RunsQueue = require('../datastores/runsQueue');
var RunsDatastore = require('../datastores/runsDatastore');
var ResultsDatastore = require('../datastores/resultsDatastore');
function ApiController(app) {
var ApiController = function(app) {
'use strict';
var queue = new RunsQueue();
var runsDatastore = new RunsDatastore();
var resultsDatastore = new ResultsDatastore();
// Retrieve the list of all runs
/*app.get('/runs', function(req, res) {
/*app.get('/api/runs', function(req, res) {
// NOT YET
});*/
// Create a new run
app.post('/runs', function(req, res) {
app.post('/api/runs', function(req, res) {
// Grab the test parameters
// Grab the test parameters and generate a random run ID
var run = {
// Generate a random run ID
_id: (Date.now()*1000 + Math.round(Math.random()*1000)).toString(36),
runId: (Date.now()*1000 + Math.round(Math.random()*1000)).toString(36),
params: {
url: req.body.url,
waitForResponse: req.body.waitForResponse || true
waitForResponse: req.body.waitForResponse !== false
}
};
// Add test to the testQueue
debug('Adding test %s to the queue', run._id);
var queuing = runsQueue.push(run._id);
debug('Adding test %s to the queue', run.runId);
var queuePromise = queue.push(run.runId);
// Save the run to the datastore
var position = runsQueue.getPosition(run._id);
run.status = {
statusCode: (position === 0) ? STATUS_RUNNING : STATUS_AWAITING,
position: position
};
runsDatastore.add(run);
runsDatastore.add(run, queuePromise.startingPosition);
// Listening for position updates
queuing.progress(function(position) {
var savedRun = runsDatastore.get(run._id);
savedRun.status = {
statusCode: STATUS_AWAITING,
position: position
};
runsDatastore.update(savedRun);
queuePromise.progress(function(position) {
runsDatastore.updatePosition(run.runId, position);
});
// Let's start the run
queuePromise.then(function() {
queuing.then(function() {
runsDatastore.updatePosition(run.runId, 0);
debug('Launching test %s on %s', run.runId, run.params.url);
new YellowLabTools(run.params.url)
.then(function(data) {
debug('Success');
runsDatastore.markAsComplete(run.runId);
// Save result in datastore
data.runId = run.runId;
resultsDatastore.saveResult(data);
// Send result if the user was waiting
if (run.params.waitForResponse) {
res.redirect(302, '/api/results/' + run.runId);
}
}).fail(function(err) {
console.error('Test failed for %s', run.params.url);
console.error(err);
console.error(err.stack);
runsDatastore.markAsFailed(run.runId);
}).finally(function() {
queue.remove(run.runId);
});
}).fail(function(err) {
console.error('Error or YLT\'s core instanciation');
console.error(err);
console.error(err.stack);
});
// The user doesn't not want to wait for the response
if (!params.waitForResponse) {
// Sending just the test id
res.setHeader('Content-Type', 'application/javascript');
res.send(JSON.stringify({
testId: testId
}));
// The user doesn't not want to wait for the response, sending the run ID only
if (!run.params.waitForResponse) {
console.log('Sending response without waiting.');
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({runId: run.runId}));
}
});
// Retrive one run by id
app.get('/run/:id', function(req, res) {
app.get('/api/runs/:id', function(req, res) {
var runId = req.params.id;
var run = runsDatastore.get(runId);
if (run) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(run, null, 2));
} else {
res.status(404).send('Not found');
}
});
// Delete one run by id
/*app.delete('/run/:id', function(req, res) {
// NOT YET
/*app.delete('/api/runs/:id', function(req, res) {
deleteRun()
});*/
// Delete all
/*app.delete('/api/runs', function(req, res) {
purgeRuns()
});
var STATUS_AWAITING = 'awaiting';
var STATUS_RUNNING = 'running';
var STATUS_DONE = 'done';
var STATUS_FAILED = 'failed';
// List all
app.get('/api/runs', function(req, res) {
listRuns()
});
}
// Exists
app.head('/api/runs/:id', function(req, res) {
existsX();
// Retourne 200 si existe ou 404 si n'existe pas
});
*/
// Retrive one result by id
app.get('/api/results/:id', function(req, res) {
var runId = req.params.id;
resultsDatastore.getResult(runId)
.then(function(data) {
// This is the pivot format, we might need to clean it first?
// Hide phantomas results
data.toolsResults.phantomas = {};
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(data, null, 2));
}).fail(function() {
res.status(404).send('Not found');
});
});
};
module.exports = ApiController;
+13
View File
@@ -0,0 +1,13 @@
var UiController = function(app) {
'use strict';
// Create a new run
app.get('/', function(req, res) {
res.setHeader('Content-Type', 'text/html');
res.send('Test');
});
};
module.exports = UiController;
+71 -1
View File
@@ -1,9 +1,79 @@
var fs = require('fs');
var rimraf = require('rimraf');
var path = require('path');
var Q = require('q');
function ResultsDatastore() {
'use strict';
var resultFileName = 'results.json';
var resultsFolderName = 'results';
var resultsDir = path.join(__dirname, '..', '..', '..', resultsFolderName);
this.saveResult = function(testResults) {
var promise = createResultFolder(testResults.runId);
promise.then(function() {
var resultFilePath = path.join(resultsDir, testResults.runId, resultFileName);
return Q.nfcall(fs.writeFile, resultFilePath, JSON.stringify(testResults, null, 2));
});
return promise;
};
this.getResult = function(runId) {
var resultFilePath = path.join(resultsDir, runId, resultFileName);
return Q.nfcall(fs.readFile, resultFilePath, {encoding: 'utf8'}).then(function(data) {
return JSON.parse(data);
});
};
this.deleteResult = function(runId) {
var folder = path.join(resultsDir, runId);
return Q.nfcall(rimraf, folder);
};
// The folder /results/folderName/
function createResultFolder(folderName) {
var folder = path.join(resultsDir, folderName);
return createGlobalFolder().then(function() {
return Q.nfcall(fs.mkdir, folder);
});
}
// The folder /results/
function createGlobalFolder() {
var deferred = Q.defer();
// Create the results folder if it doesn't exist
fs.exists(resultsDir, function(exists) {
if (exists) {
deferred.resolve();
} else {
fs.mkdir(resultsDir, function(err) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve();
}
});
}
});
return deferred.promise;
}
}
module.exports = ResultsDatastore;
+49 -4
View File
@@ -7,23 +7,68 @@ function RunsDatastore() {
// For the moment, maybe one day
var runs = {};
var STATUS_AWAITING = 'awaiting';
var STATUS_RUNNING = 'running';
var STATUS_COMPLETE = 'complete';
var STATUS_FAILED = 'failed';
this.add = function(run) {
runs[run._id] = run;
this.add = function(run, position) {
runs[run.runId] = run;
this.updatePosition(run.runId, position);
};
this.get = function(runId) {
return runs[runId];
};
this.update = function(run) {
runs[run._id] = run;
this.updatePosition = function(runId, position) {
var run = runs[runId];
if (position > 0) {
run.status = {
statusCode: STATUS_AWAITING,
position: position
};
} else {
run.status = {
statusCode: STATUS_RUNNING
};
}
runs[runId] = run;
};
this.markAsComplete = function(runId) {
var run = runs[runId];
run.status = {
statusCode: STATUS_COMPLETE
};
runs[runId] = run;
};
this.markAsFailed = function(runId) {
var run = runs[runId];
run.status = {
statusCode: STATUS_FAILED
};
runs[runId] = run;
};
this.delete = function(runId) {
delete runs[runId];
};
this.list = function() {
var runsArray = [];
Object.keys(runs).forEach(function(key) {
+9 -2
View File
@@ -9,8 +9,9 @@ function RunsQueue() {
this.push = function(runId) {
var deferred = Q.defer();
var startingPosition = queue.length;
if (queue.length === 0) {
if (startingPosition === 0) {
// The queue is empty, let's run immediatly
queue.push({
@@ -32,7 +33,9 @@ function RunsQueue() {
});
}
return deferred.promise;
var promise = deferred.promise;
promise.startingPosition = startingPosition;
return promise;
};
@@ -68,6 +71,10 @@ function RunsQueue() {
});
};
this.length = function() {
return queue.length;
};
}
module.exports = RunsQueue;