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

Add hasBody to ngResource action configuration #12181

Closed
wants to merge 10 commits into from
4 changes: 3 additions & 1 deletion src/ngResource/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ function shallowClearAndCopy(src, dst) {
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
* with `http response` object. See {@link ng.$http $http interceptors}.
* - **`hasBody`** - `{boolean}` - allows to specify if a request body should be included or not.
* If not specified only POST, PUT and PATCH requests will have a body.
*
* @param {Object} options Hash with custom settings that should extend the
* default `$resourceProvider` behavior. The supported options are:
Expand Down Expand Up @@ -640,7 +642,7 @@ angular.module('ngResource', ['ng']).
};

forEach(actions, function(action, name) {
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
var hasBody = action.hasBody === true || (action.hasBody !== false && /^(POST|PUT|PATCH)$/i.test(action.method));
var numericTimeout = action.timeout;
var cancellable = isDefined(action.cancellable) ?
action.cancellable : route.defaults.cancellable;
Expand Down
70 changes: 70 additions & 0 deletions test/ngResource/resourceSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,76 @@ describe('basic usage', function() {
$httpBackend.flush();
});

it('should include a request body when calling custom method with hasBody is true', function() {
var instant = {name: 'info.txt'};
var condition = {at: '2038-01-19 03:14:08'};

$httpBackend.expect('CREATE', '/fooresource', instant).respond({fid: 42});
$httpBackend.expect('DELETE', '/fooresource', condition).respond({});

var r = $resource('/fooresource', {}, {
create: {method: 'CREATE', hasBody: true},
delete: {method: 'DELETE', hasBody: true}
});

var creationResponse = r.create(instant);
var deleteResponse = r.delete(condition);

$httpBackend.flush();

expect(creationResponse.fid).toBe(42);
expect(deleteResponse.$resolved).toBe(true);
});

it('should not include a request body if hasBody is false on POST, PUT and PATCH', function() {
function verifyRequest(method, url, data) {
expect(data).toBeUndefined();
return [200, {id: 42}];
}

$httpBackend.expect('POST', '/foo').respond(verifyRequest);
$httpBackend.expect('PUT', '/foo').respond(verifyRequest);
$httpBackend.expect('PATCH', '/foo').respond(verifyRequest);

var R = $resource('/foo', {}, {
post: {method: 'POST', hasBody: false},
put: {method: 'PUT', hasBody: false},
patch: {method: 'PATCH', hasBody: false}
});

var postResponse = R.post();
var putResponse = R.put();
var patchResponse = R.patch();
Copy link
Member

Choose a reason for hiding this comment

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

Would these 3 calls fail if hasBody was not set? I don't think so (but I haven't tried it either). Can you check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No they don't fail if hasBody is not set. This is a bug or a feature of ngResource not enforcing the described api.

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 haven't found a way to distinguish between request parameters and the request body in httpBackend.

Did I miss something?

Copy link
Member

@gkalpak gkalpak Mar 12, 2017

Choose a reason for hiding this comment

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

If you pass two params (e.g. R.post(null, {foo: 'bar'})), then the first one is interpreted as params and the second one as data.

For example, the following test currently fails, because {id: -1} is not undefined. If something similar passes with your PR, then we are good:

    $httpBackend.expectPOST('/foo').respond(function(method, url, data) {
      expect(data).not.toBeUndefined();
      return [200, {id: 42}];
    });

    var R = $resource('/foo');
    var response = R.save(null, {id: -1});

    $httpBackend.flush();
    expect(response.id).toBe(42);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sry for the delayed response on my side. I have merged your proposed test case into the test method with hasBody true. The only problem for me was that I couldn't reproduce the failing of the test case. (Even when I used your version.)

Can you see my error or give me the error that you get?

Copy link
Member

Choose a reason for hiding this comment

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

I was probably not clear. What I meant is that my test fails with the code currently in master (i.e. without your PR), which is expected, because we don't support the hasBody yet. If the test passes with your PR, then it verifies that the feature you added (hasBody) does indeed work for PUT/POST/PATCH requests too, so it is good to be merged.


$httpBackend.flush();

expect(postResponse.id).toBe(42);
expect(putResponse.id).toBe(42);
expect(patchResponse.id).toBe(42);
});

it('should expect a body if hasBody is true', function() {
var username = 'yathos';
var loginRequest = {name: username, password: 'Smile'};
var user = {id: 1, name: username};

$httpBackend.expect('LOGIN', '/user/me', loginRequest).respond(user);

$httpBackend.expect('LOGOUT', '/user/me', null).respond(null);

var UserService = $resource('/user/me', {}, {
login: {method: 'LOGIN', hasBody: true},
logout: {method: 'LOGOUT', hasBody: false}
});

var loginResponse = UserService.login(loginRequest);
var logoutResponse = UserService.logout();

$httpBackend.flush();

expect(loginResponse.id).toBe(user.id);
expect(logoutResponse.$resolved).toBe(true);
});

it('should build resource', function() {
expect(typeof CreditCard).toBe('function');
Expand Down