Skip to content

Commit 1606ff6

Browse files
committed
Closes #103.
1 parent 87ae8b0 commit 1606ff6

File tree

12 files changed

+136
-73
lines changed

12 files changed

+136
-73
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
##### 0.10.4 - xx July 2014
2+
3+
###### Backwards compatible API changes
4+
- #103 - Add `upsert` option to `DS.create`
5+
16
##### 0.10.3 - 24 July 2014
27

38
###### Backwards compatible bug fixes

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
__Data store for Angular.js.__
44

55
__Latest Release:__ [0.10.3](http://angular-data.pseudobry.com/)
6-
__master:__ [0.10.3](http://angular-data-next.pseudobry.com/)
6+
__master:__ [0.10.4](http://angular-data-next.pseudobry.com/)
77

88
Angular-data is approaching 1.0.0 Beta. The API is stabilizing and angular-data is well tested.
99

bower.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"author": "Jason Dobry",
33
"name": "angular-data",
44
"description": "Data store for Angular.js.",
5-
"version": "0.10.3",
5+
"version": "0.10.4",
66
"homepage": "http://angular-data.pseudobry.com/",
77
"repository": {
88
"type": "git",

dist/angular-data.js

+45-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @author Jason Dobry <[email protected]>
33
* @file angular-data.js
4-
* @version 0.10.3 - Homepage <http://angular-data.pseudobry.com/>
4+
* @version 0.10.4 - Homepage <http://angular-data.pseudobry.com/>
55
* @copyright (c) 2014 Jason Dobry <https://github.com/jmdobry/>
66
* @license MIT <https://github.com/jmdobry/angular-data/blob/master/LICENSE>
77
*
@@ -2064,6 +2064,7 @@ var errorPrefix = 'DS.create(resourceName, attrs[, options]): ';
20642064
* @param {object=} options Configuration options. Properties:
20652065
*
20662066
* - `{boolean=}` - `cacheResponse` - Inject the data returned by the server into the data store. Default: `true`.
2067+
* - `{boolean=}` - `upsert` - If `attrs` already contains a primary key, then attempt to call `DS.update` instead. Default: `true`.
20672068
*
20682069
* @returns {Promise} Promise produced by the `$q` service.
20692070
*
@@ -2096,37 +2097,45 @@ function create(resourceName, attrs, options) {
20962097
options.cacheResponse = true;
20972098
}
20982099

2099-
promise = promise
2100-
.then(function (attrs) {
2101-
return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs);
2102-
})
2103-
.then(function (attrs) {
2104-
return _this.$q.promisify(definition.validate)(resourceName, attrs);
2105-
})
2106-
.then(function (attrs) {
2107-
return _this.$q.promisify(definition.afterValidate)(resourceName, attrs);
2108-
})
2109-
.then(function (attrs) {
2110-
return _this.$q.promisify(definition.beforeCreate)(resourceName, attrs);
2111-
})
2112-
.then(function (attrs) {
2113-
return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, definition.serialize(resourceName, attrs), options);
2114-
})
2115-
.then(function (res) {
2116-
return _this.$q.promisify(definition.afterCreate)(resourceName, definition.deserialize(resourceName, res));
2117-
})
2118-
.then(function (data) {
2119-
if (options.cacheResponse) {
2120-
var created = _this.inject(definition.name, data);
2121-
var id = created[definition.idAttribute];
2122-
resource.completedQueries[id] = new Date().getTime();
2123-
resource.previousAttributes[id] = _this.utils.deepMixIn({}, created);
2124-
resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]);
2125-
return _this.get(definition.name, id);
2126-
} else {
2127-
return data;
2128-
}
2129-
});
2100+
if (!('upsert' in options)) {
2101+
options.upsert = true;
2102+
}
2103+
2104+
if (options.upsert && attrs[definition.idAttribute]) {
2105+
promise = this.update(resourceName, attrs[definition.idAttribute], attrs, options);
2106+
} else {
2107+
promise = promise
2108+
.then(function (attrs) {
2109+
return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs);
2110+
})
2111+
.then(function (attrs) {
2112+
return _this.$q.promisify(definition.validate)(resourceName, attrs);
2113+
})
2114+
.then(function (attrs) {
2115+
return _this.$q.promisify(definition.afterValidate)(resourceName, attrs);
2116+
})
2117+
.then(function (attrs) {
2118+
return _this.$q.promisify(definition.beforeCreate)(resourceName, attrs);
2119+
})
2120+
.then(function (attrs) {
2121+
return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, definition.serialize(resourceName, attrs), options);
2122+
})
2123+
.then(function (res) {
2124+
return _this.$q.promisify(definition.afterCreate)(resourceName, definition.deserialize(resourceName, res));
2125+
})
2126+
.then(function (data) {
2127+
if (options.cacheResponse) {
2128+
var created = _this.inject(definition.name, data);
2129+
var id = created[definition.idAttribute];
2130+
resource.completedQueries[id] = new Date().getTime();
2131+
resource.previousAttributes[id] = _this.utils.deepMixIn({}, created);
2132+
resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]);
2133+
return _this.get(definition.name, id);
2134+
} else {
2135+
return data;
2136+
}
2137+
});
2138+
}
21302139

21312140
deferred.resolve(attrs);
21322141
} catch (err) {
@@ -2274,6 +2283,7 @@ var errorPrefix = 'DS.destroyAll(resourceName, params[, options]): ';
22742283
* - `{string|array=}` - `orderBy` - OrderBy clause.
22752284
*
22762285
* @param {object=} options Optional configuration. Properties:
2286+
*
22772287
* - `{boolean=}` - `bypassCache` - Bypass the cache. Default: `false`.
22782288
*
22792289
* @returns {Promise} Promise produced by the `$q` service.
@@ -4763,8 +4773,10 @@ var errorPrefix = 'DS.filter(resourceName[, params][, options]): ';
47634773
* - `{string|array=}` - `orderBy` - OrderBy clause.
47644774
*
47654775
* @param {object=} options Optional configuration. Properties:
4776+
*
47664777
* - `{boolean=}` - `loadFromServer` - Send the query to server if it has not been sent yet. Default: `false`.
47674778
* - `{boolean=}` - `allowSimpleWhere` - Treat top-level fields on the `params` argument as simple "where" equality clauses. Default: `true`.
4779+
*
47684780
* @returns {array} The filtered collection of items of the type specified by `resourceName`.
47694781
*/
47704782
function filter(resourceName, params, options) {
@@ -5254,7 +5266,7 @@ function _injectRelations(definition, injected) {
52545266
*
52555267
* @param {string} resourceName The resource type, e.g. 'user', 'comment', etc.
52565268
* @param {object|array} attrs The item or collection of items to inject into the data store.
5257-
* @param {object=} options Optional configuration. Properties:
5269+
* @param {object=} options Optional configuration.
52585270
* @returns {object|array} A reference to the item that was injected into the data store or an array of references to
52595271
* the items that were injected into the data store.
52605272
*/

dist/angular-data.min.js

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

guide/nav.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
<i class="icon-wrench icon-white"></i> API <b class="caret"></b>
7373
</a>
7474
<ul class="dropdown-menu">
75-
<li class="nav-header">Angular-data - 0.10.3</li>
75+
<li class="nav-header">Angular-data - 0.10.4</li>
7676
<li>
7777
<a href="/documentation/api/angular-data/angular-data">Overview</a>
7878
</li>
@@ -83,7 +83,7 @@
8383
<a href="/documentation/api/angular-data/DSHttpAdapter">DSHttpAdapter</a>
8484
</li>
8585
<li class="divider"></li>
86-
<li class="nav-header">Angular-data-mocks - 0.5.3</li>
86+
<li class="nav-header">Angular-data-mocks - 0.5.5</li>
8787
<li>
8888
<a href="/documentation/api/angular-data-mocks/angular-data-mocks">Overview</a>
8989
</li>

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "angular-data",
33
"description": "Data store for Angular.js.",
4-
"version": "0.10.3",
4+
"version": "0.10.4",
55
"homepage": "http://angular-data.pseudobry.com",
66
"repository": {
77
"type": "git",

src/datastore/async_methods/create.js

+40-31
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var errorPrefix = 'DS.create(resourceName, attrs[, options]): ';
3232
* @param {object=} options Configuration options. Properties:
3333
*
3434
* - `{boolean=}` - `cacheResponse` - Inject the data returned by the server into the data store. Default: `true`.
35+
* - `{boolean=}` - `upsert` - If `attrs` already contains a primary key, then attempt to call `DS.update` instead. Default: `true`.
3536
*
3637
* @returns {Promise} Promise produced by the `$q` service.
3738
*
@@ -64,37 +65,45 @@ function create(resourceName, attrs, options) {
6465
options.cacheResponse = true;
6566
}
6667

67-
promise = promise
68-
.then(function (attrs) {
69-
return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs);
70-
})
71-
.then(function (attrs) {
72-
return _this.$q.promisify(definition.validate)(resourceName, attrs);
73-
})
74-
.then(function (attrs) {
75-
return _this.$q.promisify(definition.afterValidate)(resourceName, attrs);
76-
})
77-
.then(function (attrs) {
78-
return _this.$q.promisify(definition.beforeCreate)(resourceName, attrs);
79-
})
80-
.then(function (attrs) {
81-
return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, definition.serialize(resourceName, attrs), options);
82-
})
83-
.then(function (res) {
84-
return _this.$q.promisify(definition.afterCreate)(resourceName, definition.deserialize(resourceName, res));
85-
})
86-
.then(function (data) {
87-
if (options.cacheResponse) {
88-
var created = _this.inject(definition.name, data);
89-
var id = created[definition.idAttribute];
90-
resource.completedQueries[id] = new Date().getTime();
91-
resource.previousAttributes[id] = _this.utils.deepMixIn({}, created);
92-
resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]);
93-
return _this.get(definition.name, id);
94-
} else {
95-
return data;
96-
}
97-
});
68+
if (!('upsert' in options)) {
69+
options.upsert = true;
70+
}
71+
72+
if (options.upsert && attrs[definition.idAttribute]) {
73+
promise = this.update(resourceName, attrs[definition.idAttribute], attrs, options);
74+
} else {
75+
promise = promise
76+
.then(function (attrs) {
77+
return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs);
78+
})
79+
.then(function (attrs) {
80+
return _this.$q.promisify(definition.validate)(resourceName, attrs);
81+
})
82+
.then(function (attrs) {
83+
return _this.$q.promisify(definition.afterValidate)(resourceName, attrs);
84+
})
85+
.then(function (attrs) {
86+
return _this.$q.promisify(definition.beforeCreate)(resourceName, attrs);
87+
})
88+
.then(function (attrs) {
89+
return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, definition.serialize(resourceName, attrs), options);
90+
})
91+
.then(function (res) {
92+
return _this.$q.promisify(definition.afterCreate)(resourceName, definition.deserialize(resourceName, res));
93+
})
94+
.then(function (data) {
95+
if (options.cacheResponse) {
96+
var created = _this.inject(definition.name, data);
97+
var id = created[definition.idAttribute];
98+
resource.completedQueries[id] = new Date().getTime();
99+
resource.previousAttributes[id] = _this.utils.deepMixIn({}, created);
100+
resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]);
101+
return _this.get(definition.name, id);
102+
} else {
103+
return data;
104+
}
105+
});
106+
}
98107

99108
deferred.resolve(attrs);
100109
} catch (err) {

src/datastore/async_methods/destroyAll.js

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ var errorPrefix = 'DS.destroyAll(resourceName, params[, options]): ';
4343
* - `{string|array=}` - `orderBy` - OrderBy clause.
4444
*
4545
* @param {object=} options Optional configuration. Properties:
46+
*
4647
* - `{boolean=}` - `bypassCache` - Bypass the cache. Default: `false`.
4748
*
4849
* @returns {Promise} Promise produced by the `$q` service.

src/datastore/sync_methods/filter.js

+2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ var errorPrefix = 'DS.filter(resourceName[, params][, options]): ';
3333
* - `{string|array=}` - `orderBy` - OrderBy clause.
3434
*
3535
* @param {object=} options Optional configuration. Properties:
36+
*
3637
* - `{boolean=}` - `loadFromServer` - Send the query to server if it has not been sent yet. Default: `false`.
3738
* - `{boolean=}` - `allowSimpleWhere` - Treat top-level fields on the `params` argument as simple "where" equality clauses. Default: `true`.
39+
*
3840
* @returns {array} The filtered collection of items of the type specified by `resourceName`.
3941
*/
4042
function filter(resourceName, params, options) {

src/datastore/sync_methods/inject.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ function _injectRelations(definition, injected) {
168168
*
169169
* @param {string} resourceName The resource type, e.g. 'user', 'comment', etc.
170170
* @param {object|array} attrs The item or collection of items to inject into the data store.
171-
* @param {object=} options Optional configuration. Properties:
171+
* @param {object=} options Optional configuration.
172172
* @returns {object|array} A reference to the item that was injected into the data store or an array of references to
173173
* the items that were injected into the data store.
174174
*/

test/integration/datastore/async_methods/create.test.js

+34
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,40 @@ describe('DS.create(resourceName, attrs[, options])', function () {
6060
assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called');
6161
assert.isUndefined(DS.get('post', 5));
6262
});
63+
it('should work with the upsert option', function () {
64+
$httpBackend.expectPUT('http://test.angular-cache.com/posts/5').respond(200, p1);
65+
66+
DS.create('post', { author: 'John', age: 30, id: 5 }).then(function (post) {
67+
assert.deepEqual(post, p1, 'post 5 should have been created');
68+
}, function (err) {
69+
console.error(err.stack);
70+
fail('should not have rejected');
71+
});
72+
73+
$httpBackend.flush();
74+
75+
$httpBackend.expectPOST('http://test.angular-cache.com/posts').respond(200, p2);
76+
77+
DS.create('post', { author: 'Sue', age: 70, id: 6 }, { upsert: false }).then(function (post) {
78+
assert.deepEqual(post, p2, 'post 6 should have been created');
79+
}, function (err) {
80+
console.error(err.stack);
81+
fail('should not have rejected');
82+
});
83+
84+
$httpBackend.flush();
85+
86+
assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called');
87+
assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called');
88+
assert.equal(lifecycle.beforeCreate.callCount, 1, 'beforeCreate should have been called');
89+
assert.equal(lifecycle.afterCreate.callCount, 1, 'afterCreate should have been called');
90+
assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called twice');
91+
assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called twice');
92+
assert.equal(lifecycle.serialize.callCount, 2, 'serialize should have been called twice');
93+
assert.equal(lifecycle.deserialize.callCount, 2, 'deserialize should have been called twice');
94+
assert.isDefined(DS.get('post', 5));
95+
assert.isDefined(DS.get('post', 6));
96+
});
6397
it('should create an item that includes relations, save them to the server and inject the results', function () {
6498
var payload = {
6599
id: 99,

0 commit comments

Comments
 (0)