Skip to content

Commit d9f2dda

Browse files
authored
Merge pull request #3137 from dhermes/ds-move-run_query-to-GAPIC
Using GAPIC datastore object (and an HTTP equivalent) for run_query.
2 parents fee3f2f + 4e509db commit d9f2dda

File tree

6 files changed

+216
-301
lines changed

6 files changed

+216
-301
lines changed

datastore/google/cloud/datastore/_gax.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -135,23 +135,6 @@ def lookup(self, project, request_pb):
135135
with _grpc_catch_rendezvous():
136136
return self._stub.Lookup(request_pb)
137137

138-
def run_query(self, project, request_pb):
139-
"""Perform a ``runQuery`` request.
140-
141-
:type project: str
142-
:param project: The project to connect to. This is
143-
usually your project name in the cloud console.
144-
145-
:type request_pb: :class:`.datastore_pb2.RunQueryRequest`
146-
:param request_pb: The request protobuf object.
147-
148-
:rtype: :class:`.datastore_pb2.RunQueryResponse`
149-
:returns: The returned protobuf response object.
150-
"""
151-
request_pb.project_id = project
152-
with _grpc_catch_rendezvous():
153-
return self._stub.RunQuery(request_pb)
154-
155138

156139
class GAPICDatastoreAPI(datastore_client.DatastoreClient):
157140
"""An API object that sends proto-over-gRPC requests.

datastore/google/cloud/datastore/_http.py

Lines changed: 44 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -181,23 +181,6 @@ def lookup(self, project, request_pb):
181181
self.connection.api_base_url,
182182
request_pb, _datastore_pb2.LookupResponse)
183183

184-
def run_query(self, project, request_pb):
185-
"""Perform a ``runQuery`` request.
186-
187-
:type project: str
188-
:param project: The project to connect to. This is
189-
usually your project name in the cloud console.
190-
191-
:type request_pb: :class:`.datastore_pb2.RunQueryRequest`
192-
:param request_pb: The request protobuf object.
193-
194-
:rtype: :class:`.datastore_pb2.RunQueryResponse`
195-
:returns: The returned protobuf response object.
196-
"""
197-
return _rpc(self.connection.http, project, 'runQuery',
198-
self.connection.api_base_url,
199-
request_pb, _datastore_pb2.RunQueryResponse)
200-
201184

202185
class Connection(connection_module.Connection):
203186
"""A connection to the Google Cloud Datastore via the Protobuf API.
@@ -264,71 +247,61 @@ def lookup(self, project, key_pbs,
264247
:rtype: :class:`.datastore_pb2.LookupResponse`
265248
:returns: The returned protobuf for the lookup request.
266249
"""
267-
lookup_request = _datastore_pb2.LookupRequest()
250+
lookup_request = _datastore_pb2.LookupRequest(keys=key_pbs)
268251
_set_read_options(lookup_request, eventual, transaction_id)
269-
_add_keys_to_request(lookup_request.keys, key_pbs)
270-
271252
return self._datastore_api.lookup(project, lookup_request)
272253

273-
def run_query(self, project, query_pb, namespace=None,
274-
eventual=False, transaction_id=None):
275-
"""Run a query on the Cloud Datastore.
276254

277-
Maps the ``DatastoreService.RunQuery`` protobuf RPC.
255+
class HTTPDatastoreAPI(object):
256+
"""An API object that sends proto-over-HTTP requests.
278257
279-
Given a Query protobuf, sends a ``runQuery`` request to the
280-
Cloud Datastore API and returns a list of entity protobufs
281-
matching the query.
258+
Intended to provide the same methods as the GAPIC ``DatastoreClient``.
282259
283-
You typically wouldn't use this method directly, in favor of the
284-
:meth:`google.cloud.datastore.query.Query.fetch` method.
260+
:type client: :class:`~google.cloud.datastore.client.Client`
261+
:param client: The client that provides configuration.
262+
"""
285263

286-
Under the hood, the :class:`google.cloud.datastore.query.Query` class
287-
uses this method to fetch data.
264+
def __init__(self, client):
265+
self.client = client
266+
267+
def run_query(self, project, partition_id, read_options,
268+
query=None, gql_query=None):
269+
"""Perform a ``runQuery`` request.
288270
289271
:type project: str
290-
:param project: The project over which to run the query.
272+
:param project: The project to connect to. This is
273+
usually your project name in the cloud console.
291274
292-
:type query_pb: :class:`.query_pb2.Query`
293-
:param query_pb: The Protobuf representing the query to run.
275+
:type partition_id: :class:`.entity_pb2.PartitionId`
276+
:param partition_id: Partition ID corresponding to an optional
277+
namespace and project ID.
294278
295-
:type namespace: str
296-
:param namespace: The namespace over which to run the query.
279+
:type read_options: :class:`.datastore_pb2.ReadOptions`
280+
:param read_options: The options for this query. Contains a
281+
either the transaction for the read or
282+
``STRONG`` or ``EVENTUAL`` read consistency.
297283
298-
:type eventual: bool
299-
:param eventual: If False (the default), request ``STRONG`` read
300-
consistency. If True, request ``EVENTUAL`` read
301-
consistency.
284+
:type query: :class:`.query_pb2.Query`
285+
:param query: (Optional) The query protobuf to run. At most one of
286+
``query`` and ``gql_query`` can be specified.
302287
303-
:type transaction_id: str
304-
:param transaction_id: If passed, make the request in the scope of
305-
the given transaction. Incompatible with
306-
``eventual==True``.
288+
:type gql_query: :class:`.query_pb2.GqlQuery`
289+
:param gql_query: (Optional) The GQL query to run. At most one of
290+
``query`` and ``gql_query`` can be specified.
307291
308292
:rtype: :class:`.datastore_pb2.RunQueryResponse`
309-
:returns: The protobuf response from a ``runQuery`` request.
293+
:returns: The returned protobuf response object.
310294
"""
311-
request = _datastore_pb2.RunQueryRequest()
312-
_set_read_options(request, eventual, transaction_id)
313-
314-
if namespace:
315-
request.partition_id.namespace_id = namespace
316-
317-
request.query.CopyFrom(query_pb)
318-
return self._datastore_api.run_query(project, request)
319-
320-
321-
class HTTPDatastoreAPI(object):
322-
"""An API object that sends proto-over-HTTP requests.
323-
324-
Intended to provide the same methods as the GAPIC ``DatastoreClient``.
325-
326-
:type client: :class:`~google.cloud.datastore.client.Client`
327-
:param client: The client that provides configuration.
328-
"""
329-
330-
def __init__(self, client):
331-
self.client = client
295+
request_pb = _datastore_pb2.RunQueryRequest(
296+
project_id=project,
297+
partition_id=partition_id,
298+
read_options=read_options,
299+
query=query,
300+
gql_query=gql_query,
301+
)
302+
return _rpc(self.client._http, project, 'runQuery',
303+
self.client._base_url,
304+
request_pb, _datastore_pb2.RunQueryResponse)
332305

333306
def begin_transaction(self, project):
334307
"""Perform a ``beginTransaction`` request.
@@ -391,8 +364,10 @@ def rollback(self, project, transaction_id):
391364
:rtype: :class:`.datastore_pb2.RollbackResponse`
392365
:returns: The returned protobuf response object.
393366
"""
394-
request_pb = _datastore_pb2.RollbackRequest()
395-
request_pb.transaction = transaction_id
367+
request_pb = _datastore_pb2.RollbackRequest(
368+
project_id=project,
369+
transaction=transaction_id,
370+
)
396371
# Response is empty (i.e. no fields) but we return it anyway.
397372
return _rpc(self.client._http, project, 'rollback',
398373
self.client._base_url,
@@ -411,8 +386,7 @@ def allocate_ids(self, project, key_pbs):
411386
:rtype: :class:`.datastore_pb2.AllocateIdsResponse`
412387
:returns: The returned protobuf response object.
413388
"""
414-
request_pb = _datastore_pb2.AllocateIdsRequest()
415-
_add_keys_to_request(request_pb.keys, key_pbs)
389+
request_pb = _datastore_pb2.AllocateIdsRequest(keys=key_pbs)
416390
return _rpc(self.client._http, project, 'allocateIds',
417391
self.client._base_url,
418392
request_pb, _datastore_pb2.AllocateIdsResponse)
@@ -434,16 +408,3 @@ def _set_read_options(request, eventual, transaction_id):
434408
opts.read_consistency = _datastore_pb2.ReadOptions.EVENTUAL
435409
elif transaction_id:
436410
opts.transaction = transaction_id
437-
438-
439-
def _add_keys_to_request(request_field_pb, key_pbs):
440-
"""Add protobuf keys to a request object.
441-
442-
:type request_field_pb: `RepeatedCompositeFieldContainer`
443-
:param request_field_pb: A repeated proto field that contains keys.
444-
445-
:type key_pbs: list of :class:`.entity_pb2.Key`
446-
:param key_pbs: The keys to add to a request.
447-
"""
448-
for key_pb in key_pbs:
449-
request_field_pb.add().CopyFrom(key_pb)

datastore/google/cloud/datastore/query.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from google.cloud.iterator import Iterator as BaseIterator
2121
from google.cloud.iterator import Page
2222

23+
from google.cloud.proto.datastore.v1 import datastore_pb2 as _datastore_pb2
24+
from google.cloud.proto.datastore.v1 import entity_pb2 as _entity_pb2
2325
from google.cloud.proto.datastore.v1 import query_pb2 as _query_pb2
2426
from google.cloud.datastore import helpers
2527
from google.cloud.datastore.key import Key
@@ -479,14 +481,22 @@ def _next_page(self):
479481
if not self._more_results:
480482
return None
481483

482-
pb = self._build_protobuf()
484+
query_pb = self._build_protobuf()
483485
transaction = self.client.current_transaction
484-
485-
response_pb = self.client._connection.run_query(
486-
query_pb=pb,
487-
project=self._query.project,
488-
namespace=self._query.namespace,
489-
transaction_id=transaction and transaction.id,
486+
if transaction is None:
487+
read_options = _datastore_pb2.ReadOptions()
488+
else:
489+
read_options = _datastore_pb2.ReadOptions(
490+
transaction=transaction.id)
491+
492+
partition_id = _entity_pb2.PartitionId(
493+
project_id=self._query.project,
494+
namespace_id=self._query.namespace)
495+
response_pb = self.client._datastore_api.run_query(
496+
self._query.project,
497+
partition_id,
498+
read_options,
499+
query=query_pb,
490500
)
491501
entity_pbs = self._process_query_results(response_pb)
492502
return Page(self, entity_pbs, self._item_to_value)

datastore/unit_tests/test__gax.py

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -200,46 +200,6 @@ def test_lookup(self):
200200
self.assertEqual(stub.method_calls,
201201
[(request_pb, 'Lookup')])
202202

203-
def test_run_query(self):
204-
return_val = object()
205-
stub = _GRPCStub(return_val)
206-
datastore_api, _ = self._make_one(stub=stub)
207-
208-
request_pb = mock.Mock(project_id=None, spec=['project_id'])
209-
project = 'PROJECT'
210-
result = datastore_api.run_query(project, request_pb)
211-
self.assertIs(result, return_val)
212-
self.assertEqual(request_pb.project_id, project)
213-
self.assertEqual(stub.method_calls,
214-
[(request_pb, 'RunQuery')])
215-
216-
def _run_query_failure_helper(self, exc, err_class):
217-
stub = _GRPCStub(side_effect=exc)
218-
datastore_api, _ = self._make_one(stub=stub)
219-
220-
request_pb = mock.Mock(project_id=None, spec=['project_id'])
221-
project = 'PROJECT'
222-
with self.assertRaises(err_class):
223-
datastore_api.run_query(project, request_pb)
224-
225-
self.assertEqual(request_pb.project_id, project)
226-
self.assertEqual(stub.method_calls,
227-
[(request_pb, 'RunQuery')])
228-
229-
@unittest.skipUnless(_HAVE_GRPC, 'No gRPC')
230-
def test_run_query_invalid_argument(self):
231-
from grpc import StatusCode
232-
from grpc._channel import _RPCState
233-
from google.cloud.exceptions import BadRequest
234-
from google.cloud.exceptions import GrpcRendezvous
235-
236-
details = ('Cannot have inequality filters on multiple '
237-
'properties: [created, priority]')
238-
exc_state = _RPCState((), None, None,
239-
StatusCode.INVALID_ARGUMENT, details)
240-
exc = GrpcRendezvous(exc_state, None, None, None)
241-
self._run_query_failure_helper(exc, BadRequest)
242-
243203

244204
@unittest.skipUnless(_HAVE_GRPC, 'No gRPC')
245205
class TestGAPICDatastoreAPI(unittest.TestCase):
@@ -307,20 +267,13 @@ def test_it(self, make_chan, mock_klass):
307267

308268
class _GRPCStub(object):
309269

310-
def __init__(self, return_val=None, side_effect=Exception):
270+
def __init__(self, return_val=None):
311271
self.return_val = return_val
312-
self.side_effect = side_effect
313272
self.method_calls = []
314273

315274
def _method(self, request_pb, name):
316275
self.method_calls.append((request_pb, name))
317-
if self.side_effect is Exception:
318-
return self.return_val
319-
else:
320-
raise self.side_effect
276+
return self.return_val
321277

322278
def Lookup(self, request_pb):
323279
return self._method(request_pb, 'Lookup')
324-
325-
def RunQuery(self, request_pb):
326-
return self._method(request_pb, 'RunQuery')

0 commit comments

Comments
 (0)