1# Copyright 2014 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Connections to Google Cloud Datastore API servers."""
16
17from google.rpc import status_pb2  # type: ignore
18
19from google.cloud import _http as connection_module
20from google.cloud import exceptions  # type: ignore
21from google.cloud.datastore_v1.types import datastore as _datastore_pb2
22
23
24DATASTORE_API_HOST = "datastore.googleapis.com"
25"""Datastore API request host."""
26API_BASE_URL = "https://" + DATASTORE_API_HOST
27"""The base of the API call URL."""
28API_VERSION = "v1"
29"""The version of the API, used in building the API call's URL."""
30API_URL_TEMPLATE = "{api_base}/{api_version}/projects" "/{project}:{method}"
31"""A template for the URL of a particular API call."""
32
33
34def _make_retry_timeout_kwargs(retry, timeout):
35    """Helper for methods taking optional retry / timout args."""
36    kwargs = {}
37
38    if retry is not None:
39        kwargs["retry"] = retry
40
41    if timeout is not None:
42        kwargs["timeout"] = timeout
43
44    return kwargs
45
46
47def _make_request_pb(request, request_pb_type):
48    """Helper for converting dicts to request messages."""
49    if not isinstance(request, request_pb_type):
50        request = request_pb_type(**request)
51
52    return request
53
54
55def _request(
56    http, project, method, data, base_url, client_info, retry=None, timeout=None,
57):
58    """Make a request over the Http transport to the Cloud Datastore API.
59
60    :type http: :class:`requests.Session`
61    :param http: HTTP object to make requests.
62
63    :type project: str
64    :param project: The project to make the request for.
65
66    :type method: str
67    :param method: The API call method name (ie, ``runQuery``,
68                   ``lookup``, etc)
69
70    :type data: str
71    :param data: The data to send with the API call.
72                 Typically this is a serialized Protobuf string.
73
74    :type base_url: str
75    :param base_url: The base URL where the API lives.
76
77    :type client_info: :class:`google.api_core.client_info.ClientInfo`
78    :param client_info: used to generate user agent.
79
80    :type retry: :class:`google.api_core.retry.Retry`
81    :param retry: (Optional) retry policy for the request
82
83    :type timeout: float or tuple(float, float)
84    :param timeout: (Optional) timeout for the request
85
86    :rtype: str
87    :returns: The string response content from the API call.
88    :raises: :class:`google.cloud.exceptions.GoogleCloudError` if the
89             response code is not 200 OK.
90    """
91    user_agent = client_info.to_user_agent()
92    headers = {
93        "Content-Type": "application/x-protobuf",
94        "User-Agent": user_agent,
95        connection_module.CLIENT_INFO_HEADER: user_agent,
96    }
97    api_url = build_api_url(project, method, base_url)
98
99    requester = http.request
100
101    if retry is not None:
102        requester = retry(requester)
103
104    if timeout is not None:
105        response = requester(
106            url=api_url, method="POST", headers=headers, data=data, timeout=timeout,
107        )
108    else:
109        response = requester(url=api_url, method="POST", headers=headers, data=data)
110
111    if response.status_code != 200:
112        error_status = status_pb2.Status.FromString(response.content)
113        raise exceptions.from_http_status(
114            response.status_code, error_status.message, errors=[error_status]
115        )
116
117    return response.content
118
119
120def _rpc(
121    http,
122    project,
123    method,
124    base_url,
125    client_info,
126    request_pb,
127    response_pb_cls,
128    retry=None,
129    timeout=None,
130):
131    """Make a protobuf RPC request.
132
133    :type http: :class:`requests.Session`
134    :param http: HTTP object to make requests.
135
136    :type project: str
137    :param project: The project to connect to. This is
138                    usually your project name in the cloud console.
139
140    :type method: str
141    :param method: The name of the method to invoke.
142
143    :type base_url: str
144    :param base_url: The base URL where the API lives.
145
146    :type client_info: :class:`google.api_core.client_info.ClientInfo`
147    :param client_info: used to generate user agent.
148
149    :type request_pb: :class:`google.protobuf.message.Message` instance
150    :param request_pb: the protobuf instance representing the request.
151
152    :type response_pb_cls: A :class:`google.protobuf.message.Message`
153                           subclass.
154    :param response_pb_cls: The class used to unmarshall the response
155                            protobuf.
156
157    :type retry: :class:`google.api_core.retry.Retry`
158    :param retry: (Optional) retry policy for the request
159
160    :type timeout: float or tuple(float, float)
161    :param timeout: (Optional) timeout for the request
162
163    :rtype: :class:`google.protobuf.message.Message`
164    :returns: The RPC message parsed from the response.
165    """
166    req_data = request_pb._pb.SerializeToString()
167    kwargs = _make_retry_timeout_kwargs(retry, timeout)
168    response = _request(
169        http, project, method, req_data, base_url, client_info, **kwargs
170    )
171    return response_pb_cls.deserialize(response)
172
173
174def build_api_url(project, method, base_url):
175    """Construct the URL for a particular API call.
176
177    This method is used internally to come up with the URL to use when
178    making RPCs to the Cloud Datastore API.
179
180    :type project: str
181    :param project: The project to connect to. This is
182                    usually your project name in the cloud console.
183
184    :type method: str
185    :param method: The API method to call (e.g. 'runQuery', 'lookup').
186
187    :type base_url: str
188    :param base_url: The base URL where the API lives.
189
190    :rtype: str
191    :returns: The API URL created.
192    """
193    return API_URL_TEMPLATE.format(
194        api_base=base_url, api_version=API_VERSION, project=project, method=method
195    )
196
197
198class HTTPDatastoreAPI(object):
199    """An API object that sends proto-over-HTTP requests.
200
201    Intended to provide the same methods as the GAPIC ``DatastoreClient``.
202
203    :type client: :class:`~google.cloud.datastore.client.Client`
204    :param client: The client that provides configuration.
205    """
206
207    def __init__(self, client):
208        self.client = client
209
210    def lookup(self, request, retry=None, timeout=None):
211        """Perform a ``lookup`` request.
212
213        :type request: :class:`_datastore_pb2.LookupRequest` or dict
214        :param request:
215            Parameter bundle for API request.
216
217        :type retry: :class:`google.api_core.retry.Retry`
218        :param retry: (Optional) retry policy for the request
219
220        :type timeout: float or tuple(float, float)
221        :param timeout: (Optional) timeout for the request
222
223        :rtype: :class:`.datastore_pb2.LookupResponse`
224        :returns: The returned protobuf response object.
225        """
226        request_pb = _make_request_pb(request, _datastore_pb2.LookupRequest)
227        project_id = request_pb.project_id
228
229        return _rpc(
230            self.client._http,
231            project_id,
232            "lookup",
233            self.client._base_url,
234            self.client._client_info,
235            request_pb,
236            _datastore_pb2.LookupResponse,
237            retry=retry,
238            timeout=timeout,
239        )
240
241    def run_query(self, request, retry=None, timeout=None):
242        """Perform a ``runQuery`` request.
243
244        :type request: :class:`_datastore_pb2.BeginTransactionRequest` or dict
245        :param request:
246            Parameter bundle for API request.
247
248        :type retry: :class:`google.api_core.retry.Retry`
249        :param retry: (Optional) retry policy for the request
250
251        :type timeout: float or tuple(float, float)
252        :param timeout: (Optional) timeout for the request
253
254        :rtype: :class:`.datastore_pb2.RunQueryResponse`
255        :returns: The returned protobuf response object.
256        """
257        request_pb = _make_request_pb(request, _datastore_pb2.RunQueryRequest)
258        project_id = request_pb.project_id
259
260        return _rpc(
261            self.client._http,
262            project_id,
263            "runQuery",
264            self.client._base_url,
265            self.client._client_info,
266            request_pb,
267            _datastore_pb2.RunQueryResponse,
268            retry=retry,
269            timeout=timeout,
270        )
271
272    def begin_transaction(self, request, retry=None, timeout=None):
273        """Perform a ``beginTransaction`` request.
274
275        :type request: :class:`_datastore_pb2.BeginTransactionRequest` or dict
276        :param request:
277            Parameter bundle for API request.
278
279        :type retry: :class:`google.api_core.retry.Retry`
280        :param retry: (Optional) retry policy for the request
281
282        :type timeout: float or tuple(float, float)
283        :param timeout: (Optional) timeout for the request
284
285        :rtype: :class:`.datastore_pb2.BeginTransactionResponse`
286        :returns: The returned protobuf response object.
287        """
288        request_pb = _make_request_pb(request, _datastore_pb2.BeginTransactionRequest)
289        project_id = request_pb.project_id
290
291        return _rpc(
292            self.client._http,
293            project_id,
294            "beginTransaction",
295            self.client._base_url,
296            self.client._client_info,
297            request_pb,
298            _datastore_pb2.BeginTransactionResponse,
299            retry=retry,
300            timeout=timeout,
301        )
302
303    def commit(self, request, retry=None, timeout=None):
304        """Perform a ``commit`` request.
305
306        :type request: :class:`_datastore_pb2.CommitRequest` or dict
307        :param request:
308            Parameter bundle for API request.
309
310        :type retry: :class:`google.api_core.retry.Retry`
311        :param retry: (Optional) retry policy for the request
312
313        :type timeout: float or tuple(float, float)
314        :param timeout: (Optional) timeout for the request
315
316        :rtype: :class:`.datastore_pb2.CommitResponse`
317        :returns: The returned protobuf response object.
318        """
319        request_pb = _make_request_pb(request, _datastore_pb2.CommitRequest)
320        project_id = request_pb.project_id
321
322        return _rpc(
323            self.client._http,
324            project_id,
325            "commit",
326            self.client._base_url,
327            self.client._client_info,
328            request_pb,
329            _datastore_pb2.CommitResponse,
330            retry=retry,
331            timeout=timeout,
332        )
333
334    def rollback(self, request, retry=None, timeout=None):
335        """Perform a ``rollback`` request.
336
337        :type request: :class:`_datastore_pb2.RollbackRequest` or dict
338        :param request:
339            Parameter bundle for API request.
340
341        :type retry: :class:`google.api_core.retry.Retry`
342        :param retry: (Optional) retry policy for the request
343
344        :type timeout: float or tuple(float, float)
345        :param timeout: (Optional) timeout for the request
346
347        :rtype: :class:`.datastore_pb2.RollbackResponse`
348        :returns: The returned protobuf response object.
349        """
350        request_pb = _make_request_pb(request, _datastore_pb2.RollbackRequest)
351        project_id = request_pb.project_id
352
353        return _rpc(
354            self.client._http,
355            project_id,
356            "rollback",
357            self.client._base_url,
358            self.client._client_info,
359            request_pb,
360            _datastore_pb2.RollbackResponse,
361            retry=retry,
362            timeout=timeout,
363        )
364
365    def allocate_ids(self, request, retry=None, timeout=None):
366        """Perform an ``allocateIds`` request.
367
368        :type request: :class:`_datastore_pb2.AllocateIdsRequest` or dict
369        :param request:
370            Parameter bundle for API request.
371
372        :type retry: :class:`google.api_core.retry.Retry`
373        :param retry: (Optional) retry policy for the request
374
375        :type timeout: float or tuple(float, float)
376        :param timeout: (Optional) timeout for the request
377
378        :rtype: :class:`.datastore_pb2.AllocateIdsResponse`
379        :returns: The returned protobuf response object.
380        """
381        request_pb = _make_request_pb(request, _datastore_pb2.AllocateIdsRequest)
382        project_id = request_pb.project_id
383
384        return _rpc(
385            self.client._http,
386            project_id,
387            "allocateIds",
388            self.client._base_url,
389            self.client._client_info,
390            request_pb,
391            _datastore_pb2.AllocateIdsResponse,
392            retry=retry,
393            timeout=timeout,
394        )
395
396    def reserve_ids(self, request, retry=None, timeout=None):
397        """Perform an ``reserveIds`` request.
398
399        :type request: :class:`_datastore_pb2.ReserveIdsRequest` or dict
400        :param request:
401            Parameter bundle for API request.
402
403        :type retry: :class:`google.api_core.retry.Retry`
404        :param retry: (Optional) retry policy for the request
405
406        :type timeout: float or tuple(float, float)
407        :param timeout: (Optional) timeout for the request
408
409        :rtype: :class:`.datastore_pb2.ReserveIdsResponse`
410        :returns: The returned protobuf response object.
411        """
412        request_pb = _make_request_pb(request, _datastore_pb2.ReserveIdsRequest)
413        project_id = request_pb.project_id
414
415        return _rpc(
416            self.client._http,
417            project_id,
418            "reserveIds",
419            self.client._base_url,
420            self.client._client_info,
421            request_pb,
422            _datastore_pb2.ReserveIdsResponse,
423            retry=retry,
424            timeout=timeout,
425        )
426