Repository: googleapis/google-cloud-python

Issue:
Saving large values to indexed properties raises _Rendezvous exception
Given this code:

``` python
from google.cloud import datastore
from google.cloud.datastore import Entity

client = datastore.Client()
with client.transaction() as xact:
    e = Entity(client.key("Test", "123"))
    e.update({"x": "a" * 1500000})
    xact.put(e)
```

version 0.19 of this library raises the following exception on `.put`:

``` python
Traceback (most recent call last):
  File "test.py", line 8, in <module>
    xact.put(e)
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/google/cloud/datastore/batch.py", line 302, in __exit__
    self.commit()
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/google/cloud/datastore/transaction.py", line 167, in commit
    super(Transaction, self).commit()
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/google/cloud/datastore/batch.py", line 274, in commit
    self._commit()
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/google/cloud/datastore/batch.py", line 251, in _commit
    self.project, self._commit_request, self._id)
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/google/cloud/datastore/connection.py", line 584, in commit
    response = self._datastore_api.commit(project, request)
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/google/cloud/datastore/connection.py", line 314, in commit
    return self._stub.Commit(request_pb)
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/grpc/_channel.py", line 481, in __call__
    return _end_unary_response_blocking(state, False, deadline)
  File "/Users/bogdan/.virtualenvs/bread/lib/python2.7/site-packages/grpc/_channel.py", line 432, in _end_unary_response_blocking
    raise _Rendezvous(state, None, None, deadline)
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with (StatusCode.INVALID_ARGUMENT, The value of property "x" is longer than 1048487 bytes.)>
```

Previously, this used to raise `gcloud.exceptions.BadRequest`. It seems like this exception should be caught by the library and a more appropriate one should be raised in its stead.

_EDIT_: It turns out the following snippet has the same issue so this is not transaction specific:

``` python
from google.cloud import datastore
from google.cloud.datastore import Entity

client = datastore.Client()
e = Entity(client.key("Test", "123"))
e.update({"x": "a" * 1500000})
client.put(e)
```

_EDIT 2_: This does not work either, raising the same exception:

``` python
from google.cloud import datastore
from google.cloud.datastore import Entity

client = datastore.Client()
e = Entity(client.key("Test", "123"), exclude_from_indexes=("x",))
e.update({"x": "a" * 1500000})
client.put(e)
```



Discussion:
@Bogdanp Thanks for the heads up. We currently just have an [ad-hoc translation](https://github.com/GoogleCloudPlatform/google-cloud-python/blob/4258b764f401f8c890aefb5a6316d0d73ced66e7/google/cloud/datastore/connection.py#L315-L317) for such errors, so it's kind of a "wait and see".


Resolving patch:
diff --git a/datastore/google/cloud/datastore/connection.py b/datastore/google/cloud/datastore/connection.py
--- a/datastore/google/cloud/datastore/connection.py
+++ b/datastore/google/cloud/datastore/connection.py
@@ -23,6 +23,7 @@
 from google.cloud import connection as connection_module
 from google.cloud.environment_vars import DISABLE_GRPC
 from google.cloud.environment_vars import GCD_HOST
+from google.cloud.exceptions import BadRequest
 from google.cloud.exceptions import Conflict
 from google.cloud.exceptions import GrpcRendezvous
 from google.cloud.exceptions import make_exception
@@ -313,8 +314,11 @@ def commit(self, project, request_pb):
         try:
             return self._stub.Commit(request_pb)
         except GrpcRendezvous as exc:
-            if exc.code() == StatusCode.ABORTED:
+            error_code = exc.code()
+            if error_code == StatusCode.ABORTED:
                 raise Conflict(exc.details())
+            if error_code == StatusCode.INVALID_ARGUMENT:
+                raise BadRequest(exc.details())
             raise
 
     def rollback(self, project, request_pb):


Test patch:
diff --git a/datastore/unit_tests/test_connection.py b/datastore/unit_tests/test_connection.py
--- a/datastore/unit_tests/test_connection.py
+++ b/datastore/unit_tests/test_connection.py
@@ -252,6 +252,19 @@ def test_commit_failure_aborted(self):
         exc = GrpcRendezvous(exc_state, None, None, None)
         self._commit_failure_helper(exc, Conflict)
 
+    @unittest.skipUnless(_HAVE_GRPC, 'No gRPC')
+    def test_commit_failure_invalid_argument(self):
+        from grpc import StatusCode
+        from grpc._channel import _RPCState
+        from google.cloud.exceptions import BadRequest
+        from google.cloud.exceptions import GrpcRendezvous
+
+        details = 'Too long content.'
+        exc_state = _RPCState((), None, None,
+                              StatusCode.INVALID_ARGUMENT, details)
+        exc = GrpcRendezvous(exc_state, None, None, None)
+        self._commit_failure_helper(exc, BadRequest)
+
     @unittest.skipUnless(_HAVE_GRPC, 'No gRPC')
     def test_commit_failure_cancelled(self):
         from grpc import StatusCode