diff --git a/api/auth.js b/api/auth.js index 3990a8b..0b16e1c 100644 --- a/api/auth.js +++ b/api/auth.js @@ -18,7 +18,7 @@ var urls = require('./urls'); */ function login(email, password) { var config = { - url: urls.LOGIN, + url: urls.login(), body: { email: email, password: password diff --git a/api/mosaics.js b/api/mosaics.js index 0b702f3..5dc217a 100644 --- a/api/mosaics.js +++ b/api/mosaics.js @@ -24,7 +24,7 @@ var util = require('./util'); function get(id, options) { options = options || {}; var config = { - url: urls.join(urls.MOSAICS, id), + url: urls.mosaics(id), terminator: options.terminator }; return request.get(config).then(function(res) { @@ -51,7 +51,7 @@ function get(id, options) { function search(query, options) { options = options || {}; var config = { - url: urls.MOSAICS, + url: urls.mosaics(), query: query, terminator: options.terminator }; diff --git a/api/quads.js b/api/quads.js index cd180c4..a253bb4 100644 --- a/api/quads.js +++ b/api/quads.js @@ -25,7 +25,7 @@ var util = require('./util'); function get(mosaicId, quadId, options) { options = options || {}; var config = { - url: urls.join(urls.MOSAICS, mosaicId, 'quads', quadId), + url: urls.mosaics(mosaicId, 'quads', quadId), terminator: options.terminator }; return request.get(config).then(function(res) { @@ -53,7 +53,7 @@ function get(mosaicId, quadId, options) { function search(mosaicId, query, options) { options = options || {}; var config = { - url: urls.join(urls.MOSAICS, mosaicId, 'quads', ''), + url: urls.mosaics(mosaicId, 'quads', ''), query: query, terminator: options.terminator }; @@ -85,7 +85,7 @@ function search(mosaicId, query, options) { function scenes(mosaicId, quadId, options) { options = options || {}; var config = { - url: urls.join(urls.MOSAICS, mosaicId, 'quads', quadId, 'scenes', ''), + url: urls.mosaics(mosaicId, 'quads', quadId, 'scenes', ''), terminator: options.terminator }; return request.get(config).then(function(res) { diff --git a/api/scenes.js b/api/scenes.js index 350acee..0a8966a 100644 --- a/api/scenes.js +++ b/api/scenes.js @@ -32,7 +32,7 @@ function get(scene, options) { }; } var config = { - url: urls.join(urls.SCENES, scene.type, scene.id), + url: urls.scenes(scene.type, scene.id), terminator: options.terminator }; return request.get(config).then(function(res) { @@ -67,7 +67,7 @@ function search(query, options) { } var config = { - url: urls.join(urls.SCENES, type, ''), + url: urls.scenes(type, ''), query: query, terminator: options.terminator }; diff --git a/api/urls.js b/api/urls.js index 37b6c18..f107936 100644 --- a/api/urls.js +++ b/api/urls.js @@ -4,27 +4,48 @@ * @private */ -var API = require('./config').API_URL; +var config = require('./config'); /** * Join multiple URL parts with slashes. Note that any trailing and preceeding * slashes will be removed from the parts before they are joined. + * A single trailing slash can forced by an empty string as the final vararg. * @return {string} The joined URL. */ function join() { - return Array.prototype.map.call(arguments, function(part) { + var components = Array.prototype.map.call(arguments, function(part) { if (!(typeof part === 'string' || typeof part === 'number')) { throw new Error( 'join must be called with strings or numbers, got: ' + part); } return String(part).replace(/^\/?(.*?)\/?$/, '$1'); - }).join('/'); + }); + + // Preserve trailing slashes but remove every other interstitial. + var lastComponent = components.pop(); + return components + .filter(function(el) { + return el !== ''; + }) + .concat(lastComponent) + .join('/'); +} + +function rootUrl() { + var baseComponents = Array.prototype.slice.call(arguments); + return function() { + return join.apply(null, + [config.API_URL] + .concat(baseComponents) + .concat(Array.prototype.slice.call(arguments)) + ); + }; } -exports.API = API; -exports.MOSAICS = join(API, 'mosaics', ''); -exports.SCENES = join(API, 'scenes', ''); -exports.WORKSPACES = join(API, 'workspaces', ''); -exports.LOGIN = join(API, 'auth', 'login'); +exports.api = rootUrl(); +exports.mosaics = rootUrl('mosaics', ''); +exports.scenes = rootUrl('scenes', ''); +exports.workspaces = rootUrl('workspaces', ''); +exports.login = rootUrl('auth', 'login'); exports.join = join; diff --git a/api/workspaces.js b/api/workspaces.js index 7da1be7..7bbc640 100644 --- a/api/workspaces.js +++ b/api/workspaces.js @@ -8,14 +8,14 @@ var request = require('./request'); var urls = require('./urls'); function get(id) { - var url = urls.join(urls.WORKSPACES, id); + var url = urls.workspaces(id); return request.get(url).then(function(res) { return res.body; }); } function search() { - var url = urls.WORKSPACES; + var url = urls.workspaces(); return request.get(url).then(function(res) { return res.body; }); diff --git a/test/api/mosaics.test.js b/test/api/mosaics.test.js index 1a44f07..3e42ca3 100644 --- a/test/api/mosaics.test.js +++ b/test/api/mosaics.test.js @@ -9,6 +9,8 @@ var mosaics = require('../../api/mosaics'); var urls = require('../../api/urls'); var util = require('../../api/util'); +var MOSAICS = 'https://api.planet.com/v0/mosaics/'; + describe('api/mosaics', function() { var mosaic; @@ -61,7 +63,7 @@ describe('api/mosaics', function() { var promise = mosaics.get('one'); assert.lengthOf(calls, 1); var arg = calls[0]; - assert.equal(arg.url, urls.join(urls.MOSAICS, 'one')); + assert.equal(arg.url, urls.join(MOSAICS, 'one')); promise.then(function(got) { assert.deepEqual(got, mosaic); @@ -130,7 +132,7 @@ describe('api/mosaics', function() { var promise = mosaics.search(query); var arg = calls[0]; - assert.equal(arg.url, urls.MOSAICS); + assert.equal(arg.url, MOSAICS); assert.deepEqual(arg.query, query); promise.then(function(got) { diff --git a/test/api/quads.test.js b/test/api/quads.test.js index 870c0b8..84c37ca 100644 --- a/test/api/quads.test.js +++ b/test/api/quads.test.js @@ -9,6 +9,8 @@ var quads = require('../../api/quads'); var urls = require('../../api/urls'); var util = require('../../api/util'); +var MOSAICS = 'https://api.planet.com/v0/mosaics/'; + describe('api/mosaics', function() { var quad; @@ -66,7 +68,7 @@ describe('api/mosaics', function() { assert.lengthOf(calls, 1); var arg = calls[0]; assert.equal(arg.url, - urls.join(urls.MOSAICS, 'my-mosaic', 'quads', 'my-quad')); + urls.join(MOSAICS, 'my-mosaic', 'quads', 'my-quad')); promise.then(function(got) { assert.deepEqual(got, quad); @@ -135,7 +137,7 @@ describe('api/mosaics', function() { var promise = quads.search('my-mosaic', query); var arg = calls[0]; - assert.equal(arg.url, urls.join(urls.MOSAICS, 'my-mosaic', 'quads', '')); + assert.equal(arg.url, urls.join(MOSAICS, 'my-mosaic', 'quads', '')); assert.deepEqual(arg.query, query); promise.then(function(got) { @@ -226,7 +228,7 @@ describe('api/mosaics', function() { var arg = calls[0]; assert.equal(arg.url, urls.join( - urls.MOSAICS, 'my-mosaic', 'quads', 'my-quad', 'scenes', '')); + MOSAICS, 'my-mosaic', 'quads', 'my-quad', 'scenes', '')); promise.then(function(got) { assert.lengthOf(got.features, 1); diff --git a/test/api/scenes.test.js b/test/api/scenes.test.js index d529e39..2443539 100644 --- a/test/api/scenes.test.js +++ b/test/api/scenes.test.js @@ -9,6 +9,8 @@ var scenes = require('../../api/scenes'); var urls = require('../../api/urls'); var util = require('../../api/util'); +var SCENES = 'https://api.planet.com/v0/scenes/'; + describe('api/scenes', function() { var get = request.get; @@ -52,7 +54,7 @@ describe('api/scenes', function() { var promise = scenes.get({type: 'foo', id: 'bar'}); assert.lengthOf(calls, 1); var arg = calls[0]; - assert.equal(arg.url, urls.join(urls.SCENES, 'foo', 'bar')); + assert.equal(arg.url, urls.join(SCENES, 'foo', 'bar')); promise.then(function(got) { assert.deepEqual(got, scene); @@ -71,7 +73,7 @@ describe('api/scenes', function() { }; var promise = scenes.get('bar'); - assert.equal(calls[0].url, urls.join(urls.SCENES, 'ortho', 'bar')); + assert.equal(calls[0].url, urls.join(SCENES, 'ortho', 'bar')); promise.then(function(got) { assert.deepEqual(got, scene); @@ -160,7 +162,7 @@ describe('api/scenes', function() { var promise = scenes.search(query); var arg = calls[0]; - assert.equal(arg.url, urls.join(urls.SCENES, 'landsat', '')); + assert.equal(arg.url, urls.join(SCENES, 'landsat', '')); assert.deepEqual(arg.query, query); promise.then(function(got) { @@ -191,7 +193,7 @@ describe('api/scenes', function() { var promise = scenes.search(query); var arg = calls[0]; - assert.equal(arg.url, urls.join(urls.SCENES, 'ortho', '')); + assert.equal(arg.url, urls.join(SCENES, 'ortho', '')); assert.deepEqual(arg.query, query); promise.then(function(got) { diff --git a/test/api/urls.test.js b/test/api/urls.test.js index fb5899f..1ab7586 100644 --- a/test/api/urls.test.js +++ b/test/api/urls.test.js @@ -39,6 +39,9 @@ describe('api/urls', function() { }, { actual: urls.join('http://example.com', 'foo/', '/bar/', 'bam/'), expected: 'http://example.com/foo/bar/bam' + }, { + actual: urls.join('http://example.com', '', 'foo/', 'bam/', '', ''), + expected: 'http://example.com/foo/bam/' }]; for (var i = 0, ii = cases.length; i < ii; ++i) { @@ -72,6 +75,21 @@ describe('api/urls', function() { assert.throws(call, Error, 'join must be called with strings or numbers'); }); + it('works with degenerate cases', function() { + var cases = [{ + actual: urls.join(), + expected: '' + }, { + actual: urls.join('', ''), + expected: '' + }]; + + for (var i = 0, ii = cases.length; i < ii; ++i) { + var c = cases[i]; + assert.deepEqual(c.actual, c.expected, 'case ' + i); + } + }); + }); });