8000 feat: Add support for REST transport (#290) · googleapis/python-analytics-data@1e4f58e · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Nov 9, 2024. It is now read-only.

Commit 1e4f58e

Browse files
feat: Add support for REST transport (#290)
- [ ] Regenerate this pull request now. PiperOrigin-RevId: 474644226 Source-Link: googleapis/googleapis@f90b329 Source-Link: https://github.com/googleapis/googleapis-gen/commit/4ad8763bde676f92a3eb70753ae1cfed0e81387e Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNGFkODc2M2JkZTY3NmY5MmEzZWI3MDc1M2FlMWNmZWQwZTgxMzg3ZSJ9 PiperOrigin-RevId: 474571730 Source-Link: googleapis/googleapis@5a9ee4d Source-Link: https://github.com/googleapis/googleapis-gen/commit/ceafe521f137680fdee2f9ca9e1947cdd825070d Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiY2VhZmU1MjFmMTM3NjgwZmRlZTJmOWNhOWUxOTQ3Y2RkODI1MDcwZCJ9 fix(deps): require google-api-core>=1.33.1,>=2.8.0 fix(deps): require protobuf >= 3.20.1
1 parent 8a9b4c9 commit 1e4f58e

File tree

30 files changed

+4418
-159
lines changed

30 files changed

+4418
-159
lines changed

google/analytics/data_v1alpha/gapic_metadata.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@
2626
]
2727
}
2828
}
29+
},
30+
"rest": {
31+
"libraryClient": "AlphaAnalyticsDataClient",
32+
"rpcs": {
33+
"RunFunnelReport": {
34+
"methods": [
35+
"run_funnel_report"
36+
]
37+
}
38+
}
2939
}
3040
}
3141
}

google/analytics/data_v1alpha/services/alpha_analytics_data/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from .transports.base import AlphaAnalyticsDataTransport, DEFAULT_CLIENT_INFO
4040
from .transports.grpc import AlphaAnalyticsDataGrpcTransport
4141
from .transports.grpc_asyncio import AlphaAnalyticsDataGrpcAsyncIOTransport
42+
from .transports.rest import AlphaAnalyticsDataRestTransport
4243

4344

4445
class AlphaAnalyticsDataClientMeta(type):
@@ -54,6 +55,7 @@ class AlphaAnalyticsDataClientMeta(type):
5455
) # type: Dict[str, Type[AlphaAnalyticsDataTransport]]
5556
_transport_registry["grpc"] = AlphaAnalyticsDataGrpcTransport
5657
_transport_registry["grpc_asyncio"] = AlphaAnalyticsDataGrpcAsyncIOTransport
58+
_transport_registry["rest"] = AlphaAnalyticsDataRestTransport
5759

