Skip to content

Commit a2c8182

Browse files
authored
Merge pull request #2097 from dhermes/datastore-prep-for-grpc
Factor out datastore API surface.
2 parents b01aa48 + 42a3353 commit a2c8182

File tree

2 files changed

+258
-120
lines changed

2 files changed

+258
-120
lines changed

gcloud/datastore/connection.py

Lines changed: 161 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,27 @@
1616

1717
import os
1818

19-
from gcloud import connection
19+
from google.rpc import status_pb2
20+
21+
from gcloud import connection as connection_module
2022
from gcloud.environment_vars import GCD_HOST
2123
from gcloud.exceptions import make_exception
2224
from gcloud.datastore._generated import datastore_pb2 as _datastore_pb2
23-
from google.rpc import status_pb2
24-
2525

26-
class Connection(connection.Connection):
27-
"""A connection to the Google Cloud Datastore via the Protobuf API.
28-
29-
This class should understand only the basic types (and protobufs)
30-
in method arguments, however should be capable of returning advanced types.
3126

32-
:type credentials: :class:`oauth2client.client.OAuth2Credentials`
33-
:param credentials: The OAuth2 Credentials to use for this connection.
27+
class _DatastoreAPIOverHttp(object):
28+
"""Helper mapping datastore API methods.
3429
35-
:type http: :class:`httplib2.Http` or class that defines ``request()``.
36-
:param http: An optional HTTP object to make requests.
30+
Methods make bare API requests without any helpers for constructing
31+
the requests or parsing the responses.
3732
38-
:type api_base_url: string
39-
:param api_base_url: The base of the API call URL. Defaults to
40-
:attr:`API_BASE_URL`.
33+
:type connection: :class:`gcloud.datastore.connection.Connection`
34+
:param connection: A connection object that contains helpful
35+
information for making requests.
4136
"""
4237

43-
API_BASE_URL = 'https://datastore.googleapis.com'
44-
"""The base of the API call URL."""
45-
46-
API_VERSION = 'v1'
47-
"""The version of the API, used in building the API call's URL."""
48-
49-
API_URL_TEMPLATE = ('{api_base}/{api_version}/projects'
50-
'/{project}:{method}')
51-
"""A template for the URL of a particular API call."""
52-
53-
SCOPE = ('https://www.googleapis.com/auth/datastore',)
54-
"""The scopes required for authenticating as a Cloud Datastore consumer."""
55-
56-
def __init__(self, credentials=None, http=None, api_base_url=None):
57-
super(Connection, self).__init__(credentials=credentials, http=http)
58-
if api_base_url is None:
59-
try:
60-
# gcd.sh has /datastore/ in the path still since it supports
61-
# v1beta2 and v1beta3 simultaneously.
62-
api_base_url = '%s/datastore' % (os.environ[GCD_HOST],)
63-
except KeyError:
64-
api_base_url = self.__class__.API_BASE_URL
65-
self.api_base_url = api_base_url
38+
def __init__(self, connection):
39+
self.connection = connection
6640

