Skip to content
Merged
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
57 changes: 32 additions & 25 deletions packages/datastore/src/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ var InvalidKeyError = createErrorClass('InvalidKey', function(opts) {
entity.KEY_SYMBOL = Symbol('KEY');

/**
* Build a Datastore Double object.
* Build a Datastore Double object. For long doubles, a string can be provided.
*
* @constructor
* @param {number} value - The double value.
Expand All @@ -60,16 +60,16 @@ function Double(value) {
entity.Double = Double;

/**
* Build a Datastore Int object.
* Build a Datastore Int object. For long integers, a string can be provided.
*
* @constructor
* @param {number} value - The integer value.
* @param {number|string} value - The integer value.
*
* @example
* var anInt = new Int(7);
*/
function Int(value) {
this.value = value;
this.value = value.toString();
}

entity.Int = Int;
Expand Down Expand Up @@ -116,8 +116,8 @@ function Key(options) {
if (options.path.length % 2 === 0) {
var identifier = options.path.pop();

if (is.number(identifier)) {
this.id = identifier;
if (is.number(identifier) || identifier instanceof entity.Int) {
this.id = identifier.value || identifier;
} else if (is.string(identifier)) {
this.name = identifier;
}
Expand Down Expand Up @@ -467,11 +467,15 @@ function keyFromKeyProto(keyProto) {
}

keyProto.path.forEach(function(path, index) {
var id = path.name || Number(path.id);

keyOptions.path.push(path.kind);

if (id) {
var id = path[path.id_type];

if (path.id_type === 'id') {
id = new entity.Int(id);
}

if (is.defined(id)) {
keyOptions.path.push(id);
} else if (index < keyProto.path.length - 1) {
throw new InvalidKeyError({
Expand Down Expand Up @@ -503,7 +507,7 @@ entity.keyFromKeyProto = keyFromKeyProto;
* // }
*/
function keyToKeyProto(key) {
if (!is.string(key.path[0])) {
if (is.undefined(key.kind)) {
throw new InvalidKeyError({
code: 'MISSING_KIND'
});
Expand All @@ -519,28 +523,31 @@ function keyToKeyProto(key) {
};
}

for (var i = 0; i < key.path.length; i += 2) {
var pathElement = {
kind: key.path[i]
};
var numKeysWalked = 0;

var value = key.path[i + 1];

if (value) {
if (is.number(value)) {
pathElement.id = value;
} else {
pathElement.name = value;
}
} else if (i < key.path.length - 2) {
// Reverse-iterate over the Key objects.
do {
if (numKeysWalked > 0 && is.undefined(key.id) && is.undefined(key.name)) {
// This isn't just an incomplete key. An ancestor key is incomplete.
throw new InvalidKeyError({
code: 'MISSING_ANCESTOR_ID'
});
}

keyProto.path.push(pathElement);
}
var pathElement = {
kind: key.kind
};

if (is.defined(key.id)) {
pathElement.id = key.id;
}

if (is.defined(key.name)) {
pathElement.name = key.name;
}

keyProto.path.unshift(pathElement);
} while ((key = key.parent) && ++numKeysWalked);

return keyProto;
}
Expand Down
20 changes: 20 additions & 0 deletions packages/datastore/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,11 +360,22 @@ Datastore.prototype.geoPoint = Datastore.geoPoint = function(coordindates) {
/**
* Helper function to get a Datastore Integer object.
*
* This is also useful when using an ID outside the bounds of a JavaScript
* Number object.
*
* @param {number} value - The integer value.
* @return {object}
*
* @example
* var sevenInteger = datastore.int(7);
*
* //-
* // Create an Int to support long Key IDs.
* //-
* var key = datastore.key([
* 'Kind',
* datastore.int('100000000000001234')
* ]);
*/
Datastore.prototype.int = Datastore.int = function(value) {
return new entity.Int(value);
Expand Down Expand Up @@ -461,6 +472,15 @@ Datastore.prototype.createQuery = function(namespace, kind) {
* var key = datastore.key(['Company', 123]);
*
* //-
* // If the ID integer is outside the bounds of a JavaScript Number object,
* // create an Int.
* //-
* var key = datastore.key([
* 'Company',
* datastore.int('100000000000001234')
* ]);
*
* //-
* // Create a complete key with a kind value of `Company` and name `Google`.
* // Note: `id` is used for numeric identifiers and `name` is used otherwise.
* //-
Expand Down
22 changes: 22 additions & 0 deletions packages/datastore/system-test/datastore.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,28 @@ describe('Datastore', function() {
});
});

it('should save and get with a string ID', function(done) {
var longIdKey = datastore.key([
'Post',
datastore.int('100000000000001234')
]);

datastore.save({
key: longIdKey,
data: {
test: true
}
}, function(err) {
assert.ifError(err);

datastore.get(longIdKey, function(err, entity) {
assert.ifError(err);
assert.strictEqual(entity.test, true);
done();
});
});
});

it('should fail explicitly set second insert on save', function(done) {
var postKey = datastore.key('Post');

Expand Down
29 changes: 16 additions & 13 deletions packages/datastore/test/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,12 @@ describe('entity', function() {
});
});

describe('Double', function() {
it('should store the value', function() {
var value = 8.3;

var double = new entity.Double(value);
assert.strictEqual(double.value, value);
});
});

describe('Int', function() {
it('should store the value', function() {
it('should store the stringified value', function() {
var value = 8;

var int = new entity.Int(value);
assert.strictEqual(int.value, value);
assert.strictEqual(int.value, value.toString());
});
});

Expand Down Expand Up @@ -97,6 +88,12 @@ describe('entity', function() {
assert.strictEqual(key.id, id);
});

it('should assign the ID from an Int', function() {
var id = new entity.Int(11);
var key = new entity.Key({ path: ['Kind', id] });
assert.strictEqual(key.id, id.value);
});

it('should assign the name', function() {
var name = 'name';
var key = new entity.Key({ path: ['Kind', name] });
Expand Down Expand Up @@ -607,10 +604,12 @@ describe('entity', function() {
},
path: [
{
id_type: 'id',
kind: 'Kind',
id: '111'
},
{
id_type: 'name',
kind: 'Kind2',
name: 'name'
}
Expand All @@ -632,7 +631,7 @@ describe('entity', function() {
namespace: NAMESPACE,
path: [
'Kind',
111,
new entity.Int(111),
'Kind2',
'name'
]
Expand Down Expand Up @@ -683,7 +682,7 @@ describe('entity', function() {
describe('keyToKeyProto', function() {
it('should handle hierarchical key definitions', function() {
var key = new entity.Key({
path: ['Kind1', 1, 'Kind2', 'name']
path: ['Kind1', 1, 'Kind2', 'name', 'Kind3', new entity.Int(3)]
});

var keyProto = entity.keyToKeyProto(key);
Expand All @@ -697,6 +696,10 @@ describe('entity', function() {
assert.strictEqual(keyProto.path[1].kind, 'Kind2');
assert.strictEqual(keyProto.path[1].id, undefined);
assert.strictEqual(keyProto.path[1].name, 'name');

assert.strictEqual(keyProto.path[2].kind, 'Kind3');
assert.strictEqual(keyProto.path[2].id, new entity.Int(3).value);
assert.strictEqual(keyProto.path[2].name, undefined);
});

it('should detect the namespace of the hierarchical keys', function() {
Expand Down
4 changes: 2 additions & 2 deletions packages/datastore/test/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ describe('Request', function() {
var incompleteKey;
var apiResponse = {
keys: [
{ path: [{ kind: 'Kind', id: 123 }] }
{ path: [{ id_type: 'id', kind: 'Kind', id: 123 }] }
]
};

Expand All @@ -173,7 +173,7 @@ describe('Request', function() {
request.allocateIds(incompleteKey, 1, function(err, keys) {
assert.ifError(err);
var generatedKey = keys[0];
assert.strictEqual(generatedKey.path.pop(), 123);
assert.strictEqual(generatedKey.path.pop(), '123');
done();
});
});
Expand Down