Skip to content

Commit

Permalink
adds timeouts based on #20, with unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mcgowan committed May 16, 2017
1 parent a22810a commit 7c96ab1
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 25 deletions.
21 changes: 16 additions & 5 deletions src/Agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,18 @@ export default class Agent {
* @param {String} query Query parameters
* @param {Object} form Form fields
* @param {Object} files array of file names and file content
* @parma {Object} context the invocation context, describing the tool and project.
* @parma {Object} context the invocation context, describing the tool and project. A timeout for the request
* in milliseconds may be optionally specified. When not specified, the request does not timeout.
* @return {Promise} A promise. fulfilled with {body, statusCode}, rejected with { statusCode, errorDescription, error, body }
*/
request({ uri, method, data = undefined, auth, query = undefined, form = undefined, files = undefined, context = undefined }) {
const requestFiles = this._sanitizeFiles(files);
return this._request({ uri, method, data, auth, query, form, context, files: requestFiles });
const timeout = context && context.timeout;
if (timeout) {
context = Object.assign({}, context);
delete context['timeout'];
}
return this._request({ uri, method, data, auth, query, form, timeout, context, files: requestFiles });
}

/**
Expand All @@ -75,11 +81,13 @@ export default class Agent {
* @param {String} query Query parameters
* @param {Object} form Form fields
* @param {Object} files array of file names and file content
* @param {Number} timeout the number of milliseconds of no response after which the request should timeout.
* When undefined, the request is not timed out.
* @param {Object} context the invocation context
* @return {Promise} A promise. fulfilled with {body, statusCode}, rejected with { statusCode, errorDescription, error, body }
*/
_request({ uri, method, data, auth, query, form, files, context }) {
const req = this._buildRequest({ uri, method, data, auth, query, form, context, files });
_request({ uri, method, data, auth, query, form, files, timeout, context }) {
const req = this._buildRequest({ uri, method, data, auth, query, form, timeout, context, files });
return this._promiseResponse(req);
}

Expand Down Expand Up @@ -125,7 +133,7 @@ export default class Agent {
});
}