6741
def _request(self, project, method, data):
6842
"""Make a request over the Http transport to the Cloud Datastore API.
@@ -86,10 +60,10 @@ def _request(self, project, method, data):
8660
headers = {
8761
'Content-Type': 'application/x-protobuf',
8862
'Content-Length': str(len(data)),
89-
'User-Agent': self.USER_AGENT,
63+
'User-Agent': self.connection.USER_AGENT,
9064
}
91-
headers, content = self.http.request(
92-
uri=self.build_api_url(project=project, method=method),
65+
headers, content = self.connection.http.request(
66+
uri=self.connection.build_api_url(project=project, method=method),
9367
method='POST', headers=headers, body=data)
9468

9569
status = headers['status']
@@ -124,6 +98,146 @@ def _rpc(self, project, method, request_pb, response_pb_cls):
12498
data=request_pb.SerializeToString())
12599
return response_pb_cls.FromString(response)
126100

101+
def lookup(self, project, request_pb):
102+
"""Perform a ``lookup`` request.
103+
104+
:type project: string
105+
:param project: The project to connect to. This is
106+
usually your project name in the cloud console.
107+
108+
:type request_pb: :class:`._generated.datastore_pb2.LookupRequest`
109+
:param request_pb: The request protobuf object.
110+
111+
:rtype: :class:`._generated.datastore_pb2.LookupResponse`
112+
:returns: The returned protobuf response object.
113+
"""
114+
return self._rpc(project, 'lookup', request_pb,
115+
_datastore_pb2.LookupResponse)
116+
117+
def run_query(self, project, request_pb):
118+
"""Perform a ``runQuery`` request.
119+
120+
:type project: string
121+
:param project: The project to connect to. This is
122+
usually your project name in the cloud console.
123+
124+
:type request_pb: :class:`._generated.datastore_pb2.RunQueryRequest`
125+
:param request_pb: The request protobuf object.
126+
127+
:rtype: :class:`._generated.datastore_pb2.RunQueryResponse`
128+
:returns: The returned protobuf response object.
129+
"""
130+
return self._rpc(project, 'runQuery', request_pb,
131+
_datastore_pb2.RunQueryResponse)
132+
133+
def begin_transaction(self, project, request_pb):
134+
"""Perform a ``beginTransaction`` request.
135+
136+
:type project: string
137+
:param project: The project to connect to. This is
138+
usually your project name in the cloud console.
139+
140+
:type request_pb:
141+
:class:`._generated.datastore_pb2.BeginTransactionRequest`
142+
:param request_pb: The request protobuf object.
143+
144+
:rtype: :class:`._generated.datastore_pb2.BeginTransactionResponse`
145+
:returns: The returned protobuf response object.
146+
"""
147+
return self._rpc(project, 'beginTransaction', request_pb,
148+
_datastore_pb2.BeginTransactionResponse)
149+
150+
def commit(self, project, request_pb):
151+
"""Perform a ``commit`` request.
152+
153+
:type project: string
154+
:param project: The project to connect to. This is
155+
usually your project name in the cloud console.
156+
157+
:type request_pb: :class:`._generated.datastore_pb2.CommitRequest`
158+
:param request_pb: The request protobuf object.
159+
160+
:rtype: :class:`._generated.datastore_pb2.CommitResponse`
161+
:returns: The returned protobuf response object.
162+
"""
163+
return self._rpc(project, 'commit', request_pb,
164+
_datastore_pb2.CommitResponse)
165+
166+
def rollback(self, project, request_pb):
167+
"""Perform a ``rollback`` request.
168+
169+
:type project: string
170+
:param project: The project to connect to. This is
171+
usually your project name in the cloud console.
172+
173+
:type request_pb: :class:`._generated.datastore_pb2.RollbackRequest`
174+
:param request_pb: The request protobuf object.
175+
176+
:rtype: :class:`._generated.datastore_pb2.RollbackResponse`
177+
:returns: The returned protobuf response object.
178+
"""
179+
return self._rpc(project, 'rollback', request_pb,
180+
_datastore_pb2.RollbackResponse)
181+
182+
def allocate_ids(self, project, request_pb):
183+
"""Perform an ``allocateIds`` request.
184+
185+
:type project: string
186+
:param project: The project to connect to. This is
187+
usually your project name in the cloud console.
188+
189+
:type request_pb: :class:`._generated.datastore_pb2.AllocateIdsRequest`
190+
:param request_pb: The request protobuf object.
191+
192+
:rtype: :class:`._generated.datastore_pb2.AllocateIdsResponse`
193+
:returns: The returned protobuf response object.
194+
"""
195+
return self._rpc(project, 'allocateIds', request_pb,
196+
_datastore_pb2.AllocateIdsResponse)
197+
198+
199+
class Connection(connection_module.Connection):
200+
"""A connection to the Google Cloud Datastore via the Protobuf API.
201+
202+
This class should understand only the basic types (and protobufs)
203+
in method arguments, however should be capable of returning advanced types.
204+
205+
:type credentials: :class:`oauth2client.client.OAuth2Credentials`
206+
:param credentials: The OAuth2 Credentials to use for this connection.
207+
208+
:type http: :class:`httplib2.Http` or class that defines ``request()``.
209+
:param http: An optional HTTP object to make requests.
210+
211+
:type api_base_url: string
212+
:param api_base_url: The base of the API call URL. Defaults to
213+
:attr:`API_BASE_URL`.
214+
"""
215+
216+
API_BASE_URL = 'https://datastore.googleapis.com'
217+
"""The base of the API call URL."""
218+
219+
API_VERSION = 'v1'
220+
"""The version of the API, used in building the API call's URL."""
221+
222+
API_URL_TEMPLATE = ('{api_base}/{api_version}/projects'
223+
'/{project}:{method}')
224+
"""A template for the URL of a particular API call."""
225+
226+
SCOPE = ('https://www.googleapis.com/auth/datastore',)
227+
"""The scopes required for authenticating as a Cloud Datastore consumer."""
228+
229+
def __init__(self, credentials=None, http=None, api_base_url=None):
230+
super(Connection, self).__init__(credentials=credentials, http=http)
231+
if api_base_url is None:
232+
try:
233+
# gcd.sh has /datastore/ in the path still since it supports
234+
# v1beta2 and v1beta3 simultaneously.
235+
api_base_url = '%s/datastore' % (os.environ[GCD_HOST],)
236+
except KeyError:
237+
api_base_url = self.__class__.API_BASE_URL
238+
self.api_base_url = api_base_url
239+
self._datastore_api = _DatastoreAPIOverHttp(self)
240+
127241
def build_api_url(self, project, method, base_url=None,
128242
api_version=None):
129243
"""Construct the URL for a particular API call.
@@ -205,8 +319,7 @@ def lookup(self, project, key_pbs,
205319
_set_read_options(lookup_request, eventual, transaction_id)
206320
_add_keys_to_request(lookup_request.keys, key_pbs)
207321

208-
lookup_response = self._rpc(project, 'lookup', lookup_request,
209-
_datastore_pb2.LookupResponse)
322+
lookup_response = self._datastore_api.lookup(project, lookup_request)
210323

211324
results = [result.entity for result in lookup_response.found]
212325
missing = [result.entity for result in lookup_response.missing]
@@ -260,8 +373,7 @@ def run_query(self, project, query_pb, namespace=None,
260373
request.partition_id.namespace_id = namespace
261374

262375
request.query.CopyFrom(query_pb)
263-
response = self._rpc(project, 'runQuery', request,
264-
_datastore_pb2.RunQueryResponse)
376+
response = self._datastore_api.run_query(project, request)
265377
return (
266378
[e.entity for e in response.batch.entity_results],
267379
response.batch.end_cursor, # Assume response always has cursor.
@@ -281,8 +393,7 @@ def begin_transaction(self, project):
281393
:returns: The serialized transaction that was begun.
282394
"""
283395
request = _datastore_pb2.BeginTransactionRequest()
284-
response = self._rpc(project, 'beginTransaction', request,
285-
_datastore_pb2.BeginTransactionResponse)
396+
response = self._datastore_api.begin_transaction(project, request)
286397
return response.transaction
287398

