planet-client
Advanced tools
Comparing version 0.6.0 to 0.7.0
@@ -24,9 +24,24 @@ /** | ||
/** | ||
* An error that occurs when the client is unathorized to make the request. | ||
* The request was bad (400). | ||
* @param {string} message Error message. | ||
* @param {XMLHttpRequest} response The response. | ||
* @param {string} body Any parsed response body. | ||
* @param {Object} body Any parsed response body (as JSON). | ||
* @extends {ResponseError} | ||
* @constructor | ||
*/ | ||
function BadRequest(message, response, body) { | ||
ResponseError.apply(this, arguments); | ||
} | ||
BadRequest.prototype = new ResponseError(); | ||
BadRequest.prototype.name = 'BadRequest'; | ||
/** | ||
* The request requires user authentication (401). | ||
* @param {string} message Error message. | ||
* @param {XMLHttpRequest} response The response. | ||
* @param {Object} body Any parsed response body (as JSON). | ||
* @extends {ResponseError} | ||
* @constructor | ||
*/ | ||
function Unauthorized(message, response, body) { | ||
@@ -40,5 +55,20 @@ ResponseError.apply(this, arguments); | ||
/** | ||
* An error that occurs the API returns an unexpected response. | ||
* The client is forbidden from making the request (403). | ||
* @param {string} message Error message. | ||
* @param {XMLHttpRequest} response The response. | ||
* @param {Object} body Any parsed response body (as JSON). | ||
* @extends {ResponseError} | ||
* @constructor | ||
*/ | ||
function Forbidden(message, response, body) { | ||
ResponseError.apply(this, arguments); | ||
} | ||
Forbidden.prototype = new ResponseError(); | ||
Forbidden.prototype.name = 'Forbidden'; | ||
/** | ||
* The API returns an unexpected response. | ||
* @param {string} message Error message. | ||
* @param {XMLHttpRequest} response The response. | ||
* @param {string} body Any parsed response body. | ||
@@ -68,4 +98,6 @@ * @extends {ResponseError} | ||
exports.ResponseError = ResponseError; | ||
exports.BadRequest = BadRequest; | ||
exports.Unauthorized = Unauthorized; | ||
exports.Forbidden = Forbidden; | ||
exports.UnexpectedResponse = UnexpectedResponse; | ||
exports.AbortedRequest = AbortedRequest; |
@@ -84,2 +84,24 @@ /** | ||
/** | ||
* Check if the response represents an error. | ||
* @param {IncomingMessage} response The response. | ||
* @param {Object} body Any parsed body (as JSON). | ||
* @return {errors.ResponseError} A response error (or null if none). | ||
*/ | ||
function errorCheck(response, body) { | ||
var err = null; | ||
var status = response.statusCode; | ||
if (status === 400) { | ||
err = new errors.BadRequest('Bad request', response, body); | ||
} else if (status === 401) { | ||
err = new errors.Unauthorized('Unauthorized', response, body); | ||
} else if (status === 403) { | ||
err = new errors.Forbidden('Forbidden', response, body); | ||
} else if (!(status >= 200 && status < 300)) { | ||
err = new errors.UnexpectedResponse('Unexpected response status: ' + | ||
status, response); | ||
} | ||
return err; | ||
} | ||
/** | ||
* Create a handler for JSON API responses. | ||
@@ -105,6 +127,7 @@ * @param {function(Object)} resolve Called on success with response and body | ||
} | ||
if (info.stream) { | ||
if (!(status >= 200 && status < 300)) { | ||
reject(new errors.UnexpectedResponse('Unexpected response status: ' + | ||
status, response)); | ||
var streamErr = errorCheck(response, null); | ||
if (streamErr) { | ||
reject(streamErr); | ||
} else { | ||
@@ -115,2 +138,3 @@ resolve({response: response, body: null}); | ||
} | ||
var data = ''; | ||
@@ -134,8 +158,3 @@ response.on('data', function(chunk) { | ||
var err = null; | ||
if (status === 401) { | ||
err = new errors.Unauthorized('Unauthorized', response, body); | ||
} else if (!(status >= 200 && status < 300)) { | ||
err = new errors.UnexpectedResponse('Unexpected response status: ' + | ||
status, response, data); | ||
} else if (data) { | ||
if (data) { | ||
try { | ||
@@ -149,10 +168,13 @@ body = JSON.parse(data); | ||
} | ||
err = errorCheck(response, body) || err; | ||
if (err) { | ||
reject(err); | ||
return; | ||
} else { | ||
resolve({ | ||
response: response, | ||
body: body | ||
}); | ||
} | ||
resolve({ | ||
response: response, | ||
body: body | ||
}); | ||
}); | ||
@@ -262,5 +284,31 @@ }; | ||
/** | ||
* Issue a PUT request. | ||
* @param {Object} config The request config. | ||
* @return {Promise<Object>} A promise that resolves on a successful | ||
* response. The object includes response and body properties, where the | ||
* body is a JSON decoded object representing the response body. Any | ||
* non-200 status will result in a rejection. | ||
*/ | ||
function put(config) { | ||
return request(assign({method: 'PUT'}, config)); | ||
} | ||
/** | ||
* Issue a DELETE request. | ||
* @param {Object} config The request config. | ||
* @return {Promise<Object>} A promise that resolves on a successful | ||
* response. The object includes response and body properties, where the | ||
* body is a JSON decoded object representing the response body. Any | ||
* non-200 status will result in a rejection. | ||
*/ | ||
function del(config) { | ||
return request(assign({method: 'DELETE'}, config)); | ||
} | ||
exports.get = get; | ||
exports.post = post; | ||
exports.put = put; | ||
exports.del = del; | ||
exports.parseConfig = parseConfig; | ||
exports.request = request; |
@@ -16,3 +16,7 @@ /** | ||
return Array.prototype.map.call(arguments, function(part) { | ||
return part.replace(/^\/?(.*?)\/?$/, '$1'); | ||
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('/'); | ||
@@ -19,0 +23,0 @@ } |
@@ -57,2 +57,5 @@ /** | ||
} | ||
if (links.thumbnail) { | ||
links.thumbnail = addQueryParams(links.thumbnail, {'api_key': key}); | ||
} | ||
} | ||
@@ -59,0 +62,0 @@ |
@@ -208,3 +208,6 @@ var path = require('path'); | ||
exports.fetch = fetch; | ||
exports.parseAcquired = parseAcquired; | ||
exports.parseWhere = parseWhere; | ||
exports.resolveIntersects = resolveIntersects; | ||
exports.resolveQuery = resolveQuery; |
{ | ||
"name": "planet-client", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "A client for Planet's imagery API", | ||
@@ -16,3 +16,4 @@ "repository": { | ||
"pretest": "eslint bin examples api cli test", | ||
"test": "mocha --recursive test", | ||
"test": "nyc mocha --recursive test", | ||
"coverage": "nyc report --reporter=text-lcov | coveralls", | ||
"test-debug": "mocha --debug-brk --recursive test", | ||
@@ -29,2 +30,3 @@ "start": "watchy --watch bin,examples,api,cli,test -- npm test", | ||
"chai": "^3.0.0", | ||
"coveralls": "^2.11.3", | ||
"eslint": "^0.22.1", | ||
@@ -36,2 +38,3 @@ "eslint-config-planet": "^2.0.0", | ||
"mocha": "^2.2.5", | ||
"nyc": "^3.1.0", | ||
"sinon": "^1.15.3", | ||
@@ -38,0 +41,0 @@ "watchy": "^0.6.2" |
@@ -41,3 +41,3 @@ ## planet-client | ||
[![screen shot](https://raw.githubusercontent.com/wiki/planetlabs/planet-client-js/planet-client.png)](https://vimeo.com/134018559) | ||
[![screen shot][video-image]][video-url] | ||
@@ -60,4 +60,6 @@ ### Contributing | ||
[![Current Status](https://travis-ci.org/planetlabs/planet-client-js.svg?branch=master)](https://travis-ci.org/planetlabs/planet-client-js) | ||
[![Build Status][travis-image]][travis-url] | ||
[![Coverage Status][coveralls-image]][coveralls-url] | ||
### License | ||
@@ -70,1 +72,8 @@ | ||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See [the License](http://www.apache.org/licenses/LICENSE-2.0) for the specific language governing permissions and limitations under the License. | ||
[video-url]: https://vimeo.com/134018559 | ||
[video-image]: https://raw.githubusercontent.com/wiki/planetlabs/planet-client-js/planet-client.png | ||
[travis-url]: https://travis-ci.org/planetlabs/planet-client-js | ||
[travis-image]: https://img.shields.io/travis/planetlabs/planet-client-js.svg | ||
[coveralls-url]: https://coveralls.io/github/planetlabs/planet-client-js | ||
[coveralls-image]: https://coveralls.io/repos/planetlabs/planet-client-js/badge.svg?branch=master&service=github |
@@ -6,3 +6,3 @@ /* eslint-env mocha */ | ||
describe('authStore', function() { | ||
describe('api/auth-store', function() { | ||
afterEach(function() { | ||
@@ -27,2 +27,13 @@ authStore.clear(); | ||
it('throws if the token does not contain an api_key claim', function() { | ||
// {foo: 'bar'} | ||
var bogus = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIifQ.' + | ||
'yPmf5QFV26W-3ldVCrsvRdnecy7QjA0fnCWCDLDZ-M4'; | ||
function call() { | ||
authStore.setToken(bogus); | ||
} | ||
assert.throws(call, Error, 'Expected api_key in token payload'); | ||
}); | ||
}); | ||
@@ -29,0 +40,0 @@ |
@@ -12,4 +12,5 @@ /* eslint-env mocha */ | ||
var authStore = require('../../api/auth-store'); | ||
var errors = require('../../api/errors'); | ||
describe('auth', function() { | ||
describe('api/auth', function() { | ||
@@ -43,5 +44,2 @@ var httpRequest = http.request; | ||
authStore.clear(); | ||
}); | ||
afterEach(function() { | ||
auth.logout(); | ||
@@ -83,3 +81,3 @@ }); | ||
done(); | ||
}, done); | ||
}).catch(done); | ||
@@ -95,2 +93,47 @@ assert.equal(https.request.callCount, 1); | ||
it('rejects if body does not contain a token', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 200; | ||
var body = {foo: 'bar'}; | ||
var email = 'user@email.com'; | ||
var password = 'psswd'; | ||
auth.login(email, password).then(function(success) { | ||
done(new Error('Expected rejection')); | ||
}, function(err) { | ||
assert.instanceOf(err, errors.UnexpectedResponse); | ||
done(); | ||
}).catch(done); | ||
assert.equal(https.request.callCount, 1); | ||
var args = https.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', JSON.stringify(body)); | ||
response.emit('end'); | ||
}); | ||
it('rejects if body contains a bogus token', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 200; | ||
var body = {token: 'bogus'}; | ||
var email = 'user@email.com'; | ||
var password = 'psswd'; | ||
auth.login(email, password).then(function(success) { | ||
done(new Error('Expected rejection')); | ||
}, function(err) { | ||
assert.instanceOf(err, errors.UnexpectedResponse); | ||
done(); | ||
}).catch(done); | ||
assert.equal(https.request.callCount, 1); | ||
var args = https.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', JSON.stringify(body)); | ||
response.emit('end'); | ||
}); | ||
}); | ||
@@ -97,0 +140,0 @@ |
@@ -6,6 +6,6 @@ /* eslint-env mocha */ | ||
describe('errors', function() { | ||
describe('api/errors', function() { | ||
describe('ResponseError', function() { | ||
it('is a generic response error', function() { | ||
it('is a base class for response errors', function() { | ||
var message = 'foo'; | ||
@@ -20,4 +20,16 @@ var response = {}; | ||
describe('BadRequest', function() { | ||
it('represents a 400 response', function() { | ||
var message = 'foo'; | ||
var response = {}; | ||
var err = new errors.BadRequest(message, response); | ||
assert.equal(err.message, message); | ||
assert.equal(err.response, response); | ||
assert.instanceOf(err, errors.ResponseError); | ||
assert.instanceOf(err, Error); | ||
}); | ||
}); | ||
describe('Unauthorized', function() { | ||
it('represents an unauthorized request', function() { | ||
it('represents a 401 response', function() { | ||
var message = 'foo'; | ||
@@ -33,4 +45,16 @@ var response = {}; | ||
describe('Forbidden', function() { | ||
it('represents a 403 response', function() { | ||
var message = 'foo'; | ||
var response = {}; | ||
var err = new errors.Forbidden(message, response); | ||
assert.equal(err.message, message); | ||
assert.equal(err.response, response); | ||
assert.instanceOf(err, errors.ResponseError); | ||
assert.instanceOf(err, Error); | ||
}); | ||
}); | ||
describe('UnexpectedResponse', function() { | ||
it('represents authentication with bad credentials', function() { | ||
it('represents a response that we do not expect', function() { | ||
var message = 'foo'; | ||
@@ -37,0 +61,0 @@ var response = {}; |
@@ -9,3 +9,3 @@ /* eslint-env mocha */ | ||
describe('Page', function() { | ||
describe('api/page', function() { | ||
@@ -79,2 +79,63 @@ var spy; | ||
describe('#prev()', function() { | ||
it('is only assigned if data includes prev link', function() { | ||
var query = { | ||
foo: 'bar', | ||
num: '42' | ||
}; | ||
var data = { | ||
links: { | ||
next: url.format({ | ||
query: query | ||
}) | ||
} | ||
}; | ||
var page = new Page(data, spy); | ||
assert.isNull(page.prev); | ||
}); | ||
it('calls the factory with a query from the prev url', function() { | ||
var query = { | ||
foo: 'bar', | ||
num: '42' | ||
}; | ||
var data = { | ||
links: { | ||
prev: url.format({ | ||
query: query | ||
}) | ||
} | ||
}; | ||
var page = new Page(data, spy); | ||
assert.typeOf(page.prev, 'function'); | ||
page.prev(); | ||
assert.equal(spy.callCount, 1); | ||
var call = spy.getCall(0); | ||
assert.deepEqual(call.args[0], query); | ||
}); | ||
it('passes along options as second arg to factory', function() { | ||
var data = { | ||
links: { | ||
prev: 'http://example.com' | ||
} | ||
}; | ||
var page = new Page(data, spy); | ||
assert.typeOf(page.prev, 'function'); | ||
var options = {}; | ||
page.prev(options); | ||
assert.equal(spy.callCount, 1); | ||
var call = spy.getCall(0); | ||
assert.equal(call.args[1], options); | ||
}); | ||
}); | ||
}); |
@@ -14,2 +14,3 @@ /* eslint-env mocha */ | ||
var req = require('../../api/request'); | ||
var util = require('../../api/util'); | ||
@@ -19,3 +20,3 @@ chai.config.truncateThreshold = 0; | ||
describe('request', function() { | ||
describe('api/request', function() { | ||
@@ -90,3 +91,3 @@ var httpRequest = http.request; | ||
done(); | ||
}, done); | ||
}).catch(done); | ||
@@ -102,2 +103,36 @@ assert.equal(http.request.callCount, 1); | ||
it('follows location header on 302', function(done) { | ||
var firstResponse = new stream.Readable(); | ||
firstResponse.statusCode = 302; | ||
firstResponse.headers = { | ||
location: 'https://redirect.com' | ||
}; | ||
var secondResponse = new stream.Readable(); | ||
secondResponse.statusCode = 200; | ||
var body = { | ||
foo: 'bar' | ||
}; | ||
var promise = request({ | ||
url: 'https://example.com' | ||
}); | ||
promise.then(function(obj) { | ||
assert.equal(obj.response, secondResponse); | ||
assert.deepEqual(obj.body, body); | ||
done(); | ||
}).catch(done); | ||
assert.equal(https.request.callCount, 1); | ||
var firstCallback = https.request.getCall(0).args[1]; | ||
firstCallback(firstResponse); | ||
firstResponse.emit('end'); | ||
assert.equal(https.request.callCount, 2); | ||
var secondCallback = https.request.getCall(1).args[1]; | ||
secondCallback(secondResponse); | ||
secondResponse.emit('data', JSON.stringify(body)); | ||
secondResponse.emit('end'); | ||
}); | ||
it('resolves before parsing body if stream is true', function(done) { | ||
@@ -118,3 +153,3 @@ var response = new stream.Readable(); | ||
done(); | ||
}, done); | ||
}).catch(done); | ||
@@ -130,2 +165,28 @@ assert.equal(http.request.callCount, 1); | ||
it('rejects on non 2xx if stream is true', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 502; | ||
var body = 'too much request'; | ||
var promise = request({ | ||
url: 'http://example.com', | ||
stream: true | ||
}); | ||
promise.then(function(obj) { | ||
done(new Error('Expected rejection')); | ||
}, function(err) { | ||
assert.instanceOf(err, errors.UnexpectedResponse); | ||
assert.include(err.message, 'Unexpected response status: 502'); | ||
done(); | ||
}).catch(done); | ||
assert.equal(http.request.callCount, 1); | ||
var args = http.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', JSON.stringify(body)); | ||
response.emit('end'); | ||
}); | ||
it('rejects for invalid JSON in successful response', function(done) { | ||
@@ -155,2 +216,98 @@ var response = new stream.Readable(); | ||
it('rejects with UnexpectedResponse for 500 response', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 500; | ||
var body = 'server error (maybe a secret in the stack trace)'; | ||
var promise = request({url: 'http://example.com'}); | ||
promise.then(function(obj) { | ||
done(new Error('Expected promise to be rejected')); | ||
}, function(err) { | ||
assert.instanceOf(err, errors.UnexpectedResponse); | ||
assert.include(err.message, 'Unexpected response status: 500'); | ||
assert.equal(err.body, null); // don't leak unexpected responses | ||
done(); | ||
}).catch(done); | ||
assert.equal(http.request.callCount, 1); | ||
var args = http.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', body); | ||
response.emit('end'); | ||
}); | ||
it('rejects with BadRequest for 400', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 400; | ||
var body = {message: 'Invalid email or password', errors: []}; | ||
var promise = request({url: 'http://example.com'}); | ||
promise.then(function(obj) { | ||
done(new Error('Expected promise to be rejected')); | ||
}, function(err) { | ||
assert.instanceOf(err, errors.BadRequest); | ||
assert.include(err.message, 'Bad request'); | ||
assert.deepEqual(err.body, body); | ||
done(); | ||
}).catch(done); | ||
assert.equal(http.request.callCount, 1); | ||
var args = http.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', JSON.stringify(body)); | ||
response.emit('end'); | ||
}); | ||
it('rejects with Unauthorized for 401', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 401; | ||
var body = {message: 'Invalid email or password', errors: []}; | ||
var promise = request({url: 'http://example.com'}); | ||
promise.then(function(obj) { | ||
done(new Error('Expected promise to be rejected')); | ||
}, function(err) { | ||
assert.instanceOf(err, errors.Unauthorized); | ||
assert.include(err.message, 'Unauthorized'); | ||
assert.deepEqual(err.body, body); | ||
done(); | ||
}).catch(done); | ||
assert.equal(http.request.callCount, 1); | ||
var args = http.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', JSON.stringify(body)); | ||
response.emit('end'); | ||
}); | ||
it('rejects with Forbidden for 403', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 403; | ||
var body = {message: 'some user info here'}; | ||
var promise = request({url: 'http://example.com'}); | ||
promise.then(function(obj) { | ||
done(new Error('Expected promise to be rejected')); | ||
}, function(err) { | ||
assert.instanceOf(err, errors.Forbidden); | ||
assert.include(err.message, 'Forbidden'); | ||
assert.deepEqual(err.body, body); | ||
done(); | ||
}).catch(done); | ||
assert.equal(http.request.callCount, 1); | ||
var args = http.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', JSON.stringify(body)); | ||
response.emit('end'); | ||
}); | ||
it('accepts a terminator for aborting requests', function(done) { | ||
@@ -172,2 +329,58 @@ var promise = request({ | ||
it('calls request.xhr.abort() if request.abort is absent', function(done) { | ||
var promise = request({ | ||
url: 'http//example.com', | ||
terminator: function(abort) { | ||
setTimeout(abort, 10); | ||
} | ||
}); | ||
delete mockRequest.abort; | ||
mockRequest.xhr = { | ||
abort: sinon.spy() | ||
}; | ||
promise.then(function() { | ||
done(new Error('Expected promise to be rejected')); | ||
}).catch(function(err) { | ||
assert.instanceOf(err, errors.AbortedRequest); | ||
assert.equal(mockRequest.xhr.abort.callCount, 1); | ||
done(); | ||
}); | ||
}); | ||
it('allows termination on partial response', function(done) { | ||
var response = new stream.Readable(); | ||
response.statusCode = 200; | ||
var body = 'partial body'; | ||
var promise = request({ | ||
url: 'http//example.com', | ||
terminator: function(abort) { | ||
setTimeout(abort, 10); | ||
} | ||
}); | ||
var rejected = false; | ||
promise.then(function() { | ||
done(new Error('Expected promise to be rejected')); | ||
}).catch(function(err) { | ||
rejected = true; | ||
assert.instanceOf(err, errors.AbortedRequest); | ||
assert.equal(mockRequest.abort.callCount, 1); | ||
}); | ||
assert.equal(http.request.callCount, 1); | ||
var args = http.request.getCall(0).args; | ||
assert.lengthOf(args, 2); | ||
var callback = args[1]; | ||
callback(response); | ||
response.emit('data', body); | ||
setTimeout(function() { | ||
response.emit('end'); | ||
assert.equal(rejected, true); | ||
done(); | ||
}, 20); | ||
}); | ||
}); | ||
@@ -198,2 +411,44 @@ | ||
describe('post()', function() { | ||
it('calls request() with method set to POST', function() { | ||
req.post({url: 'http://example.com'}); | ||
assert.equal(http.request.callCount, 1); | ||
var call = http.request.getCall(0); | ||
assert.lengthOf(call.args, 2); | ||
var config = call.args[0]; | ||
assert.equal(config.method, 'POST'); | ||
assert.equal(config.hostname, 'example.com'); | ||
}); | ||
}); | ||
describe('put()', function() { | ||
it('calls request() with method set to PUT', function() { | ||
req.put({url: 'http://example.com'}); | ||
assert.equal(http.request.callCount, 1); | ||
var call = http.request.getCall(0); | ||
assert.lengthOf(call.args, 2); | ||
var config = call.args[0]; | ||
assert.equal(config.method, 'PUT'); | ||
assert.equal(config.hostname, 'example.com'); | ||
}); | ||
}); | ||
describe('del()', function() { | ||
it('calls request() with method set to DELETE', function() { | ||
req.del({url: 'http://example.com'}); | ||
assert.equal(http.request.callCount, 1); | ||
var call = http.request.getCall(0); | ||
assert.lengthOf(call.args, 2); | ||
var config = call.args[0]; | ||
assert.equal(config.method, 'DELETE'); | ||
assert.equal(config.hostname, 'example.com'); | ||
}); | ||
}); | ||
describe('parseConfig()', function() { | ||
@@ -222,2 +477,21 @@ // {api_key: 'my-api-key'} | ||
it('adds user provided headers', function() { | ||
var config = { | ||
url: 'http://example.com', | ||
headers: { | ||
foo: 'bar' | ||
} | ||
}; | ||
var options = { | ||
protocol: 'http:', | ||
hostname: 'example.com', | ||
port: '80', | ||
method: 'GET', | ||
path: '/', | ||
headers: util.assign({}, defaultHeaders, config.headers) | ||
}; | ||
assert.deepEqual(parseConfig(config), options); | ||
}); | ||
it('uses the correct default port for https', function() { | ||
@@ -224,0 +498,0 @@ var config = { |
@@ -7,3 +7,3 @@ /* eslint-env mocha */ | ||
describe('urls', function() { | ||
describe('api/urls', function() { | ||
@@ -51,4 +51,29 @@ describe('join()', function() { | ||
it('works with numbers', function() { | ||
var cases = [{ | ||
actual: urls.join('http://example.com', 42), | ||
expected: 'http://example.com/42' | ||
}, { | ||
actual: urls.join('http://example.com', 0, 'foo'), | ||
expected: 'http://example.com/0/foo' | ||
}, { | ||
actual: urls.join('http://example.com', 10, 'bar', 20), | ||
expected: 'http://example.com/10/bar/20' | ||
}]; | ||
for (var i = 0, ii = cases.length; i < ii; ++i) { | ||
var c = cases[i]; | ||
assert.deepEqual(c.actual, c.expected, 'case ' + i); | ||
} | ||
}); | ||
it('throws for invalid input', function() { | ||
function call() { | ||
urls.join('http://example.com', new Date()); | ||
} | ||
assert.throws(call, Error, 'join must be called with strings or numbers'); | ||
}); | ||
}); | ||
}); |
@@ -7,4 +7,56 @@ /* eslint-env mocha */ | ||
describe('util', function() { | ||
describe('api/util', function() { | ||
describe('addQueryParams()', function() { | ||
it('adds params from a query object', function() { | ||
var cases = [{ | ||
url: 'http://example.com/', | ||
query: { | ||
foo: 'bar' | ||
}, | ||
expect: 'http://example.com/?foo=bar' | ||
}, { | ||
url: 'http://example.com/?foo=bam', | ||
query: { | ||
baz: 'bar' | ||
}, | ||
expect: 'http://example.com/?foo=bam&baz=bar' | ||
}, { | ||
url: 'http://example.com/?foo=bam', | ||
query: { | ||
foo: 'bar' | ||
}, | ||
expect: 'http://example.com/?foo=bar' | ||
}, { | ||
url: 'http://example.com/#anchor', | ||
query: { | ||
foo: 'bar' | ||
}, | ||
expect: 'http://example.com/?foo=bar#anchor' | ||
}, { | ||
url: 'http://example.com/?bam=baz#anchor', | ||
query: { | ||
foo: 'bar' | ||
}, | ||
expect: 'http://example.com/?bam=baz&foo=bar#anchor' | ||
}, { | ||
url: 'http://example.com/?foo=bam#anchor', | ||
query: { | ||
foo: 'bar' | ||
}, | ||
expect: 'http://example.com/?foo=bar#anchor' | ||
}]; | ||
var add = util.addQueryParams; | ||
for (var i = 0, ii = cases.length; i < ii; ++i) { | ||
var c = cases[i]; | ||
assert.equal(add(c.url, c.query), c.expect, 'case ' + i); | ||
} | ||
}); | ||
}); | ||
describe('augmentSceneLinks()', function() { | ||
@@ -71,2 +123,201 @@ var scene; | ||
describe('augmentQuadLinks()', function() { | ||
var quad; | ||
beforeEach(function() { | ||
quad = util.assign({}, { | ||
geometry: { | ||
type: 'Polygon', | ||
coordinates: [ | ||
[ | ||
[-78.925781239, 39.0959629318], | ||
[-78.925781239, 38.9594087879], | ||
[-78.749999989, 38.9594087879], | ||
[-78.749999989, 39.0959629318], | ||
[-78.925781239, 39.0959629318] | ||
] | ||
] | ||
}, | ||
type: 'Feature', | ||
id: 'L15-0575E-1265N', | ||
properties: { | ||
updated: '2015-07-20T13:39:49.550576+00:00', | ||
'num_input_scenes': 28, | ||
links: { | ||
self: 'https://example.com/mosaics/one/quads/two', | ||
full: 'https://example.com/mosaics/one/quads/two/full', | ||
thumbnail: 'https://example.com/mosaics/one/quads/two/thumb', | ||
mosaic: 'https://example.com/mosaics/one', | ||
scenes: 'https://example.com/mosaics/one/quads/two/scenes/' | ||
}, | ||
'percent_covered': 100 | ||
} | ||
}); | ||
}); | ||
afterEach(function() { | ||
authStore.clear(); | ||
}); | ||
it('adds a API key from stored token to data URLs', function() { | ||
// {api_key: 'my-api-key'} | ||
var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcGlfa2V5Ijoib' + | ||
'XktYXBpLWtleSJ9.sYcuJzdUThIsvJGNymbobOh-nY6ZKFEqXTqwZS-4QvE'; | ||
authStore.setToken(token); | ||
var key = authStore.getKey(); | ||
var augmented = util.augmentQuadLinks(quad); | ||
assert.equal(augmented.properties.links.full, | ||
'https://example.com/mosaics/one/quads/two/full?api_key=' + key); | ||
assert.equal(augmented.properties.links.thumbnail, | ||
'https://example.com/mosaics/one/quads/two/thumb?api_key=' + key); | ||
}); | ||
it('adds a stored API key to data URLs', function() { | ||
var key = 'my-key'; | ||
authStore.setKey(key); | ||
var augmented = util.augmentQuadLinks(quad); | ||
assert.equal(augmented.properties.links.full, | ||
'https://example.com/mosaics/one/quads/two/full?api_key=' + key); | ||
assert.equal(augmented.properties.links.thumbnail, | ||
'https://example.com/mosaics/one/quads/two/thumb?api_key=' + key); | ||
}); | ||
}); | ||
describe('augmentMosaicLinks()', function() { | ||
var mosaic; | ||
beforeEach(function() { | ||
mosaic = util.assign({}, { | ||
name: 'one', | ||
links: { | ||
quads: 'https://example.com/mosaics/one/quads/', | ||
self: 'https://example.com/mosaics/one', | ||
tiles: 'https://s{0-3}.example.com/v0/mosaics/one/{z}/{x}/{y}.png', | ||
quadmap: 'https://example.com/mosaics/one/quad-map.png' | ||
}, | ||
'first_acquired': '2014-03-20T15:57:11+00:00', | ||
datatype: 'byte', | ||
'quad_size': 4096, | ||
title: 'A Mosaic', | ||
'coordinate_system': 'EPSG:3857', | ||
geometry: { | ||
type: 'Polygon', | ||
coordinates: [ | ||
[[-180, -90], [-180, 90], [180, 90], [180, -90], [-180, -90]] | ||
] | ||
}, | ||
'last_acquired': '2015-07-20T02:11:31.947579+00:00', | ||
'scene_type': 'ortho', | ||
'quad_pattern': 'L{glevel:d}-{tilex:04d}E-{tiley:04d}N', | ||
level: 15, | ||
resolution: 4.77731426716 | ||
}); | ||
}); | ||
afterEach(function() { | ||
authStore.clear(); | ||
}); | ||
it('adds a API key from stored token to data URLs', function() { | ||
// {api_key: 'my-api-key'} | ||
var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcGlfa2V5Ijoib' + | ||
'XktYXBpLWtleSJ9.sYcuJzdUThIsvJGNymbobOh-nY6ZKFEqXTqwZS-4QvE'; | ||
authStore.setToken(token); | ||
var key = authStore.getKey(); | ||
var augmented = util.augmentMosaicLinks(mosaic); | ||
assert.equal(augmented.links.tiles, | ||
'https://s{0-3}.example.com/v0/mosaics/one/{z}/{x}/{y}.png?api_key=' + | ||
key); | ||
assert.equal(augmented.links.quadmap, | ||
'https://example.com/mosaics/one/quad-map.png?api_key=' + key); | ||
}); | ||
it('adds a stored API key to data URLs', function() { | ||
var key = 'my-key'; | ||
authStore.setKey(key); | ||
var augmented = util.augmentMosaicLinks(mosaic); | ||
assert.equal(augmented.links.tiles, | ||
'https://s{0-3}.example.com/v0/mosaics/one/{z}/{x}/{y}.png?api_key=' + | ||
key); | ||
assert.equal(augmented.links.quadmap, | ||
'https://example.com/mosaics/one/quad-map.png?api_key=' + key); | ||
}); | ||
}); | ||
describe('assign()', function() { | ||
it('assigns source properties to target object', function() { | ||
var source = { | ||
foo: 'bar' | ||
}; | ||
var target = { | ||
num: 42 | ||
}; | ||
util.assign(target, source); | ||
assert.deepEqual(target, { | ||
foo: 'bar', | ||
num: 42 | ||
}); | ||
}); | ||
it('returns the target object', function() { | ||
var target = {}; | ||
var got = util.assign(target, {foo: 'bar'}); | ||
assert.equal(got, target); | ||
}); | ||
it('overwrites target properties', function() { | ||
var target = { | ||
foo: 'bar' | ||
}; | ||
util.assign(target, {foo: 'bam'}); | ||
assert.equal(target.foo, 'bam'); | ||
}); | ||
it('works with multiple sources', function() { | ||
var target = { | ||
foo: 'bar' | ||
}; | ||
var source1 = { | ||
foo1: 'bar1' | ||
}; | ||
var source2 = { | ||
foo2: 'bar2' | ||
}; | ||
util.assign(target, source1, source2); | ||
assert.deepEqual(target, { | ||
foo: 'bar', | ||
foo1: 'bar1', | ||
foo2: 'bar2' | ||
}); | ||
}); | ||
it('prefers later sources, does not modify earlier ones', function() { | ||
var target = { | ||
foo: 'bar' | ||
}; | ||
var source1 = { | ||
foo: 'bam' | ||
}; | ||
var source2 = { | ||
foo: 'baz' | ||
}; | ||
util.assign(target, source1, source2); | ||
assert.deepEqual(target, {foo: 'baz'}); | ||
assert.deepEqual(source1, {foo: 'bam'}); | ||
}); | ||
}); | ||
}); |
/* eslint-env mocha */ | ||
var assert = require('chai').assert; | ||
var Page = require('../../api/page'); | ||
var findScenes = require('../../cli/find-scenes'); | ||
var scenes = require('../../api/scenes'); | ||
var util = require('../../cli/util'); | ||
describe('cli/find-scenes', function() { | ||
describe('fetch()', function() { | ||
var numPages = 10; | ||
var fetched = 0; | ||
function makePage() { | ||
++fetched; | ||
var more = fetched < numPages; | ||
var data = { | ||
features: ['first feature', 'second feature'], | ||
links: { | ||
next: more ? 'http://example.com/more' : null | ||
} | ||
}; | ||
return new Page(data, factory); | ||
} | ||
function factory() { | ||
return Promise.resolve(makePage()); | ||
} | ||
beforeEach(function() { | ||
fetched = 0; | ||
}); | ||
it('concatenates pages of features', function(done) { | ||
var promise = Promise.resolve(makePage()); | ||
findScenes.fetch(promise, [], 100).then(function(features) { | ||
assert.lengthOf(features, 20); | ||
done(); | ||
}).catch(done); | ||
}); | ||
it('stops when the limit is reached', function(done) { | ||
var promise = Promise.resolve(makePage()); | ||
findScenes.fetch(promise, [], 11).then(function(features) { | ||
assert.lengthOf(features, 11); | ||
done(); | ||
}).catch(done); | ||
}); | ||
}); | ||
describe('main()', function() { | ||
var search = scenes.search; | ||
afterEach(function() { | ||
scenes.search = search; | ||
}); | ||
it('calls scenes.search() with a query', function(done) { | ||
var calls = []; | ||
var features = []; | ||
scenes.search = function() { | ||
calls.push(arguments); | ||
var data = { | ||
features: features, | ||
links: {} | ||
}; | ||
var page = new Page(data, scenes.search); | ||
return Promise.resolve(page); | ||
}; | ||
var opts = { | ||
type: 'landsat', | ||
limit: 250 | ||
}; | ||
findScenes.main(opts).then(function(str) { | ||
assert.typeOf(str, 'string'); | ||
done(); | ||
}).catch(done); | ||
}); | ||
}); | ||
describe('parseWhere()', function() { | ||
@@ -128,2 +209,127 @@ | ||
describe('resolveIntersects()', function() { | ||
var orig = {}; | ||
beforeEach(function() { | ||
for (var key in util) { | ||
orig[key] = util[key]; | ||
} | ||
}); | ||
afterEach(function() { | ||
for (var key in orig) { | ||
util[key] = orig[key]; | ||
} | ||
orig = {}; | ||
}); | ||
var resolveIntersects = findScenes.resolveIntersects; | ||
it('resolves to null for falsey values', function(done) { | ||
resolveIntersects('').then(function(val) { | ||
assert.isNull(val); | ||
done(); | ||
}).catch(done); | ||
}); | ||
it('resolves stdin for @-', function(done) { | ||
util.stdin = function() { | ||
return Promise.resolve('read stdin'); | ||
}; | ||
resolveIntersects('@-').then(function(val) { | ||
assert.equal(val, 'read stdin'); | ||
done(); | ||
}).catch(done); | ||
}); | ||
it('resolves stdin for @-', function(done) { | ||
util.stdin = function() { | ||
return Promise.resolve('read stdin'); | ||
}; | ||
resolveIntersects('@-').then(function(val) { | ||
assert.equal(val, 'read stdin'); | ||
done(); | ||
}).catch(done); | ||
}); | ||
it('reads a file for other @', function(done) { | ||
util.readFile = function(name) { | ||
return Promise.resolve('read ' + name); | ||
}; | ||
resolveIntersects('@foo.txt').then(function(val) { | ||
assert.equal(val, 'read foo.txt'); | ||
done(); | ||
}).catch(done); | ||
}); | ||
it('resolves to the value for all other', function(done) { | ||
resolveIntersects('POINT(1 1)').then(function(val) { | ||
assert.equal(val, 'POINT(1 1)'); | ||
done(); | ||
}).catch(done); | ||
}); | ||
}); | ||
describe('resolveQuery()', function() { | ||
var resolveQuery = findScenes.resolveQuery; | ||
it('resolves to a query given command options', function(done) { | ||
var opts = { | ||
intersects: 'POINT(1 1)', | ||
type: 'landsat', | ||
limit: 300 | ||
}; | ||
resolveQuery(opts).then(function(query) { | ||
assert.deepEqual(query, { | ||
intersects: 'POINT(1 1)', | ||
type: 'landsat', | ||
count: 300 | ||
}); | ||
done(); | ||
}).catch(done); | ||
}); | ||
it('generates a query given acquired option', function(done) { | ||
var opts = { | ||
acquired: '2000..', | ||
type: 'ortho', | ||
limit: 250 | ||
}; | ||
resolveQuery(opts).then(function(query) { | ||
assert.deepEqual(query, { | ||
'acquired.gte': '2000-01-01T00:00:00.000Z', | ||
type: 'ortho', | ||
count: 250 | ||
}); | ||
done(); | ||
}).catch(done); | ||
}); | ||
it('generates a query given where options', function(done) { | ||
var opts = { | ||
where: ['acquired.gt=2000', 'gsd.gt=10'], | ||
type: 'ortho', | ||
limit: 250 | ||
}; | ||
resolveQuery(opts).then(function(query) { | ||
assert.deepEqual(query, { | ||
'acquired.gt': '2000', | ||
'gsd.gt': '10', | ||
type: 'ortho', | ||
count: 250 | ||
}); | ||
done(); | ||
}).catch(done); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
133940
50
4066
77
11
13
17