Skip to content
This repository was archived by the owner on Jul 6, 2023. It is now read-only.

Commit e5028d3

Browse files
feat: enable "rest" transport in Python for services supporting numeric enums (#177)
* feat: enable "rest" transport in Python for services supporting numeric enums PiperOrigin-RevId: 508143576 Source-Link: googleapis/googleapis@7a702a9 Source-Link: https://github.com/googleapis/googleapis-gen/commit/6ad1279c0e7aa787ac6b66c9fd4a210692edffcd Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNmFkMTI3OWMwZTdhYTc4N2FjNmI2NmM5ZmQ0YTIxMDY5MmVkZmZjZCJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent f0b577e commit e5028d3

File tree

5 files changed

+526
-4
lines changed

5 files changed

+526
-4
lines changed

google/cloud/policytroubleshooter_v1/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": "IamCheckerClient",
32+
"rpcs": {
33+
"TroubleshootIamPolicy": {
34+
"methods": [
35+
"troubleshoot_iam_policy"
36+
]
37+
}
38+
}
2939
}
3040
}
3141
}

google/cloud/policytroubleshooter_v1/services/iam_checker/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from .transports.base import DEFAULT_CLIENT_INFO, IamCheckerTransport
5252
from .transports.grpc import IamCheckerGrpcTransport
5353
from .transports.grpc_asyncio import IamCheckerGrpcAsyncIOTransport
54+
from .transports.rest import IamCheckerRestTransport
5455

5556

5657
class IamCheckerClientMeta(type):
@@ -64,6 +65,7 @@ class IamCheckerClientMeta(type):
6465
_transport_registry = OrderedDict() # type: Dict[str, Type[IamCheckerTransport]]
6566
_transport_registry["grpc"] = IamCheckerGrpcTransport
6667
_transport_registry["grpc_asyncio"] = IamCheckerGrpcAsyncIOTransport
68+
_transport_registry["rest"] = IamCheckerRestTransport
6769

