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

feat($resource): add support for wrapped responses #6139

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/content/error/resource/badresourceloc.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ngdoc error
@name $resource:badresourceloc
@fullName responseData is not a function or string
@description

This error occurs when a {@link api/ngResource.$resource `$resource`} service action's responseData field
is not a string or a function.
17 changes: 17 additions & 0 deletions src/ngResource/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ 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}.
* - **`resourceData`** - `{string=|function(data)=}` - A string specifying which field in the
* response should be converted to a Resource object or a function which is passed the
* data from the request and should return the object to be converted to a Resource object.
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions:
Expand Down Expand Up @@ -512,6 +515,20 @@ angular.module('ngResource', ['ng']).
var data = response.data,
promise = value.$promise;

if(action.resourceData) {
if(isFunction(action.resourceData)) {
data = action.resourceData(response.data);
}
else if(angular.isString(action.resourceData)) {
data = data[action.resourceData];
}
else {
throw $resourceMinErr('badresourceloc',
"Error in resource data configuration. " +
"Expected string or function, got {0}", typeof action.resourceData);
}
}

if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
Expand Down
159 changes: 159 additions & 0 deletions test/ngResource/resourceSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,97 @@ describe("resource", function() {
expect(item).toEqualData({id: 'abc'});
});
});

describe('wrapped responses', function()
{
var objectResponse = {meta: {metaField: 'value'}, data: {id: 123}};
var listResponse = {meta: {metaField: 'value'}, items: [{id: 123}]};

it('should get resource object from wrapped response via a string', function() {
var Person = $resource('/Person/:id', {id: '@id'}, {
get: {
method: 'GET',
resourceData: 'data'
}
});

$httpBackend.expect('GET', '/Person/123').respond(objectResponse);

var p = Person.get({id: 123});
$httpBackend.flush();

expect(p.meta).not.toBeDefined();
expect(p.id).toEqual(123);
expect(p.$save).toBeDefined();
});

it('should get resource object from wrapped response via a function', function()
{
var Person = $resource('/Person/:id', {id: '@id'}, {
get: {
method: 'GET',
resourceData: function(response)
{
return response.data;
}
}
});

$httpBackend.expect('GET', '/Person/123').respond(objectResponse);

var p = Person.get({id: 123});
$httpBackend.flush();

expect(p.meta).not.toBeDefined();
expect(p.id).toEqual(123);
expect(p.$save).toBeDefined();
});

it('should get resource list from wrapped response via string', function()
{
var Person = $resource('/Person/:id', {id: '@id'}, {
query: {
method: 'GET',
isArray: true,
resourceData: 'items'
}
});

$httpBackend.expect('GET', '/Person').respond(listResponse);

var people = Person.query();
$httpBackend.flush();

var p = people[0];

expect(p.id).toEqual(123);
expect(p.$save).toBeDefined();
});

it('should get resource list from wrapped response via function', function()
{
var Person = $resource('/Person/:id', {id: '@id'}, {
query: {
method: 'GET',
isArray: true,
resourceData: function(response)
{
return response.items;
}
}
});

$httpBackend.expect('GET', '/Person').respond(listResponse);

var people = Person.query();
$httpBackend.flush();

var p = people[0];

expect(p.id).toEqual(123);
expect(p.$save).toBeDefined();
});
});
});

describe('resource', function() {
Expand Down Expand Up @@ -1251,5 +1342,73 @@ describe('resource', function() {
)
});

it('should fail if responseData is an object', function() {
var successSpy = jasmine.createSpy('successSpy');
var failureSpy = jasmine.createSpy('failureSpy');

$httpBackend.expect('GET', '/Person/123').respond({meta: {metaField: 'value'}, data: {id: 123}});

var Person = $resource('/Person/:id', {id: '@id'}, {
get: {
method: 'GET',
resourceData: {}
}
});

Person.get({id: 123}).$promise.then(successSpy, function(e) { failureSpy(e.message); });
$httpBackend.flush();

expect(successSpy).not.toHaveBeenCalled();
expect(failureSpy).toHaveBeenCalled();
expect(failureSpy.mostRecentCall.args[0]).toMatch(
/^\[\$resource:badresourceloc\] Error in resource data configuration\. Expected string or function, got object/
);
});

it('should fail if responseData is an array', function() {
var successSpy = jasmine.createSpy('successSpy');
var failureSpy = jasmine.createSpy('failureSpy');

$httpBackend.expect('GET', '/Person/123').respond({meta: {metaField: 'value'}, data: {id: 123}});

var Person = $resource('/Person/:id', {id: '@id'}, {
get: {
method: 'GET',
resourceData: []
}
});

Person.get({id: 123}).$promise.then(successSpy, function(e) { failureSpy(e.message); });
$httpBackend.flush();

expect(successSpy).not.toHaveBeenCalled();
expect(failureSpy).toHaveBeenCalled();
expect(failureSpy.mostRecentCall.args[0]).toMatch(
/^\[\$resource:badresourceloc\] Error in resource data configuration\. Expected string or function, got object/
);
});

it('should fail if responseData is a number', function() {
var successSpy = jasmine.createSpy('successSpy');
var failureSpy = jasmine.createSpy('failureSpy');

$httpBackend.expect('GET', '/Person/123').respond({meta: {metaField: 'value'}, data: {id: 123}});

var Person = $resource('/Person/:id', {id: '@id'}, {
get: {
method: 'GET',
resourceData: 123
}
});

Person.get({id: 123}).$promise.then(successSpy, function(e) { failureSpy(e.message); });
$httpBackend.flush();

expect(successSpy).not.toHaveBeenCalled();
expect(failureSpy).toHaveBeenCalled();
expect(failureSpy.mostRecentCall.args[0]).toMatch(
/^\[\$resource:badresourceloc\] Error in resource data configuration\. Expected string or function, got number/
);
});

});