Skip to content

Commit

Permalink
REST api endpoints are working for Cache entry manipulation. Closed #18
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan-Hendrik Kuperus committed Mar 20, 2015
1 parent 3ba92ed commit f6028a2
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 127 deletions.
129 changes: 107 additions & 22 deletions lib/cacheEntryRepository.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
'use strict';
var _ = require('lodash');
var $crypto = require('crypto');
var $utils = require('./utils');

module.exports = CacheEntries;
Expand All @@ -18,25 +20,54 @@ function CacheEntries (Nocca) {
// --- Simple recording playback

function considerRecordingRequest(reqContext) {
addSingleRecording(reqContext.endpoint.key, reqContext.requestKey, reqContext.getProxyResponse());
addSingleRecording(reqContext.endpoint.key, reqContext.requestKey, reqContext.getProxyResponse().dump());

// Always accepts recordings
return true;
}

function hashRequestKey(requestKey) { return $crypto.createHash('sha256').update(requestKey).digest('hex'); }

function addSingleRecording (endpoint, requestKey, recordedResponse) {
function addSingleRecording (endpointKey, requestKey, recordedResponse) {

// ensure presence of endpoint
recordings[endpoint] = recordings[endpoint] || {};
// ensure presence of endpointKey
recordings[endpointKey] = recordings[endpointKey] || {};

recordings[endpoint][requestKey] = recordedResponse;
var generatedHash = hashRequestKey(requestKey);

recordings[endpointKey][generatedHash] = {
hash: generatedHash,
requestKey: requestKey,
playbackResponse: recordedResponse
};

return generatedHash;
}

function removeSingleRecording(endpointKey, requestKey) {

removeSingleRecordingByHash(endpointKey, hashRequestKey(requestKey));

}

function removeSingleRecordingByHash(endpointKey, requestKeyHash) {

if (recordings.hasOwnProperty(endpointKey)) {

delete recordings[endpointKey][requestKeyHash];

}

}

function simpleRequestKeyRequestMatcher (reqContext) {
var response = null;
var playbackResponse = null;
var requestKeyHash = hashRequestKey(reqContext.requestKey);

if (typeof recordings[reqContext.endpoint.key] !== 'undefined' &&
typeof recordings[reqContext.endpoint.key][reqContext.requestKey] !== 'undefined') {
typeof recordings[reqContext.endpoint.key][requestKeyHash] !== 'undefined') {

response = recordings[reqContext.endpoint.key][reqContext.requestKey];
playbackResponse = recordings[reqContext.endpoint.key][requestKeyHash].playbackResponse;

Nocca.logInfo('Found a matching record using simpleMatcher!');

Expand All @@ -46,7 +77,7 @@ function CacheEntries (Nocca) {
Nocca.logDebug('No matching record found using simpleMatcher');
}

return response;
return playbackResponse;

}

Expand All @@ -61,18 +92,45 @@ function CacheEntries (Nocca) {

function initRestRoutes() {

Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['GET:/caches', getCaches]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['PUT:/caches/memory-caches/:endpoint/:entryHash', true, saveOrReplaceCacheEntry]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['GET:/repositories/memory-caches/endpoints/', getCachesForAllEndpoints]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['GET:/repositories/memory-caches/endpoints/:endpointKey/caches/', true, getCacheEntriesForEndpoint]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['GET:/repositories/memory-caches/endpoints/:endpointKey/caches/:requestKeyHash', true, getCacheEntryByKey]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['PUT:/repositories/memory-caches/endpoints/:endpointKey/caches/:requestKeyHash', true, saveOrReplaceCacheEntry]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['POST:/repositories/memory-caches/endpoints/:endpointKey/caches/', true, saveOrReplaceCacheEntry]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['DELETE:/repositories/memory-caches/endpoints/:endpointKey/caches/:requestKeyHash', true, deleteCacheEntry]);
Nocca.pubsub.publish(Nocca.constants.PUBSUB_REST_ROUTE_ADDED, ['POST:/caches/package', addCachePackage]);

}

function getCaches (req, res, config, matches, writeHead, writeEnd) {
writeHead(res, 200).writeEnd(JSON.stringify(exportRecordings(), null, 4));
function getCachesForAllEndpoints (apiReq) {
apiReq.ok().end(JSON.stringify(recordings));
}

function getCacheEntriesForEndpoint (apiReq) {

if (typeof recordings[apiReq.matches.endpointKey] !== 'undefined') {

apiReq.ok().end(JSON.stringify(recordings[apiReq.matches.endpointKey]));
}
else {
apiReq.notFound().end();
}
}

function addCachePackage (req, res, config, matches, writeHead, writeEnd) {
$utils.readBody(req).then(function(body) {
function getCacheEntryByKey (apiReq) {

if (typeof recordings[apiReq.matches.endpointKey] !== 'undefined' &&
typeof recordings[apiReq.matches.endpointKey][apiReq.matches.requestKeyHash] !== 'undefined') {

apiReq.ok().end(JSON.stringify(recordings[apiReq.matches.endpointKey][apiReq.matches.requestKeyHash]));
}
else {
apiReq.notFound().end();
}
}

function addCachePackage (apiReq) {
$utils.readBody(apiReq.req).then(function(body) {

body = JSON.parse(body);

Expand All @@ -91,31 +149,58 @@ function CacheEntries (Nocca) {
downloadObj = recordings;
}

writeHead(res, 200, {
apiReq.ok({
'Content-Type': 'application/json'
}).writeEnd(JSON.stringify(downloadObj));
}).end(JSON.stringify(downloadObj));

}).fail(function() {

writeHead(res, 400).writeEnd('Request body could not be parsed, is it a valid JSON string?');
apiReq.badRequest().end('Request body could not be parsed, is it a valid JSON string?');

});
}

function saveOrReplaceCacheEntry(req, res, config, matches, writeHead, writeEnd) {
$utils.readBody(req).then(function(body) {
function saveOrReplaceCacheEntry(apiReq) {
$utils.readBody(apiReq.req).then(function(body) {

body = JSON.parse(body);

if (!_.has(body, 'requestKey') || !_.has(body, 'playbackResponse')) {
apiReq.badRequest().end('Object requires at least a requestKey and response property');
return;
}

var newHash = addSingleRecording(apiReq.matches.endpointKey, body.requestKey, body.playbackResponse);

if (typeof apiReq.matches.requestKeyHash !== 'undefined' && newHash !== apiReq.matches.requestKeyHash) {
// Request key changed, delete old entry
removeSingleRecordingByHash(apiReq.matches.endpointKey, apiReq.matches.requestKeyHash);
}

apiReq.ok('Ok', { 'Location': '/repositories/memory-caches/endpoints/' + apiReq.matches.endpointKey + '/caches/' + newHash }).end();

}).fail(function() {
}).fail(function(err) {

writeHead(res, 400).writeEnd('Request body could not be parsed, is it a valid JSON string?');
apiReq.badRequest().end('Request body could not be parsed, is it a valid JSON string?');

});
}

function deleteCacheEntry(apiReq) {

if (typeof recordings[apiReq.matches.endpointKey] !== 'undefined' &&
typeof recordings[apiReq.matches.endpointKey][apiReq.matches.requestKeyHash] !== 'undefined') {

var entryToDelete = recordings[apiReq.matches.endpointKey][apiReq.matches.requestKeyHash];
delete recordings[apiReq.matches.endpointKey][apiReq.matches.requestKeyHash];

apiReq.ok().end(JSON.stringify(entryToDelete));
}
else {
apiReq.notFound().end();
}

}



Expand Down
129 changes: 76 additions & 53 deletions lib/httpApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function HttpApi (Nocca) {
Nocca.logDebug('Checking route: ' + route);

if (routes.direct.hasOwnProperty(route)) {
routes.direct[route](req, res, config, null, _writeHead, _writeEnd);
invokeRoute(routes.direct[route], req, res);//(req, res, config, null, _writeHead, _writeEnd);
}
else {
var match, handler;
Expand All @@ -73,7 +73,7 @@ function HttpApi (Nocca) {
}

if (typeof handler !== 'undefined') {
handler(req, res, config, match, _writeHead, _writeEnd);
invokeRoute(handler, req, res, match);//, config, match, _writeHead, _writeEnd);
}
else {
res.writeHead(404, 'Not found', {
Expand All @@ -88,6 +88,73 @@ function HttpApi (Nocca) {
}

}

function invokeRoute(handler, req, res, matches) {
handler(new ApiRequest(req, res, matches));
}

function ApiRequest(req, res, matches) {
this.req = req;
this.res = res;
this.matches = matches;
this.headWritten = false;
}

ApiRequest.prototype.nocca = function() { return Nocca; };
ApiRequest.prototype.writeHead = function (statusCode, statusMessage, headers) {

var writeHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,PUT,DELETE',
'Access-Control-Allow-Headers': 'content-type'
};

statusCode = statusCode || 200;
statusMessage = statusMessage || $http.STATUS_CODES[statusCode];

if (typeof headers === 'undefined' &&
typeof statusMessage === 'object') {

headers = statusMessage;
statusMessage = $http.STATUS_CODES[statusCode];

}

if (typeof headers === 'object') {
// add headers for cross domain access
writeHeaders = $extend(true, {}, writeHeaders, headers);
}

this.res.writeHead(statusCode, statusMessage, writeHeaders);
this.headWritten = true;

var self = this;
return {
end: function (data) {
self.end(data);
}
};
};
ApiRequest.prototype.end = function (data) {

if (typeof data !== 'undefined') {
// Hmm... I want ES6
var self = this;
this.res.write(data, function () {
self.res.end();
});
}
else {
this.res.end();
}

};
ApiRequest.prototype.ok = function(message, headers) { return this.writeHead(200, message, headers); };
ApiRequest.prototype.moved = function(message, headers) { return this.writeHead(301, message, headers); };
ApiRequest.prototype.badRequest = function(message, headers) { return this.writeHead(400, message, headers); };
ApiRequest.prototype.notFound = function(message, headers) { return this.writeHead(404, message, headers); };
ApiRequest.prototype.conflict = function(message, headers) { return this.writeHead(409, message, headers); };
ApiRequest.prototype.internalError = function(message, headers) { return this.writeHead(500, message, headers); };

// --- Route definitions

Expand Down Expand Up @@ -122,63 +189,19 @@ function HttpApi (Nocca) {
}


function getConfig (req, res, config) {
_writeHead(res, 200).writeEnd(JSON.stringify(config, null, 4));
function getConfig (apiReq) {
apiReq.ok().end(JSON.stringify(apiReq.nocca().config, null, 4));
}

function getEnumScenariosType (req, res) {
_writeHead(res).writeEnd(JSON.stringify($scenario.TYPE));
function getEnumScenariosType (apiReq) {
apiReq.ok().end(JSON.stringify(Nocca.scenario.TYPE));
}

function getEnumScenariosRepeatable (req, res) {
_writeHead(res).writeEnd(JSON.stringify($scenario.REPEATABLE));
function getEnumScenariosRepeatable (apiReq) {
apiReq.ok().end(JSON.stringify(Nocca.scenario.REPEATABLE));
}

function _writeHead (res, statusCode, statusMessage, headers) {

var writeHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,PUT,DELETE',
'Access-Control-Allow-Headers': 'content-type'
};

statusCode = statusCode || 200;
statusMessage = statusMessage || $http.STATUS_CODES[statusCode];

if (typeof headers === 'undefined' &&
typeof statusMessage === 'object') {

headers = statusMessage;
statusMessage = $http.STATUS_CODES[statusCode];

}

if (typeof headers === 'object') {
// add headers for cross domain access
writeHeaders = $extend(true, {}, writeHeaders, headers);
}

res.writeHead(statusCode, statusMessage, writeHeaders);

return {
writeEnd: function (data) {
_writeEnd(res, data);
}
};

}

function _writeEnd (res, data) {

if (typeof data !== 'undefined') {
res.write(data, function () {
res.end();
});
}
else {
res.end();
}

}


}
2 changes: 1 addition & 1 deletion lib/recorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function Recorder (Nocca) {

if (reqContext.getProxyResponse()) {

var acceptedRepository = _.find(Nocca.repositories, function(repository) { repository.considerRecording(reqContext); });
var acceptedRepository = _.find(Nocca.repositories, function(repository) { return repository.considerRecording(reqContext); });

reqContext.flagRecorded = typeof acceptedRepository !== 'undefined';

Expand Down
Loading

0 comments on commit f6028a2

Please sign in to comment.