6870
def get_transport_class(
6971
cls,

google/cloud/policytroubleshooter_v1/services/iam_checker/transports/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,18 @@
1919
from .base import IamCheckerTransport
2020
from .grpc import IamCheckerGrpcTransport
2121
from .grpc_asyncio import IamCheckerGrpcAsyncIOTransport
22+
from .rest import IamCheckerRestInterceptor, IamCheckerRestTransport
2223

2324
# Compile a registry of transports.
2425
_transport_registry = OrderedDict() # type: Dict[str, Type[IamCheckerTransport]]
2526
_transport_registry["grpc"] = IamCheckerGrpcTransport
2627
_transport_registry["grpc_asyncio"] = IamCheckerGrpcAsyncIOTransport
28+
_transport_registry["rest"] = IamCheckerRestTransport
2729

2830
__all__ = (
2931
"IamCheckerTransport",
3032
"IamCheckerGrpcTransport",
3133
"IamCheckerGrpcAsyncIOTransport",
34+
"IamCheckerRestTransport",
35+
"IamCheckerRestInterceptor",
3236
)
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
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+
import dataclasses
18+
import json # type: ignore
19+
import re
20+
from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union
21+
import warnings
22+
23+
from google.api_core import gapic_v1, path_template, rest_helpers, rest_streaming
24+
from google.api_core import exceptions as core_exceptions
25+
from google.api_core import retry as retries
26+
from google.auth import credentials as ga_credentials # type: ignore
27+
from google.auth.transport.grpc import SslCredentials # type: ignore
28+
from google.auth.transport.requests import AuthorizedSession # type: ignore
29+
from google.protobuf import json_format
30+
import grpc # type: ignore
31+
from requests import __version__ as requests_version
32+
33+
try:
34+
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault]
35+
except AttributeError: # pragma: NO COVER
36+
OptionalRetry = Union[retries.Retry, object] # type: ignore
37+
38+
39+
from google.cloud.policytroubleshooter_v1.types import checker
40+
41+
from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO
42+
from .base import IamCheckerTransport
43+
44+
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
45+
gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version,
46+
grpc_version=None,
47+
rest_version=requests_version,
48+
)
49+
50+
51+
class IamCheckerRestInterceptor:
52+
"""Interceptor for IamChecker.
53+
54+
Interceptors are used to manipulate requests, request metadata, and responses
55+
in arbitrary ways.
56+
Example use cases include:
57+
* Logging
58+
* Verifying requests according to service or custom semantics
59+
* Stripping extraneous information from responses
60+
61+
These use cases and more can be enabled by injecting an
62+
instance of a custom subclass when constructing the IamCheckerRestTransport.
63+
64+
.. code-block:: python
65+
class MyCustomIamCheckerInterceptor(IamCheckerRestInterceptor):
66+
def pre_troubleshoot_iam_policy(self, request, metadata):
67+
logging.log(f"Received request: {request}")
68+
return request, metadata
69+
70+
def post_troubleshoot_iam_policy(self, response):
71+
logging.log(f"Received response: {response}")
72+
return response
73+
74+
transport = IamCheckerRestTransport(interceptor=MyCustomIamCheckerInterceptor())
75+
client = IamCheckerClient(transport=transport)
76+
77+
78+
"""
79+
80+
def pre_troubleshoot_iam_policy(
81+
self,
82+
request: checker.TroubleshootIamPolicyRequest,
83+
metadata: Sequence[Tuple[str, str]],
84+
) -> Tuple[checker.TroubleshootIamPolicyRequest, Sequence[Tuple[str, str]]]:
85+
"""Pre-rpc interceptor for troubleshoot_iam_policy
86+
87+
Override in a subclass to manipulate the request or metadata
88+
before they are sent to the IamChecker server.
89+
"""
90+
return request, metadata
91+
92+
def post_troubleshoot_iam_policy(
93+
self, response: checker.TroubleshootIamPolicyResponse
94+
) -> checker.TroubleshootIamPolicyResponse:
95+
"""Post-rpc interceptor for troubleshoot_iam_policy
96+
97+
Override in a subclass to manipulate the response
98+
after it is returned by the IamChecker server but before
99+
it is returned to user code.
100+
"""
101+
return response
102+
103+
104+
@dataclasses.dataclass
105+
class IamCheckerRestStub:
106+
_session: AuthorizedSession
107+
_host: str
108+
_interceptor: IamCheckerRestInterceptor
109+
110+
111+
class IamCheckerRestTransport(IamCheckerTransport):
112+
"""REST backend transport for IamChecker.
113+
114+
IAM Policy Troubleshooter service.
115+
This service helps you troubleshoot access issues for Google
116+
Cloud resources.
117+
118+
This class defines the same methods as the primary client, so the
119+
primary client can load the underlying transport implementation
120+
and call it.
121+
122+
It sends JSON representations of protocol buffers over HTTP/1.1
123+
124+
"""
125+
126+
def __init__(
127+
self,
128+
*,
129+
host: str = "policytroubleshooter.googleapis.com",
130+
credentials: Optional[ga_credentials.Credentials] = None,
131+
credentials_file: Optional[str] = None,
132+
scopes: Optional[Sequence[str]] = None,
133+
client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
134+
quota_project_id: Optional[str] = None,
135+
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
136+
always_use_jwt_access: Optional[bool] = False,
137+
url_scheme: str = "https",
138+
interceptor: Optional[IamCheckerRestInterceptor] = None,
139+
api_audience: Optional[str] = None,
140+
) -> None:
141+
"""Instantiate the transport.
142+
143+
Args:
144+
host (Optional[str]):
145+
The hostname to connect to.
146+
credentials (Optional[google.auth.credentials.Credentials]): The
147+
authorization credentials to attach to requests. These
148+
credentials identify the application to the service; if none
149+
are specified, the client will attempt to ascertain the
150+
credentials from the environment.
151+
152+
credentials_file (Optional[str]): A file with credentials that can
153+
be loaded with :func:`google.auth.load_credentials_from_file`.
154+
This argument is ignored if ``channel`` is provided.
155+
scopes (Optional(Sequence[str])): A list of scopes. This argument is
156+
ignored if ``channel`` is provided.
157+
client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
158+
certificate to configure mutual TLS HTTP channel. It is ignored
159+
if ``channel`` is provided.
160+
quota_project_id (Optional[str]): An optional project to use for billing
161+
and quota.
162+
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
163+
The client info used to send a user-agent string along with
164+
API requests. If ``None``, then default info will be used.
165+
Generally, you only need to set this if you are developing
166+
your own client library.
167+
always_use_jwt_access (Optional[bool]): Whether self signed JWT should
168+
be used for service account credentials.
169+
url_scheme: the protocol scheme for the API endpoint. Normally
170+
"https", but for testing or local servers,
171+
"http" can be specified.
172+
"""
173+
# Run the base constructor
174+
# TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
175+
# TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
176+
# credentials object
177+
maybe_url_match = re.match("^(?P<scheme>http(?:s)?://)?(?P<host>.*)$", host)
178+
if maybe_url_match is None:
179+
raise ValueError(
180+
f"Unexpected hostname structure: {host}"
181+
) # pragma: NO COVER
182+
183+
url_match_items = maybe_url_match.groupdict()
184+
185+
host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
186+
187+
super().__init__(
188+
host=host,
189+
credentials=credentials,
190+
client_info=client_info,
191+
always_use_jwt_access=always_use_jwt_access,
192+
api_audience=api_audience,
193+
)
194+
self._session = AuthorizedSession(
195+
self._credentials, default_host=self.DEFAULT_HOST
196+
)
197+
if client_cert_source_for_mtls:
198+
self._session.configure_mtls_channel(client_cert_source_for_mtls)
199+
self._interceptor = interceptor or IamCheckerRestInterceptor()
200+
self._prep_wrapped_messages(client_info)
201+
202+
class _TroubleshootIamPolicy(IamCheckerRestStub):
203+
def __hash__(self):
204+
return hash("TroubleshootIamPolicy")
205+
206+
def __call__(
207+
self,
208+
request: checker.TroubleshootIamPolicyRequest,
209+
*,
210+
retry: OptionalRetry = gapic_v1.method.DEFAULT,
211+
timeout: Optional[float] = None,
212+
metadata: Sequence[Tuple[str, str]] = (),
213+
) -> checker.TroubleshootIamPolicyResponse:
214+
r"""Call the troubleshoot iam policy method over HTTP.
215+
216+
Args:
217+
request (~.checker.TroubleshootIamPolicyRequest):
218+
The request object. Request for
219+
[TroubleshootIamPolicy][google.cloud.policytroubleshooter.v1.IamChecker.TroubleshootIamPolicy].
220+
221+
retry (google.api_core.retry.Retry): Designation of what errors, if any,
222+
should be retried.
223+
timeout (float): The timeout for this request.
224+
metadata (Sequence[Tuple[str, str]]): Strings which should be
225+
sent along with the request as metadata.
226+
227+
Returns:
228+
~.checker.TroubleshootIamPolicyResponse:
229+
Response for
230+
[TroubleshootIamPolicy][google.cloud.policytroubleshooter.v1.IamChecker.TroubleshootIamPolicy].
231+
232+
"""
233+
234+
http_options: List[Dict[str, str]] = [
235+
{
236+
"method": "post",
237+
"uri": "/v1/iam:troubleshoot",
238+
"body": "*",
239+
},
240+
]
241+
request, metadata = self._interceptor.pre_troubleshoot_iam_policy(
242+
request, metadata
243+
)
244+
pb_request = checker.TroubleshootIamPolicyRequest.pb(request)
245+
transcoded_request = path_template.transcode(http_options, pb_request)
246+
247+
# Jsonify the request body
248+
249+
body = json_format.MessageToJson(
250+
transcoded_request["body"],
251+
including_default_value_fields=False,
252+
use_integers_for_enums=True,
253+
)
254+
uri = transcoded_request["uri"]
255+
method = transcoded_request["method"]
256+
257+
# Jsonify the query params
258+
query_params = json.loads(
259+
json_format.MessageToJson(
260+
transcoded_request["query_params"],
261+
including_default_value_fields=False,
262+
use_integers_for_enums=True,
263+
)
264+
)
265+
266+
query_params["$alt"] = "json;enum-encoding=int"
267+
268+
# Send the request
269+
headers = dict(metadata)
270+
headers["Content-Type"] = "application/json"
271+
response = getattr(self._session, method)(
272+
"{host}{uri}".format(host=self._host, uri=uri),
273+
timeout=timeout,
274+
headers=headers,
275+
params=rest_helpers.flatten_query_params(query_params, strict=True),
276+
data=body,
277+
)
278+
279+
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
280+
# subclass.
281+
if response.status_code >= 400:
282+
raise core_exceptions.from_http_response(response)
283+
284+
# Return the response
285+
resp = checker.TroubleshootIamPolicyResponse()
286+
pb_resp = checker.TroubleshootIamPolicyResponse.pb(resp)
287+
288+
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
289+
resp = self._interceptor.post_troubleshoot_iam_policy(resp)
290+
return resp
291+
292+
@property
293+
def troubleshoot_iam_policy(
294+
self,
295+
) -> Callable[
296+
[checker.TroubleshootIamPolicyRequest], checker.TroubleshootIamPolicyResponse
297+
]:
298+
# The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
299+
# In C++ this would require a dynamic_cast
300+
return self._TroubleshootIamPolicy(self._session, self._host, self._interceptor) # type: ignore
301+
302+
@property
303+
def kind(self) -> str:
304+
return "rest"
305+
306+
def close(self):
307+
self._session.close()
308+
309+
310+
__all__ = ("IamCheckerRestTransport",)

0 commit comments

Comments
 (0)