_buildRequest({ uri, method, data, auth, query, form, files, context, makerequest=request }) {
_buildRequest({ uri, method, data, auth, query, form, files, timeout, context, makerequest=request }) {
const req = makerequest(method, uri);
if (this.prefix) {
req.use(this.prefix);
Expand All @@ -137,6 +145,9 @@ export default class Agent {
if (query) {
req.query(query);
}
if (timeout!==undefined) {
req.timeout(timeout);
}
if (files) {
for (let [name, file] of Object.entries(files)) {
req._getFormData().append(name, file.data, {
Expand Down
3 changes: 2 additions & 1 deletion src/Defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export default {
baseUrl: 'https://api.particle.io',
clientSecret: 'particle-api',
clientId: 'particle-api',
tokenDuration: 7776000 // 90 days
tokenDuration: 7776000, // 90 days
timeout: 60000
};
30 changes: 15 additions & 15 deletions src/Particle.js
Original file line number Diff line number Diff line change
Expand Up @@ -520,9 +520,9 @@ class Particle {
uri += `orgs/${org}/`;
}

if (product) {
uri += `products/${product}/`;
}
if (product) {
uri += `products/${product}/`;
}

if (deviceId) {
uri += 'devices/';
Expand Down Expand Up @@ -1002,7 +1002,7 @@ class Particle {
const uri = product ? `/v1/products/${product}/clients` : '/v1/clients';
const data = { name, type, redirect_uri, scope };
return this.post(uri, data, auth, context);
}
}

/**
* Update an OAuth client
Expand Down Expand Up @@ -1090,8 +1090,8 @@ class Particle {
},
context,
auth
});
}
});
}

/**
* Get information about a product firmware version
Expand All @@ -1103,7 +1103,7 @@ class Particle {
*/
getProductFirmware({ version, product, auth, context }) {
return this.get(`/v1/products/${product}/firmware/${version}`, auth, undefined, context);
}
}

/**
* Update information for a product firmware version
Expand Down Expand Up @@ -1133,9 +1133,9 @@ class Particle {
const req = request('get', uri);
req.use(this.prefix);
this.headers(req, auth);
if (this.debug) {
this.debug(req);
}
if (this.debug) {
this.debug(req);
}
return req;
}

Expand All @@ -1150,7 +1150,7 @@ class Particle {
releaseProductFirmware({ version, product, auth, context }) {
const uri = `/v1/products/${product}/firmware/release`;
return this.put(uri, { version }, auth, context);
}
}

/**
* List product team members
Expand All @@ -1161,7 +1161,7 @@ class Particle {
*/
listTeamMembers({ product, auth, context }) {
return this.get(`/v1/products/${product}/team`, auth, undefined, context);
}
}

/**
* Invite Particle user to a product team
Expand All @@ -1185,7 +1185,7 @@ class Particle {
*/
removeTeamMember({ username, product, auth, context }) {
return this.delete(`/v1/products/${product}/team/${username}`, undefined, auth, context);
}
}

/**
* API URI to access a device
Expand Down Expand Up @@ -1217,7 +1217,7 @@ class Particle {
put(uri, data, auth, context) {
context = this._buildContext(context);
return this.agent.put(uri, data, auth, context);
}
}

delete(uri, data, auth, context) {
context = this._buildContext(context);
Expand All @@ -1231,7 +1231,7 @@ class Particle {

client(options = {}) {
return new Client(Object.assign({ api: this }, options));
}
}
}

// Aliases for backwards compatibility
Expand Down
39 changes: 35 additions & 4 deletions test/Agent.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ describe('Agent', () => {
expect(sut._applyContext).to.not.be.called;
});


it('should invoke authorize with the request and auth', () => {
const sut = new Agent();
sut.prefix = undefined;
Expand Down Expand Up @@ -243,6 +242,25 @@ describe('Agent', () => {
expect(field).to.be.calledWith('form1', 'value1');
expect(field).to.be.calledWith('form2', 'value2');
});

it('sets the timeout on the request when defined', () => {
const sut = new Agent();
sut.prefix = undefined;
const req = { timeout: sinon.stub() };
const makerequest = sinon.stub().returns(req);
sut._buildRequest({timeout: 123, makerequest});
expect(req.timeout).calledOnce.calledWith(123);
});

it('does not set the timeout on the request when not defined', () => {
const sut = new Agent();
sut.prefix = undefined;
const req = { timeout: sinon.stub() };
const makerequest = sinon.stub().returns(req);
sut._buildRequest({makerequest});
expect(req.timeout).not.called;
});

});

it('sanitizes files from a request', () => {
Expand All @@ -258,7 +276,19 @@ describe('Agent', () => {
expect(result).to.be.equal('request_result');
expect(sut._sanitizeFiles).calledOnce.calledWith(sinon.match.same(files));
expect(sut._request).calledOnce.calledWith({uri: 'abc', auth: undefined, method: 'post', data: '123', query: 'all', form:form,
files:sanitizedFiles, context: undefined});
files:sanitizedFiles, timeout: undefined, context: undefined});
});

describe('timeout', () => {
it('sets the timeout from the context', () => {
const sut = new Agent();
sut._request = sinon.stub();
const context = { timeout: 123, foo: 'bar' };
sut.request({context});
expect(sut._request).calledOnce.calledWith({uri: undefined, method: undefined,
auth: undefined, data: undefined, files: undefined, form: undefined, query: undefined, timeout: 123,
context: { foo: 'bar' } });
});
});

it('uses default arguments for request', () => {
Expand All @@ -269,20 +299,21 @@ describe('Agent', () => {
const result = sut.request({uri: 'abc', method:'post'});
expect(result).to.equal('123');
expect(sut._request).calledOnce.calledWith({uri: 'abc', method:'post',
auth: undefined, data: undefined, files: undefined, form: undefined, query: undefined, context: undefined });
auth: undefined, data: undefined, files: undefined, form: undefined, query: undefined, timeout: undefined, context: undefined });
});

it('builds and sends the request', () => {
const sut = new Agent();
const buildRequest = sinon.stub();
const promiseResponse = sinon.stub();
const context = {};
sut._buildRequest = buildRequest;
sut._promiseResponse = promiseResponse;
buildRequest.returns('arequest');
promiseResponse.returns('promise');

const requestArgs = {uri:'uri', method:'method', data:'data', auth:'auth', query: 'query',
form: 'form', files: 'files', context};
form: 'form', files: 'files', timeout: undefined, context};
const result = sut._request(requestArgs);
expect(result).to.be.equal('promise');
expect(buildRequest).calledWith(requestArgs);
Expand Down

0 comments on commit 7c96ab1

Please sign in to comment.