288399
def commit(self, project, request, transaction_id):
@@ -316,8 +427,7 @@ def commit(self, project, request, transaction_id):
316427
else:
317428
request.mode = _datastore_pb2.CommitRequest.NON_TRANSACTIONAL
318429

319-
response = self._rpc(project, 'commit', request,
320-
_datastore_pb2.CommitResponse)
430+
response = self._datastore_api.commit(project, request)
321431
return _parse_commit_response(response)
322432

323433
def rollback(self, project, transaction_id):
@@ -335,8 +445,7 @@ def rollback(self, project, transaction_id):
335445
request = _datastore_pb2.RollbackRequest()
336446
request.transaction = transaction_id
337447
# Nothing to do with this response, so just execute the method.
338-
self._rpc(project, 'rollback', request,
339-
_datastore_pb2.RollbackResponse)
448+
self._datastore_api.rollback(project, request)
340449

341450
def allocate_ids(self, project, key_pbs):
342451
"""Obtain backend-generated IDs for a set of keys.
@@ -356,8 +465,7 @@ def allocate_ids(self, project, key_pbs):
356465
request = _datastore_pb2.AllocateIdsRequest()
357466
_add_keys_to_request(request.keys, key_pbs)
358467
# Nothing to do with this response, so just execute the method.
359-
response = self._rpc(project, 'allocateIds', request,
360-
_datastore_pb2.AllocateIdsResponse)
468+
response = self._datastore_api.allocate_ids(project, request)
361469
return list(response.keys)
362470

363471

0 commit comments

Comments
 (0)