Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Entity and List property support #29

Merged
merged 4 commits into from
Nov 21, 2014
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
2 changes: 1 addition & 1 deletion lib/gcloud.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Gcloud
##
# Standard Gcloud exception class.
# Base Gcloud exception class.
class Error < StandardError
end
end
7 changes: 6 additions & 1 deletion lib/gcloud/datastore/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
module Gcloud
module Datastore
##
# Standard Datastore exception class.
# Base Datastore exception class.
class Error < Gcloud::Error
end

Expand All @@ -39,6 +39,11 @@ def initialize method, response = nil
end
end

##
# Raised when a property is not correct.
class PropertyError < Gcloud::Datastore::Error

This comment was marked as spam.

This comment was marked as spam.

end

##
# General error for Transaction problems.
class TransactionError < Gcloud::Datastore::Error
Expand Down
16 changes: 14 additions & 2 deletions lib/gcloud/datastore/proto.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def self.from_proto_value proto_value
self.time_from_microseconds microseconds
elsif !proto_value.key_value.nil?
Gcloud::Datastore::Key.from_proto(proto_value.key_value)
elsif !proto_value.entity_value.nil?
Gcloud::Datastore::Entity.from_proto(proto_value.entity_value)
elsif !proto_value.boolean_value.nil?
proto_value.boolean_value
elsif !proto_value.double_value.nil?
Expand All @@ -43,9 +45,13 @@ def self.from_proto_value proto_value
proto_value.integer_value
elsif !proto_value.string_value.nil?
return proto_value.string_value
elsif !proto_value.list_value.nil?
return Array(proto_value.list_value).map do |item|
from_proto_value item
end
else
nil
end # TODO: Entity, Array
end
end

def self.to_proto_value value
Expand All @@ -54,6 +60,8 @@ def self.to_proto_value value
v.timestamp_microseconds_value = self.microseconds_from_time value
elsif Gcloud::Datastore::Key === value
v.key_value = value.to_proto
elsif Gcloud::Datastore::Entity === value
v.entity_value = value.to_proto
elsif TrueClass === value
v.boolean_value = true
elsif FalseClass === value
Expand All @@ -66,7 +74,11 @@ def self.to_proto_value value
v.integer_value = value
elsif String === value
v.string_value = value
end # TODO: entity, list_value
elsif Array === value
v.list_value = value.map { |item| to_proto_value item }
else
fail PropertyError, "A property of type #{value.class} is not supported."
end
v
end

Expand Down
72 changes: 72 additions & 0 deletions test/gcloud/datastore/proto/test_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
value.string_value.must_equal raw
value.timestamp_microseconds_value.must_be :nil?
value.key_value.must_be :nil?
value.entity_value.must_be :nil?
value.boolean_value.must_be :nil?
value.double_value.must_be :nil?
value.integer_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes a string" do
Expand All @@ -50,9 +52,11 @@
value.boolean_value.must_equal true
value.timestamp_microseconds_value.must_be :nil?
value.key_value.must_be :nil?
value.entity_value.must_be :nil?
value.double_value.must_be :nil?
value.integer_value.must_be :nil?
value.string_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes true" do
Expand All @@ -67,9 +71,11 @@
value.boolean_value.must_equal false
value.timestamp_microseconds_value.must_be :nil?
value.key_value.must_be :nil?
value.entity_value.must_be :nil?
value.double_value.must_be :nil?
value.integer_value.must_be :nil?
value.string_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes false" do
Expand All @@ -83,10 +89,12 @@
value = Gcloud::Datastore::Proto.to_proto_value time_obj
value.timestamp_microseconds_value.must_equal time_num
value.key_value.must_be :nil?
value.entity_value.must_be :nil?
value.boolean_value.must_be :nil?
value.double_value.must_be :nil?
value.integer_value.must_be :nil?
value.string_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes timestamp" do
Expand All @@ -102,9 +110,11 @@
value.integer_value.must_equal raw
value.timestamp_microseconds_value.must_be :nil?
value.key_value.must_be :nil?
value.entity_value.must_be :nil?
value.boolean_value.must_be :nil?
value.double_value.must_be :nil?
value.string_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes integer" do
Expand All @@ -121,9 +131,11 @@
value.double_value.must_equal raw
value.timestamp_microseconds_value.must_be :nil?
value.key_value.must_be :nil?
value.entity_value.must_be :nil?
value.boolean_value.must_be :nil?
value.integer_value.must_be :nil?
value.string_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes float" do
Expand All @@ -139,10 +151,12 @@
value = Gcloud::Datastore::Proto.to_proto_value key
value.key_value.must_equal key.to_proto
value.timestamp_microseconds_value.must_be :nil?
value.entity_value.must_be :nil?
value.boolean_value.must_be :nil?
value.double_value.must_be :nil?
value.integer_value.must_be :nil?
value.string_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes Key" do
Expand All @@ -157,4 +171,62 @@
# (they are actually the same object, so this works...)
raw.to_proto.must_equal key.to_proto
end

it "encodes Entity" do
entity = Gcloud::Datastore::Entity.new
entity.key = Gcloud::Datastore::Key.new "Thing", 123
entity["name"] = "Thing 1"
value = Gcloud::Datastore::Proto.to_proto_value entity
value.key_value.must_be :nil?
value.entity_value.must_equal entity.to_proto
value.timestamp_microseconds_value.must_be :nil?
value.boolean_value.must_be :nil?
value.double_value.must_be :nil?
value.integer_value.must_be :nil?
value.string_value.must_be :nil?
value.list_value.must_be :nil?
end

it "decodes Entity" do
entity = Gcloud::Datastore::Entity.new
entity.key = Gcloud::Datastore::Key.new "Thing", 123
entity["name"] = "Thing 1"
value = Gcloud::Datastore::Proto::Value.new
value.entity_value = entity.to_proto
raw = Gcloud::Datastore::Proto.from_proto_value value
assert_kind_of Gcloud::Datastore::Entity, raw
refute_kind_of Gcloud::Datastore::Proto::Entity, raw
# We don't have equality on entity yet,
# so let's make sure the proto values are equal.
# (they are actually the same object, so this works...)
raw.to_proto.must_equal entity.to_proto
end

it "encodes Array" do
array = ["string", 123, true]
value = Gcloud::Datastore::Proto.to_proto_value array
value.list_value.wont_be :nil?
value.key_value.must_be :nil?
value.entity_value.must_be :nil?
value.timestamp_microseconds_value.must_be :nil?
value.boolean_value.must_be :nil?
value.double_value.must_be :nil?
value.integer_value.must_be :nil?
value.string_value.must_be :nil?
end

it "decodes List" do
value = Gcloud::Datastore::Proto::Value.new
value.list_value = [
Gcloud::Datastore::Proto::Value.new.tap { |v| v.string_value = "string" },
Gcloud::Datastore::Proto::Value.new.tap { |v| v.integer_value = 123 },
Gcloud::Datastore::Proto::Value.new.tap { |v| v.boolean_value = true },
]
raw = Gcloud::Datastore::Proto.from_proto_value value
assert_kind_of Array, raw
raw.count.must_equal 3
raw[0].must_equal "string"
raw[1].must_equal 123
raw[2].must_equal true
end
end
36 changes: 36 additions & 0 deletions test/gcloud/datastore/test_entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,40 @@
entity_from_proto.properties.must_include ["name", "User McNumber"]
entity_from_proto.properties.must_include ["email", "[email protected]"]
end

it "can store other entities as properties" do
task1 = Gcloud::Datastore::Entity.new.tap do |t|
t.key = Gcloud::Datastore::Key.new "Task", 1111
t["description"] = "can persist entities"
t["completed"] = true
end
task2 = Gcloud::Datastore::Entity.new.tap do |t|
t.key = Gcloud::Datastore::Key.new "Task", 2222
t["description"] = "can persist lists"
t["completed"] = true
end
entity["tasks"] = [task1, task2]

proto = entity.to_proto

task_property = proto.property.last
task_property.name.must_equal "tasks"
task_property.value.list_value.wont_be :nil?
task_property.value.list_value.count.must_equal 2
proto_task_1 = task_property.value.list_value.first
proto_task_2 = task_property.value.list_value.last
proto_task_1.wont_be :nil?
proto_task_2.wont_be :nil?
proto_task_1.entity_value.wont_be :nil?
proto_task_2.entity_value.wont_be :nil?
proto_task_1.entity_value.property.find { |p| p.name == "description" }.value.string_value.must_equal "can persist entities"
proto_task_2.entity_value.property.find { |p| p.name == "description" }.value.string_value.must_equal "can persist lists"
end

it "raises when setting an unsupported property type" do
error = assert_raises Gcloud::Datastore::PropertyError do
entity["thing"] = Gcloud::Datastore::Credentials::Empty.new
end
error.message.must_equal "A property of type Gcloud::Datastore::Credentials::Empty is not supported."
end
end
35 changes: 35 additions & 0 deletions test/gcloud/datastore/test_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@
key.parent.name.must_be :nil?
end

it "can set a dataset_id" do
key = Gcloud::Datastore::Key.new "ThisThing", 1234
key.kind.must_equal "ThisThing"
key.id.must_equal 1234
key.name.must_be :nil?

key.dataset_id.must_be :nil?
key.dataset_id = "custom-ds"
key.dataset_id.wont_be :nil?
key.dataset_id.must_equal "custom-ds"
end

it "can set a namespace" do
key = Gcloud::Datastore::Key.new "ThisThing", 1234
key.kind.must_equal "ThisThing"
key.id.must_equal 1234
key.name.must_be :nil?

key.namespace.must_be :nil?
key.namespace = "custom-ns"
key.namespace.wont_be :nil?
key.namespace.must_equal "custom-ns"
end

describe "path" do
it "returns kind and id" do
key = Gcloud::Datastore::Key.new "Task", 123456
Expand Down Expand Up @@ -125,9 +149,13 @@
proto.path_element.last.kind.must_equal "ThisThing"
proto.path_element.last.id.must_equal 1234
proto.path_element.last.name.must_be :nil?
proto.partition_id.dataset_id.must_be :nil?
proto.partition_id.namespace.must_be :nil?

key = Gcloud::Datastore::Key.new "ThisThing", "charlie"
key.parent = Gcloud::Datastore::Key.new "ThatThing", "henry"
key.dataset_id = "custom-ds"
key.namespace = "custom-ns"
proto = key.to_proto
proto.path_element.count.must_equal 2
proto.path_element.first.kind.must_equal "ThatThing"
Expand All @@ -136,18 +164,25 @@
proto.path_element.last.kind.must_equal "ThisThing"
proto.path_element.last.id.must_be :nil?
proto.path_element.last.name.must_equal "charlie"
proto.partition_id.dataset_id.must_equal "custom-ds"
proto.partition_id.namespace.must_equal "custom-ns"
end

it "can be created with a protocol buffer object" do
proto = Gcloud::Datastore::Proto::Key.new
proto.path_element = [Gcloud::Datastore::Proto::Key::PathElement.new]
proto.path_element.first.kind = "AnotherThing"
proto.path_element.first.id = 56789
proto.partition_id = Gcloud::Datastore::Proto::PartitionId.new
proto.partition_id.dataset_id = "custom-ds"
proto.partition_id.namespace = "custom-ns"
key = Gcloud::Datastore::Key.from_proto proto

key.wont_be :nil?
key.kind.must_equal "AnotherThing"
key.id.must_equal 56789
key.name.must_be :nil?
key.dataset_id.must_equal "custom-ds"
key.namespace.must_equal "custom-ns"
end
end