Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 4fb0b6d5634e2b0ef9e3660fe05eb6d32eef9c5e 0 parents
@avdgaag authored
1  .gitignore
@@ -0,0 +1 @@
+node_modules
0  HISTORY.md
No changes.
0  LICENSE
No changes.
0  README.md
No changes.
1  index.js
@@ -0,0 +1 @@
+module.exports = require(__dirname + '/lib/heroku.js');
11 lib/authentication_buffer.js
@@ -0,0 +1,11 @@
+module.exports = (function() {
+ function AuthenticationBuffer(username, password) {
+ this.auth_string = '' + username + ':' + password;
+ }
+
+ AuthenticationBuffer.prototype.toString = function() {
+ return new Buffer(this.auth_string).toString('base64');
+ };
+
+ return AuthenticationBuffer;
+})();
77 lib/heroku.js
@@ -0,0 +1,77 @@
+module.exports = (function() {
+ var http = require('https'),
+ AuthenticationBuffer = require('lib/authentication_buffer'),
+ JsonData = require('lib/json_data');
+
+ /**
+ * Constructor function
+ */
+ function Heroku(options) {
+ if(!options.api_token) throw new Error('API token required');
+ if(!options.app) throw new Error('Application name required');
+ this.api_token = options.api_token;
+ this.app = options.app;
+ }
+
+ /**
+ * Internal utility function to actually make the HTTP requests against the
+ * Heroku API.
+ */
+ Heroku.prototype.request = function(path, fn) {
+ var options = {
+ host: 'api.heroku.com',
+ method: 'GET',
+ path: path,
+ headers: {
+ 'Accept': 'application/json',
+ 'X-Heroku-Gem-Version': '2.1.2',
+ 'Authorization': 'Basic'
+ }
+ };
+ http.request(options, function(response) {
+ var json_response = new JsonData();
+ response.setEncoding('utf-8');
+ response.on('data', json_response.push);
+ response.on('end', function() {
+ var err, rval, json;
+ json = json_response.toJSON();
+ if(response.statusCode >= 200 && response.statusCode <=299) {
+ rval = json;
+ } else {
+ err = json;
+ }
+ fn(err, rval);
+ });
+ }).end();
+ };
+
+ /**
+ * Generate a subpath local to an application root path on the Heroku API.
+ */
+ Heroku.prototype.appPath = function(subpath) {
+ return '/apps/' + this.app + '/' + subpath;
+ };
+
+ /**
+ * Load the value for a given environment variable on the current
+ * application. The results are cached locally, so only a single request will
+ * be made.
+ */
+ Heroku.prototype.config = function(key, fn) {
+ var that = this;
+ if(this._config_cache) {
+ if(this._config_cache.hasOwnProperty(key)) {
+ fn(null, this._config_cache[key]);
+ } else {
+ fn({ 'error': 'Key not found' });
+ }
+ } else {
+ this.request(this.appPath('config_vars'), function(err, config) {
+ that._config_cache = err ? {} : config;
+ that.config(key, fn);
+ });
+ }
+ };
+
+ return Heroku;
+})();
21 lib/json_data.js
@@ -0,0 +1,21 @@
+module.exports = (function() {
+ var bind = function(fn, context) {
+ return function() {
+ fn.apply(context, arguments);
+ };
+ };
+
+ function JsonData(initial) {
+ if(!initial) initial = '';
+ this.data = initial;
+ this.push = bind(this.push, this);
+ }
+ JsonData.prototype.push = function(data) {
+ this.data += data;
+ };
+ JsonData.prototype.toJSON = function() {
+ return JSON.parse(this.data);
+ };
+ return JsonData;
+})();
+
17 package.json
@@ -0,0 +1,17 @@
+{
+ "name": "heroku-api",
+ "version": "0.1.0",
+ "description": "A thin wrapper around the Heroku API",
+ "keywords": ["API", "Heroku"],
+ "author": "Arjan van der Gaag <arjan@arjanvandergaag.nl>",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/avdgaag/heroku-api.git"
+ },
+ "main": "./index",
+ "devDependencies": {
+ "expect.js": "*",
+ "mocha": "*",
+ "nock": "*"
+ }
+}
15 test/authentication_buffer_test.js
@@ -0,0 +1,15 @@
+var AuthenticationBuffer = require('../lib/authentication_buffer'),
+ expect = require('expect.js');
+
+describe(AuthenticationBuffer, function() {
+ it('requires a username and password', function() {
+ expect(function() {
+ new AuthenticationBuffer('username', 'password');
+ }).to.not.throwException();
+ });
+
+ it('outputs a Base64-encoded string', function() {
+ var ab = new AuthenticationBuffer('username', 'password');
+ expect(ab.toString()).to.eql('dXNlcm5hbWU6cGFzc3dvcmQ=');
+ });
+});
78 test/heroku_test.js
@@ -0,0 +1,78 @@
+var Heroku = require('../index'),
+ expect = require('expect.js'),
+ nock = require('nock');
+
+describe('Heroku', function() {
+ describe('instantiation', function() {
+ it('creates new object given API token and app name', function() {
+ expect(function() {
+ new Heroku({ app: 'my-app', api_token: 'foo' });
+ }).to.not.throwException()
+ });
+
+ it('requires an API token', function() {
+ expect(function() {
+ new Heroku({ app: 'my-app' });
+ }).to.throwException(/API token required/);
+ });
+
+ it('requires an application name', function() {
+ expect(function() {
+ new Heroku({ api_token: 'foo' });
+ }).to.throwException(/Application name required/);
+ });
+ });
+
+ describe('requests', function() {
+ describe('config', function() {
+ var heroku, mocked_request;
+
+ beforeEach(function() {
+ heroku = new Heroku({ app: 'foo', api_token: 'bar' });
+ });
+
+ describe('when unsuccessful', function() {
+ beforeEach(function() {
+ mocked_request = nock('https://api.heroku.com')
+ .get('/apps/foo/config_vars')
+ .reply(500, { 'error': 'oh noes' });
+ });
+
+ it('sets an error', function(done) {
+ heroku.config('bla', function(err) {
+ expect(err).to.be.ok();
+ mocked_request.done();
+ done();
+ });
+ });
+ });
+
+ describe('when successful', function() {
+ beforeEach(function() {
+ mocked_request = nock('https://api.heroku.com')
+ .get('/apps/foo/config_vars')
+ .reply(200, { 'foo': 'bar', 'msg': 'Hello, world' });
+ });
+
+ it('reads a config key', function(done) {
+ heroku.config('msg', function(err, val) {
+ expect(val).to.eql('Hello, world');
+ mocked_request.done();
+ done();
+ });
+ });
+
+ it('memoizes the results', function(done) {
+ heroku.config('msg', function(err, msg) {
+ expect(msg).to.eql('Hello, world');
+ mocked_request.done();
+ heroku.config('foo', function(err, foo) {
+ expect(foo).to.eql('bar');
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+});
23 test/json_data_test.js
@@ -0,0 +1,23 @@
+var JsonData = require('../lib/json_data'),
+ expect = require('expect.js');
+
+describe('JsonData', function() {
+ it('should start with empty data', function() {
+ expect(new JsonData().data).to.eql('');
+ });
+
+ it('allows setting arbitrary initial data', function() {
+ expect(new JsonData('foo').data).to.eql('foo');
+ });
+
+ it('parses data as JSON', function() {
+ json_string = '{ "foo": "bar" }';
+ expect(new JsonData(json_string).toJSON()).to.eql({ 'foo': 'bar' });
+ });
+
+ it('adds new data to the data streaM', function() {
+ var jd = new JsonData('foo');
+ jd.push('bar');
+ expect(jd.data).to.eql('foobar');
+ });
+});
Please sign in to comment.
Something went wrong with that request. Please try again.