diff --git a/Manifest.txt b/Manifest.txt index 21c55f70e4b4..08a6cb8641df 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -12,6 +12,7 @@ lib/gcloud/datastore/connection.rb lib/gcloud/datastore/credentials.rb lib/gcloud/datastore/dataset.rb lib/gcloud/datastore/entity.rb +lib/gcloud/datastore/errors.rb lib/gcloud/datastore/key.rb lib/gcloud/datastore/list.rb lib/gcloud/datastore/proto.rb diff --git a/gcloud.gemspec b/gcloud.gemspec index 2a0b6c7e8af5..d0c61e1a91f8 100644 --- a/gcloud.gemspec +++ b/gcloud.gemspec @@ -1,18 +1,18 @@ # -*- encoding: utf-8 -*- -# stub: gcloud 0.0.1.20141114161251 ruby lib +# stub: gcloud 0.0.1.20141119145908 ruby lib Gem::Specification.new do |s| s.name = "gcloud" - s.version = "0.0.1.20141114161251" + s.version = "0.0.1.20141119145908" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Silvano Luciani", "Mike Moore"] - s.date = "2014-11-14" + s.date = "2014-11-19" s.description = "Gcloud is the official library for interacting with Google Cloud." s.email = ["silvano@google.com", "mike@blowmage.com"] s.extra_rdoc_files = ["CHANGELOG.md", "CONTRIBUTING.md", "Manifest.txt", "README.md"] - s.files = [".gemtest", ".rubocop.yml", "CHANGELOG.md", "CONTRIBUTING.md", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "gcloud.gemspec", "lib/gcloud.rb", "lib/gcloud/datastore.rb", "lib/gcloud/datastore/connection.rb", "lib/gcloud/datastore/credentials.rb", "lib/gcloud/datastore/dataset.rb", "lib/gcloud/datastore/entity.rb", "lib/gcloud/datastore/key.rb", "lib/gcloud/datastore/list.rb", "lib/gcloud/datastore/proto.rb", "lib/gcloud/datastore/query.rb", "lib/gcloud/datastore/transaction.rb", "lib/gcloud/proto/datastore_v1.pb.rb", "lib/gcloud/version.rb", "rakelib/console.rake", "rakelib/proto.rake", "rakelib/rubocop.rake", "rakelib/test.rake", "regression/data/index.yaml", "regression/datastore/test_datastore.rb", "regression/datastore_helper.rb", "test/gcloud/datastore/proto/test_cursor.rb", "test/gcloud/datastore/proto/test_direction.rb", "test/gcloud/datastore/proto/test_operator.rb", "test/gcloud/datastore/proto/test_value.rb", "test/gcloud/datastore/test_connection.rb", "test/gcloud/datastore/test_credentials.rb", "test/gcloud/datastore/test_dataset.rb", "test/gcloud/datastore/test_entity.rb", "test/gcloud/datastore/test_key.rb", "test/gcloud/datastore/test_query.rb", "test/gcloud/datastore/test_transaction.rb", "test/gcloud/test_version.rb", "test/helper.rb"] + s.files = [".gemtest", ".rubocop.yml", "CHANGELOG.md", "CONTRIBUTING.md", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "gcloud.gemspec", "lib/gcloud.rb", "lib/gcloud/datastore.rb", "lib/gcloud/datastore/connection.rb", "lib/gcloud/datastore/credentials.rb", "lib/gcloud/datastore/dataset.rb", "lib/gcloud/datastore/entity.rb", "lib/gcloud/datastore/errors.rb", "lib/gcloud/datastore/key.rb", "lib/gcloud/datastore/list.rb", "lib/gcloud/datastore/proto.rb", "lib/gcloud/datastore/query.rb", "lib/gcloud/datastore/transaction.rb", "lib/gcloud/proto/datastore_v1.pb.rb", "lib/gcloud/version.rb", "rakelib/console.rake", "rakelib/proto.rake", "rakelib/rubocop.rake", "rakelib/test.rake", "regression/data/index.yaml", "regression/datastore/test_datastore.rb", "regression/datastore_helper.rb", "test/gcloud/datastore/proto/test_cursor.rb", "test/gcloud/datastore/proto/test_direction.rb", "test/gcloud/datastore/proto/test_operator.rb", "test/gcloud/datastore/proto/test_value.rb", "test/gcloud/datastore/test_connection.rb", "test/gcloud/datastore/test_credentials.rb", "test/gcloud/datastore/test_dataset.rb", "test/gcloud/datastore/test_entity.rb", "test/gcloud/datastore/test_key.rb", "test/gcloud/datastore/test_query.rb", "test/gcloud/datastore/test_transaction.rb", "test/gcloud/test_version.rb", "test/helper.rb"] s.homepage = "http://googlecloudplatform.github.io/gcloud-ruby/" s.licenses = ["Apache-2.0"] s.rdoc_options = ["--main", "README.md", "--exclude", "lib/gcloud/proto/"] diff --git a/lib/gcloud.rb b/lib/gcloud.rb index 9a006a6d23b9..be1cc8421c2f 100644 --- a/lib/gcloud.rb +++ b/lib/gcloud.rb @@ -1 +1,8 @@ require "gcloud/version" + +module Gcloud + ## + # Standard Gcloud exception class. + class Error < StandardError + end +end diff --git a/lib/gcloud/datastore.rb b/lib/gcloud/datastore.rb index e17a256c1e59..22bb1906c287 100644 --- a/lib/gcloud/datastore.rb +++ b/lib/gcloud/datastore.rb @@ -13,6 +13,7 @@ # limitations under the License. require "gcloud" +require "gcloud/datastore/errors" require "gcloud/datastore/dataset" require "gcloud/datastore/transaction" require "gcloud/datastore/credentials" diff --git a/lib/gcloud/datastore/connection.rb b/lib/gcloud/datastore/connection.rb index 44c0052a8514..05aff662ea81 100644 --- a/lib/gcloud/datastore/connection.rb +++ b/lib/gcloud/datastore/connection.rb @@ -117,8 +117,8 @@ def rpc proto_method, proto_request @credentials.sign_http_request req end - # TODO: Raise a proper error if the response is not 2xx (success) - fail response.inspect unless response.success? + fail ConnectionError, proto_method, response unless response.success? + response.body end diff --git a/lib/gcloud/datastore/credentials.rb b/lib/gcloud/datastore/credentials.rb index 118d10b251f1..0a5dc99c85a9 100644 --- a/lib/gcloud/datastore/credentials.rb +++ b/lib/gcloud/datastore/credentials.rb @@ -29,9 +29,9 @@ class Credentials # :nodoc: def initialize keyfile if keyfile.nil? - fail "You must provide a keyfile to connect with." + fail KeyfileError, "You must provide a keyfile to connect with." elsif !File.exist?(keyfile) - fail "The keyfile '#{keyfile}' is not a valid file." + fail KeyfileError, "The keyfile '#{keyfile}' is not a valid file." end options = JSON.parse(File.read(keyfile)) diff --git a/lib/gcloud/datastore/dataset.rb b/lib/gcloud/datastore/dataset.rb index 5c95c746cff7..a32d9de7b8dc 100644 --- a/lib/gcloud/datastore/dataset.rb +++ b/lib/gcloud/datastore/dataset.rb @@ -46,7 +46,9 @@ def dataset_id # empty_key = Gcloud::Datastore::Key.new "Task" # task_keys = conn.allocate_ids empty_key, 5 def allocate_ids incomplete_key, count = 1 - fail "An incomplete key must be provided." if incomplete_key.complete? + if incomplete_key.complete? + fail Gcloud::Datastore::Error, "An incomplete key must be provided." + end incomplete_keys = count.times.map { incomplete_key.to_proto } response = connection.allocate_ids(*incomplete_keys) @@ -134,9 +136,9 @@ def transaction begin yield tx tx.commit - rescue + rescue => e tx.rollback - raise "Transaction failed to commit." + raise TransactionError.new("Transaction failed to commit.", e) end end diff --git a/lib/gcloud/datastore/errors.rb b/lib/gcloud/datastore/errors.rb new file mode 100644 index 000000000000..e4beebf48939 --- /dev/null +++ b/lib/gcloud/datastore/errors.rb @@ -0,0 +1,55 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "gcloud" + +module Gcloud + module Datastore + ## + # Standard Datastore exception class. + class Error < Gcloud::Error + end + + ## + # Raised when a keyfile is not correct. + class KeyfileError < Gcloud::Datastore::Error + end + + ## + # Raised when an API call is not successful. + class ApiError < Gcloud::Datastore::Error + ## + # The response object of the failed HTTP request. + attr_reader :response + + def initialize method, response = nil + super "API call to #{method} was not successful" + @response = response + end + end + + ## + # General error for Transaction problems. + class TransactionError < Gcloud::Datastore::Error + ## + # An error that occurred within the transaction. (optional) + attr_reader :inner + + def initialize message, inner = nil + super message + @inner = inner + end + end + end +end diff --git a/lib/gcloud/datastore/transaction.rb b/lib/gcloud/datastore/transaction.rb index 4ff1889d1287..de75676c40d3 100644 --- a/lib/gcloud/datastore/transaction.rb +++ b/lib/gcloud/datastore/transaction.rb @@ -45,14 +45,16 @@ def delete *entities end def start - fail "Transaction already opened" unless @id.nil? + fail TransactionError, "Transaction already opened." unless @id.nil? response = connection.begin_transaction @id = response.transaction end def commit - fail "Cannot commit when not in a transaction" if @id.nil? + if @id.nil? + fail TransactionError, "Cannot commit when not in a transaction." + end response = connection.commit shared_mutation, @id auto_id_assign_ids response.mutation_result.insert_auto_id_key @@ -60,7 +62,9 @@ def commit end def rollback - fail "Cannot rollback when not in a transaction" if @id.nil? + if @id.nil? + fail TransactionError, "Cannot rollback when not in a transaction." + end connection.rollback @id true diff --git a/test/gcloud/datastore/test_dataset.rb b/test/gcloud/datastore/test_dataset.rb index 865febe7671a..9797aad44070 100644 --- a/test/gcloud/datastore/test_dataset.rb +++ b/test/gcloud/datastore/test_dataset.rb @@ -86,7 +86,7 @@ it "allocate_ids raises when not given an incomplete key" do complete_key = Gcloud::Datastore::Key.new "ds-test", 789 complete_key.must_be :complete? - assert_raises RuntimeError do + assert_raises Gcloud::Datastore::Error do dataset.allocate_ids complete_key end end @@ -208,4 +208,21 @@ tx.save entity end end + + it "transaction will wrap errors in TransactionError" do + dataset.connection.expect :begin_transaction, + begin_transaction_response + dataset.connection.expect :rollback, nil, [String] + + error = assert_raises Gcloud::Datastore::TransactionError do + dataset.transaction do |tx| + fail "This error should be wrapped by TransactionError." + end + end + + error.wont_be :nil? + error.message.must_equal "Transaction failed to commit." + error.inner.wont_be :nil? + error.inner.message.must_equal "This error should be wrapped by TransactionError." + end end diff --git a/test/gcloud/datastore/test_entity.rb b/test/gcloud/datastore/test_entity.rb index 8c706febedb0..ca4f5a18bffb 100644 --- a/test/gcloud/datastore/test_entity.rb +++ b/test/gcloud/datastore/test_entity.rb @@ -13,7 +13,7 @@ # limitations under the License. require "helper" -require "gcloud/datastore/entity" +require "gcloud/datastore" describe Gcloud::Datastore::Entity do diff --git a/test/gcloud/datastore/test_key.rb b/test/gcloud/datastore/test_key.rb index 0d1edb521738..c27ba1d59396 100644 --- a/test/gcloud/datastore/test_key.rb +++ b/test/gcloud/datastore/test_key.rb @@ -13,7 +13,7 @@ # limitations under the License. require "helper" -require "gcloud/datastore/key" +require "gcloud/datastore" describe Gcloud::Datastore::Key do diff --git a/test/gcloud/datastore/test_query.rb b/test/gcloud/datastore/test_query.rb index 015c52b57e5a..d6c6b626bf12 100644 --- a/test/gcloud/datastore/test_query.rb +++ b/test/gcloud/datastore/test_query.rb @@ -13,7 +13,7 @@ # limitations under the License. require "helper" -require "gcloud/datastore/query" +require "gcloud/datastore" describe Gcloud::Datastore::Query do it "can query on kind" do diff --git a/test/gcloud/datastore/test_transaction.rb b/test/gcloud/datastore/test_transaction.rb index 82de92719828..48f8d193a20b 100644 --- a/test/gcloud/datastore/test_transaction.rb +++ b/test/gcloud/datastore/test_transaction.rb @@ -81,4 +81,41 @@ transaction.save entity transaction.rollback end + + describe "error handling" do + it "start will raise if transaction is already open" do + transaction.id.wont_be :nil? + error = assert_raises Gcloud::Datastore::TransactionError do + transaction.start + end + error.wont_be :nil? + error.message.must_equal "Transaction already opened." + end + + it "commit will raise if transaction is not open" do + transaction.connection.expect :begin_transaction, begin_transaction_response + + transaction.id.wont_be :nil? + transaction.reset! + transaction.id.must_be :nil? + error = assert_raises Gcloud::Datastore::TransactionError do + transaction.commit + end + error.wont_be :nil? + error.message.must_equal "Cannot commit when not in a transaction." + end + + it "transaction will raise if transaction is not open" do + transaction.connection.expect :begin_transaction, begin_transaction_response + + transaction.id.wont_be :nil? + transaction.reset! + transaction.id.must_be :nil? + error = assert_raises Gcloud::Datastore::TransactionError do + transaction.rollback + end + error.wont_be :nil? + error.message.must_equal "Cannot rollback when not in a transaction." + end + end end