5860
def get_transport_class(
5961
cls,
@@ -325,6 +327,9 @@ def __init__(
325327
transport (Union[str, AlphaAnalyticsDataTransport]): The
326328
transport to use. If set to None, a transport is chosen
327329
automatically.
330+
NOTE: "rest" transport functionality is currently in a
331+
beta state (preview). We welcome your feedback via an
332+
issue in this library's source repository.
328333
client_options (google.api_core.client_options.ClientOptions): Custom options for the
329334
client. It won't take effect if a ``transport`` instance is provided.
330335
(1) The ``api_endpoint`` property can be used to override the

google/analytics/data_v1alpha/services/alpha_analytics_data/transports/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from .base import AlphaAnalyticsDataTransport
2020
from .grpc import AlphaAnalyticsDataGrpcTransport
2121
10000 from .grpc_asyncio import AlphaAnalyticsDataGrpcAsyncIOTransport
22+
from .rest import AlphaAnalyticsDataRestTransport
23+
from .rest import AlphaAnalyticsDataRestInterceptor
2224

2325

2426
# Compile a registry of transports.
@@ -27,9 +29,12 @@
2729
) # type: Dict[str, Type[AlphaAnalyticsDataTransport]]
2830
_transport_registry["grpc"] = AlphaAnalyticsDataGrpcTransport
2931
_transport_registry["grpc_asyncio"] = AlphaAnalyticsDataGrpcAsyncIOTransport
32+
_transport_registry["rest"] = AlphaAnalyticsDataRestTransport
3033

3134
__all__ = (
3235
"AlphaAnalyticsDataTransport",
3336
"AlphaAnalyticsDataGrpcTransport",
3437
"AlphaAnalyticsDataGrpcAsyncIOTransport",
38+
"AlphaAnalyticsDataRestTransport",
39+
"AlphaAnalyticsDataRestInterceptor",
3540
)
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2022 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
from google.auth.transport.requests import AuthorizedSession # type: ignore
18+
import json # type: ignore
19+
import grpc # type: ignore
20+
from google.auth.transport.grpc import SslCredentials # type: ignore
21+
from google.auth import credentials as ga_credentials # type: ignore
22+
from google.api_core import exceptions as core_exceptions
23+
from google.api_core import retry as retries
24+
from google.api_core import rest_helpers
25+
from google.api_core import rest_streaming
26+
from google.api_core import path_template
27+
from google.api_core import gapic_v1
28+
29+
from google.protobuf import json_format
30+
from requests import __version__ as requests_version
31+
import dataclasses
32+
import re
33+
from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union
34+
import warnings
35+
36+
try:
37+
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault]
38+
except AttributeError: # pragma: NO COVER
39+
OptionalRetry = Union[retries.Retry, object] # type: ignore
40+
41+
42+
from google.analytics.data_v1alpha.types import analytics_data_api
43+
44+
from .base import (
45+
AlphaAnalyticsDataTransport,
46+
DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO,
47+
)
48+
49+
50+
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
51+
gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version,
52+
grpc_version=None,
53+
rest_version=requests_version,
54+
)
55+
56+
57+
class AlphaAnalyticsDataRestInterceptor:
58+
"""Interceptor for AlphaAnalyticsData.
59+
60+
Interceptors are used to manipulate requests, request metadata, and responses
61+
in arbitrary ways.
62+
Example use cases include:
63+
* Logging
64+
* Verifying requests according to service or custom semantics
65+
* Stripping extraneous information from responses
66+
67+
These use cases and more can be enabled by injecting an
68+
instance of a custom subclass when constructing the AlphaAnalyticsDataRestTransport.
69+
70+
.. code-block:: python
71+
class MyCustomAlphaAnalyticsDataInterceptor(AlphaAnalyticsDataRestInterceptor):
72+
def pre_run_funnel_report(request, metadata):
73+
logging.log(f"Received request: {request}")
74+
return request, metadata
75+
76+
def post_run_funnel_report(response):
77+
logging.log(f"Received response: {response}")
78+
79+
transport = AlphaAnalyticsDataRestTransport(interceptor=MyCustomAlphaAnalyticsDataInterceptor())
80+
client = AlphaAnalyticsDataClient(transport=transport)
81+
82+
83+
"""
84+
85+
def pre_run_funnel_report(
86+
self,
87+
request: analytics_data_api.RunFunnelReportRequest,
88+
metadata: Sequence[Tuple[str, str]],
89+
) -> Tuple[analytics_data_api.RunFunnelReportRequest, Sequence[Tuple[str, str]]]:
90+
"""Pre-rpc interceptor for run_funnel_report
91+
92+
Override in a subclass to manipulate the request or metadata
93+
before they are sent to the AlphaAnalyticsData server.
94+
"""
95+
return request, metadata
96+
97+
def post_run_funnel_report(
98+
self, response: analytics_data_api.RunFunnelReportResponse
99+
) -> analytics_data_api.RunFunnelReportResponse:
100+
"""Post-rpc interceptor for run_funnel_report
101+
102+
Override in a subclass to manipulate the response
103+
after it is returned by the AlphaAnalyticsData server but before
104+
it is returned to user code.
105+
"""
106+
return response
107+
108+
109+
@dataclasses.dataclass
110+
class AlphaAnalyticsDataRestStub:
111+
_session: AuthorizedSession
112+
_host: str
113+
_interceptor: AlphaAnalyticsDataRestInterceptor
114+
115+
116+
class AlphaAnalyticsDataRestTransport(AlphaAnalyticsDataTransport):
117+
"""REST backend transport for AlphaAnalyticsData.
118+
119+
Google Analytics reporting data service.
120+
121+
This class defines the same methods as the primary client, so the
122+
primary client can load the underlying transport implementation
123+
and call it.
124+
125+
It sends JSON representations of protocol buffers over HTTP/1.1
126+
127+
NOTE: This REST transport functionality is currently in a beta
128+
state (preview). We welcome your feedback via an issue in this
129+
library's source repository. Thank you!
130+
"""
131+
132+
def __init__(
133+
self,
134+
*,
135+
host: str = "analyticsdata.googleapis.com",
136+
credentials: ga_credentials.Credentials = None,
137+
credentials_file: str = None,
138+
scopes: Sequence[str] = None,
139+
client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
140+
quota_project_id: Optional[str] = None,
141+
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
142+
always_use_jwt_access: Optional[bool] = False,
143+
url_scheme: str = "https",
144+
interceptor: Optional[AlphaAnalyticsDataRestInterceptor] = None,
145+
api_audience: Optional[str] = None,
146+
) -> None:
147+
"""Instantiate the transport.
148+
149+
NOTE: This REST transport functionality is currently in a beta
150+
state (preview). We welcome your feedback via a GitHub issue in
151+
this library's repository. Thank you!
152+
153+
Args:
154+
host (Optional[str]):
155+
The hostname to connect to.
156+
credentials (Optional[google.auth.credentials.Credentials]): The
157+
authorization credentials to attach to requests. These
158+
credentials identify the application to the service; if none
159+
are specified, the client will attempt to ascertain the
160+
credentials from the environment.
161+
162+
credentials_file (Optional[str]): A file with credentials that can
163+
be loaded with :func:`google.auth.load_credentials_from_file`.
164+
This argument is ignored if ``channel`` is provided.
165+
scopes (Optional(Sequence[str])): A list of scopes. This argument is
166+
ignored if ``channel`` is provided.
167+
client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
168+
certificate to configure mutual TLS HTTP channel. It is ignored
169+
if ``channel`` is provided.
170+
quota_project_id (Optional[str]): An optional project to use for billing
171+
and quota.
172+
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
173+
The client info used to send a user-agent string along with
174+
API requests. If ``None``, then default info will be used.
175+
Generally, you only need to set this if you are developing
176+
your own client library.
177+
always_use_jwt_access (Optional[bool]): Whether self signed JWT should
178+
be used for service account credentials.
179+
url_scheme: the protocol scheme for the API endpoint. Normally
180+
"https", but for testing or local servers,
181+
"http" can be specified.
182+
"""
183+
# Run the base constructor
184+
# TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
185+
# TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
186+
# credentials object
187+
maybe_url_match = re.match("^(?P<scheme>http(?:s)?://)?(?P<host>.*)$", host)
188+
if maybe_url_match is None:
189+
raise ValueError(
190+
f"Unexpected hostname structure: {host}"
191+
) # pragma: NO COVER
192+
193+
url_match_items = maybe_url_match.groupdict()
194+
195+
host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
196+
197+
super().__init__(
198+
host=host,
199+
credentials=credentials,
200+
client_info=client_info,
201+
always_use_jwt_access=always_use_jwt_access,
202+
api_audience=api_audience,
203+
)
204+
self._session = AuthorizedSession(
205+
self._credentials, default_host=self.DEFAULT_HOST
206+
)
207+
if client_cert_source_for_mtls:
208+
self._session.configure_mtls_channel(client_cert_source_for_mtls)
209+
self._interceptor = interceptor or AlphaAnalyticsDataRestInterceptor()
210+
self._prep_wrapped_messages(client_info)
211+
212+
class _RunFunnelReport(AlphaAnalyticsDataRestStub):
213+
def __hash__(self):
214+
return hash("RunFunnelReport")
215+
216+
def __call__(
217+
self,
218+
request: analytics_data_api.RunFunnelReportRequest,
219+
*,
220+
retry: OptionalRetry = gapic_v1.method.DEFAULT,
221+
timeout: float = None,
222+
metadata: Sequence[Tuple[str, str]] = (),
223+
) -> analytics_data_api.RunFunnelReportResponse:
224+
r"""Call the run funnel report method over HTTP.
225+
226+
Args:
227+
request (~.analytics_data_api.RunFunnelReportRequest):
228+
The request object. The request for a funnel report.
229+
retry (google.api_core.retry.Retry): Designation of what errors, if any,
230+
should be retried.
231+
timeout (float): The timeout for this request.
232+
metadata (Sequence[Tuple[str, str]]): Strings which should be
233+
sent along with the request as metadata.
234+
235+
Returns:
236+
~.analytics_data_api.RunFunnelReportResponse:
237+
The funnel report response contains
238+
two sub reports. The two sub reports are
239+
different combinations of dimensions and
240+
metrics.
241+
242+
"""
243+
244+
http_options: List[Dict[str, str]] = [
245+
{
246+
"method": "post",
247+
"uri": "/v1alpha/{property=properties/*}:runFunnelReport",
248+
"body": "*",
249+
},
250+
]
251+
request, metadata = self._interceptor.pre_run_funnel_report(
252+
request, metadata
253+
)
254+
pb_request = analytics_data_api.RunFunnelReportRequest.pb(request)
255+
transcoded_request = path_template.transcode(http_options, pb_request)
256+
257+
# Jsonify the request body
258+
259+
body = json_format.MessageToJson(
260+
transcoded_request["body"],
261+
including_default_value_fields=False,
262+
use_integers_for_enums=False,
263+
)
264+
uri = transcoded_request["uri"]
265+
method = transcoded_request["method"]
266+
267+
# Jsonify the query params
268+
query_params = json.loads(
269+
json_format.MessageToJson(
270+
transcoded_request["query_params"],
271+
including_default_value_fields=False,
272+
use_integers_for_enums=False,
273+
)
274+
)
275+
276+
# Send the request
277+
headers = dict(metadata)
278+
headers["Content-Type"] = "application/json"
279+
response = getattr(self._session, method)(
280+
"{host}{uri}".format(host=self._host, uri=uri),
281+
timeout=timeout,
282+
headers=headers,
283+
params=rest_helpers.flatten_query_params(query_params, strict=True),
284+
data=body,
285+
)
286+
287+
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
288+
# subclass.
289+
if response.status_code >= 400:
290+
raise core_exceptions.from_http_response(response)
291+
292+
# Return the response
293+
resp = analytics_data_api.RunFunnelReportResponse()
294+
pb_resp = analytics_data_api.RunFunnelReportResponse.pb(resp)
295+
296+
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
297+
resp = self._interceptor.post_run_funnel_report(resp)
298+
return resp
299+
300+
@property
301+
def run_funnel_report(
302+
self,
303+
) -> Callable[
304+
[analytics_data_api.RunFunnelReportRequest],
305+
analytics_data_api.RunFunnelReportResponse,
306+
]:
307+
# The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
308+
# In C++ this would require a dynamic_cast
309+
return self._RunFunnelReport(self._session, self._host, self._interceptor) # type: ignore
310+
311+
@property
312+
def kind(self) -> str:
313+
return "rest"
314+
315+
def close(self):
316+
self._session.close()
317+
318+
319+
__all__ = ("AlphaAnalyticsDataRestTransport",)

0 commit comments

Comments
 (0)
0