Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add request interception hooks #92

Merged
merged 7 commits into from Feb 20, 2017
Merged

Add request interception hooks #92

merged 7 commits into from Feb 20, 2017

Conversation

pimterry
Copy link
Contributor

While working on https://github.com/resin-io/resin-ui/issues/400, it became clear the SDK currently isn't a usable replacement for our api calls in the UI, because it can't use Angular's built-in $http client internally. We depend on all requests going through that $http client so we can intercept them all in various places, e.g. to support offline status monitoring.

This PR adds Angular-inspired request/requestError/response/responseError hooks to resin-request, so that they can be exposed on resin-sdk shortly. These should have the same semantics as Angular's, which seem as good a model as any.

@pimterry
Copy link
Contributor Author

Build fails because the new version of fetch-mock (used via resin-fetch-mock) has quietly starting expecting a global promise when it's run in browsers, and Phantom doesn't have one. Node tests in CI pass fine though, and my browsers tests do locally. I'm going to ignore these browser tests here for now, and get that sorted properly in fetch-mock itself soon.

@emirotin
Copy link
Contributor

Can we just globally inject something like https://www.npmjs.com/package/es6-promise to make it happy? It won't add the bluebird sugar methods so won't mask any errors related to that.

interceptResponseOrError(Promise.reject(responseError))

interceptRequestOrError = (initialPromise) ->
Promise.resolve(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this wrapper? The inner thing is already a promise

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this the result of reduce could be a non-bluebird promise, if a hook internally returns a native promise - this makes sure we definitely always get a bluebird promise at the end.

interceptResponseOrError = (initialPromise) ->
Promise.resolve(
exports.interceptors.slice().reverse().reduce (promise, interceptor) ->
promise.then(interceptor.response, interceptor.responseError)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if both are null-ish will p.then(null, null) resolve with the original promise value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK it works but shows a warning: https://monosnap.com/file/1uCMR5wTIKJtfoxQwm90SmJtKosfTE.png

So would change to

{ response, responseError } = interceptor 
return if (response or responseError) then promise.then(interceptor.response, interceptor.responseError) else promise`

(and same below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - destructuring here is a great idea.

# reverse order for responses.
#
# @example
# resin.interceptors.push(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resin -> resinRequest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot. I've gone for request in the end, for consistency with the other examples.


it 'should be able to asynchronously change a request before it is sent', ->
request.interceptors[0] = request: (request) ->
Promise.fromCallback (callback) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

promise = request.stream
url: 'https://500.com'

m.chai.expect(promise).to.be.rejectedWith('caught error')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice test suite!

Copy link
Contributor

@emirotin emirotin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (G === grreat), but couple of nits (and would love to see the CI tests passing, suggested an option that looks pretty safe).

@pimterry
Copy link
Contributor Author

All done - one comment on the Promise.resolve outstanding, let me know if that makes sense @emirotin.

I fixed the build by putting a patch in to fetch-mock directly, which seems to now all be working here very nicely, as I've just done a resin-fetch-mock release.

@emirotin
Copy link
Contributor

Yeah the reasoning behind using Promise.resolve makes sense.

@pimterry pimterry merged commit c17ddbc into master Feb 20, 2017
@pimterry pimterry deleted the request-interceptors branch February 20, 2017 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants