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

Commit

Permalink
Setup error data for easier overriding of URL variables.
Browse files Browse the repository at this point in the history
Now there's a concept of "common" error data variables, which we can use
so that contact URLs (and other things like this) only have to be
defined once per API backend, which makes it much easier to define
variables only once which can then be embedded in other areas of the
error response.

See: 18F/api.data.gov#285

This also begins to cleanup and standardize the error data variables to
snake_case moving forward (instead of some of our odd mixtures of
camel-case that had built up over time).
  • Loading branch information
GUI committed Sep 18, 2015
1 parent 6c1704b commit 6d2e657
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 17 deletions.
19 changes: 11 additions & 8 deletions config/default.yml
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
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
24 changes: 22 additions & 2 deletions lib/gatekeeper/utils.js
Expand Up @@ -44,14 +44,34 @@ 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);
base_url: request.base,
}, data || {}, commonErrorData, errorData);

// 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;
}

var prop;
for(prop in data) {
Expand Down
91 changes: 84 additions & 7 deletions test/server/formatted_errors.js
Expand Up @@ -99,15 +99,92 @@ 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}}',
},
},
error_templates: {
json: '{' +
'"base_url": {{base_url}},' +
'"baseUrl": {{baseUrl}},' +
'"signup_url": {{signup_url}},' +
'"signupUrl": {{signupUrl}},' +
'"contact_url": {{contact_url}},' +
'"contactUrl": {{contactUrl}},' +
'"embedded": {{embedded}} ' +
'}',
},
},
},
],
});

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();
});
});
});
Expand Down

0 comments on commit 6d2e657

Please sign in to comment.