Skip to content

Commit

Permalink
Custom responses for failed actions (#322)
Browse files Browse the repository at this point in the history
* Added handler for custom action errors

* Added Device.ActionError custom error object for use with action callbacks

* updated zetta-device version

* Updated action callback handler and tests

* Corrected properties value being passed to Siren response
  • Loading branch information
kyork-cl authored and AdamMagaluk committed Sep 23, 2016
1 parent e8e307f commit 241e629
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 26 deletions.
58 changes: 39 additions & 19 deletions lib/api_resources/servers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var MediaType = require('api-media-type');
var querytopic = require('../query_topic');
var streams = require('zetta-streams');
var ObjectStream = streams.ObjectStream;
var ActionError = require('zetta-device').ActionError;

var ServerResource = module.exports = function(server) {
this.server = server;
Expand All @@ -17,7 +18,7 @@ ServerResource.prototype.getServer = function(env) {
var parsed = url.parse(env.request.url);
var re = /^\/servers\/([^\/]+)/;
var match = re.exec(parsed.pathname);

var serverId = match && match[1] ? decodeURI(match[1]) : this.server.id;

return { path: '/servers/' + encodeURI(serverId) };
Expand Down Expand Up @@ -119,7 +120,7 @@ ServerResource.prototype.subscribe = function(env, next) {
if (qt) {
topic = querytopic.format(qt);
self.server.pubsub.subscribe(topic, env);

setImmediate(function() {
self.server.httpServer.eventBroker.subscribeToDeviceQuery(topic);
env.response.on('close', unsubscribe);
Expand Down Expand Up @@ -285,7 +286,7 @@ ServerResource.prototype._queryDevices = function(env, next) {

function done() {
var devices = {};

results.forEach(function(device){
var deviceOnRuntime = self.server.runtime._jsDevices[device.id];
if (deviceOnRuntime) {
Expand All @@ -299,7 +300,7 @@ ServerResource.prototype._queryDevices = function(env, next) {
loader: self.getServer(env),
env: env,
classes:['search-results'],
query: params.ql
query: params.ql
};

env.format.render('server', context);
Expand All @@ -310,7 +311,7 @@ ServerResource.prototype._queryDevices = function(env, next) {

ServerResource.prototype.destroyDevice = function(env, next) {
if(this.shouldProxy(env)) {
return this.proxy(env, next);
return this.proxy(env, next);
}

var device = this.server.runtime._jsDevices[env.route.params.deviceId];
Expand Down Expand Up @@ -338,23 +339,23 @@ ServerResource.prototype.destroyDevice = function(env, next) {
return next(env);
} else {
env.response.statusCode = 204;
return next(env);
return next(env);
}
});
});
} else {
env.response.statusCode = 500;
return next(env);
}

});

};

ServerResource.prototype.showDevice = function(env, next) {
if(this.shouldProxy(env)) {
return this.proxy(env, next);
}

var device = this.server.runtime._jsDevices[env.route.params.deviceId];
if(!device) {
env.response.body = 'Device does not exist';
Expand Down Expand Up @@ -447,7 +448,7 @@ ServerResource.prototype.deviceAction = function(env, next) {
env.response.statusCode = 404;
return next(env);
}

env.request.getBody(function(err, body) {
if (err || !body) {
env.response.statusCode = 400;
Expand All @@ -464,7 +465,7 @@ ServerResource.prototype.deviceAction = function(env, next) {
env.response.statusCode = 400;
return next(env);
}

var action = device._transitions[body.action];
if (!action) {
env.response.statusCode = 400;
Expand All @@ -475,13 +476,13 @@ ServerResource.prototype.deviceAction = function(env, next) {
env.response.statusCode = 400;
return next(env);
}


// device.call(actionName, arg1, arg2, argn, cb);
var args = [body.action];

if (action.fields && action.fields.length) {

var parseErrors = [];
action.fields.forEach(function(field) {
if (field.name !== 'action') {
Expand All @@ -505,7 +506,7 @@ ServerResource.prototype.deviceAction = function(env, next) {
}
});


// Test is any did not decode properly
if (parseErrors.length > 0) {
env.response.statusCode = 400;
Expand All @@ -525,7 +526,26 @@ ServerResource.prototype.deviceAction = function(env, next) {

var cb = function(err) {
if (err) {
env.response.statusCode = 500;
var properties = {};
var statusCode = 500;

if(err instanceof ActionError) {
statusCode = err.statusCode;
properties = err.properties;
} else if (err instanceof Error) {
properties.message = err.message;
} else if(typeof error === 'string') {
properties.message = error
}

env.response.statusCode = statusCode;
env.response.body = {
class: ['action-error'],
properties: properties,
links: [
{ rel: ['self'], href: env.helpers.url.current() }
]
};
} else {
var model = {
model: device,
Expand All @@ -535,11 +555,11 @@ ServerResource.prototype.deviceAction = function(env, next) {
};
env.format.render('device', model);
}

next(env);
};
args.push(cb);

args.push(cb);
device.call.apply(device, args);
}
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"titan": "^1.1.0",
"ws": "^0.4.31",
"zetta-auto-scout": "^0.10.0",
"zetta-device": "^0.19.0",
"zetta-device": "^0.20.0",
"zetta-events-stream-protocol": "^5.0.0",
"zetta-http-device": "^0.6.0",
"zetta-rels": "^0.5.0",
Expand Down
13 changes: 9 additions & 4 deletions test/fixture/example_driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ TestDriver.prototype.init = function(config) {
.state('ready')
.type('testdriver')
.name('Matt\'s Test Device')
.when('ready', { allow: ['change', 'test', 'error', 'test-number', 'test-text', 'test-none', 'test-date'] })
.when('ready', { allow: ['change', 'test', 'error', 'test-number', 'test-text', 'test-none', 'test-date', 'test-custom-error'] })
.when('changed', { allow: ['prepare', 'test', 'error'] })
.map('change', this.change)
.map('prepare', this.prepare)
Expand All @@ -36,8 +36,13 @@ TestDriver.prototype.init = function(config) {
.map('test-text', function(x, cb) { this.message = x; cb(); }, [{ name: 'value', type: 'text'}])
.map('test-none', function(x, cb) { cb(); }, [{ name: 'value'}])
.map('test-date', function(x, cb) { cb(); }, [{ name: 'value', type: 'date'}])
.map('test-custom-error', this.customError);
};

TestDriver.prototype.customError = function(cb) {
cb(new Device.ActionError(401, {message: 'custom error message'}))
}

TestDriver.prototype.test = function(value, cb) {
this.value = value;
cb();
Expand All @@ -54,7 +59,7 @@ TestDriver.prototype.prepare = function(cb) {
};

TestDriver.prototype.streamObject = function(stream) {
this._streamObject = stream;
this._streamObject = stream;
};

TestDriver.prototype.returnError = function(error, cb) {
Expand All @@ -70,8 +75,8 @@ TestDriver.prototype.incrementStreamValue = function() {

TestDriver.prototype.publishStreamObject = function(obj) {
if(this._streamObject) {
this._streamObject.write(obj);
}
this._streamObject.write(obj);
}
};

TestDriver.prototype.streamBar = function(stream) {
Expand Down
21 changes: 19 additions & 2 deletions test/test_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ describe('Zetta Api', function() {
request(getHttpServer(app))
.get(url)
.expect(getBody(function(res, body) {
assert.equal(body.actions.length, 7);
assert.equal(body.actions.length, 8);
var action = body.actions[0];
assert.equal(action.name, 'change');
assert.equal(action.method, 'POST');
Expand All @@ -785,7 +785,7 @@ describe('Zetta Api', function() {
request(getHttpServer(app))
.get(url)
.expect(getBody(function(res, body) {
assert.equal(body.actions.length, 7);
assert.equal(body.actions.length, 8);
body.actions.forEach(function(action) {
assert(action.class.indexOf('transition') >= 0);
})
Expand Down Expand Up @@ -1044,6 +1044,23 @@ describe('Zetta Api', function() {
.end(done);
});

it('should return custom error information when a error is passed in a callback of device driver', function(done) {
request(getHttpServer(app))
.post(url)
.type('form')
.send({action: 'test-custom-error'})
.expect(getBody(function(res, body) {
assert.equal(res.statusCode, 401);
assert(body.class.indexOf('action-error') >= 0);

assert(body.properties.message);
assert.equal('custom error message', body.properties.message);

hasLinkRel(body.links, rels.self);
}))
.end(done);
});

it('should support device updates using PUT', function(done) {
request(getHttpServer(app))
.put(url)
Expand Down

0 comments on commit 241e629

Please sign in to comment.