Skip to content
This repository has been archived by the owner on Jan 7, 2018. It is now read-only.

Setup error data for easier overriding of URL variables. #21

Merged
merged 2 commits into from
Oct 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,39 +85,42 @@ apiSettings:
</body>
</html>
error_data:
common:
signup_url: "{{base_url}}"
contact_url: "{{base_url}}/contact/"
not_found:
status_code: 404
code: NOT_FOUND
message: The requested URL was not found on this server.
api_key_missing:
status_code: 403
code: API_KEY_MISSING
message: No api_key was supplied. Get one at {{baseUrl}}
message: No api_key was supplied. Get one at {{signup_url}}
api_key_invalid:
status_code: 403
code: API_KEY_INVALID
message: An invalid api_key was supplied. Get one at {{baseUrl}}
message: An invalid api_key was supplied. Get one at {{signup_url}}
api_key_disabled:
status_code: 403
code: API_KEY_DISABLED
message: The api_key supplied has been disabled. Contact us at {{baseUrl}}/contact for assistance
message: The api_key supplied has been disabled. Contact us at {{contact_url}} for assistance
api_key_unverified:
status_code: 403
code: API_KEY_UNVERIFIED
message: The api_key supplied has not been verified yet. Please check your e-mail to verify the API key. Contact us at {{baseUrl}}/contact for assistance
message: The api_key supplied has not been verified yet. Please check your e-mail to verify the API key. Contact us at {{contact_url}} for assistance
api_key_unauthorized:
status_code: 403
code: API_KEY_UNAUTHORIZED
message: The api_key supplied is not authorized to access the given service. Contact us at {{baseUrl}}/contact for assistance
message: The api_key supplied is not authorized to access the given service. Contact us at {{contact_url}} for assistance
over_rate_limit:
status_code: 429
code: OVER_RATE_LIMIT
message: You have exceeded your rate limit. Try again later or contact us at {{baseUrl}}/contact for assistance
message: You have exceeded your rate limit. Try again later or contact us at {{contact_url}} for assistance
internal_server_error:
status_code: 500
code: INTERNAL_SERVER_ERROR
message: An unexpected error has occurred. Try again later or contact us at {{baseUrl}}/contact for assistance
message: An unexpected error has occurred. Try again later or contact us at {{contact_url}} for assistance
https_required:
status_code: 400
code: HTTPS_REQUIRED
message: "Requests must be made over HTTPS. Try accessing the API at: {{httpsUrl}}"
message: "Requests must be made over HTTPS. Try accessing the API at: {{https_url}}"
1 change: 1 addition & 0 deletions lib/gatekeeper/middleware/https_requirements.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ _.extend(HttpsRequirements.prototype, {
response.end(body);
} else {
utils.errorHandler(request, response, 'https_required', {
https_url: httpsUrl,
httpsUrl: httpsUrl,
});
}
Expand Down
30 changes: 26 additions & 4 deletions lib/gatekeeper/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var _ = require('lodash'),
Negotiator = require('negotiator'),
url = require('url');

exports.errorHandler = function(request, response, error, data) {
exports.errorHandler = function(request, response, error, errorSpecificData) {
var availableMediaTypes = ['application/json', 'application/xml', 'text/csv', 'text/html'];

// Prefer the format from the extension given in the URL.
Expand Down Expand Up @@ -44,14 +44,36 @@ exports.errorHandler = function(request, response, error, data) {
// introduce in multi-line templates and XML doesn't like if there's any
// leading space before the XML declaration.
var templateContent = settings.error_templates[format].replace(/^\s+|\s+$/g, '');

var commonErrorData = settings.error_data.common;
if(!commonErrorData || !_.isPlainObject(commonErrorData)) {
commonErrorData = {};
logger.error({ error_type: error }, 'Common error data not found.');
}

var errorData = settings.error_data[error];
if(!errorData || !_.isPlainObject(errorData)) {
errorData = settings.error_data.internal_server_error;
logger.error({ error_type: error }, 'Error data not found for error type: ' + error);
}
data = _.merge({
baseUrl: request.base,
}, data || {}, errorData);

var data = _.merge({
base_url: request.base,
}, commonErrorData);

// Support legacy camel-case capitalization of variables. Moving forward,
// we're trying to clean things up and standardize on snake-case.
if(!data.baseUrl) {
data.baseUrl = data.base_url;
}
if(!data.signupUrl) {
data.signupUrl = data.signup_url;
}
if(!data.contactUrl) {
data.contactUrl = data.contact_url;
}

data = _.merge(data, errorSpecificData || {}, errorData);

var prop;
for(prop in data) {
Expand Down
101 changes: 94 additions & 7 deletions test/server/formatted_errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,102 @@ describe('formatted error responses', function() {
});

describe('data variables', function() {
shared.runServer();
shared.runServer({
apis: [
{
frontend_host: 'localhost',
backend_host: 'example.com',
url_matches: [
{
frontend_prefix: '/',
backend_prefix: '/',
}
],
settings: {
error_data: {
api_key_missing: {
embedded: 'base_url: {{base_url}} signup_url: {{signup_url}} contact_url: {{contact_url}}',
embedded_legacy: 'baseUrl: {{baseUrl}} signupUrl: {{signupUrl}} contactUrl: {{contactUrl}}',
},
},
error_templates: {
json: '{' +
'"base_url": {{base_url}},' +
'"baseUrl": {{baseUrl}},' +
'"signup_url": {{signup_url}},' +
'"signupUrl": {{signupUrl}},' +
'"contact_url": {{contact_url}},' +
'"contactUrl": {{contactUrl}},' +
'"embedded": {{embedded}},' +
'"embedded_legacy": {{embedded_legacy}} ' +
'}',
},
},
},
],
});

it('substitutes the base_url variable', function(done) {
request.get('http://localhost:9333/base_url.json', function(error, response, body) {
var data = JSON.parse(body);
data.base_url.should.eql('http://localhost:9333');
done();
});
});

it('substitutes the baseUrl variable', function(done) {
Factory.create('api_user', { disabled_at: new Date() }, function(user) {
request.get('http://localhost:9333/hello.json?api_key=' + user.api_key, function(error, response, body) {
var data = JSON.parse(body);
data.error.message.should.include(' http://localhost:9333/contact ');
done();
});
request.get('http://localhost:9333/baseUrl.json', function(error, response, body) {
var data = JSON.parse(body);
data.baseUrl.should.eql('http://localhost:9333');
done();
});
});

it('substitutes the signup_url variable', function(done) {
request.get('http://localhost:9333/signup_url.json', function(error, response, body) {
var data = JSON.parse(body);
data.signup_url.should.eql('http://localhost:9333');
done();
});
});

it('substitutes the signupUrl variable', function(done) {
request.get('http://localhost:9333/signupUrl.json', function(error, response, body) {
var data = JSON.parse(body);
data.signupUrl.should.eql('http://localhost:9333');
done();
});
});

it('substitutes the contact_url variable', function(done) {
request.get('http://localhost:9333/contact_url.json', function(error, response, body) {
var data = JSON.parse(body);
data.contact_url.should.eql('http://localhost:9333/contact/');
done();
});
});

it('substitutes the contactUrl variable', function(done) {
request.get('http://localhost:9333/contactUrl.json', function(error, response, body) {
var data = JSON.parse(body);
data.contactUrl.should.eql('http://localhost:9333/contact/');
done();
});
});

it('substitutes variables embedded inside of other variables', function(done) {
request.get('http://localhost:9333/embedded.json', function(error, response, body) {
var data = JSON.parse(body);
data.embedded.should.eql('base_url: http://localhost:9333 signup_url: http://localhost:9333 contact_url: http://localhost:9333/contact/');
done();
});
});

it('substitutes legacy camel case variables embedded inside of other variables', function(done) {
request.get('http://localhost:9333/embedded_legacy.json', function(error, response, body) {
var data = JSON.parse(body);
data.embedded_legacy.should.eql('baseUrl: http://localhost:9333 signupUrl: http://localhost:9333 contactUrl: http://localhost:9333/contact/');
done();
});
});
});
Expand Down