From 9d3ca4843ac829fab669a54405fc4c0f986a5c2d Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:39:34 -0400 Subject: [PATCH] feat: Add support for REST transport (#68) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: enable REST transport for Python Preview clients PiperOrigin-RevId: 474571730 Source-Link: https://github.com/googleapis/googleapis/commit/5a9ee4d5deca8e3da550b0419ed336e22521fc8e Source-Link: https://github.com/googleapis/googleapis-gen/commit/ceafe521f137680fdee2f9ca9e1947cdd825070d Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiY2VhZmU1MjFmMTM3NjgwZmRlZTJmOWNhOWUxOTQ3Y2RkODI1MDcwZCJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix(deps): require google-api-core>=1.33.1,>=2.8.0 * fix(deps): require protobuf >= 3.20.1 Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .../gapic_metadata.json | 15 + .../services/publisher/client.py | 5 + .../services/publisher/transports/__init__.py | 5 + .../services/publisher/transports/rest.py | 466 ++++++++++++++++++ ...channel_connection_events_sync_538f0722.py | 51 ++ ...channel_connection_events_sync_7148f0f3.py | 51 ++ ..._publisher_publish_events_sync_a73f5992.py | 51 ++ ..._publisher_publish_events_sync_bfdc22ee.py | 51 ++ ...ippet_metadata_eventarc publishing_v1.json | 160 +++++- .../google-cloud-eventarc-publishing/setup.py | 4 +- .../testing/constraints-3.7.txt | 4 +- .../eventarc_publishing_v1/test_publisher.py | 332 ++++++++++++- 12 files changed, 1183 insertions(+), 12 deletions(-) create mode 100644 packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/rest.py create mode 100644 packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_538f0722.py create mode 100644 packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_7148f0f3.py create mode 100644 packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_a73f5992.py create mode 100644 packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_bfdc22ee.py diff --git a/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/gapic_metadata.json b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/gapic_metadata.json index 1f2fd6de4807..e3ed38d1e4ce 100644 --- a/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/gapic_metadata.json +++ b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/gapic_metadata.json @@ -36,6 +36,21 @@ ] } } + }, + "rest": { + "libraryClient": "PublisherClient", + "rpcs": { + "PublishChannelConnectionEvents": { + "methods": [ + "publish_channel_connection_events" + ] + }, + "PublishEvents": { + "methods": [ + "publish_events" + ] + } + } } } } diff --git a/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/client.py b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/client.py index 12f62e8eaed0..b9f50d639ae0 100644 --- a/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/client.py +++ b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/client.py @@ -38,6 +38,7 @@ from .transports.base import PublisherTransport, DEFAULT_CLIENT_INFO from .transports.grpc import PublisherGrpcTransport from .transports.grpc_asyncio import PublisherGrpcAsyncIOTransport +from .transports.rest import PublisherRestTransport class PublisherClientMeta(type): @@ -51,6 +52,7 @@ class PublisherClientMeta(type): _transport_registry = OrderedDict() # type: Dict[str, Type[PublisherTransport]] _transport_registry["grpc"] = PublisherGrpcTransport _transport_registry["grpc_asyncio"] = PublisherGrpcAsyncIOTransport + _transport_registry["rest"] = PublisherRestTransport def get_transport_class( cls, @@ -346,6 +348,9 @@ def __init__( transport (Union[str, PublisherTransport]): The transport to use. If set to None, a transport is chosen automatically. + NOTE: "rest" transport functionality is currently in a + beta state (preview). We welcome your feedback via an + issue in this library's source repository. client_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the diff --git a/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/__init__.py b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/__init__.py index e73fe8901f80..8a2b06839029 100644 --- a/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/__init__.py +++ b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/__init__.py @@ -19,15 +19,20 @@ from .base import PublisherTransport from .grpc import PublisherGrpcTransport from .grpc_asyncio import PublisherGrpcAsyncIOTransport +from .rest import PublisherRestTransport +from .rest import PublisherRestInterceptor # Compile a registry of transports. _transport_registry = OrderedDict() # type: Dict[str, Type[PublisherTransport]] _transport_registry["grpc"] = PublisherGrpcTransport _transport_registry["grpc_asyncio"] = PublisherGrpcAsyncIOTransport +_transport_registry["rest"] = PublisherRestTransport __all__ = ( "PublisherTransport", "PublisherGrpcTransport", "PublisherGrpcAsyncIOTransport", + "PublisherRestTransport", + "PublisherRestInterceptor", ) diff --git a/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/rest.py b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/rest.py new file mode 100644 index 000000000000..b11435fe8ec8 --- /dev/null +++ b/packages/google-cloud-eventarc-publishing/google/cloud/eventarc_publishing_v1/services/publisher/transports/rest.py @@ -0,0 +1,466 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from google.auth.transport.requests import AuthorizedSession # type: ignore +import json # type: ignore +import grpc # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.api_core import exceptions as core_exceptions +from google.api_core import retry as retries +from google.api_core import rest_helpers +from google.api_core import rest_streaming +from google.api_core import path_template +from google.api_core import gapic_v1 + +from google.protobuf import json_format +from requests import __version__ as requests_version +import dataclasses +import re +from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union +import warnings + +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object] # type: ignore + + +from google.cloud.eventarc_publishing_v1.types import publisher + +from .base import PublisherTransport, DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + + +DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, + grpc_version=None, + rest_version=requests_version, +) + + +class PublisherRestInterceptor: + """Interceptor for Publisher. + + Interceptors are used to manipulate requests, request metadata, and responses + in arbitrary ways. + Example use cases include: + * Logging + * Verifying requests according to service or custom semantics + * Stripping extraneous information from responses + + These use cases and more can be enabled by injecting an + instance of a custom subclass when constructing the PublisherRestTransport. + + .. code-block:: python + class MyCustomPublisherInterceptor(PublisherRestInterceptor): + def pre_publish_channel_connection_events(request, metadata): + logging.log(f"Received request: {request}") + return request, metadata + + def post_publish_channel_connection_events(response): + logging.log(f"Received response: {response}") + + def pre_publish_events(request, metadata): + logging.log(f"Received request: {request}") + return request, metadata + + def post_publish_events(response): + logging.log(f"Received response: {response}") + + transport = PublisherRestTransport(interceptor=MyCustomPublisherInterceptor()) + client = PublisherClient(transport=transport) + + + """ + + def pre_publish_channel_connection_events( + self, + request: publisher.PublishChannelConnectionEventsRequest, + metadata: Sequence[Tuple[str, str]], + ) -> Tuple[ + publisher.PublishChannelConnectionEventsRequest, Sequence[Tuple[str, str]] + ]: + """Pre-rpc interceptor for publish_channel_connection_events + + Override in a subclass to manipulate the request or metadata + before they are sent to the Publisher server. + """ + return request, metadata + + def post_publish_channel_connection_events( + self, response: publisher.PublishChannelConnectionEventsResponse + ) -> publisher.PublishChannelConnectionEventsResponse: + """Post-rpc interceptor for publish_channel_connection_events + + Override in a subclass to manipulate the response + after it is returned by the Publisher server but before + it is returned to user code. + """ + return response + + def pre_publish_events( + self, + request: publisher.PublishEventsRequest, + metadata: Sequence[Tuple[str, str]], + ) -> Tuple[publisher.PublishEventsRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for publish_events + + Override in a subclass to manipulate the request or metadata + before they are sent to the Publisher server. + """ + return request, metadata + + def post_publish_events( + self, response: publisher.PublishEventsResponse + ) -> publisher.PublishEventsResponse: + """Post-rpc interceptor for publish_events + + Override in a subclass to manipulate the response + after it is returned by the Publisher server but before + it is returned to user code. + """ + return response + + +@dataclasses.dataclass +class PublisherRestStub: + _session: AuthorizedSession + _host: str + _interceptor: PublisherRestInterceptor + + +class PublisherRestTransport(PublisherTransport): + """REST backend transport for Publisher. + + Eventarc processes events generated by an event provider and + delivers them to a subscriber. + + An event provider is a software-as-a-service (SaaS) system or + product that can generate and deliver events through Eventarc. + A third-party event provider is an event provider from outside + of Google. + A partner is a third-party event provider that is integrated + with Eventarc. + A subscriber is a GCP customer interested in receiving events. + Channel is a first-class Eventarc resource that is created and + managed by the subscriber in their GCP project. A Channel + represents a subscriber's intent to receive events from an event + provider. A Channel is associated with exactly one event + provider. + + ChannelConnection is a first-class Eventarc resource that is + created and managed by the partner in their GCP project. A + ChannelConnection represents a connection between a partner and + a subscriber's Channel. A ChannelConnection has a one-to-one + mapping with a Channel. + + Publisher allows an event provider to publish events to + Eventarc. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends JSON representations of protocol buffers over HTTP/1.1 + + NOTE: This REST transport functionality is currently in a beta + state (preview). We welcome your feedback via an issue in this + library's source repository. Thank you! + """ + + def __init__( + self, + *, + host: str = "eventarcpublishing.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + url_scheme: str = "https", + interceptor: Optional[PublisherRestInterceptor] = None, + api_audience: Optional[str] = None, + ) -> None: + """Instantiate the transport. + + NOTE: This REST transport functionality is currently in a beta + state (preview). We welcome your feedback via a GitHub issue in + this library's repository. Thank you! + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client + certificate to configure mutual TLS HTTP channel. It is ignored + if ``channel`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you are developing + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + url_scheme: the protocol scheme for the API endpoint. Normally + "https", but for testing or local servers, + "http" can be specified. + """ + # Run the base constructor + # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. + # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the + # credentials object + maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host) + if maybe_url_match is None: + raise ValueError( + f"Unexpected hostname structure: {host}" + ) # pragma: NO COVER + + url_match_items = maybe_url_match.groupdict() + + host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host + + super().__init__( + host=host, + credentials=credentials, + client_info=client_info, + always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, + ) + self._session = AuthorizedSession( + self._credentials, default_host=self.DEFAULT_HOST + ) + if client_cert_source_for_mtls: + self._session.configure_mtls_channel(client_cert_source_for_mtls) + self._interceptor = interceptor or PublisherRestInterceptor() + self._prep_wrapped_messages(client_info) + + class _PublishChannelConnectionEvents(PublisherRestStub): + def __hash__(self): + return hash("PublishChannelConnectionEvents") + + def __call__( + self, + request: publisher.PublishChannelConnectionEventsRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> publisher.PublishChannelConnectionEventsResponse: + r"""Call the publish channel + connection events method over HTTP. + + Args: + request (~.publisher.PublishChannelConnectionEventsRequest): + The request object. The request message for the + PublishChannelConnectionEvents method. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.publisher.PublishChannelConnectionEventsResponse: + The response message for the + PublishChannelConnectionEvents method. + + """ + + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{channel_connection=projects/*/locations/*/channelConnections/*}:publishEvents", + "body": "*", + }, + ] + request, metadata = self._interceptor.pre_publish_channel_connection_events( + request, metadata + ) + pb_request = publisher.PublishChannelConnectionEventsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], + including_default_value_fields=False, + use_integers_for_enums=False, + ) + uri = transcoded_request["uri"] + method = transcoded_request["method"] + + # Jsonify the query params + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + including_default_value_fields=False, + use_integers_for_enums=False, + ) + ) + + # Send the request + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(self._session, method)( + "{host}{uri}".format(host=self._host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + raise core_exceptions.from_http_response(response) + + # Return the response + resp = publisher.PublishChannelConnectionEventsResponse() + pb_resp = publisher.PublishChannelConnectionEventsResponse.pb(resp) + + json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_publish_channel_connection_events(resp) + return resp + + class _PublishEvents(PublisherRestStub): + def __hash__(self): + return hash("PublishEvents") + + def __call__( + self, + request: publisher.PublishEventsRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> publisher.PublishEventsResponse: + r"""Call the publish events method over HTTP. + + Args: + request (~.publisher.PublishEventsRequest): + The request object. The request message for the + PublishEvents method. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.publisher.PublishEventsResponse: + The response message for the + PublishEvents method. + + """ + + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{channel=projects/*/locations/*/channels/*}:publishEvents", + "body": "*", + }, + ] + request, metadata = self._interceptor.pre_publish_events(request, metadata) + pb_request = publisher.PublishEventsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], + including_default_value_fields=False, + use_integers_for_enums=False, + ) + uri = transcoded_request["uri"] + method = transcoded_request["method"] + + # Jsonify the query params + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + including_default_value_fields=False, + use_integers_for_enums=False, + ) + ) + + # Send the request + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(self._session, method)( + "{host}{uri}".format(host=self._host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + raise core_exceptions.from_http_response(response) + + # Return the response + resp = publisher.PublishEventsResponse() + pb_resp = publisher.PublishEventsResponse.pb(resp) + + json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_publish_events(resp) + return resp + + @property + def publish_channel_connection_events( + self, + ) -> Callable[ + [publisher.PublishChannelConnectionEventsRequest], + publisher.PublishChannelConnectionEventsResponse, + ]: + # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here. + # In C++ this would require a dynamic_cast + return self._PublishChannelConnectionEvents(self._session, self._host, self._interceptor) # type: ignore + + @property + def publish_events( + self, + ) -> Callable[[publisher.PublishEventsRequest], publisher.PublishEventsResponse]: + # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here. + # In C++ this would require a dynamic_cast + return self._PublishEvents(self._session, self._host, self._interceptor) # type: ignore + + @property + def kind(self) -> str: + return "rest" + + def close(self): + self._session.close() + + +__all__ = ("PublisherRestTransport",) diff --git a/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_538f0722.py b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_538f0722.py new file mode 100644 index 000000000000..47489036ea15 --- /dev/null +++ b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_538f0722.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Generated code. DO NOT EDIT! +# +# Snippet for PublishChannelConnectionEvents +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-eventarc-publishing + + +# [START eventarcpublishing_v1_generated_Publisher_PublishChannelConnectionEvents_sync_538f0722] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import eventarc_publishing_v1 + + +def sample_publish_channel_connection_events(): + # Create a client + client = eventarc_publishing_v1.PublisherClient() + + # Initialize request argument(s) + request = eventarc_publishing_v1.PublishChannelConnectionEventsRequest( + ) + + # Make the request + response = client.publish_channel_connection_events(request=request) + + # Handle the response + print(response) + +# [END eventarcpublishing_v1_generated_Publisher_PublishChannelConnectionEvents_sync_538f0722] diff --git a/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_7148f0f3.py b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_7148f0f3.py new file mode 100644 index 000000000000..e835524143bd --- /dev/null +++ b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_7148f0f3.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Generated code. DO NOT EDIT! +# +# Snippet for PublishChannelConnectionEvents +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-eventarc-publishing + + +# [START eventarcpublishing_v1_generated_Publisher_PublishChannelConnectionEvents_sync_7148f0f3] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import eventarc_publishing_v1 + + +def sample_publish_channel_connection_events(): + # Create a client + client = eventarc_publishing_v1.PublisherClient() + + # Initialize request argument(s) + request = eventarc_publishing_v1.PublishChannelConnectionEventsRequest( + ) + + # Make the request + response = client.publish_channel_connection_events(request=request) + + # Handle the response + print(response) + +# [END eventarcpublishing_v1_generated_Publisher_PublishChannelConnectionEvents_sync_7148f0f3] diff --git a/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_a73f5992.py b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_a73f5992.py new file mode 100644 index 000000000000..8e6bbc72874d --- /dev/null +++ b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_a73f5992.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Generated code. DO NOT EDIT! +# +# Snippet for PublishEvents +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-eventarc-publishing + + +# [START eventarcpublishing_v1_generated_Publisher_PublishEvents_sync_a73f5992] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import eventarc_publishing_v1 + + +def sample_publish_events(): + # Create a client + client = eventarc_publishing_v1.PublisherClient() + + # Initialize request argument(s) + request = eventarc_publishing_v1.PublishEventsRequest( + ) + + # Make the request + response = client.publish_events(request=request) + + # Handle the response + print(response) + +# [END eventarcpublishing_v1_generated_Publisher_PublishEvents_sync_a73f5992] diff --git a/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_bfdc22ee.py b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_bfdc22ee.py new file mode 100644 index 000000000000..0a8cd304d0f6 --- /dev/null +++ b/packages/google-cloud-eventarc-publishing/samples/generated_samples/eventarcpublishing_v1_generated_publisher_publish_events_sync_bfdc22ee.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Generated code. DO NOT EDIT! +# +# Snippet for PublishEvents +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-eventarc-publishing + + +# [START eventarcpublishing_v1_generated_Publisher_PublishEvents_sync_bfdc22ee] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import eventarc_publishing_v1 + + +def sample_publish_events(): + # Create a client + client = eventarc_publishing_v1.PublisherClient() + + # Initialize request argument(s) + request = eventarc_publishing_v1.PublishEventsRequest( + ) + + # Make the request + response = client.publish_events(request=request) + + # Handle the response + print(response) + +# [END eventarcpublishing_v1_generated_Publisher_PublishEvents_sync_bfdc22ee] diff --git a/packages/google-cloud-eventarc-publishing/samples/generated_samples/snippet_metadata_eventarc publishing_v1.json b/packages/google-cloud-eventarc-publishing/samples/generated_samples/snippet_metadata_eventarc publishing_v1.json index e00e5c42d0a2..d754e61ad5d0 100644 --- a/packages/google-cloud-eventarc-publishing/samples/generated_samples/snippet_metadata_eventarc publishing_v1.json +++ b/packages/google-cloud-eventarc-publishing/samples/generated_samples/snippet_metadata_eventarc publishing_v1.json @@ -125,7 +125,7 @@ "shortName": "publish_channel_connection_events" }, "description": "Sample for PublishChannelConnectionEvents", - "file": "eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync.py", + "file": "eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_538f0722.py", "language": "PYTHON", "origin": "API_DEFINITION", "regionTag": "eventarcpublishing_v1_generated_Publisher_PublishChannelConnectionEvents_sync", @@ -161,7 +161,83 @@ "type": "RESPONSE_HANDLING" } ], - "title": "eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync.py" + "title": "eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_538f0722.py" + }, + { + "canonical": true, + "clientMethod": { + "client": { + "fullName": "google.cloud.eventarc_publishing_v1.PublisherClient", + "shortName": "PublisherClient" + }, + "fullName": "google.cloud.eventarc_publishing_v1.PublisherClient.publish_channel_connection_events", + "method": { + "fullName": "google.cloud.eventarc.publishing.v1.Publisher.PublishChannelConnectionEvents", + "service": { + "fullName": "google.cloud.eventarc.publishing.v1.Publisher", + "shortName": "Publisher" + }, + "shortName": "PublishChannelConnectionEvents" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.eventarc_publishing_v1.types.PublishChannelConnectionEventsRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.eventarc_publishing_v1.types.PublishChannelConnectionEventsResponse", + "shortName": "publish_channel_connection_events" + }, + "description": "Sample for PublishChannelConnectionEvents", + "file": "eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_7148f0f3.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "eventarcpublishing_v1_generated_Publisher_PublishChannelConnectionEvents_sync", + "segments": [ + { + "end": 50, + "start": 27, + "type": "FULL" + }, + { + "end": 50, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 44, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 47, + "start": 45, + "type": "REQUEST_EXECUTION" + }, + { + "end": 51, + "start": 48, + "type": "RESPONSE_HANDLING" + } + ], + "title": "eventarcpublishing_v1_generated_publisher_publish_channel_connection_events_sync_7148f0f3.py" }, { "canonical": true, @@ -278,7 +354,83 @@ "shortName": "publish_events" }, "description": "Sample for PublishEvents", - "file": "eventarcpublishing_v1_generated_publisher_publish_events_sync.py", + "file": "eventarcpublishing_v1_generated_publisher_publish_events_sync_bfdc22ee.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "eventarcpublishing_v1_generated_Publisher_PublishEvents_sync", + "segments": [ + { + "end": 50, + "start": 27, + "type": "FULL" + }, + { + "end": 50, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 44, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 47, + "start": 45, + "type": "REQUEST_EXECUTION" + }, + { + "end": 51, + "start": 48, + "type": "RESPONSE_HANDLING" + } + ], + "title": "eventarcpublishing_v1_generated_publisher_publish_events_sync_bfdc22ee.py" + }, + { + "canonical": true, + "clientMethod": { + "client": { + "fullName": "google.cloud.eventarc_publishing_v1.PublisherClient", + "shortName": "PublisherClient" + }, + "fullName": "google.cloud.eventarc_publishing_v1.PublisherClient.publish_events", + "method": { + "fullName": "google.cloud.eventarc.publishing.v1.Publisher.PublishEvents", + "service": { + "fullName": "google.cloud.eventarc.publishing.v1.Publisher", + "shortName": "Publisher" + }, + "shortName": "PublishEvents" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.eventarc_publishing_v1.types.PublishEventsRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.eventarc_publishing_v1.types.PublishEventsResponse", + "shortName": "publish_events" + }, + "description": "Sample for PublishEvents", + "file": "eventarcpublishing_v1_generated_publisher_publish_events_sync_a73f5992.py", "language": "PYTHON", "origin": "API_DEFINITION", "regionTag": "eventarcpublishing_v1_generated_Publisher_PublishEvents_sync", @@ -314,7 +466,7 @@ "type": "RESPONSE_HANDLING" } ], - "title": "eventarcpublishing_v1_generated_publisher_publish_events_sync.py" + "title": "eventarcpublishing_v1_generated_publisher_publish_events_sync_a73f5992.py" } ] } diff --git a/packages/google-cloud-eventarc-publishing/setup.py b/packages/google-cloud-eventarc-publishing/setup.py index c8f2d4c9c0c4..a2cb8dba1f4d 100644 --- a/packages/google-cloud-eventarc-publishing/setup.py +++ b/packages/google-cloud-eventarc-publishing/setup.py @@ -26,9 +26,9 @@ url = "https://github.com/googleapis/python-eventarc-publishing" release_status = "Development Status :: 4 - Beta" dependencies = [ - "google-api-core[grpc] >= 1.32.0, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*", + "google-api-core[grpc] >= 1.33.1, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*", "proto-plus >= 1.22.0, <2.0.0dev", - "protobuf >= 3.19.0, <5.0.0dev", + "protobuf >= 3.20.1, <5.0.0dev", ] package_root = os.path.abspath(os.path.dirname(__file__)) diff --git a/packages/google-cloud-eventarc-publishing/testing/constraints-3.7.txt b/packages/google-cloud-eventarc-publishing/testing/constraints-3.7.txt index 810c7cb36c5f..12cb23a09318 100644 --- a/packages/google-cloud-eventarc-publishing/testing/constraints-3.7.txt +++ b/packages/google-cloud-eventarc-publishing/testing/constraints-3.7.txt @@ -4,6 +4,6 @@ # Pin the version to the lower bound. # e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0dev", # Then this file should have google-cloud-foo==1.14.0 -google-api-core==1.32.0 +google-api-core==1.33.1 proto-plus==1.22.0 -protobuf==3.19.0 +protobuf==3.20.1 diff --git a/packages/google-cloud-eventarc-publishing/tests/unit/gapic/eventarc_publishing_v1/test_publisher.py b/packages/google-cloud-eventarc-publishing/tests/unit/gapic/eventarc_publishing_v1/test_publisher.py index d171b671683e..298d6f275a7b 100644 --- a/packages/google-cloud-eventarc-publishing/tests/unit/gapic/eventarc_publishing_v1/test_publisher.py +++ b/packages/google-cloud-eventarc-publishing/tests/unit/gapic/eventarc_publishing_v1/test_publisher.py @@ -24,10 +24,17 @@ import grpc from grpc.experimental import aio +from collections.abc import Iterable +from google.protobuf import json_format +import json import math import pytest from proto.marshal.rules.dates import DurationRule, TimestampRule from proto.marshal.rules import wrappers +from requests import Response +from requests import Request, PreparedRequest +from requests.sessions import Session +from google.protobuf import json_format from google.api_core import client_options from google.api_core import exceptions as core_exceptions @@ -90,6 +97,7 @@ def test__get_default_mtls_endpoint(): [ (PublisherClient, "grpc"), (PublisherAsyncClient, "grpc_asyncio"), + (PublisherClient, "rest"), ], ) def test_publisher_client_from_service_account_info(client_class, transport_name): @@ -103,7 +111,11 @@ def test_publisher_client_from_service_account_info(client_class, transport_name assert client.transport._credentials == creds assert isinstance(client, client_class) - assert client.transport._host == ("eventarcpublishing.googleapis.com:443") + assert client.transport._host == ( + "eventarcpublishing.googleapis.com:443" + if transport_name in ["grpc", "grpc_asyncio"] + else "https://eventarcpublishing.googleapis.com" + ) @pytest.mark.parametrize( @@ -111,6 +123,7 @@ def test_publisher_client_from_service_account_info(client_class, transport_name [ (transports.PublisherGrpcTransport, "grpc"), (transports.PublisherGrpcAsyncIOTransport, "grpc_asyncio"), + (transports.PublisherRestTransport, "rest"), ], ) def test_publisher_client_service_account_always_use_jwt( @@ -136,6 +149,7 @@ def test_publisher_client_service_account_always_use_jwt( [ (PublisherClient, "grpc"), (PublisherAsyncClient, "grpc_asyncio"), + (PublisherClient, "rest"), ], ) def test_publisher_client_from_service_account_file(client_class, transport_name): @@ -156,13 +170,18 @@ def test_publisher_client_from_service_account_file(client_class, transport_name assert client.transport._credentials == creds assert isinstance(client, client_class) - assert client.transport._host == ("eventarcpublishing.googleapis.com:443") + assert client.transport._host == ( + "eventarcpublishing.googleapis.com:443" + if transport_name in ["grpc", "grpc_asyncio"] + else "https://eventarcpublishing.googleapis.com" + ) def test_publisher_client_get_transport_class(): transport = PublisherClient.get_transport_class() available_transports = [ transports.PublisherGrpcTransport, + transports.PublisherRestTransport, ] assert transport in available_transports @@ -179,6 +198,7 @@ def test_publisher_client_get_transport_class(): transports.PublisherGrpcAsyncIOTransport, "grpc_asyncio", ), + (PublisherClient, transports.PublisherRestTransport, "rest"), ], ) @mock.patch.object( @@ -320,6 +340,8 @@ def test_publisher_client_client_options(client_class, transport_class, transpor "grpc_asyncio", "false", ), + (PublisherClient, transports.PublisherRestTransport, "rest", "true"), + (PublisherClient, transports.PublisherRestTransport, "rest", "false"), ], ) @mock.patch.object( @@ -513,6 +535,7 @@ def test_publisher_client_get_mtls_endpoint_and_cert_source(client_class): transports.PublisherGrpcAsyncIOTransport, "grpc_asyncio", ), + (PublisherClient, transports.PublisherRestTransport, "rest"), ], ) def test_publisher_client_client_options_scopes( @@ -548,6 +571,7 @@ def test_publisher_client_client_options_scopes( "grpc_asyncio", grpc_helpers_async, ), + (PublisherClient, transports.PublisherRestTransport, "rest", None), ], ) def test_publisher_client_client_options_credentials_file( @@ -952,6 +976,255 @@ async def test_publish_events_field_headers_async(): ) in kw["metadata"] +@pytest.mark.parametrize( + "request_type", + [ + publisher.PublishChannelConnectionEventsRequest, + dict, + ], +) +def test_publish_channel_connection_events_rest(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # send a request that will satisfy transcoding + request_init = { + "channel_connection": "projects/sample1/locations/sample2/channelConnections/sample3" + } + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = publisher.PublishChannelConnectionEventsResponse() + + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 200 + pb_return_value = publisher.PublishChannelConnectionEventsResponse.pb( + return_value + ) + json_return_value = json_format.MessageToJson(pb_return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.publish_channel_connection_events(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, publisher.PublishChannelConnectionEventsResponse) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_publish_channel_connection_events_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_publish_channel_connection_events" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_publish_channel_connection_events" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = publisher.PublishChannelConnectionEventsRequest.pb( + publisher.PublishChannelConnectionEventsRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = Response() + req.return_value.status_code = 200 + req.return_value.request = PreparedRequest() + req.return_value._content = ( + publisher.PublishChannelConnectionEventsResponse.to_json( + publisher.PublishChannelConnectionEventsResponse() + ) + ) + + request = publisher.PublishChannelConnectionEventsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = publisher.PublishChannelConnectionEventsResponse() + + client.publish_channel_connection_events( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_publish_channel_connection_events_rest_bad_request( + transport: str = "rest", + request_type=publisher.PublishChannelConnectionEventsRequest, +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # send a request that will satisfy transcoding + request_init = { + "channel_connection": "projects/sample1/locations/sample2/channelConnections/sample3" + } + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.publish_channel_connection_events(request) + + +def test_publish_channel_connection_events_rest_error(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + +@pytest.mark.parametrize( + "request_type", + [ + publisher.PublishEventsRequest, + dict, + ], +) +def test_publish_events_rest(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # send a request that will satisfy transcoding + request_init = {"channel": "projects/sample1/locations/sample2/channels/sample3"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = publisher.PublishEventsResponse() + + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 200 + pb_return_value = publisher.PublishEventsResponse.pb(return_value) + json_return_value = json_format.MessageToJson(pb_return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.publish_events(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, publisher.PublishEventsResponse) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_publish_events_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_publish_events" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_publish_events" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = publisher.PublishEventsRequest.pb(publisher.PublishEventsRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = Response() + req.return_value.status_code = 200 + req.return_value.request = PreparedRequest() + req.return_value._content = publisher.PublishEventsResponse.to_json( + publisher.PublishEventsResponse() + ) + + request = publisher.PublishEventsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = publisher.PublishEventsResponse() + + client.publish_events( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_publish_events_rest_bad_request( + transport: str = "rest", request_type=publisher.PublishEventsRequest +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # send a request that will satisfy transcoding + request_init = {"channel": "projects/sample1/locations/sample2/channels/sample3"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.publish_events(request) + + +def test_publish_events_rest_error(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.PublisherGrpcTransport( @@ -1033,6 +1306,7 @@ def test_transport_get_channel(): [ transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport, + transports.PublisherRestTransport, ], ) def test_transport_adc(transport_class): @@ -1047,6 +1321,7 @@ def test_transport_adc(transport_class): "transport_name", [ "grpc", + "rest", ], ) def test_transport_kind(transport_name): @@ -1177,6 +1452,7 @@ def test_publisher_transport_auth_adc(transport_class): [ transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport, + transports.PublisherRestTransport, ], ) def test_publisher_transport_auth_gdch_credentials(transport_class): @@ -1271,11 +1547,23 @@ def test_publisher_grpc_transport_client_cert_source_for_mtls(transport_class): ) +def test_publisher_http_transport_client_cert_source_for_mtls(): + cred = ga_credentials.AnonymousCredentials() + with mock.patch( + "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel" + ) as mock_configure_mtls_channel: + transports.PublisherRestTransport( + credentials=cred, client_cert_source_for_mtls=client_cert_source_callback + ) + mock_configure_mtls_channel.assert_called_once_with(client_cert_source_callback) + + @pytest.mark.parametrize( "transport_name", [ "grpc", "grpc_asyncio", + "rest", ], ) def test_publisher_host_no_port(transport_name): @@ -1286,7 +1574,11 @@ def test_publisher_host_no_port(transport_name): ), transport=transport_name, ) - assert client.transport._host == ("eventarcpublishing.googleapis.com:443") + assert client.transport._host == ( + "eventarcpublishing.googleapis.com:443" + if transport_name in ["grpc", "grpc_asyncio"] + else "https://eventarcpublishing.googleapis.com" + ) @pytest.mark.parametrize( @@ -1294,6 +1586,7 @@ def test_publisher_host_no_port(transport_name): [ "grpc", "grpc_asyncio", + "rest", ], ) def test_publisher_host_with_port(transport_name): @@ -1304,7 +1597,36 @@ def test_publisher_host_with_port(transport_name): ), transport=transport_name, ) - assert client.transport._host == ("eventarcpublishing.googleapis.com:8000") + assert client.transport._host == ( + "eventarcpublishing.googleapis.com:8000" + if transport_name in ["grpc", "grpc_asyncio"] + else "https://eventarcpublishing.googleapis.com:8000" + ) + + +@pytest.mark.parametrize( + "transport_name", + [ + "rest", + ], +) +def test_publisher_client_transport_session_collision(transport_name): + creds1 = ga_credentials.AnonymousCredentials() + creds2 = ga_credentials.AnonymousCredentials() + client1 = PublisherClient( + credentials=creds1, + transport=transport_name, + ) + client2 = PublisherClient( + credentials=creds2, + transport=transport_name, + ) + session1 = client1.transport.publish_channel_connection_events._session + session2 = client2.transport.publish_channel_connection_events._session + assert session1 != session2 + session1 = client1.transport.publish_events._session + session2 = client2.transport.publish_events._session + assert session1 != session2 def test_publisher_grpc_transport_channel(): @@ -1567,6 +1889,7 @@ async def test_transport_close_async(): def test_transport_close(): transports = { + "rest": "_session", "grpc": "_grpc_channel", } @@ -1584,6 +1907,7 @@ def test_transport_close(): def test_client_ctx(): transports = [ + "rest", "grpc", ] for transport in transports: