Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Add request and requestError interception in ngResource #13273

Closed
wants to merge 1 commit into from
Closed

Add request and requestError interception in ngResource #13273

wants to merge 1 commit into from

Conversation

krotscheck
Copy link

This patch adds request and requestError interception for ngResource actions,
as per the documentation found for $http interceptors. It is important to
note that returning an error at this stage of the request - before the
call to $http - will completely bypass any global interceptors and/or recovery
handlers, as those are added to a separate context. This is intentional; intercepting
a request before it is passed to the HTTP stack indicates that the resource itself
has made a decision, and that it accepts the responsibility for recovery.

Closes #5146

@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

@googlebot
Copy link

CLAs look good, thanks!

@googlebot googlebot added cla: yes and removed cla: no labels Nov 7, 2015
@@ -593,7 +601,13 @@ angular.module('ngResource', ['ng']).
extend({}, extractParams(data, action.params || {}), params),
action.url);

var promise = $http(httpConfig).then(function(response) {
var promise = $q.resolve(httpConfig);
Copy link
Member

Choose a reason for hiding this comment

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

Why not directly: $q.when(requestInterceptor(httpConfig), null, requestErrorInterceptor)

Copy link
Author

Choose a reason for hiding this comment

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

Well, that's how my brain addressed the problem. First, peel everything into a promise chain. Second, add the desired step into that chain. While I can collapse it easily enough, I personally find it more legible, as each statement is discrete and easily understood.

I don't mind updating the commit though, if there's consensus that your proposed syntax is more desirable.

@krotscheck
Copy link
Author

As an additional note: The reason I'd like to land this in angular, is because I'd like to build resources who get their root API url from a promise.

  1. Resource action is requested (GET /foo/:bar)
  2. request interceptor decorates the relative URL with the discovered API endpoint (GET https://example.com/api/v1/foo/:bar)
  3. request is made.

Since I live in a multi-api environment, resolving this in the resource made the most sense.

@krotscheck
Copy link
Author

@petebacondarwin Heya- with 1.5 out now, what are the next steps for this PR? Should I rebase, update, add docs... any guidance?

@gkalpak
Copy link
Member

gkalpak commented Feb 8, 2016

@krotscheck, now that 1.5.0 is out (and since this is on the 1.6.x milestone), I will take a look soon and get back to you. You don't need to rebase for now. I'll let you know if you need to update anything.

Thx for working on this !

@krotscheck
Copy link
Author

@gkalpak Awesome! Looking forward to your comments :)

@@ -664,7 +672,9 @@ angular.module('ngResource', ['ng']).
extend({}, extractParams(data, action.params || {}), params),
action.url);

var promise = $http(httpConfig).then(function(response) {
var promise = $q.when(requestInterceptor(httpConfig), null, requestErrorInterceptor);
Copy link
Member

Choose a reason for hiding this comment

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

This will not catch errors thrown in the requestInterceptor. The requestErrorInterceptor will only be called if requestInterceptor returns a rejecting promise.

I think the following is better:

$q.when(httpConfig).then(requestInterceptor).catch(requestErrorInterceptor)

@gkalpak
Copy link
Member

gkalpak commented Feb 19, 2016

@krotscheck, I left a couple of comments.

I think it's a good change overall, but I would like to have more tests. E.g.:

  • Test that $http is called with the value returned from requestInterceptor.
  • Test that throwing inside the requestInterceptor calls the requestErrorInterceptor.
  • Test that a rejecting requestInterceptor aborts the operation.
  • Test that a rejecting requestErrorInterceptor aborts the operation.
  • Test that a requestErrorInterceptor can recover from rejection (and the operation continues as usual).
  • ...something else that I have missed...

Feel free to ping me when/if you make any changes, so I can take another look.

BTW, there is some ongoing discussion about response interceptors (or interceptors in general) in the context of $resource (e.g. in #9334 and elsewhere). Their behavior is currently kind of inconsistent and confusing - I would like to get it right for v1.6.x. @krotscheck, if you have any insights, please do chime in 😃

@krotscheck
Copy link
Author

@gkalpak Tests added, feedback incorporated. I also concatenated the promise chain, which had the unexpected impact of creating a gross diff (indentation had to be updated). I can go back and separate the two if you'd like me to.

@gkalpak
Copy link
Member

gkalpak commented Feb 22, 2016

@krotscheck, thx ! The diff is fine if you ignore whitespace 😉
Taking a look...


// Ensure the resource promise was rejected
expect(card.$resolved).toBeTruthy();
expect(card.$promise.$$state.status).toEqual(2);
Copy link
Member

Choose a reason for hiding this comment

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

This is private API surface. We don't need to verify that. (It might change anytime anyway.)

@gkalpak
Copy link
Member

gkalpak commented Feb 22, 2016

I left a couple of comments. Additionally, where applicable, I think it's a good idea to use success/error callbacks to verify that the appropriate callback has been called in each case.

@krotscheck
Copy link
Author

@gkalpak Alright, the most recent patch has the changes you asked for.

@gkalpak
Copy link
Member

gkalpak commented Apr 13, 2016

I left some minor comments, but generally LGTM.

WRT #13273 (comment), I think we should do it.
It's better to be consistent, than have people wondering why their tests are not making a request when they add an interceptor. Besides, in most cases people will call $httpBackend.flush(), which will take care of $rootScope.$digest(), so this shouldn't affect many people.

@krotscheck
Copy link
Author

Okies, I'm on it.

This patch adds request and requestError interception for ngResource actions,
as per the documentation found for $http interceptors. It is important to
note that returning an error at this stage of the request - before the
call to $http - will completely bypass any global interceptors and/or recovery
handlers, as those are added to a separate context. This is intentional; intercepting
a request before it is passed to the HTTP stack indicates that the resource itself
has made a decision, and that it accepts the responsibility for recovery.

Closes #5146
@krotscheck
Copy link
Author

Theeeere we go. Was temporarily derailed by #13123, but used the workaround for great justice.

@krotscheck
Copy link
Author

@gkalpak Hey there! It's been a month since my last patch, which incorporates all the changes you asked for. Is there anything else we're missing?

@gkalpak
Copy link
Member

gkalpak commented Jun 7, 2016

@krotscheck: Time 😃

I still wish to get the ngResource interceptors/transforms story fixed for 1.6, but I have to talk people into this 😛
I'll do my best!

@gkalpak
Copy link
Member

gkalpak commented Jun 7, 2016

BTW, this should also fix #12095. I would be nice to add a testcase for that (i.e. testing that the request interceptor will be able to modify the headers).

@krotscheck
Copy link
Author

Oh, all you need is time? I had some of that lying around here somewhere... now where did it... hold on... drat!

A few questions:

  • What's the cutoff for 1.6? I don't have a lot of extra time, but I may be able to convince someone in the OpenStack community to look at the interceptor implementation.
  • When you say: "It would be nice to add a testcase", is that a "Add this testcase or I won't merge it", or is this a "If you get around to it, yay, but otherwise this is fine."?
  • What's the process from here? Do you just merge it? Will things get approved in a batch? Do I have to do a rain dance?

@petebacondarwin
Copy link
Contributor

The aim is to release 1.6 in September. So there is time (of one kind or another) but perhaps not the kind of time that you need :-)

@gkalpak
Copy link
Member

gkalpak commented Jun 7, 2016

I may be able to convince someone in the OpenStack community to look at the interceptor implementation.

If this is a response to my "I have to talk people into this", I meant I people on the ng1 team 😃
(Not that they need much talking into - just a tiny bit 😃 )

is that a "Add this testcase or I won't merge it", or is this a "If you get around to it, yay, but otherwise this is fine."

The latter

What's the process from here?

Review --> LGTM --> LGT another team member --> Merge
(All sprinkled with a good overall strategy/plan for moving forward. But I prefer to make small incremental changes.)

Do you just merge it?

We don't merge - we rebase 😛

Will things get approved in a batch?

I hope not.

Do I have to do a rain dance?

It's not necessary, but people have reported it helps 😛

@gercheq
Copy link

gercheq commented Dec 2, 2016

@gkalpak When do you think this will be released? Currently it's not possible to transform the request headers with $resource dynamically.

                        search: {
                            method: 'GET',
                            params: {
                                organizationId: "@organizationId",
                                q: '@q'
                            },
                            transformRequest: function(data, getHeaders) {
                            
                                var token = ContactsContinuationService.getToken(),
                                    headers = getHeaders();
                            
                                headers['Continuation'] = token;
                                headers['RECEP'] = "SUCKS";
                            
                                return data;
                            },

                            url: ApiPath('/organisations/:organizationId/contacts')
                        },

Declaration above doesn't work. It transforms the header but the final request does not have these new headers.

I ended up wrapping the $resource with another function as mentioned here : http://stackoverflow.com/questions/20566593/dynamic-resource-headers

Thanks for the PR @krotscheck

@gkalpak
Copy link
Member

gkalpak commented Dec 2, 2016

The milestone is 1.6.x (post 1.6.0), so after 1.6.0 has been released (probably in 1.6.1).

@gkalpak gkalpak modified the milestones: 1.6.2, 1.6.x Dec 23, 2016
isArray: true,
interceptor: {
request: function(httpConfig) {
callback(httpConfig);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the callback defined somewhere?

@Narretz
Copy link
Contributor

Narretz commented Jan 30, 2017

There's now a merge conflict (due to 39a90a9 I think). Must be resolved, but should be easy. Can you update the PR @krotscheck ?

@krotscheck
Copy link
Author

Sadly, I've switched jobs. You'll need to find someone else to update this patch.

@Narretz
Copy link
Contributor

Narretz commented Jan 30, 2017 via email

@Narretz
Copy link
Contributor

Narretz commented Feb 3, 2017

I wrote this in Slack but it must have been overlooked: I know we talked previously about how the interceptor API is different between ngResource and $http. Did you take this into consideration when approving the PR @gkalpak ? I wonder if we could theoretically move the intereption to $http. Basically, have request-level interceptors in http, which are followed by the app-global interceptors? We don't have to do this in this PR, I just don't want to introduce an API that is blocking us in the future

@gkalpak
Copy link
Member

gkalpak commented Feb 3, 2017

Yeah, I have proposed something similar here and there. My "problem" with the current implementation is that is is too $resource-specific and the interceptor has to know about $resource-internals (e.g. manipulate and return response.resource as explained here). This only affects response interceptors, so I don't think requestInterceptors will pose any problems.

Even if we decided to make this an $http feature, this PR would makewould not make it more difficult, because the requestInterceptors introduced here have the same signature as the per-request $http ones.

So, I think we are good.

@Narretz
Copy link
Contributor

Narretz commented Feb 3, 2017

Regarding if we decided to make this an $http feature, this PR would make it more difficult, because the requestInterceptors introduced here have the same signature as the per-request $http ones.

I guess you mean, "would not make it more difficult"?
And there's currently no per-request http interceptor, only the global ones, and transformRequest / transformResponse

@gkalpak
Copy link
Member

gkalpak commented Feb 3, 2017

That's what I said 😛

@gkalpak
Copy link
Member

gkalpak commented Feb 3, 2017

I now realize I have no idea why I didn't put it on the 1.6.0 milestone, as I clearly think this is (tiny) breaking change (see here, here and here) 🤔

We can make the BC go away by undoing this, but then it will be a little inconsistent (in practice only tests would be affected).

@gkalpak
Copy link
Member

gkalpak commented Feb 3, 2017

Here is the rebased version btw: #15674

@petebacondarwin
Copy link
Contributor

Closing in favour of #15674

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants