Skip to content

Commit e6fe4ce

Browse files
stephenpluspluscallmehiphop
authored andcommitted
datastore: accept entity object to mutation methods (#1804)
1 parent ffcc8fd commit e6fe4ce

4 files changed

Lines changed: 221 additions & 45 deletions

File tree

handwritten/datastore/src/index.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,7 @@ var Transaction = require('./transaction.js');
281281
*
282282
* entity.symbol = 'GOOG';
283283
*
284-
* transaction.save({
285-
* key: key,
286-
* data: entity
287-
* });
284+
* transaction.save(entity);
288285
*
289286
* transaction.commit(function(err) {
290287
* if (!err) {

handwritten/datastore/src/request.js

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,42 @@ var CONSISTENCY_PROTO_CODE = {
6767
*/
6868
function DatastoreRequest() {}
6969

70+
/**
71+
* Format a user's input to mutation methods. This will create a deep clone of
72+
* the input, as well as allow users to pass an object in the format of an
73+
* entity.
74+
*
75+
* Both of the following formats can be supplied supported:
76+
*
77+
* datastore.save({
78+
* key: datastore.key('Kind'),
79+
* data: { foo: 'bar' }
80+
* }, function(err) {})
81+
*
82+
* var entity = { foo: 'bar' }
83+
* entity[datastore.KEY] = datastore.key('Kind')
84+
* datastore.save(entity, function(err) {})
85+
*
86+
* @private
87+
*
88+
* @resource [#1803]{@link https://github.com/GoogleCloudPlatform/google-cloud-node/issues/1803}
89+
*
90+
* @param {object} obj - The user's input object.
91+
*/
92+
DatastoreRequest.prepareEntityObject_ = function(obj) {
93+
var entityObject = extend(true, {}, obj);
94+
95+
// Entity objects are also supported.
96+
if (obj[entity.KEY_SYMBOL]) {
97+
return {
98+
key: obj[entity.KEY_SYMBOL],
99+
data: entityObject
100+
};
101+
}
102+
103+
return entityObject;
104+
};
105+
70106
/**
71107
* Generate IDs without creating entities.
72108
*
@@ -426,7 +462,10 @@ DatastoreRequest.prototype.get = function(keys, options, callback) {
426462
* Maps to {module:datastore#save}, forcing the method to be `insert`.
427463
*/
428464
DatastoreRequest.prototype.insert = function(entities, callback) {
429-
entities = arrify(entities).map(propAssign('method', 'insert'));
465+
entities = arrify(entities)
466+
.map(DatastoreRequest.prepareEntityObject_)
467+
.map(propAssign('method', 'insert'));
468+
430469
this.save(entities, callback);
431470
};
432471

@@ -863,53 +902,53 @@ DatastoreRequest.prototype.save = function(entities, callback) {
863902

864903
// Iterate over the entity objects, build a proto from all keys and values,
865904
// then place in the correct mutation array (insert, update, etc).
866-
entities.forEach(function(entityObject, index) {
867-
entityObject = extend(true, {}, entityObject);
868-
869-
var mutation = {};
870-
var entityProto = {};
871-
var method = 'upsert';
872-
873-
if (entityObject.method) {
874-
if (methods[entityObject.method]) {
875-
method = entityObject.method;
876-
} else {
877-
throw new Error('Method ' + entityObject.method + ' not recognized.');
905+
entities
906+
.map(DatastoreRequest.prepareEntityObject_)
907+
.forEach(function(entityObject, index) {
908+
var mutation = {};
909+
var entityProto = {};
910+
var method = 'upsert';
911+
912+
if (entityObject.method) {
913+
if (methods[entityObject.method]) {
914+
method = entityObject.method;
915+
} else {
916+
throw new Error('Method ' + entityObject.method + ' not recognized.');
917+
}
878918
}
879-
}
880919

881-
if (!entity.isKeyComplete(entityObject.key)) {
882-
insertIndexes[index] = true;
883-
}
920+
if (!entity.isKeyComplete(entityObject.key)) {
921+
insertIndexes[index] = true;
922+
}
884923

885-
if (is.array(entityObject.data)) {
886-
entityProto.properties = entityObject.data.reduce(function(acc, data) {
887-
var value = entity.encodeValue(data.value);
924+
if (is.array(entityObject.data)) {
925+
entityProto.properties = entityObject.data.reduce(function(acc, data) {
926+
var value = entity.encodeValue(data.value);
888927

889-
if (is.boolean(data.excludeFromIndexes)) {
890-
var excluded = data.excludeFromIndexes;
891-
var values = value.arrayValue && value.arrayValue.values;
928+
if (is.boolean(data.excludeFromIndexes)) {
929+
var excluded = data.excludeFromIndexes;
930+
var values = value.arrayValue && value.arrayValue.values;
892931

893-
if (values) {
894-
values = values.map(propAssign('excludeFromIndexes', excluded));
895-
} else {
896-
value.excludeFromIndexes = data.excludeFromIndexes;
932+
if (values) {
933+
values = values.map(propAssign('excludeFromIndexes', excluded));
934+
} else {
935+
value.excludeFromIndexes = data.excludeFromIndexes;
936+
}
897937
}
898-
}
899938

900-
acc[data.name] = value;
939+
acc[data.name] = value;
901940

902-
return acc;
903-
}, {});
904-
} else {
905-
entityProto = entity.entityToEntityProto(entityObject.data);
906-
}
941+
return acc;
942+
}, {});
943+
} else {
944+
entityProto = entity.entityToEntityProto(entityObject.data);
945+
}
907946

908-
entityProto.key = entity.keyToKeyProto(entityObject.key);
947+
entityProto.key = entity.keyToKeyProto(entityObject.key);
909948

910-
mutation[method] = entityProto;
911-
mutations.push(mutation);
912-
});
949+
mutation[method] = entityProto;
950+
mutations.push(mutation);
951+
});
913952

914953
var protoOpts = {
915954
service: 'Datastore',
@@ -953,15 +992,21 @@ DatastoreRequest.prototype.save = function(entities, callback) {
953992
* Maps to {module:datastore#save}, forcing the method to be `update`.
954993
*/
955994
DatastoreRequest.prototype.update = function(entities, callback) {
956-
entities = arrify(entities).map(propAssign('method', 'update'));
995+
entities = arrify(entities)
996+
.map(DatastoreRequest.prepareEntityObject_)
997+
.map(propAssign('method', 'update'));
998+
957999
this.save(entities, callback);
9581000
};
9591001

9601002
/**
9611003
* Maps to {module:datastore#save}, forcing the method to be `upsert`.
9621004
*/
9631005
DatastoreRequest.prototype.upsert = function(entities, callback) {
964-
entities = arrify(entities).map(propAssign('method', 'upsert'));
1006+
entities = arrify(entities)
1007+
.map(DatastoreRequest.prepareEntityObject_)
1008+
.map(propAssign('method', 'upsert'));
1009+
9651010
this.save(entities, callback);
9661011
};
9671012

handwritten/datastore/system-test/datastore.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ var env = require('../../../system-test/env.js');
2626
describe('Datastore', function() {
2727
var testKinds = [];
2828
var datastore = new Datastore(env);
29-
3029
// Override the Key method so we can track what keys are created during the
3130
// tests. They are then deleted in the `after` hook.
3231
var key = datastore.key;
@@ -156,6 +155,32 @@ describe('Datastore', function() {
156155
});
157156
});
158157

158+
it('should save/get/update', function(done) {
159+
var postKey = datastore.key('Post');
160+
161+
datastore.save({ key: postKey, data: post }, function(err) {
162+
assert.ifError(err);
163+
164+
datastore.get(postKey, function(err, entity) {
165+
assert.ifError(err);
166+
167+
assert.strictEqual(entity.title, post.title);
168+
169+
entity.title = 'Updated';
170+
171+
datastore.save(entity, function(err) {
172+
assert.ifError(err);
173+
174+
datastore.get(postKey, function(err, entity) {
175+
assert.ifError(err);
176+
assert.strictEqual(entity.title, 'Updated');
177+
datastore.delete(postKey, done);
178+
});
179+
});
180+
});
181+
});
182+
});
183+
159184
it('should fail explicitly set second insert on save', function(done) {
160185
var postKey = datastore.key('Post');
161186

handwritten/datastore/test/request.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ describe('Request', function() {
8888
'./entity.js': entity,
8989
'./query.js': FakeQuery
9090
});
91+
92+
override('Request', Request);
9193
});
9294

9395
after(function() {
@@ -110,6 +112,42 @@ describe('Request', function() {
110112
});
111113
});
112114

115+
describe('prepareEntityObject_', function() {
116+
it('should clone an object', function() {
117+
var obj = {
118+
data: {
119+
nested: {
120+
obj: true
121+
}
122+
},
123+
method: 'insert'
124+
};
125+
var expectedPreparedEntityObject = extend(true, {}, obj);
126+
127+
var preparedEntityObject = Request.prepareEntityObject_(obj);
128+
129+
assert.notStrictEqual(preparedEntityObject, obj);
130+
131+
assert.notStrictEqual(
132+
preparedEntityObject.data.nested,
133+
obj.data.nested
134+
);
135+
136+
assert.deepEqual(preparedEntityObject, expectedPreparedEntityObject);
137+
});
138+
139+
it('should format an entity', function() {
140+
var key = {};
141+
var entityObject = { data: true };
142+
entityObject[entity.KEY_SYMBOL] = key;
143+
144+
var preparedEntityObject = Request.prepareEntityObject_(entityObject);
145+
146+
assert.strictEqual(preparedEntityObject.key, key);
147+
assert.deepEqual(preparedEntityObject.data, entityObject);
148+
});
149+
});
150+
113151
describe('allocateIds', function() {
114152
var incompleteKey;
115153
var apiResponse = {
@@ -597,6 +635,26 @@ describe('Request', function() {
597635
});
598636

599637
describe('insert', function() {
638+
it('should prepare entity objects', function(done) {
639+
var entityObject = {};
640+
var preparedEntityObject = { prepared: true };
641+
var expectedEntityObject = extend({}, preparedEntityObject, {
642+
method: 'insert'
643+
});
644+
645+
overrides.Request.prepareEntityObject_ = function(obj) {
646+
assert.strictEqual(obj, entityObject);
647+
return preparedEntityObject;
648+
};
649+
650+
request.save = function(entities) {
651+
assert.deepEqual(entities[0], expectedEntityObject);
652+
done();
653+
};
654+
655+
request.insert(entityObject, assert.ifError);
656+
});
657+
600658
it('should pass the correct arguments to save', function(done) {
601659
request.save = function(entities, callback) {
602660
assert.deepEqual(entities, [{
@@ -1134,6 +1192,17 @@ describe('Request', function() {
11341192
], done);
11351193
});
11361194

1195+
it('should prepare entity objects', function(done) {
1196+
var entityObject = {};
1197+
1198+
overrides.Request.prepareEntityObject_ = function(obj) {
1199+
assert.strictEqual(obj, entityObject);
1200+
done();
1201+
};
1202+
1203+
request.save(entityObject, assert.ifError);
1204+
});
1205+
11371206
it('should save with specific method', function(done) {
11381207
request.request_ = function(protoOpts, reqOpts, callback) {
11391208
assert.equal(reqOpts.mutations.length, 3);
@@ -1324,6 +1393,26 @@ describe('Request', function() {
13241393
});
13251394

13261395
describe('update', function() {
1396+
it('should prepare entity objects', function(done) {
1397+
var entityObject = {};
1398+
var preparedEntityObject = { prepared: true };
1399+
var expectedEntityObject = extend({}, preparedEntityObject, {
1400+
method: 'update'
1401+
});
1402+
1403+
overrides.Request.prepareEntityObject_ = function(obj) {
1404+
assert.strictEqual(obj, entityObject);
1405+
return preparedEntityObject;
1406+
};
1407+
1408+
request.save = function(entities) {
1409+
assert.deepEqual(entities[0], expectedEntityObject);
1410+
done();
1411+
};
1412+
1413+
request.update(entityObject, assert.ifError);
1414+
});
1415+
13271416
it('should pass the correct arguments to save', function(done) {
13281417
request.save = function(entities, callback) {
13291418
assert.deepEqual(entities, [{
@@ -1345,6 +1434,26 @@ describe('Request', function() {
13451434
});
13461435

13471436
describe('upsert', function() {
1437+
it('should prepare entity objects', function(done) {
1438+
var entityObject = {};
1439+
var preparedEntityObject = { prepared: true };
1440+
var expectedEntityObject = extend({}, preparedEntityObject, {
1441+
method: 'upsert'
1442+
});
1443+
1444+
overrides.Request.prepareEntityObject_ = function(obj) {
1445+
assert.strictEqual(obj, entityObject);
1446+
return preparedEntityObject;
1447+
};
1448+
1449+
request.save = function(entities) {
1450+
assert.deepEqual(entities[0], expectedEntityObject);
1451+
done();
1452+
};
1453+
1454+
request.upsert(entityObject, assert.ifError);
1455+
});
1456+
13481457
it('should pass the correct arguments to save', function(done) {
13491458
request.save = function(entities, callback) {
13501459
assert.deepEqual(entities, [{

0 commit comments

Comments
 (0)