Skip to content

Commit 87a9652

Browse files
committed
Closes #14.
Closes #59. Closes #61. Closes #62.
1 parent 0b4e109 commit 87a9652

29 files changed

+1347
-329
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
##### 0.9.0 - xx May 2014
22

3+
###### Breading API changes
4+
- #61 - Make custom serializers/deserializers more valuable
5+
- #59, #62 - Make queryTransform() consistent with the rest of the API
6+
37
###### Backwards compatible API changes
48
- #30, #48 - DSCacheFactory integration
59
- #49 - DS.bindOne($scope, prop, resourceName, id)

dist/angular-data.js

+490-155
Large diffs are not rendered by default.

dist/angular-data.min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

guide/angular-data/resource/resource.doc

+9
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ The following asynchronous operations support a model lifecycle:
105105
- `destroy` - Implementation provided by adapter
106106
- `afterDestroy` - Default: `noop`
107107

108+
### Additional hooks
109+
110+
- `serialize` - Default: `noop` - Called before data is sent through adapters.
111+
- `deserialize` - Default: `noop` - Called after data is returned from adapters.
112+
- `beforeInject` - Default: `noop` - Called before data is injected into the data store.
113+
- `afterInject` - Default: `noop` - Called after data is injected into the data store.
114+
115+
See the [DSProvider.defaults API](/documentation/api/angular-data/DSProvider.properties:defaults) for detailed information.
116+
108117
### Define lifecycle hooks
109118
All lifecycle hooks will be executed according to the following signature:
110119
```js

karma.start.js

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Setup global test variables
2-
var $rootScope, $q, $log, DSProvider, DSHttpAdapterProvider, DS, app, $httpBackend, p1, p2, p3, p4, p5;
2+
var $rootScope, $q, $log, DSProvider, DSHttpAdapterProvider, DS, DSHttpAdapter, app, $httpBackend, p1, p2, p3, p4, p5;
33

44
var lifecycle = {};
55

@@ -73,9 +73,22 @@ beforeEach(function (done) {
7373
lifecycle.afterInject = function () {
7474
lifecycle.afterInject.callCount += 1;
7575
};
76+
lifecycle.serialize = function (resourceName, data) {
77+
lifecycle.serialize.callCount += 1;
78+
return data;
79+
};
80+
lifecycle.deserialize = function (resourceName, data) {
81+
lifecycle.deserialize.callCount += 1;
82+
return data.data;
83+
};
84+
lifecycle.queryTransform = function (resourceName, query) {
85+
lifecycle.queryTransform.callCount += 1;
86+
return query;
87+
};
7688
module('app', function (_DSProvider_, _DSHttpAdapterProvider_) {
77-
DSProvider = _DSProvider_;
7889
DSHttpAdapterProvider = _DSHttpAdapterProvider_;
90+
DSHttpAdapterProvider.defaults.queryTransform = lifecycle.queryTransform;
91+
DSProvider = _DSProvider_;
7992
DSProvider.defaults.baseUrl = 'http://test.angular-cache.com';
8093
DSProvider.defaults.beforeValidate = lifecycle.beforeValidate;
8194
DSProvider.defaults.validate = lifecycle.validate;
@@ -88,12 +101,15 @@ beforeEach(function (done) {
88101
DSProvider.defaults.afterDestroy = lifecycle.afterDestroy;
89102
DSProvider.defaults.beforeInject = lifecycle.beforeInject;
90103
DSProvider.defaults.afterInject = lifecycle.afterInject;
104+
DSProvider.defaults.serialize = lifecycle.serialize;
105+
DSProvider.defaults.deserialize = lifecycle.deserialize;
91106
});
92-
inject(function (_$rootScope_, _$q_, _$httpBackend_, _DS_, _$log_) {
107+
inject(function (_$rootScope_, _$q_, _$httpBackend_, _DS_, _$log_, _DSHttpAdapter_) {
93108
// Setup global mocks
94109
$q = _$q_;
95110
$rootScope = _$rootScope_;
96111
DS = _DS_;
112+
DSHttpAdapter = _DSHttpAdapter_;
97113
$httpBackend = _$httpBackend_;
98114
DS.defineResource({
99115
name: 'post',
@@ -112,6 +128,9 @@ beforeEach(function (done) {
112128
lifecycle.afterDestroy.callCount = 0;
113129
lifecycle.beforeInject.callCount = 0;
114130
lifecycle.afterInject.callCount = 0;
131+
lifecycle.serialize.callCount = 0;
132+
lifecycle.deserialize.callCount = 0;
133+
lifecycle.queryTransform.callCount = 0;
115134

116135
p1 = { author: 'John', age: 30, id: 5 };
117136
p2 = { author: 'Sally', age: 31, id: 6 };

src/adapters/http.js

+34-80
Original file line numberDiff line numberDiff line change
@@ -14,68 +14,21 @@ function DSHttpAdapterProvider() {
1414
*
1515
* Properties:
1616
*
17-
* - `{function}` - `serialize` - See [the guide](/documentation/guide/adapters/index). Default: No-op.
18-
* - `{function}` - `deserialize` - See [the guide](/documentation/guide/adapters/index). Default: No-op.
1917
* - `{function}` - `queryTransform` - See [the guide](/documentation/guide/adapters/index). Default: No-op.
2018
*/
2119
var defaults = this.defaults = {
22-
/**
23-
* @doc property
24-
* @id DSHttpAdapterProvider.properties:defaults.serialize
25-
* @name defaults.serialize
26-
* @description
27-
* Your server might expect a custom request object rather than the plain POJO payload. Use `serialize` to
28-
* create your custom request object.
29-
*
30-
* ## Example:
31-
* ```js
32-
* DSHttpAdapterProvider.defaults.serialize = function (data) {
33-
* return {
34-
* payload: data
35-
* };
36-
* };
37-
* ```
38-
*
39-
* @param {object} data Data to be sent to the server.
40-
* @returns {*} Returns `data` as-is.
41-
*/
42-
serialize: function (data) {
43-
return data;
44-
},
45-
46-
/**
47-
* @doc property
48-
* @id DSHttpAdapterProvider.properties:defaults.deserialize
49-
* @name defaults.deserialize
50-
* @description
51-
* Your server might return a custom response object instead of the plain POJO payload. Use `deserialize` to
52-
* pull the payload out of your response object so angular-data can use it.
53-
*
54-
* ## Example:
55-
* ```js
56-
* DSHttpAdapterProvider.defaults.deserialize = function (data) {
57-
* return data ? data.payload : data;
58-
* };
59-
* ```
60-
*
61-
* @param {object} data Response object from `$http()`.
62-
* @returns {*} Returns `data.data`.
63-
*/
64-
deserialize: function (data) {
65-
return data.data;
66-
},
67-
6820
/**
6921
* @doc property
7022
* @id DSHttpAdapterProvider.properties:defaults.queryTransform
7123
* @name defaults.queryTransform
7224
* @description
7325
* Transform the angular-data query to something your server understands. You might just do this on the server instead.
7426
*
27+
* @param {string} resourceName The name of the resource.
7528
* @param {object} query Response object from `$http()`.
7629
* @returns {*} Returns `query` as-is.
7730
*/
78-
queryTransform: function (query) {
31+
queryTransform: function (resourceName, query) {
7932
return query;
8033
}
8134
};
@@ -361,7 +314,7 @@ function DSHttpAdapterProvider() {
361314

362315
return $http(config).then(function (data) {
363316
$log.debug(data.config.method + ' request:' + data.config.url + ' Time taken: ' + (new Date().getTime() - start) + 'ms', arguments);
364-
return defaults.deserialize(data);
317+
return data;
365318
});
366319
}
367320

@@ -399,75 +352,76 @@ function DSHttpAdapterProvider() {
399352
}));
400353
}
401354

402-
function find(resourceConfig, id, options) {
355+
function create(resourceConfig, attrs, options) {
403356
options = options || {};
404-
return this.GET(
405-
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint, id),
357+
return this.POST(
358+
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint),
359+
attrs,
406360
options
407361
);
408362
}
409363

410-
function findAll(resourceConfig, params, options) {
364+
function destroy(resourceConfig, id, options) {
411365
options = options || {};
412-
options.params = options.params || {};
413-
if (options.params.query) {
414-
options.params.query = defaults.queryTransform(options.params.query);
415-
}
416-
DSUtils.deepMixIn(options, params);
417-
return this.GET(
418-
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint),
366+
return this.DEL(
367+
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint, id),
419368
options
420369
);
421370
}
422371

423-
function create(resourceConfig, attrs, options) {
372+
function destroyAll(resourceConfig, params, options) {
424373
options = options || {};
425-
return this.POST(
374+
options.params = options.params || {};
375+
if (params) {
376+
params.query = params.query ? defaults.queryTransform(resourceConfig.name, params.query) : params.query;
377+
DSUtils.deepMixIn(options.params, params);
378+
}
379+
return this.DEL(
426380
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint),
427-
defaults.serialize(attrs),
428381
options
429382
);
430383
}
431384

432-
function update(resourceConfig, id, attrs, options) {
433-
return this.PUT(
385+
function find(resourceConfig, id, options) {
386+
options = options || {};
387+
return this.GET(
434388
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint, id),
435-
defaults.serialize(attrs),
436389
options
437390
);
438391
}
439392

440-
function updateAll(resourceConfig, attrs, params, options) {
393+
function findAll(resourceConfig, params, options) {
441394
options = options || {};
442395
options.params = options.params || {};
443-
if (options.params.query) {
444-
options.params.query = defaults.queryTransform(options.params.query);
396+
if (params) {
397+
params.query = params.query ? defaults.queryTransform(resourceConfig.name, params.query) : params.query;
398+
DSUtils.deepMixIn(options.params, params);
445399
}
446-
DSUtils.deepMixIn(options, params);
447-
return this.PUT(
400+
return this.GET(
448401
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint),
449-
defaults.serialize(attrs),
450402
options
451403
);
452404
}
453405

454-
function destroy(resourceConfig, id, options) {
406+
function update(resourceConfig, id, attrs, options) {
455407
options = options || {};
456-
return this.DEL(
408+
return this.PUT(
457409
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint, id),
410+
attrs,
458411
options
459412
);
460413
}
461414

462-
function destroyAll(resourceConfig, params, options) {
415+
function updateAll(resourceConfig, attrs, params, options) {
463416
options = options || {};
464417
options.params = options.params || {};
465-
if (options.params.query) {
466-
options.params.query = defaults.queryTransform(options.params.query);
418+
if (params) {
419+
params.query = params.query ? defaults.queryTransform(resourceConfig.name, params.query) : params.query;
420+
DSUtils.deepMixIn(options.params, params);
467421
}
468-
DSUtils.deepMixIn(options, params);
469-
return this.DEL(
422+
return this.PUT(
470423
DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint),
424+
attrs,
471425
options
472426
);
473427
}

src/datastore/async_methods/create.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ function create(resourceName, attrs, options) {
7272
return _this.$q.promisify(definition.beforeCreate)(resourceName, attrs);
7373
})
7474
.then(function (attrs) {
75-
return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, attrs, options);
75+
return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, definition.serialize(resourceName, attrs), options);
7676
})
77-
.then(function (data) {
78-
return _this.$q.promisify(definition.afterCreate)(resourceName, data);
77+
.then(function (res) {
78+
return _this.$q.promisify(definition.afterCreate)(resourceName, definition.deserialize(resourceName, res));
7979
})
8080
.then(function (data) {
81-
var created = _this.inject(definition.name, data),
82-
id = created[definition.idAttribute];
81+
var created = _this.inject(definition.name, data);
82+
var id = created[definition.idAttribute];
8383
resource.previousAttributes[id] = _this.utils.deepMixIn({}, created);
8484
resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]);
8585
return _this.get(definition.name, id);

src/datastore/async_methods/destroyAll.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,16 @@ function destroyAll(resourceName, params, options) {
6767
if (!this.definitions[resourceName]) {
6868
deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'));
6969
} else if (!this.utils.isObject(params)) {
70-
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } }));
70+
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!'));
7171
} else if (!this.utils.isObject(options)) {
72-
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }));
72+
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!'));
7373
} else {
7474
try {
7575
var definition = this.definitions[resourceName];
7676

7777
promise = promise
7878
.then(function () {
79-
return _this.adapters[options.adapter || definition.defaultAdapter].destroyAll(definition, { params: params }, options);
79+
return _this.adapters[options.adapter || definition.defaultAdapter].destroyAll(definition, params, options);
8080
})
8181
.then(function () {
8282
return _this.ejectAll(resourceName, params);

src/datastore/async_methods/find.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ function find(resourceName, id, options) {
7474
if (!(id in resource.completedQueries)) {
7575
if (!(id in resource.pendingQueries)) {
7676
promise = resource.pendingQueries[id] = _this.adapters[options.adapter || definition.defaultAdapter].find(definition, id, options)
77-
.then(function (data) {
77+
.then(function (res) {
78+
var data = definition.deserialize(resourceName, res);
7879
if (options.cacheResponse) {
7980
// Query is no longer pending
8081
delete resource.pendingQueries[id];

src/datastore/async_methods/findAll.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ function _findAll(utils, resourceName, params, options) {
3333
if (!(queryHash in resource.pendingQueries)) {
3434

3535
// This particular query has never even been made
36-
resource.pendingQueries[queryHash] = _this.adapters[options.adapter || definition.defaultAdapter].findAll(definition, { params: params }, options)
37-
.then(function (data) {
36+
resource.pendingQueries[queryHash] = _this.adapters[options.adapter || definition.defaultAdapter].findAll(definition, params, options)
37+
.then(function (res) {
38+
var data = definition.deserialize(resourceName, res);
3839
if (options.cacheResponse) {
3940
try {
4041
return processResults.apply(_this, [utils, data, resourceName, queryHash]);
@@ -132,9 +133,9 @@ function findAll(resourceName, params, options) {
132133
if (!this.definitions[resourceName]) {
133134
deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'));
134135
} else if (!this.utils.isObject(params)) {
135-
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } }));
136+
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!'));
136137
} else if (!this.utils.isObject(options)) {
137-
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }));
138+
deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!'));
138139
} else {
139140
if (!('cacheResponse' in options)) {
140141
options.cacheResponse = true;

src/datastore/async_methods/save.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ function save(resourceName, id, options) {
9696
attrs = changes;
9797
}
9898
}
99-
return _this.adapters[options.adapter || definition.defaultAdapter].update(definition, id, attrs, options);
99+
return _this.adapters[options.adapter || definition.defaultAdapter].update(definition, id, definition.serialize(resourceName, attrs), options);
100100
})
101-
.then(function (data) {
102-
return _this.$q.promisify(definition.afterUpdate)(resourceName, data);
101+
.then(function (res) {
102+
return _this.$q.promisify(definition.afterUpdate)(resourceName, definition.deserialize(resourceName, res));
103103
})
104104
.then(function (data) {
105105
_this.inject(definition.name, data, options);

src/datastore/async_methods/update.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ var errorPrefix = 'DS.update(resourceName, id, attrs[, options]): ';
4545
* - `{RuntimeError}`
4646
* - `{UnhandledError}`
4747
*/
48-
function save(resourceName, id, attrs, options) {
48+
function update(resourceName, id, attrs, options) {
4949
var deferred = this.$q.defer(),
5050
promise = deferred.promise;
5151

@@ -84,17 +84,18 @@ function save(resourceName, id, attrs, options) {
8484
return _this.$q.promisify(definition.beforeUpdate)(resourceName, attrs);
8585
})
8686
.then(function (attrs) {
87-
return _this.adapters[options.adapter || definition.defaultAdapter].update(definition, id, attrs, options);
87+
return _this.adapters[options.adapter || definition.defaultAdapter].update(definition, id, definition.serialize(resourceName, attrs), options);
8888
})
89-
.then(function (data) {
90-
return _this.$q.promisify(definition.afterUpdate)(resourceName, data);
89+
.then(function (res) {
90+
return _this.$q.promisify(definition.afterUpdate)(resourceName, definition.deserialize(resourceName, res));
9191
})
9292
.then(function (data) {
9393
if (options.cacheResponse) {
94-
var item = _this.inject(definition.name, data, options);
95-
resource.previousAttributes[id] = _this.utils.deepMixIn({}, data);
94+
var updated = _this.inject(definition.name, data, options);
95+
var id = updated[definition.idAttribute];
96+
resource.previousAttributes[id] = _this.utils.deepMixIn({}, updated);
9697
resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]);
97-
return item;
98+
return _this.get(definition.name, id);
9899
} else {
99100
return data;
100101
}
@@ -105,4 +106,4 @@ function save(resourceName, id, attrs, options) {
105106
return promise;
106107
}
107108

108-
module.exports = save;
109+
module.exports = update;

0 commit comments

Comments
 (0)