Skip to content

Commit 78b0a00

Browse files
author
Stephen Leicht
committed
feat($resource): add support for wrapped responses
add a resourceData field on the action configuration. This allows you to specify which field in the response should be turned into the resource object instead of the whole thing always being decorated. You can use a string to use just a single simple field or a function for more advanced control Closes angular#6138
1 parent 95522cc commit 78b0a00

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@ngdoc error
2+
@name $resource:badresourceloc
3+
@fullName responseData is not a function or string
4+
@description
5+
6+
This error occurs when a {@link api/ngResource.$resource `$resource`} service action's responseData field
7+
is not a string or a function.

src/ngResource/resource.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ function shallowClearAndCopy(src, dst) {
141141
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
142142
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
143143
* with `http response` object. See {@link ng.$http $http interceptors}.
144+
* - **`resourceData`** - `{string=|function(data)=}` - A string specifying which field in the
145+
* response should be converted to a Resource object or a function which is passed the
146+
* data from the request and should return the object to be converted to a Resource object.
144147
*
145148
* @returns {Object} A resource "class" object with methods for the default set of resource actions
146149
* optionally extended with custom `actions`. The default set contains these actions:
@@ -512,6 +515,20 @@ angular.module('ngResource', ['ng']).
512515
var data = response.data,
513516
promise = value.$promise;
514517

518+
if(action.resourceData) {
519+
if(isFunction(action.resourceData)) {
520+
data = action.resourceData(response.data);
521+
}
522+
else if(angular.isString(action.resourceData)) {
523+
data = data[action.resourceData];
524+
}
525+
else {
526+
throw $resourceMinErr('badresourceloc',
527+
"Error in resource data configuration. " +
528+
"Expected string or function, got {0}", typeof action.resourceData);
529+
}
530+
}
531+
515532
if (data) {
516533
// Need to convert action.isArray to boolean in case it is undefined
517534
// jshint -W018

test/ngResource/resourceSpec.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,97 @@ describe("resource", function() {
12001200
expect(item).toEqualData({id: 'abc'});
12011201
});
12021202
});
1203+
1204+
describe('wrapped responses', function()
1205+
{
1206+
var objectResponse = {meta: {metaField: 'value'}, data: {id: 123}};
1207+
var listResponse = {meta: {metaField: 'value'}, items: [{id: 123}]};
1208+
1209+
it('should get resource object from wrapped response via a string', function() {
1210+
var Person = $resource('/Person/:id', {id: '@id'}, {
1211+
get: {
1212+
method: 'GET',
1213+
resourceData: 'data'
1214+
}
1215+
});
1216+
1217+
$httpBackend.expect('GET', '/Person/123').respond(objectResponse);
1218+
1219+
var p = Person.get({id: 123});
1220+
$httpBackend.flush();
1221+
1222+
expect(p.meta).not.toBeDefined();
1223+
expect(p.id).toEqual(123);
1224+
expect(p.$save).toBeDefined();
1225+
});
1226+
1227+
it('should get resource object from wrapped response via a function', function()
1228+
{
1229+
var Person = $resource('/Person/:id', {id: '@id'}, {
1230+
get: {
1231+
method: 'GET',
1232+
resourceData: function(response)
1233+
{
1234+
return response.data;
1235+
}
1236+
}
1237+
});
1238+
1239+
$httpBackend.expect('GET', '/Person/123').respond(objectResponse);
1240+
1241+
var p = Person.get({id: 123});
1242+
$httpBackend.flush();
1243+
1244+
expect(p.meta).not.toBeDefined();
1245+
expect(p.id).toEqual(123);
1246+
expect(p.$save).toBeDefined();
1247+
});
1248+
1249+
it('should get resource list from wrapped response via string', function()
1250+
{
1251+
var Person = $resource('/Person/:id', {id: '@id'}, {
1252+
query: {
1253+
method: 'GET',
1254+
isArray: true,
1255+
resourceData: 'items'
1256+
}
1257+
});
1258+
1259+
$httpBackend.expect('GET', '/Person').respond(listResponse);
1260+
1261+
var people = Person.query();
1262+
$httpBackend.flush();
1263+
1264+
var p = people[0];
1265+
1266+
expect(p.id).toEqual(123);
1267+
expect(p.$save).toBeDefined();
1268+
});
1269+
1270+
it('should get resource list from wrapped response via function', function()
1271+
{
1272+
var Person = $resource('/Person/:id', {id: '@id'}, {
1273+
query: {
1274+
method: 'GET',
1275+
isArray: true,
1276+
resourceData: function(response)
1277+
{
1278+
return response.items;
1279+
}
1280+
}
1281+
});
1282+
1283+
$httpBackend.expect('GET', '/Person').respond(listResponse);
1284+
1285+
var people = Person.query();
1286+
$httpBackend.flush();
1287+
1288+
var p = people[0];
1289+
1290+
expect(p.id).toEqual(123);
1291+
expect(p.$save).toBeDefined();
1292+
});
1293+
});
12031294
});
12041295

12051296
describe('resource', function() {
@@ -1251,5 +1342,73 @@ describe('resource', function() {
12511342
)
12521343
});
12531344

1345+
it('should fail if responseData is an object', function() {
1346+
var successSpy = jasmine.createSpy('successSpy');
1347+
var failureSpy = jasmine.createSpy('failureSpy');
1348+
1349+
$httpBackend.expect('GET', '/Person/123').respond({meta: {metaField: 'value'}, data: {id: 123}});
1350+
1351+
var Person = $resource('/Person/:id', {id: '@id'}, {
1352+
get: {
1353+
method: 'GET',
1354+
resourceData: {}
1355+
}
1356+
});
1357+
1358+
Person.get({id: 123}).$promise.then(successSpy, function(e) { failureSpy(e.message); });
1359+
$httpBackend.flush();
1360+
1361+
expect(successSpy).not.toHaveBeenCalled();
1362+
expect(failureSpy).toHaveBeenCalled();
1363+
expect(failureSpy.mostRecentCall.args[0]).toMatch(
1364+
/^\[\$resource:badresourceloc\] Error in resource data configuration\. Expected string or function, got object/
1365+
);
1366+
});
1367+
1368+
it('should fail if responseData is an array', function() {
1369+
var successSpy = jasmine.createSpy('successSpy');
1370+
var failureSpy = jasmine.createSpy('failureSpy');
1371+
1372+
$httpBackend.expect('GET', '/Person/123').respond({meta: {metaField: 'value'}, data: {id: 123}});
1373+
1374+
var Person = $resource('/Person/:id', {id: '@id'}, {
1375+
get: {
1376+
method: 'GET',
1377+
resourceData: []
1378+
}
1379+
});
1380+
1381+
Person.get({id: 123}).$promise.then(successSpy, function(e) { failureSpy(e.message); });
1382+
$httpBackend.flush();
1383+
1384+
expect(successSpy).not.toHaveBeenCalled();
1385+
expect(failureSpy).toHaveBeenCalled();
1386+
expect(failureSpy.mostRecentCall.args[0]).toMatch(
1387+
/^\[\$resource:badresourceloc\] Error in resource data configuration\. Expected string or function, got object/
1388+
);
1389+
});
1390+
1391+
it('should fail if responseData is a number', function() {
1392+
var successSpy = jasmine.createSpy('successSpy');
1393+
var failureSpy = jasmine.createSpy('failureSpy');
1394+
1395+
$httpBackend.expect('GET', '/Person/123').respond({meta: {metaField: 'value'}, data: {id: 123}});
1396+
1397+
var Person = $resource('/Person/:id', {id: '@id'}, {
1398+
get: {
1399+
method: 'GET',
1400+
resourceData: 123
1401+
}
1402+
});
1403+
1404+
Person.get({id: 123}).$promise.then(successSpy, function(e) { failureSpy(e.message); });
1405+
$httpBackend.flush();
1406+
1407+
expect(successSpy).not.toHaveBeenCalled();
1408+
expect(failureSpy).toHaveBeenCalled();
1409+
expect(failureSpy.mostRecentCall.args[0]).toMatch(
1410+
/^\[\$resource:badresourceloc\] Error in resource data configuration\. Expected string or function, got number/
1411+
);
1412+
});
12541413

12551414
});

0 commit comments

Comments
 (0)