diff --git a/.coveragerc b/.coveragerc index dd39c8546..580a30e10 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,35 +1,18 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 -# -# https://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 by synthtool. DO NOT EDIT! [run] branch = True [report] fail_under = 100 show_missing = True +omit = + google/pubsub/__init__.py exclude_lines = # Re-enable the standard pragma pragma: NO COVER # Ignore debug-only repr def __repr__ - # Ignore abstract methods - raise NotImplementedError -omit = - */gapic/*.py - */proto/*.py - */core/*.py - */site-packages/*.py \ No newline at end of file + # Ignore pkg_resources exceptions. + # This is added at the module level as a safeguard for if someone + # generates the code and tries to run it without pip installing. This + # makes it virtually impossible to test properly. + except pkg_resources.DistributionNotFound diff --git a/.github/header-checker-lint.yml b/.github/header-checker-lint.yml new file mode 100644 index 000000000..fc281c05b --- /dev/null +++ b/.github/header-checker-lint.yml @@ -0,0 +1,15 @@ +{"allowedCopyrightHolders": ["Google LLC"], + "allowedLicenses": ["Apache-2.0", "MIT", "BSD-3"], + "ignoreFiles": ["**/requirements.txt", "**/requirements-test.txt"], + "sourceFileExtensions": [ + "ts", + "js", + "java", + "sh", + "Dockerfile", + "yaml", + "py", + "html", + "txt" + ] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index b9daa52f1..b4243ced7 100644 --- a/.gitignore +++ b/.gitignore @@ -50,8 +50,10 @@ docs.metadata # Virtual environment env/ + +# Test logs coverage.xml -sponge_log.xml +*sponge_log.xml # System test environment variables. system_tests/local_test_setup diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 95bc0a438..8286412b6 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -15,7 +15,11 @@ set -eo pipefail -cd github/python-pubsub +if [[ -z "${PROJECT_ROOT:-}" ]]; then + PROJECT_ROOT="github/python-pubsub" +fi + +cd "${PROJECT_ROOT}" # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 @@ -30,16 +34,26 @@ export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") # Remove old nox -python3.6 -m pip uninstall --yes --quiet nox-automation +python3 -m pip uninstall --yes --quiet nox-automation # Install nox -python3.6 -m pip install --upgrade --quiet nox -python3.6 -m nox --version +python3 -m pip install --upgrade --quiet nox +python3 -m nox --version + +# If this is a continuous build, send the test log to the FlakyBot. +# See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. +if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]]; then + cleanup() { + chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot + $KOKORO_GFILE_DIR/linux_amd64/flakybot + } + trap cleanup EXIT HUP +fi # If NOX_SESSION is set, it only runs the specified session, # otherwise run all the sessions. if [[ -n "${NOX_SESSION:-}" ]]; then - python3.6 -m nox -s "${NOX_SESSION:-}" + python3 -m nox -s ${NOX_SESSION:-} else - python3.6 -m nox + python3 -m nox fi diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 111810782..2c532d9db 100644 --- a/.kokoro/docs/docs-presubmit.cfg +++ b/.kokoro/docs/docs-presubmit.cfg @@ -15,3 +15,14 @@ env_vars: { key: "TRAMPOLINE_IMAGE_UPLOAD" value: "false" } + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/build.sh" +} + +# Only run this nox session. +env_vars: { + key: "NOX_SESSION" + value: "docs docfx" +} diff --git a/.trampolinerc b/.trampolinerc index 995ee2911..383b6ec89 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -24,6 +24,7 @@ required_envvars+=( pass_down_envvars+=( "STAGING_BUCKET" "V2_STAGING_BUCKET" + "NOX_SESSION" ) # Prevent unintentional override on the default image. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 290271295..6dbc9d2d6 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -70,9 +70,14 @@ We use `nox `__ to instrument our tests. - To test your changes, run unit tests with ``nox``:: $ nox -s unit-2.7 - $ nox -s unit-3.7 + $ nox -s unit-3.8 $ ... +- Args to pytest can be passed through the nox command separated by a `--`. For + example, to run a single test:: + + $ nox -s unit-3.8 -- -k + .. note:: The unit tests and system tests are described in the @@ -93,8 +98,12 @@ On Debian/Ubuntu:: ************ Coding Style ************ +- We use the automatic code formatter ``black``. You can run it using + the nox session ``blacken``. This will eliminate many lint errors. Run via:: + + $ nox -s blacken -- PEP8 compliance, with exceptions defined in the linter configuration. +- PEP8 compliance is required, with exceptions defined in the linter configuration. If you have ``nox`` installed, you can test that you have not introduced any non-compliant code via:: @@ -133,13 +142,18 @@ Running System Tests - To run system tests, you can execute:: - $ nox -s system-3.7 + # Run all system tests + $ nox -s system-3.8 $ nox -s system-2.7 + # Run a single system test + $ nox -s system-3.8 -- -k + + .. note:: System tests are only configured to run under Python 2.7 and - Python 3.7. For expediency, we do not run them in older versions + Python 3.8. For expediency, we do not run them in older versions of Python 3. This alone will not run the tests. You'll need to change some local diff --git a/LICENSE b/LICENSE index a8ee855de..d64569567 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ - Apache License + + Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -192,7 +193,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 + 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, diff --git a/MANIFEST.in b/MANIFEST.in index e9e29d120..e783f4c62 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,10 +16,10 @@ # Generated by synthtool. DO NOT EDIT! include README.rst LICENSE -recursive-include google *.json *.proto +recursive-include google *.json *.proto py.typed recursive-include tests * global-exclude *.py[co] global-exclude __pycache__ # Exclude scripts for samples readmegen -prune scripts/readme-gen \ No newline at end of file +prune scripts/readme-gen diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 0abaf229f..bcd37bbd3 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,4 +1,9 @@ div#python2-eol { border-color: red; border-width: medium; -} \ No newline at end of file +} + +/* Ensure minimum width for 'Parameters' / 'Returns' column */ +dl.field-list > dt { + min-width: 100px +} diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 0597b6e88..f8572a448 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -78,6 +78,7 @@ class PublisherAsyncClient: PublisherClient.parse_common_location_path ) + from_service_account_info = PublisherClient.from_service_account_info from_service_account_file = PublisherClient.from_service_account_file from_service_account_json = from_service_account_file @@ -156,7 +157,7 @@ async def create_topic( (https://cloud.google.com/pubsub/docs/admin#resource_names). Args: - request (:class:`~.pubsub.Topic`): + request (:class:`google.pubsub_v1.types.Topic`): The request object. A topic resource. name (:class:`str`): Required. The name of the topic. It must have the format @@ -167,6 +168,7 @@ async def create_topic( plus (``+``) or percent signs (``%``). It must be between 3 and 255 characters in length, and it must not start with ``"goog"``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -178,7 +180,7 @@ async def create_topic( sent along with the request as metadata. Returns: - ~.pubsub.Topic: + google.pubsub_v1.types.Topic: A topic resource. """ # Create or coerce a protobuf request object. @@ -237,7 +239,7 @@ async def update_topic( properties of a topic are not modifiable. Args: - request (:class:`~.pubsub.UpdateTopicRequest`): + request (:class:`google.pubsub_v1.types.UpdateTopicRequest`): The request object. Request for the UpdateTopic method. retry (google.api_core.retry.Retry): Designation of what errors, if any, @@ -247,7 +249,7 @@ async def update_topic( sent along with the request as metadata. Returns: - ~.pubsub.Topic: + google.pubsub_v1.types.Topic: A topic resource. """ # Create or coerce a protobuf request object. @@ -296,16 +298,17 @@ async def publish( the topic does not exist. Args: - request (:class:`~.pubsub.PublishRequest`): + request (:class:`google.pubsub_v1.types.PublishRequest`): The request object. Request for the Publish method. topic (:class:`str`): Required. The messages in the request will be published on this topic. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - messages (:class:`Sequence[~.pubsub.PubsubMessage]`): + messages (:class:`Sequence[google.pubsub_v1.types.PubsubMessage]`): Required. The messages to publish. This corresponds to the ``messages`` field on the ``request`` instance; if ``request`` is provided, this @@ -318,8 +321,8 @@ async def publish( sent along with the request as metadata. Returns: - ~.pubsub.PublishResponse: - Response for the ``Publish`` method. + google.pubsub_v1.types.PublishResponse: + Response for the Publish method. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have @@ -388,11 +391,12 @@ async def get_topic( r"""Gets the configuration of a topic. Args: - request (:class:`~.pubsub.GetTopicRequest`): + request (:class:`google.pubsub_v1.types.GetTopicRequest`): The request object. Request for the GetTopic method. topic (:class:`str`): Required. The name of the topic to get. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -404,7 +408,7 @@ async def get_topic( sent along with the request as metadata. Returns: - ~.pubsub.Topic: + google.pubsub_v1.types.Topic: A topic resource. """ # Create or coerce a protobuf request object. @@ -467,11 +471,12 @@ async def list_topics( r"""Lists matching topics. Args: - request (:class:`~.pubsub.ListTopicsRequest`): + request (:class:`google.pubsub_v1.types.ListTopicsRequest`): The request object. Request for the `ListTopics` method. project (:class:`str`): Required. The name of the project in which to list topics. Format is ``projects/{project-id}``. + This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -483,8 +488,8 @@ async def list_topics( sent along with the request as metadata. Returns: - ~.pagers.ListTopicsAsyncPager: - Response for the ``ListTopics`` method. + google.pubsub_v1.services.publisher.pagers.ListTopicsAsyncPager: + Response for the ListTopics method. Iterating over this object will yield results and resolve additional pages automatically. @@ -557,13 +562,14 @@ async def list_topic_subscriptions( topic. Args: - request (:class:`~.pubsub.ListTopicSubscriptionsRequest`): + request (:class:`google.pubsub_v1.types.ListTopicSubscriptionsRequest`): The request object. Request for the `ListTopicSubscriptions` method. topic (:class:`str`): Required. The name of the topic that subscriptions are attached to. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -575,8 +581,8 @@ async def list_topic_subscriptions( sent along with the request as metadata. Returns: - ~.pagers.ListTopicSubscriptionsAsyncPager: - Response for the ``ListTopicSubscriptions`` method. + google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsAsyncPager: + Response for the ListTopicSubscriptions method. Iterating over this object will yield results and resolve additional pages automatically. @@ -653,13 +659,14 @@ async def list_topic_snapshots( in an existing subscription to the state captured by a snapshot. Args: - request (:class:`~.pubsub.ListTopicSnapshotsRequest`): + request (:class:`google.pubsub_v1.types.ListTopicSnapshotsRequest`): The request object. Request for the `ListTopicSnapshots` method. topic (:class:`str`): Required. The name of the topic that snapshots are attached to. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -671,8 +678,8 @@ async def list_topic_snapshots( sent along with the request as metadata. Returns: - ~.pagers.ListTopicSnapshotsAsyncPager: - Response for the ``ListTopicSnapshots`` method. + google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsAsyncPager: + Response for the ListTopicSnapshots method. Iterating over this object will yield results and resolve additional pages automatically. @@ -749,12 +756,13 @@ async def delete_topic( field is set to ``_deleted-topic_``. Args: - request (:class:`~.pubsub.DeleteTopicRequest`): + request (:class:`google.pubsub_v1.types.DeleteTopicRequest`): The request object. Request for the `DeleteTopic` method. topic (:class:`str`): Required. Name of the topic to delete. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -823,7 +831,7 @@ async def detach_subscription( will stop. Args: - request (:class:`~.pubsub.DetachSubscriptionRequest`): + request (:class:`google.pubsub_v1.types.DetachSubscriptionRequest`): The request object. Request for the DetachSubscription method. @@ -834,7 +842,7 @@ async def detach_subscription( sent along with the request as metadata. Returns: - ~.pubsub.DetachSubscriptionResponse: + google.pubsub_v1.types.DetachSubscriptionResponse: Response for the DetachSubscription method. Reserved for future use. diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index bc18586bb..f74e85a0f 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -17,6 +17,7 @@ from collections import OrderedDict from distutils import util +import functools import os import re from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union @@ -37,6 +38,8 @@ from google.pubsub_v1.services.publisher import pagers from google.pubsub_v1.types import pubsub +import grpc + from .transports.base import PublisherTransport, DEFAULT_CLIENT_INFO from .transports.grpc import PublisherGrpcTransport from .transports.grpc_asyncio import PublisherGrpcAsyncIOTransport @@ -124,6 +127,23 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + PublisherClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -137,7 +157,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + PublisherClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -269,10 +289,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.PublisherTransport]): The + transport (Union[str, PublisherTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + 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 default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -308,21 +328,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -360,12 +376,21 @@ def __init__( self._transport = transport else: Transport = type(self).get_transport_class(transport) + + emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") + if emulator_host: + if issubclass(Transport, type(self)._transport_registry["grpc"]): + channel = grpc.insecure_channel(target=emulator_host) + else: + channel = grpc.aio.insecure_channel(target=emulator_host) + Transport = functools.partial(Transport, channel=channel) + self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -385,9 +410,9 @@ def create_topic( Args: - request (:class:`~.pubsub.Topic`): + request (google.pubsub_v1.types.Topic): The request object. A topic resource. - name (:class:`str`): + name (str): Required. The name of the topic. It must have the format ``"projects/{project}/topics/{topic}"``. ``{topic}`` must start with a letter, and contain only letters @@ -396,6 +421,7 @@ def create_topic( plus (``+``) or percent signs (``%``). It must be between 3 and 255 characters in length, and it must not start with ``"goog"``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -407,7 +433,7 @@ def create_topic( sent along with the request as metadata. Returns: - ~.pubsub.Topic: + google.pubsub_v1.types.Topic: A topic resource. """ # Create or coerce a protobuf request object. @@ -462,7 +488,7 @@ def update_topic( Args: - request (:class:`~.pubsub.UpdateTopicRequest`): + request (google.pubsub_v1.types.UpdateTopicRequest): The request object. Request for the UpdateTopic method. retry (google.api_core.retry.Retry): Designation of what errors, if any, @@ -472,7 +498,7 @@ def update_topic( sent along with the request as metadata. Returns: - ~.pubsub.Topic: + google.pubsub_v1.types.Topic: A topic resource. """ # Create or coerce a protobuf request object. @@ -517,16 +543,17 @@ def publish( Args: - request (:class:`~.pubsub.PublishRequest`): + request (google.pubsub_v1.types.PublishRequest): The request object. Request for the Publish method. - topic (:class:`str`): + topic (str): Required. The messages in the request will be published on this topic. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - messages (:class:`Sequence[~.pubsub.PubsubMessage]`): + messages (Sequence[google.pubsub_v1.types.PubsubMessage]): Required. The messages to publish. This corresponds to the ``messages`` field on the ``request`` instance; if ``request`` is provided, this @@ -539,8 +566,8 @@ def publish( sent along with the request as metadata. Returns: - ~.pubsub.PublishResponse: - Response for the ``Publish`` method. + google.pubsub_v1.types.PublishResponse: + Response for the Publish method. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have @@ -564,9 +591,8 @@ def publish( if topic is not None: request.topic = topic - - if messages: - request.messages.extend(messages) + if messages is not None: + request.messages = messages # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -597,11 +623,12 @@ def get_topic( Args: - request (:class:`~.pubsub.GetTopicRequest`): + request (google.pubsub_v1.types.GetTopicRequest): The request object. Request for the GetTopic method. - topic (:class:`str`): + topic (str): Required. The name of the topic to get. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -613,7 +640,7 @@ def get_topic( sent along with the request as metadata. Returns: - ~.pubsub.Topic: + google.pubsub_v1.types.Topic: A topic resource. """ # Create or coerce a protobuf request object. @@ -668,11 +695,12 @@ def list_topics( Args: - request (:class:`~.pubsub.ListTopicsRequest`): + request (google.pubsub_v1.types.ListTopicsRequest): The request object. Request for the `ListTopics` method. - project (:class:`str`): + project (str): Required. The name of the project in which to list topics. Format is ``projects/{project-id}``. + This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -684,8 +712,8 @@ def list_topics( sent along with the request as metadata. Returns: - ~.pagers.ListTopicsPager: - Response for the ``ListTopics`` method. + google.pubsub_v1.services.publisher.pagers.ListTopicsPager: + Response for the ListTopics method. Iterating over this object will yield results and resolve additional pages automatically. @@ -750,13 +778,14 @@ def list_topic_subscriptions( Args: - request (:class:`~.pubsub.ListTopicSubscriptionsRequest`): + request (google.pubsub_v1.types.ListTopicSubscriptionsRequest): The request object. Request for the `ListTopicSubscriptions` method. - topic (:class:`str`): + topic (str): Required. The name of the topic that subscriptions are attached to. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -768,8 +797,8 @@ def list_topic_subscriptions( sent along with the request as metadata. Returns: - ~.pagers.ListTopicSubscriptionsPager: - Response for the ``ListTopicSubscriptions`` method. + google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsPager: + Response for the ListTopicSubscriptions method. Iterating over this object will yield results and resolve additional pages automatically. @@ -838,13 +867,14 @@ def list_topic_snapshots( Args: - request (:class:`~.pubsub.ListTopicSnapshotsRequest`): + request (google.pubsub_v1.types.ListTopicSnapshotsRequest): The request object. Request for the `ListTopicSnapshots` method. - topic (:class:`str`): + topic (str): Required. The name of the topic that snapshots are attached to. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -856,8 +886,8 @@ def list_topic_snapshots( sent along with the request as metadata. Returns: - ~.pagers.ListTopicSnapshotsPager: - Response for the ``ListTopicSnapshots`` method. + google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsPager: + Response for the ListTopicSnapshots method. Iterating over this object will yield results and resolve additional pages automatically. @@ -926,12 +956,13 @@ def delete_topic( Args: - request (:class:`~.pubsub.DeleteTopicRequest`): + request (google.pubsub_v1.types.DeleteTopicRequest): The request object. Request for the `DeleteTopic` method. - topic (:class:`str`): + topic (str): Required. Name of the topic to delete. Format is ``projects/{project}/topics/{topic}``. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -996,7 +1027,7 @@ def detach_subscription( Args: - request (:class:`~.pubsub.DetachSubscriptionRequest`): + request (google.pubsub_v1.types.DetachSubscriptionRequest): The request object. Request for the DetachSubscription method. @@ -1007,7 +1038,7 @@ def detach_subscription( sent along with the request as metadata. Returns: - ~.pubsub.DetachSubscriptionResponse: + google.pubsub_v1.types.DetachSubscriptionResponse: Response for the DetachSubscription method. Reserved for future use. diff --git a/google/pubsub_v1/services/publisher/pagers.py b/google/pubsub_v1/services/publisher/pagers.py index 52242ff17..e8836d410 100644 --- a/google/pubsub_v1/services/publisher/pagers.py +++ b/google/pubsub_v1/services/publisher/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.pubsub_v1.types import pubsub @@ -24,7 +33,7 @@ class ListTopicsPager: """A pager for iterating through ``list_topics`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListTopicsResponse` object, and + :class:`google.pubsub_v1.types.ListTopicsResponse` object, and provides an ``__iter__`` method to iterate through its ``topics`` field. @@ -33,7 +42,7 @@ class ListTopicsPager: through the ``topics`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListTopicsResponse` + All the usual :class:`google.pubsub_v1.types.ListTopicsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListTopicsRequest`): + request (google.pubsub_v1.types.ListTopicsRequest): The initial request object. - response (:class:`~.pubsub.ListTopicsResponse`): + response (google.pubsub_v1.types.ListTopicsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListTopicsAsyncPager: """A pager for iterating through ``list_topics`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListTopicsResponse` object, and + :class:`google.pubsub_v1.types.ListTopicsResponse` object, and provides an ``__aiter__`` method to iterate through its ``topics`` field. @@ -95,7 +104,7 @@ class ListTopicsAsyncPager: through the ``topics`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListTopicsResponse` + All the usual :class:`google.pubsub_v1.types.ListTopicsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListTopicsRequest`): + request (google.pubsub_v1.types.ListTopicsRequest): The initial request object. - response (:class:`~.pubsub.ListTopicsResponse`): + response (google.pubsub_v1.types.ListTopicsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -152,7 +161,7 @@ class ListTopicSubscriptionsPager: """A pager for iterating through ``list_topic_subscriptions`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListTopicSubscriptionsResponse` object, and + :class:`google.pubsub_v1.types.ListTopicSubscriptionsResponse` object, and provides an ``__iter__`` method to iterate through its ``subscriptions`` field. @@ -161,7 +170,7 @@ class ListTopicSubscriptionsPager: through the ``subscriptions`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListTopicSubscriptionsResponse` + All the usual :class:`google.pubsub_v1.types.ListTopicSubscriptionsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -179,9 +188,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListTopicSubscriptionsRequest`): + request (google.pubsub_v1.types.ListTopicSubscriptionsRequest): The initial request object. - response (:class:`~.pubsub.ListTopicSubscriptionsResponse`): + response (google.pubsub_v1.types.ListTopicSubscriptionsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -214,7 +223,7 @@ class ListTopicSubscriptionsAsyncPager: """A pager for iterating through ``list_topic_subscriptions`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListTopicSubscriptionsResponse` object, and + :class:`google.pubsub_v1.types.ListTopicSubscriptionsResponse` object, and provides an ``__aiter__`` method to iterate through its ``subscriptions`` field. @@ -223,7 +232,7 @@ class ListTopicSubscriptionsAsyncPager: through the ``subscriptions`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListTopicSubscriptionsResponse` + All the usual :class:`google.pubsub_v1.types.ListTopicSubscriptionsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -241,9 +250,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListTopicSubscriptionsRequest`): + request (google.pubsub_v1.types.ListTopicSubscriptionsRequest): The initial request object. - response (:class:`~.pubsub.ListTopicSubscriptionsResponse`): + response (google.pubsub_v1.types.ListTopicSubscriptionsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -280,7 +289,7 @@ class ListTopicSnapshotsPager: """A pager for iterating through ``list_topic_snapshots`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListTopicSnapshotsResponse` object, and + :class:`google.pubsub_v1.types.ListTopicSnapshotsResponse` object, and provides an ``__iter__`` method to iterate through its ``snapshots`` field. @@ -289,7 +298,7 @@ class ListTopicSnapshotsPager: through the ``snapshots`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListTopicSnapshotsResponse` + All the usual :class:`google.pubsub_v1.types.ListTopicSnapshotsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -307,9 +316,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListTopicSnapshotsRequest`): + request (google.pubsub_v1.types.ListTopicSnapshotsRequest): The initial request object. - response (:class:`~.pubsub.ListTopicSnapshotsResponse`): + response (google.pubsub_v1.types.ListTopicSnapshotsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -342,7 +351,7 @@ class ListTopicSnapshotsAsyncPager: """A pager for iterating through ``list_topic_snapshots`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListTopicSnapshotsResponse` object, and + :class:`google.pubsub_v1.types.ListTopicSnapshotsResponse` object, and provides an ``__aiter__`` method to iterate through its ``snapshots`` field. @@ -351,7 +360,7 @@ class ListTopicSnapshotsAsyncPager: through the ``snapshots`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListTopicSnapshotsResponse` + All the usual :class:`google.pubsub_v1.types.ListTopicSnapshotsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -369,9 +378,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListTopicSnapshotsRequest`): + request (google.pubsub_v1.types.ListTopicSnapshotsRequest): The initial request object. - response (:class:`~.pubsub.ListTopicSnapshotsResponse`): + response (google.pubsub_v1.types.ListTopicSnapshotsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index d1212ecea..670a08bb7 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -61,6 +61,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = 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, ) -> None: @@ -91,6 +92,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` 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): @@ -107,6 +112,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -116,11 +126,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -165,12 +170,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index 2b15178ef..ea6e04837 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -105,6 +105,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -136,6 +137,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` 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): @@ -152,6 +157,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -161,11 +171,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -210,12 +215,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index c333e76c2..542053f39 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -82,6 +82,7 @@ class SchemaServiceAsyncClient: SchemaServiceClient.parse_common_location_path ) + from_service_account_info = SchemaServiceClient.from_service_account_info from_service_account_file = SchemaServiceClient.from_service_account_file from_service_account_json = from_service_account_file @@ -160,20 +161,22 @@ async def create_schema( r"""Creates a schema. Args: - request (:class:`~.gp_schema.CreateSchemaRequest`): + request (:class:`google.pubsub_v1.types.CreateSchemaRequest`): The request object. Request for the CreateSchema method. parent (:class:`str`): Required. The name of the project in which to create the schema. Format is ``projects/{project-id}``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - schema (:class:`~.gp_schema.Schema`): + schema (:class:`google.pubsub_v1.types.Schema`): Required. The schema object to create. This schema's ``name`` parameter is ignored. The schema object returned by CreateSchema will have a ``name`` made using the given ``parent`` and ``schema_id``. + This corresponds to the ``schema`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -184,6 +187,7 @@ async def create_schema( See https://cloud.google.com/pubsub/docs/admin#resource_names for resource name constraints. + This corresponds to the ``schema_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -195,7 +199,7 @@ async def create_schema( sent along with the request as metadata. Returns: - ~.gp_schema.Schema: + google.pubsub_v1.types.Schema: A schema resource. """ # Create or coerce a protobuf request object. @@ -252,11 +256,12 @@ async def get_schema( r"""Gets a schema. Args: - request (:class:`~.schema.GetSchemaRequest`): + request (:class:`google.pubsub_v1.types.GetSchemaRequest`): The request object. Request for the GetSchema method. name (:class:`str`): Required. The name of the schema to get. Format is ``projects/{project}/schemas/{schema}``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -268,7 +273,7 @@ async def get_schema( sent along with the request as metadata. Returns: - ~.schema.Schema: + google.pubsub_v1.types.Schema: A schema resource. """ # Create or coerce a protobuf request object. @@ -321,12 +326,13 @@ async def list_schemas( r"""Lists schemas in a project. Args: - request (:class:`~.schema.ListSchemasRequest`): + request (:class:`google.pubsub_v1.types.ListSchemasRequest`): The request object. Request for the `ListSchemas` method. parent (:class:`str`): Required. The name of the project in which to list schemas. Format is ``projects/{project-id}``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -338,8 +344,8 @@ async def list_schemas( sent along with the request as metadata. Returns: - ~.pagers.ListSchemasAsyncPager: - Response for the ``ListSchemas`` method. + google.pubsub_v1.services.schema_service.pagers.ListSchemasAsyncPager: + Response for the ListSchemas method. Iterating over this object will yield results and resolve additional pages automatically. @@ -401,12 +407,13 @@ async def delete_schema( r"""Deletes a schema. Args: - request (:class:`~.schema.DeleteSchemaRequest`): + request (:class:`google.pubsub_v1.types.DeleteSchemaRequest`): The request object. Request for the `DeleteSchema` method. name (:class:`str`): Required. Name of the schema to delete. Format is ``projects/{project}/schemas/{schema}``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -467,18 +474,20 @@ async def validate_schema( r"""Validates a schema. Args: - request (:class:`~.gp_schema.ValidateSchemaRequest`): + request (:class:`google.pubsub_v1.types.ValidateSchemaRequest`): The request object. Request for the `ValidateSchema` method. parent (:class:`str`): Required. The name of the project in which to validate schemas. Format is ``projects/{project-id}``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - schema (:class:`~.gp_schema.Schema`): + schema (:class:`google.pubsub_v1.types.Schema`): Required. The schema object to validate. + This corresponds to the ``schema`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -490,8 +499,8 @@ async def validate_schema( sent along with the request as metadata. Returns: - ~.gp_schema.ValidateSchemaResponse: - Response for the ``ValidateSchema`` method. + google.pubsub_v1.types.ValidateSchemaResponse: + Response for the ValidateSchema method. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have @@ -544,7 +553,7 @@ async def validate_message( r"""Validates a message against a schema. Args: - request (:class:`~.schema.ValidateMessageRequest`): + request (:class:`google.pubsub_v1.types.ValidateMessageRequest`): The request object. Request for the `ValidateMessage` method. @@ -555,8 +564,8 @@ async def validate_message( sent along with the request as metadata. Returns: - ~.schema.ValidateMessageResponse: - Response for the ``ValidateMessage`` method. + google.pubsub_v1.types.ValidateMessageResponse: + Response for the ValidateMessage method. """ # Create or coerce a protobuf request object. diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 90fbce9f4..d569af8f3 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -114,6 +114,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SchemaServiceClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -126,7 +142,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + SchemaServiceClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -231,10 +247,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SchemaServiceTransport]): The + transport (Union[str, SchemaServiceTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + 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 default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -270,21 +286,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -327,7 +339,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -346,30 +358,33 @@ def create_schema( r"""Creates a schema. Args: - request (:class:`~.gp_schema.CreateSchemaRequest`): + request (google.pubsub_v1.types.CreateSchemaRequest): The request object. Request for the CreateSchema method. - parent (:class:`str`): + parent (str): Required. The name of the project in which to create the schema. Format is ``projects/{project-id}``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - schema (:class:`~.gp_schema.Schema`): + schema (google.pubsub_v1.types.Schema): Required. The schema object to create. This schema's ``name`` parameter is ignored. The schema object returned by CreateSchema will have a ``name`` made using the given ``parent`` and ``schema_id``. + This corresponds to the ``schema`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - schema_id (:class:`str`): + schema_id (str): The ID to use for the schema, which will become the final component of the schema's resource name. See https://cloud.google.com/pubsub/docs/admin#resource_names for resource name constraints. + This corresponds to the ``schema_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -381,7 +396,7 @@ def create_schema( sent along with the request as metadata. Returns: - ~.gp_schema.Schema: + google.pubsub_v1.types.Schema: A schema resource. """ # Create or coerce a protobuf request object. @@ -439,11 +454,12 @@ def get_schema( r"""Gets a schema. Args: - request (:class:`~.schema.GetSchemaRequest`): + request (google.pubsub_v1.types.GetSchemaRequest): The request object. Request for the GetSchema method. - name (:class:`str`): + name (str): Required. The name of the schema to get. Format is ``projects/{project}/schemas/{schema}``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -455,7 +471,7 @@ def get_schema( sent along with the request as metadata. Returns: - ~.schema.Schema: + google.pubsub_v1.types.Schema: A schema resource. """ # Create or coerce a protobuf request object. @@ -509,12 +525,13 @@ def list_schemas( r"""Lists schemas in a project. Args: - request (:class:`~.schema.ListSchemasRequest`): + request (google.pubsub_v1.types.ListSchemasRequest): The request object. Request for the `ListSchemas` method. - parent (:class:`str`): + parent (str): Required. The name of the project in which to list schemas. Format is ``projects/{project-id}``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -526,8 +543,8 @@ def list_schemas( sent along with the request as metadata. Returns: - ~.pagers.ListSchemasPager: - Response for the ``ListSchemas`` method. + google.pubsub_v1.services.schema_service.pagers.ListSchemasPager: + Response for the ListSchemas method. Iterating over this object will yield results and resolve additional pages automatically. @@ -590,12 +607,13 @@ def delete_schema( r"""Deletes a schema. Args: - request (:class:`~.schema.DeleteSchemaRequest`): + request (google.pubsub_v1.types.DeleteSchemaRequest): The request object. Request for the `DeleteSchema` method. - name (:class:`str`): + name (str): Required. Name of the schema to delete. Format is ``projects/{project}/schemas/{schema}``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -657,18 +675,20 @@ def validate_schema( r"""Validates a schema. Args: - request (:class:`~.gp_schema.ValidateSchemaRequest`): + request (google.pubsub_v1.types.ValidateSchemaRequest): The request object. Request for the `ValidateSchema` method. - parent (:class:`str`): + parent (str): Required. The name of the project in which to validate schemas. Format is ``projects/{project-id}``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - schema (:class:`~.gp_schema.Schema`): + schema (google.pubsub_v1.types.Schema): Required. The schema object to validate. + This corresponds to the ``schema`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -680,8 +700,8 @@ def validate_schema( sent along with the request as metadata. Returns: - ~.gp_schema.ValidateSchemaResponse: - Response for the ``ValidateSchema`` method. + google.pubsub_v1.types.ValidateSchemaResponse: + Response for the ValidateSchema method. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have @@ -735,7 +755,7 @@ def validate_message( r"""Validates a message against a schema. Args: - request (:class:`~.schema.ValidateMessageRequest`): + request (google.pubsub_v1.types.ValidateMessageRequest): The request object. Request for the `ValidateMessage` method. @@ -746,8 +766,8 @@ def validate_message( sent along with the request as metadata. Returns: - ~.schema.ValidateMessageResponse: - Response for the ``ValidateMessage`` method. + google.pubsub_v1.types.ValidateMessageResponse: + Response for the ValidateMessage method. """ # Create or coerce a protobuf request object. diff --git a/google/pubsub_v1/services/schema_service/pagers.py b/google/pubsub_v1/services/schema_service/pagers.py index e4da22697..2712f37c6 100644 --- a/google/pubsub_v1/services/schema_service/pagers.py +++ b/google/pubsub_v1/services/schema_service/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.pubsub_v1.types import schema @@ -24,7 +33,7 @@ class ListSchemasPager: """A pager for iterating through ``list_schemas`` requests. This class thinly wraps an initial - :class:`~.schema.ListSchemasResponse` object, and + :class:`google.pubsub_v1.types.ListSchemasResponse` object, and provides an ``__iter__`` method to iterate through its ``schemas`` field. @@ -33,7 +42,7 @@ class ListSchemasPager: through the ``schemas`` field on the corresponding responses. - All the usual :class:`~.schema.ListSchemasResponse` + All the usual :class:`google.pubsub_v1.types.ListSchemasResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.schema.ListSchemasRequest`): + request (google.pubsub_v1.types.ListSchemasRequest): The initial request object. - response (:class:`~.schema.ListSchemasResponse`): + response (google.pubsub_v1.types.ListSchemasResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListSchemasAsyncPager: """A pager for iterating through ``list_schemas`` requests. This class thinly wraps an initial - :class:`~.schema.ListSchemasResponse` object, and + :class:`google.pubsub_v1.types.ListSchemasResponse` object, and provides an ``__aiter__`` method to iterate through its ``schemas`` field. @@ -95,7 +104,7 @@ class ListSchemasAsyncPager: through the ``schemas`` field on the corresponding responses. - All the usual :class:`~.schema.ListSchemasResponse` + All the usual :class:`google.pubsub_v1.types.ListSchemasResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.schema.ListSchemasRequest`): + request (google.pubsub_v1.types.ListSchemasRequest): The initial request object. - response (:class:`~.schema.ListSchemasResponse`): + response (google.pubsub_v1.types.ListSchemasResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index 02f91d358..3a724e073 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -63,6 +63,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = 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, ) -> None: @@ -93,6 +94,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` 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): @@ -109,6 +114,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -118,11 +128,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -167,12 +172,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index 3455c207b..080bcc6f5 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -107,6 +107,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -138,6 +139,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` 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): @@ -154,6 +159,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -163,11 +173,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -212,12 +217,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index bd0191193..7bfaeb903 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -91,6 +91,7 @@ class SubscriberAsyncClient: SubscriberClient.parse_common_location_path ) + from_service_account_info = SubscriberClient.from_service_account_info from_service_account_file = SubscriberClient.from_service_account_file from_service_account_json = from_service_account_file @@ -182,7 +183,7 @@ async def create_subscription( request. Args: - request (:class:`~.pubsub.Subscription`): + request (:class:`google.pubsub_v1.types.Subscription`): The request object. A subscription resource. name (:class:`str`): Required. The name of the subscription. It must have the @@ -194,6 +195,7 @@ async def create_subscription( (``~``), plus (``+``) or percent signs (``%``). It must be between 3 and 255 characters in length, and it must not start with ``"goog"``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -203,14 +205,16 @@ async def create_subscription( ``projects/{project}/topics/{topic}``. The value of this field will be ``_deleted-topic_`` if the topic has been deleted. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - push_config (:class:`~.pubsub.PushConfig`): + push_config (:class:`google.pubsub_v1.types.PushConfig`): If push delivery is used with this subscription, this field is used to configure it. An empty ``pushConfig`` signifies that the subscriber will pull and ack messages using API methods. + This corresponds to the ``push_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -239,6 +243,7 @@ async def create_subscription( If the subscriber never acknowledges the message, the Pub/Sub system will eventually redeliver the message. + This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -250,7 +255,7 @@ async def create_subscription( sent along with the request as metadata. Returns: - ~.pubsub.Subscription: + google.pubsub_v1.types.Subscription: A subscription resource. """ # Create or coerce a protobuf request object. @@ -319,12 +324,13 @@ async def get_subscription( r"""Gets the configuration details of a subscription. Args: - request (:class:`~.pubsub.GetSubscriptionRequest`): + request (:class:`google.pubsub_v1.types.GetSubscriptionRequest`): The request object. Request for the GetSubscription method. subscription (:class:`str`): Required. The name of the subscription to get. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -336,7 +342,7 @@ async def get_subscription( sent along with the request as metadata. Returns: - ~.pubsub.Subscription: + google.pubsub_v1.types.Subscription: A subscription resource. """ # Create or coerce a protobuf request object. @@ -402,7 +408,7 @@ async def update_subscription( modifiable. Args: - request (:class:`~.pubsub.UpdateSubscriptionRequest`): + request (:class:`google.pubsub_v1.types.UpdateSubscriptionRequest`): The request object. Request for the UpdateSubscription method. @@ -413,7 +419,7 @@ async def update_subscription( sent along with the request as metadata. Returns: - ~.pubsub.Subscription: + google.pubsub_v1.types.Subscription: A subscription resource. """ # Create or coerce a protobuf request object. @@ -460,12 +466,13 @@ async def list_subscriptions( r"""Lists matching subscriptions. Args: - request (:class:`~.pubsub.ListSubscriptionsRequest`): + request (:class:`google.pubsub_v1.types.ListSubscriptionsRequest`): The request object. Request for the `ListSubscriptions` method. project (:class:`str`): Required. The name of the project in which to list subscriptions. Format is ``projects/{project-id}``. + This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -477,8 +484,8 @@ async def list_subscriptions( sent along with the request as metadata. Returns: - ~.pagers.ListSubscriptionsAsyncPager: - Response for the ``ListSubscriptions`` method. + google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsAsyncPager: + Response for the ListSubscriptions method. Iterating over this object will yield results and resolve additional pages automatically. @@ -555,12 +562,13 @@ async def delete_subscription( topic unless the same topic is specified. Args: - request (:class:`~.pubsub.DeleteSubscriptionRequest`): + request (:class:`google.pubsub_v1.types.DeleteSubscriptionRequest`): The request object. Request for the DeleteSubscription method. subscription (:class:`str`): Required. The subscription to delete. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -635,12 +643,13 @@ async def modify_ack_deadline( used for subsequent messages. Args: - request (:class:`~.pubsub.ModifyAckDeadlineRequest`): + request (:class:`google.pubsub_v1.types.ModifyAckDeadlineRequest`): The request object. Request for the ModifyAckDeadline method. subscription (:class:`str`): Required. The name of the subscription. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -661,6 +670,7 @@ async def modify_ack_deadline( minimum deadline you can specify is 0 seconds. The maximum deadline you can specify is 600 seconds (10 minutes). + This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -741,12 +751,13 @@ async def acknowledge( error. Args: - request (:class:`~.pubsub.AcknowledgeRequest`): + request (:class:`google.pubsub_v1.types.AcknowledgeRequest`): The request object. Request for the Acknowledge method. subscription (:class:`str`): Required. The subscription whose message is being acknowledged. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -754,6 +765,7 @@ async def acknowledge( Required. The acknowledgment ID for the messages being acknowledged that was returned by the Pub/Sub system in the ``Pull`` response. Must not be empty. + This corresponds to the ``ack_ids`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -828,12 +840,13 @@ async def pull( pending for the given subscription. Args: - request (:class:`~.pubsub.PullRequest`): + request (:class:`google.pubsub_v1.types.PullRequest`): The request object. Request for the `Pull` method. subscription (:class:`str`): Required. The subscription from which messages should be pulled. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -847,6 +860,7 @@ async def pull( discouraged because it adversely impacts the performance of ``Pull`` operations. We recommend that users do not set this field. + This corresponds to the ``return_immediately`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -856,6 +870,7 @@ async def pull( Must be a positive integer. The Pub/Sub system may return fewer than the number specified. + This corresponds to the ``max_messages`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -867,8 +882,8 @@ async def pull( sent along with the request as metadata. Returns: - ~.pubsub.PullResponse: - Response for the ``Pull`` method. + google.pubsub_v1.types.PullResponse: + Response for the Pull method. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have @@ -942,7 +957,7 @@ def streaming_pull( configuring the underlying RPC channel. Args: - requests (AsyncIterator[`~.pubsub.StreamingPullRequest`]): + requests (AsyncIterator[`google.pubsub_v1.types.StreamingPullRequest`]): The request object AsyncIterator. Request for the `StreamingPull` streaming RPC method. This request is used to establish the initial stream as well as to stream acknowledgements @@ -955,10 +970,9 @@ def streaming_pull( sent along with the request as metadata. Returns: - AsyncIterable[~.pubsub.StreamingPullResponse]: - Response for the ``StreamingPull`` method. This response - is used to stream messages from the server to the - client. + AsyncIterable[google.pubsub_v1.types.StreamingPullResponse]: + Response for the StreamingPull method. This response is used to stream + messages from the server to the client. """ @@ -1007,16 +1021,17 @@ async def modify_push_config( call regardless of changes to the ``PushConfig``. Args: - request (:class:`~.pubsub.ModifyPushConfigRequest`): + request (:class:`google.pubsub_v1.types.ModifyPushConfigRequest`): The request object. Request for the ModifyPushConfig method. subscription (:class:`str`): Required. The name of the subscription. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - push_config (:class:`~.pubsub.PushConfig`): + push_config (:class:`google.pubsub_v1.types.PushConfig`): Required. The push configuration for future deliveries. An empty ``pushConfig`` indicates that the Pub/Sub @@ -1024,6 +1039,7 @@ async def modify_push_config( subscription and allow messages to be pulled and acknowledged - effectively pausing the subscription if ``Pull`` or ``StreamingPull`` is not called. + This corresponds to the ``push_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1099,11 +1115,12 @@ async def get_snapshot( subscription to the state captured by a snapshot. Args: - request (:class:`~.pubsub.GetSnapshotRequest`): + request (:class:`google.pubsub_v1.types.GetSnapshotRequest`): The request object. Request for the GetSnapshot method. snapshot (:class:`str`): Required. The name of the snapshot to get. Format is ``projects/{project}/snapshots/{snap}``. + This corresponds to the ``snapshot`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1115,13 +1132,13 @@ async def get_snapshot( sent along with the request as metadata. Returns: - ~.pubsub.Snapshot: + google.pubsub_v1.types.Snapshot: A snapshot resource. Snapshots are used in - `Seek `__ - operations, which allow you to manage message - acknowledgments in bulk. That is, you can set the - acknowledgment state of messages in an existing - subscription to the state captured by a snapshot. + [Seek](https://cloud.google.com/pubsub/docs/replay-overview) + operations, which allow you to manage message + acknowledgments in bulk. That is, you can set the + acknowledgment state of messages in an existing + subscription to the state captured by a snapshot. """ # Create or coerce a protobuf request object. @@ -1188,12 +1205,13 @@ async def list_snapshots( in an existing subscription to the state captured by a snapshot. Args: - request (:class:`~.pubsub.ListSnapshotsRequest`): + request (:class:`google.pubsub_v1.types.ListSnapshotsRequest`): The request object. Request for the `ListSnapshots` method. project (:class:`str`): Required. The name of the project in which to list snapshots. Format is ``projects/{project-id}``. + This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1205,8 +1223,8 @@ async def list_snapshots( sent along with the request as metadata. Returns: - ~.pagers.ListSnapshotsAsyncPager: - Response for the ``ListSnapshots`` method. + google.pubsub_v1.services.subscriber.pagers.ListSnapshotsAsyncPager: + Response for the ListSnapshots method. Iterating over this object will yield results and resolve additional pages automatically. @@ -1297,7 +1315,7 @@ async def create_snapshot( request. Args: - request (:class:`~.pubsub.CreateSnapshotRequest`): + request (:class:`google.pubsub_v1.types.CreateSnapshotRequest`): The request object. Request for the `CreateSnapshot` method. name (:class:`str`): @@ -1308,6 +1326,7 @@ async def create_snapshot( requests, you must specify a name. See the resource name rules. Format is ``projects/{project}/snapshots/{snap}``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1322,6 +1341,7 @@ async def create_snapshot( published to the subscription's topic following the successful completion of the CreateSnapshot request. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1333,13 +1353,13 @@ async def create_snapshot( sent along with the request as metadata. Returns: - ~.pubsub.Snapshot: + google.pubsub_v1.types.Snapshot: A snapshot resource. Snapshots are used in - `Seek `__ - operations, which allow you to manage message - acknowledgments in bulk. That is, you can set the - acknowledgment state of messages in an existing - subscription to the state captured by a snapshot. + [Seek](https://cloud.google.com/pubsub/docs/replay-overview) + operations, which allow you to manage message + acknowledgments in bulk. That is, you can set the + acknowledgment state of messages in an existing + subscription to the state captured by a snapshot. """ # Create or coerce a protobuf request object. @@ -1405,7 +1425,7 @@ async def update_snapshot( snapshot. Args: - request (:class:`~.pubsub.UpdateSnapshotRequest`): + request (:class:`google.pubsub_v1.types.UpdateSnapshotRequest`): The request object. Request for the UpdateSnapshot method. @@ -1416,13 +1436,13 @@ async def update_snapshot( sent along with the request as metadata. Returns: - ~.pubsub.Snapshot: + google.pubsub_v1.types.Snapshot: A snapshot resource. Snapshots are used in - `Seek `__ - operations, which allow you to manage message - acknowledgments in bulk. That is, you can set the - acknowledgment state of messages in an existing - subscription to the state captured by a snapshot. + [Seek](https://cloud.google.com/pubsub/docs/replay-overview) + operations, which allow you to manage message + acknowledgments in bulk. That is, you can set the + acknowledgment state of messages in an existing + subscription to the state captured by a snapshot. """ # Create or coerce a protobuf request object. @@ -1478,12 +1498,13 @@ async def delete_snapshot( the same subscription is specified. Args: - request (:class:`~.pubsub.DeleteSnapshotRequest`): + request (:class:`google.pubsub_v1.types.DeleteSnapshotRequest`): The request object. Request for the `DeleteSnapshot` method. snapshot (:class:`str`): Required. The name of the snapshot to delete. Format is ``projects/{project}/snapshots/{snap}``. + This corresponds to the ``snapshot`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1556,7 +1577,7 @@ async def seek( same topic. Args: - request (:class:`~.pubsub.SeekRequest`): + request (:class:`google.pubsub_v1.types.SeekRequest`): The request object. Request for the `Seek` method. retry (google.api_core.retry.Retry): Designation of what errors, if any, @@ -1566,10 +1587,8 @@ async def seek( sent along with the request as metadata. Returns: - ~.pubsub.SeekResponse: - Response for the ``Seek`` method (this response is - empty). - + google.pubsub_v1.types.SeekResponse: + Response for the Seek method (this response is empty). """ # Create or coerce a protobuf request object. diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index b80af2d81..67956a53d 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -17,6 +17,7 @@ from collections import OrderedDict from distutils import util +import functools import os import re from typing import ( @@ -49,6 +50,8 @@ from google.pubsub_v1.services.subscriber import pagers from google.pubsub_v1.types import pubsub +import grpc + from .transports.base import SubscriberTransport, DEFAULT_CLIENT_INFO from .transports.grpc import SubscriberGrpcTransport from .transports.grpc_asyncio import SubscriberGrpcAsyncIOTransport @@ -138,6 +141,23 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SubscriberClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -151,7 +171,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + SubscriberClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -283,10 +303,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SubscriberTransport]): The + transport (Union[str, SubscriberTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + 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 default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -322,21 +342,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -374,12 +390,21 @@ def __init__( self._transport = transport else: Transport = type(self).get_transport_class(transport) + + emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") + if emulator_host: + if issubclass(Transport, type(self)._transport_registry["grpc"]): + channel = grpc.insecure_channel(target=emulator_host) + else: + channel = grpc.aio.insecure_channel(target=emulator_host) + Transport = functools.partial(Transport, channel=channel) + self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -412,9 +437,9 @@ def create_subscription( Args: - request (:class:`~.pubsub.Subscription`): + request (google.pubsub_v1.types.Subscription): The request object. A subscription resource. - name (:class:`str`): + name (str): Required. The name of the subscription. It must have the format ``"projects/{project}/subscriptions/{subscription}"``. @@ -424,27 +449,30 @@ def create_subscription( (``~``), plus (``+``) or percent signs (``%``). It must be between 3 and 255 characters in length, and it must not start with ``"goog"``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - topic (:class:`str`): + topic (str): Required. The name of the topic from which this subscription is receiving messages. Format is ``projects/{project}/topics/{topic}``. The value of this field will be ``_deleted-topic_`` if the topic has been deleted. + This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - push_config (:class:`~.pubsub.PushConfig`): + push_config (google.pubsub_v1.types.PushConfig): If push delivery is used with this subscription, this field is used to configure it. An empty ``pushConfig`` signifies that the subscriber will pull and ack messages using API methods. + This corresponds to the ``push_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - ack_deadline_seconds (:class:`int`): + ack_deadline_seconds (int): The approximate amount of time (on a best-effort basis) Pub/Sub waits for the subscriber to acknowledge receipt before resending the message. In the interval after the @@ -469,6 +497,7 @@ def create_subscription( If the subscriber never acknowledges the message, the Pub/Sub system will eventually redeliver the message. + This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -480,7 +509,7 @@ def create_subscription( sent along with the request as metadata. Returns: - ~.pubsub.Subscription: + google.pubsub_v1.types.Subscription: A subscription resource. """ # Create or coerce a protobuf request object. @@ -541,12 +570,13 @@ def get_subscription( Args: - request (:class:`~.pubsub.GetSubscriptionRequest`): + request (google.pubsub_v1.types.GetSubscriptionRequest): The request object. Request for the GetSubscription method. - subscription (:class:`str`): + subscription (str): Required. The name of the subscription to get. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -558,7 +588,7 @@ def get_subscription( sent along with the request as metadata. Returns: - ~.pubsub.Subscription: + google.pubsub_v1.types.Subscription: A subscription resource. """ # Create or coerce a protobuf request object. @@ -616,7 +646,7 @@ def update_subscription( Args: - request (:class:`~.pubsub.UpdateSubscriptionRequest`): + request (google.pubsub_v1.types.UpdateSubscriptionRequest): The request object. Request for the UpdateSubscription method. @@ -627,7 +657,7 @@ def update_subscription( sent along with the request as metadata. Returns: - ~.pubsub.Subscription: + google.pubsub_v1.types.Subscription: A subscription resource. """ # Create or coerce a protobuf request object. @@ -670,12 +700,13 @@ def list_subscriptions( Args: - request (:class:`~.pubsub.ListSubscriptionsRequest`): + request (google.pubsub_v1.types.ListSubscriptionsRequest): The request object. Request for the `ListSubscriptions` method. - project (:class:`str`): + project (str): Required. The name of the project in which to list subscriptions. Format is ``projects/{project-id}``. + This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -687,8 +718,8 @@ def list_subscriptions( sent along with the request as metadata. Returns: - ~.pagers.ListSubscriptionsPager: - Response for the ``ListSubscriptions`` method. + google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsPager: + Response for the ListSubscriptions method. Iterating over this object will yield results and resolve additional pages automatically. @@ -757,12 +788,13 @@ def delete_subscription( Args: - request (:class:`~.pubsub.DeleteSubscriptionRequest`): + request (google.pubsub_v1.types.DeleteSubscriptionRequest): The request object. Request for the DeleteSubscription method. - subscription (:class:`str`): + subscription (str): Required. The subscription to delete. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -833,21 +865,22 @@ def modify_ack_deadline( Args: - request (:class:`~.pubsub.ModifyAckDeadlineRequest`): + request (google.pubsub_v1.types.ModifyAckDeadlineRequest): The request object. Request for the ModifyAckDeadline method. - subscription (:class:`str`): + subscription (str): Required. The name of the subscription. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - ack_ids (:class:`Sequence[str]`): + ack_ids (Sequence[str]): Required. List of acknowledgment IDs. This corresponds to the ``ack_ids`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - ack_deadline_seconds (:class:`int`): + ack_deadline_seconds (int): Required. The new ack deadline with respect to the time this request was sent to the Pub/Sub system. For example, if the value is 10, the new ack deadline will @@ -859,6 +892,7 @@ def modify_ack_deadline( minimum deadline you can specify is 0 seconds. The maximum deadline you can specify is 600 seconds (10 minutes). + This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -891,12 +925,11 @@ def modify_ack_deadline( if subscription is not None: request.subscription = subscription + if ack_ids is not None: + request.ack_ids = ack_ids if ack_deadline_seconds is not None: request.ack_deadline_seconds = ack_deadline_seconds - if ack_ids: - request.ack_ids.extend(ack_ids) - # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.modify_ack_deadline] @@ -935,19 +968,21 @@ def acknowledge( Args: - request (:class:`~.pubsub.AcknowledgeRequest`): + request (google.pubsub_v1.types.AcknowledgeRequest): The request object. Request for the Acknowledge method. - subscription (:class:`str`): + subscription (str): Required. The subscription whose message is being acknowledged. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - ack_ids (:class:`Sequence[str]`): + ack_ids (Sequence[str]): Required. The acknowledgment ID for the messages being acknowledged that was returned by the Pub/Sub system in the ``Pull`` response. Must not be empty. + This corresponds to the ``ack_ids`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -980,9 +1015,8 @@ def acknowledge( if subscription is not None: request.subscription = subscription - - if ack_ids: - request.ack_ids.extend(ack_ids) + if ack_ids is not None: + request.ack_ids = ack_ids # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -1018,16 +1052,17 @@ def pull( Args: - request (:class:`~.pubsub.PullRequest`): + request (google.pubsub_v1.types.PullRequest): The request object. Request for the `Pull` method. - subscription (:class:`str`): + subscription (str): Required. The subscription from which messages should be pulled. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - return_immediately (:class:`bool`): + return_immediately (bool): Optional. If this field set to true, the system will respond immediately even if it there are no messages available to return in the ``Pull`` response. Otherwise, @@ -1037,15 +1072,17 @@ def pull( discouraged because it adversely impacts the performance of ``Pull`` operations. We recommend that users do not set this field. + This corresponds to the ``return_immediately`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - max_messages (:class:`int`): + max_messages (int): Required. The maximum number of messages to return for this request. Must be a positive integer. The Pub/Sub system may return fewer than the number specified. + This corresponds to the ``max_messages`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1057,8 +1094,8 @@ def pull( sent along with the request as metadata. Returns: - ~.pubsub.PullResponse: - Response for the ``Pull`` method. + google.pubsub_v1.types.PullResponse: + Response for the Pull method. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have @@ -1124,7 +1161,7 @@ def streaming_pull( Args: - requests (Iterator[`~.pubsub.StreamingPullRequest`]): + requests (Iterator[google.pubsub_v1.types.StreamingPullRequest]): The request object iterator. Request for the `StreamingPull` streaming RPC method. This request is used to establish the initial stream as well as to stream acknowledgements @@ -1137,10 +1174,9 @@ def streaming_pull( sent along with the request as metadata. Returns: - Iterable[~.pubsub.StreamingPullResponse]: - Response for the ``StreamingPull`` method. This response - is used to stream messages from the server to the - client. + Iterable[google.pubsub_v1.types.StreamingPullResponse]: + Response for the StreamingPull method. This response is used to stream + messages from the server to the client. """ @@ -1179,16 +1215,17 @@ def modify_push_config( Args: - request (:class:`~.pubsub.ModifyPushConfigRequest`): + request (google.pubsub_v1.types.ModifyPushConfigRequest): The request object. Request for the ModifyPushConfig method. - subscription (:class:`str`): + subscription (str): Required. The name of the subscription. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - push_config (:class:`~.pubsub.PushConfig`): + push_config (google.pubsub_v1.types.PushConfig): Required. The push configuration for future deliveries. An empty ``pushConfig`` indicates that the Pub/Sub @@ -1196,6 +1233,7 @@ def modify_push_config( subscription and allow messages to be pulled and acknowledged - effectively pausing the subscription if ``Pull`` or ``StreamingPull`` is not called. + This corresponds to the ``push_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1267,11 +1305,12 @@ def get_snapshot( Args: - request (:class:`~.pubsub.GetSnapshotRequest`): + request (google.pubsub_v1.types.GetSnapshotRequest): The request object. Request for the GetSnapshot method. - snapshot (:class:`str`): + snapshot (str): Required. The name of the snapshot to get. Format is ``projects/{project}/snapshots/{snap}``. + This corresponds to the ``snapshot`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1283,13 +1322,13 @@ def get_snapshot( sent along with the request as metadata. Returns: - ~.pubsub.Snapshot: + google.pubsub_v1.types.Snapshot: A snapshot resource. Snapshots are used in - `Seek `__ - operations, which allow you to manage message - acknowledgments in bulk. That is, you can set the - acknowledgment state of messages in an existing - subscription to the state captured by a snapshot. + [Seek](https://cloud.google.com/pubsub/docs/replay-overview) + operations, which allow you to manage message + acknowledgments in bulk. That is, you can set the + acknowledgment state of messages in an existing + subscription to the state captured by a snapshot. """ # Create or coerce a protobuf request object. @@ -1348,12 +1387,13 @@ def list_snapshots( Args: - request (:class:`~.pubsub.ListSnapshotsRequest`): + request (google.pubsub_v1.types.ListSnapshotsRequest): The request object. Request for the `ListSnapshots` method. - project (:class:`str`): + project (str): Required. The name of the project in which to list snapshots. Format is ``projects/{project-id}``. + This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1365,8 +1405,8 @@ def list_snapshots( sent along with the request as metadata. Returns: - ~.pagers.ListSnapshotsPager: - Response for the ``ListSnapshots`` method. + google.pubsub_v1.services.subscriber.pagers.ListSnapshotsPager: + Response for the ListSnapshots method. Iterating over this object will yield results and resolve additional pages automatically. @@ -1449,10 +1489,10 @@ def create_snapshot( Args: - request (:class:`~.pubsub.CreateSnapshotRequest`): + request (google.pubsub_v1.types.CreateSnapshotRequest): The request object. Request for the `CreateSnapshot` method. - name (:class:`str`): + name (str): Required. User-provided name for this snapshot. If the name is not provided in the request, the server will assign a random name for this snapshot on the same @@ -1460,10 +1500,11 @@ def create_snapshot( requests, you must specify a name. See the resource name rules. Format is ``projects/{project}/snapshots/{snap}``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - subscription (:class:`str`): + subscription (str): Required. The subscription whose backlog the snapshot retains. Specifically, the created snapshot is guaranteed to retain: (a) The existing backlog on the @@ -1474,6 +1515,7 @@ def create_snapshot( published to the subscription's topic following the successful completion of the CreateSnapshot request. Format is ``projects/{project}/subscriptions/{sub}``. + This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1485,13 +1527,13 @@ def create_snapshot( sent along with the request as metadata. Returns: - ~.pubsub.Snapshot: + google.pubsub_v1.types.Snapshot: A snapshot resource. Snapshots are used in - `Seek `__ - operations, which allow you to manage message - acknowledgments in bulk. That is, you can set the - acknowledgment state of messages in an existing - subscription to the state captured by a snapshot. + [Seek](https://cloud.google.com/pubsub/docs/replay-overview) + operations, which allow you to manage message + acknowledgments in bulk. That is, you can set the + acknowledgment state of messages in an existing + subscription to the state captured by a snapshot. """ # Create or coerce a protobuf request object. @@ -1553,7 +1595,7 @@ def update_snapshot( Args: - request (:class:`~.pubsub.UpdateSnapshotRequest`): + request (google.pubsub_v1.types.UpdateSnapshotRequest): The request object. Request for the UpdateSnapshot method. @@ -1564,13 +1606,13 @@ def update_snapshot( sent along with the request as metadata. Returns: - ~.pubsub.Snapshot: + google.pubsub_v1.types.Snapshot: A snapshot resource. Snapshots are used in - `Seek `__ - operations, which allow you to manage message - acknowledgments in bulk. That is, you can set the - acknowledgment state of messages in an existing - subscription to the state captured by a snapshot. + [Seek](https://cloud.google.com/pubsub/docs/replay-overview) + operations, which allow you to manage message + acknowledgments in bulk. That is, you can set the + acknowledgment state of messages in an existing + subscription to the state captured by a snapshot. """ # Create or coerce a protobuf request object. @@ -1622,12 +1664,13 @@ def delete_snapshot( Args: - request (:class:`~.pubsub.DeleteSnapshotRequest`): + request (google.pubsub_v1.types.DeleteSnapshotRequest): The request object. Request for the `DeleteSnapshot` method. - snapshot (:class:`str`): + snapshot (str): Required. The name of the snapshot to delete. Format is ``projects/{project}/snapshots/{snap}``. + This corresponds to the ``snapshot`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1696,7 +1739,7 @@ def seek( Args: - request (:class:`~.pubsub.SeekRequest`): + request (google.pubsub_v1.types.SeekRequest): The request object. Request for the `Seek` method. retry (google.api_core.retry.Retry): Designation of what errors, if any, @@ -1706,10 +1749,8 @@ def seek( sent along with the request as metadata. Returns: - ~.pubsub.SeekResponse: - Response for the ``Seek`` method (this response is - empty). - + google.pubsub_v1.types.SeekResponse: + Response for the Seek method (this response is empty). """ # Create or coerce a protobuf request object. diff --git a/google/pubsub_v1/services/subscriber/pagers.py b/google/pubsub_v1/services/subscriber/pagers.py index 713184d79..b7ec9f6e3 100644 --- a/google/pubsub_v1/services/subscriber/pagers.py +++ b/google/pubsub_v1/services/subscriber/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.pubsub_v1.types import pubsub @@ -24,7 +33,7 @@ class ListSubscriptionsPager: """A pager for iterating through ``list_subscriptions`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListSubscriptionsResponse` object, and + :class:`google.pubsub_v1.types.ListSubscriptionsResponse` object, and provides an ``__iter__`` method to iterate through its ``subscriptions`` field. @@ -33,7 +42,7 @@ class ListSubscriptionsPager: through the ``subscriptions`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListSubscriptionsResponse` + All the usual :class:`google.pubsub_v1.types.ListSubscriptionsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListSubscriptionsRequest`): + request (google.pubsub_v1.types.ListSubscriptionsRequest): The initial request object. - response (:class:`~.pubsub.ListSubscriptionsResponse`): + response (google.pubsub_v1.types.ListSubscriptionsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListSubscriptionsAsyncPager: """A pager for iterating through ``list_subscriptions`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListSubscriptionsResponse` object, and + :class:`google.pubsub_v1.types.ListSubscriptionsResponse` object, and provides an ``__aiter__`` method to iterate through its ``subscriptions`` field. @@ -95,7 +104,7 @@ class ListSubscriptionsAsyncPager: through the ``subscriptions`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListSubscriptionsResponse` + All the usual :class:`google.pubsub_v1.types.ListSubscriptionsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListSubscriptionsRequest`): + request (google.pubsub_v1.types.ListSubscriptionsRequest): The initial request object. - response (:class:`~.pubsub.ListSubscriptionsResponse`): + response (google.pubsub_v1.types.ListSubscriptionsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -152,7 +161,7 @@ class ListSnapshotsPager: """A pager for iterating through ``list_snapshots`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListSnapshotsResponse` object, and + :class:`google.pubsub_v1.types.ListSnapshotsResponse` object, and provides an ``__iter__`` method to iterate through its ``snapshots`` field. @@ -161,7 +170,7 @@ class ListSnapshotsPager: through the ``snapshots`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListSnapshotsResponse` + All the usual :class:`google.pubsub_v1.types.ListSnapshotsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -179,9 +188,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListSnapshotsRequest`): + request (google.pubsub_v1.types.ListSnapshotsRequest): The initial request object. - response (:class:`~.pubsub.ListSnapshotsResponse`): + response (google.pubsub_v1.types.ListSnapshotsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -214,7 +223,7 @@ class ListSnapshotsAsyncPager: """A pager for iterating through ``list_snapshots`` requests. This class thinly wraps an initial - :class:`~.pubsub.ListSnapshotsResponse` object, and + :class:`google.pubsub_v1.types.ListSnapshotsResponse` object, and provides an ``__aiter__`` method to iterate through its ``snapshots`` field. @@ -223,7 +232,7 @@ class ListSnapshotsAsyncPager: through the ``snapshots`` field on the corresponding responses. - All the usual :class:`~.pubsub.ListSnapshotsResponse` + All the usual :class:`google.pubsub_v1.types.ListSnapshotsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -241,9 +250,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.pubsub.ListSnapshotsRequest`): + request (google.pubsub_v1.types.ListSnapshotsRequest): The initial request object. - response (:class:`~.pubsub.ListSnapshotsResponse`): + response (google.pubsub_v1.types.ListSnapshotsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index 1be01d024..83815049e 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -63,6 +63,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = 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, ) -> None: @@ -93,6 +94,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` 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): @@ -109,6 +114,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -118,11 +128,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -167,12 +172,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index fa89c11bc..d5efabf2f 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -107,6 +107,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -138,6 +139,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` 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): @@ -154,6 +159,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -163,11 +173,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -212,12 +217,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 39563bb58..001eea88a 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -104,7 +104,7 @@ class SchemaSettings(proto.Message): ``projects/{project}/schemas/{schema}``. The value of this field will be ``_deleted-schema_`` if the schema has been deleted. - encoding (~.gp_schema.Encoding): + encoding (google.pubsub_v1.types.Encoding): The encoding of messages validated against ``schema``. """ @@ -126,10 +126,10 @@ class Topic(proto.Message): (``+``) or percent signs (``%``). It must be between 3 and 255 characters in length, and it must not start with ``"goog"``. - labels (Sequence[~.pubsub.Topic.LabelsEntry]): + labels (Sequence[google.pubsub_v1.types.Topic.LabelsEntry]): See [Creating and managing labels] (https://cloud.google.com/pubsub/docs/labels). - message_storage_policy (~.pubsub.MessageStoragePolicy): + message_storage_policy (google.pubsub_v1.types.MessageStoragePolicy): Policy constraining the set of Google Cloud Platform regions where messages published to the topic may be stored. If not present, then no @@ -140,7 +140,7 @@ class Topic(proto.Message): The expected format is ``projects/*/locations/*/keyRings/*/cryptoKeys/*``. - schema_settings (~.pubsub.SchemaSettings): + schema_settings (google.pubsub_v1.types.SchemaSettings): Settings for validating messages published against a schema. EXPERIMENTAL: Schema support is in development @@ -182,7 +182,7 @@ class PubsubMessage(proto.Message): The message data field. If this field is empty, the message must contain at least one attribute. - attributes (Sequence[~.pubsub.PubsubMessage.AttributesEntry]): + attributes (Sequence[google.pubsub_v1.types.PubsubMessage.AttributesEntry]): Attributes for this message. If this field is empty, the message must contain non-empty data. This can be used to filter messages on the @@ -194,7 +194,7 @@ class PubsubMessage(proto.Message): ``PubsubMessage`` via a ``Pull`` call or a push delivery. It must not be populated by the publisher in a ``Publish`` call. - publish_time (~.timestamp.Timestamp): + publish_time (google.protobuf.timestamp_pb2.Timestamp): The time at which the message was published, populated by the server when it receives the ``Publish`` call. It must not be populated by the publisher in a ``Publish`` call. @@ -236,9 +236,9 @@ class UpdateTopicRequest(proto.Message): r"""Request for the UpdateTopic method. Attributes: - topic (~.pubsub.Topic): + topic (google.pubsub_v1.types.Topic): Required. The updated topic object. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Required. Indicates which fields in the provided topic to update. Must be specified and non-empty. Note that if ``update_mask`` contains "message_storage_policy" but the @@ -259,7 +259,7 @@ class PublishRequest(proto.Message): topic (str): Required. The messages in the request will be published on this topic. Format is ``projects/{project}/topics/{topic}``. - messages (Sequence[~.pubsub.PubsubMessage]): + messages (Sequence[google.pubsub_v1.types.PubsubMessage]): Required. The messages to publish. """ @@ -309,7 +309,7 @@ class ListTopicsResponse(proto.Message): r"""Response for the ``ListTopics`` method. Attributes: - topics (Sequence[~.pubsub.Topic]): + topics (Sequence[google.pubsub_v1.types.Topic]): The resulting topics. next_page_token (str): If not empty, indicates that there may be more topics that @@ -468,7 +468,7 @@ class Subscription(proto.Message): ``projects/{project}/topics/{topic}``. The value of this field will be ``_deleted-topic_`` if the topic has been deleted. - push_config (~.pubsub.PushConfig): + push_config (google.pubsub_v1.types.PushConfig): If push delivery is used with this subscription, this field is used to configure it. An empty ``pushConfig`` signifies that the subscriber will pull and ack messages using API @@ -503,7 +503,7 @@ class Subscription(proto.Message): of the ``message_retention_duration`` window. This must be true if you would like to [Seek to a timestamp] (https://cloud.google.com/pubsub/docs/replay-overview#seek_to_a_time). - message_retention_duration (~.duration.Duration): + message_retention_duration (google.protobuf.duration_pb2.Duration): How long to retain unacknowledged messages in the subscription's backlog, from the moment a message is published. If ``retain_acked_messages`` is true, then this @@ -511,7 +511,7 @@ class Subscription(proto.Message): thus configures how far back in time a ``Seek`` can be done. Defaults to 7 days. Cannot be more than 7 days or less than 10 minutes. - labels (Sequence[~.pubsub.Subscription.LabelsEntry]): + labels (Sequence[google.pubsub_v1.types.Subscription.LabelsEntry]): See Creating and managing labels. @@ -520,7 +520,7 @@ class Subscription(proto.Message): in ``PubsubMessage`` will be delivered to the subscribers in the order in which they are received by the Pub/Sub system. Otherwise, they may be delivered in any order. - expiration_policy (~.pubsub.ExpirationPolicy): + expiration_policy (google.pubsub_v1.types.ExpirationPolicy): A policy that specifies the conditions for this subscription's expiration. A subscription is considered active as long as any connected subscriber is successfully @@ -536,7 +536,7 @@ class Subscription(proto.Message): ``attributes`` field matches the filter are delivered on this subscription. If empty, then no messages are filtered out. - dead_letter_policy (~.pubsub.DeadLetterPolicy): + dead_letter_policy (google.pubsub_v1.types.DeadLetterPolicy): A policy that specifies the conditions for dead lettering messages in this subscription. If dead_letter_policy is not set, dead lettering is disabled. @@ -546,7 +546,7 @@ class Subscription(proto.Message): service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com) must have permission to Acknowledge() messages on this subscription. - retry_policy (~.pubsub.RetryPolicy): + retry_policy (google.pubsub_v1.types.RetryPolicy): A policy that specifies how Pub/Sub retries message delivery for this subscription. @@ -613,12 +613,12 @@ class RetryPolicy(proto.Message): backoff. Attributes: - minimum_backoff (~.duration.Duration): + minimum_backoff (google.protobuf.duration_pb2.Duration): The minimum delay between consecutive deliveries of a given message. Value should be between 0 and 600 seconds. Defaults to 10 seconds. - maximum_backoff (~.duration.Duration): + maximum_backoff (google.protobuf.duration_pb2.Duration): The maximum delay between consecutive deliveries of a given message. Value should be between 0 and 600 seconds. Defaults to 600 @@ -679,7 +679,7 @@ class ExpirationPolicy(proto.Message): expiration (i.e., automatic resource deletion). Attributes: - ttl (~.duration.Duration): + ttl (google.protobuf.duration_pb2.Duration): Specifies the "time-to-live" duration for an associated resource. The resource expires if it is not active for a period of ``ttl``. The definition of "activity" depends on @@ -700,7 +700,7 @@ class PushConfig(proto.Message): A URL locating the endpoint to which messages should be pushed. For example, a Webhook endpoint might use ``https://example.com/push``. - attributes (Sequence[~.pubsub.PushConfig.AttributesEntry]): + attributes (Sequence[google.pubsub_v1.types.PushConfig.AttributesEntry]): Endpoint configuration attributes that can be used to control different aspects of the message delivery. @@ -730,7 +730,7 @@ class PushConfig(proto.Message): .. raw:: html
attributes { "x-goog-version": "v1" } 
- oidc_token (~.pubsub.PushConfig.OidcToken): + oidc_token (google.pubsub_v1.types.PushConfig.OidcToken): If specified, Pub/Sub will generate and attach an OIDC JWT token as an ``Authorization`` header in the HTTP request for every pushed message. @@ -781,7 +781,7 @@ class ReceivedMessage(proto.Message): ack_id (str): This ID can be used to acknowledge the received message. - message (~.pubsub.PubsubMessage): + message (google.pubsub_v1.types.PubsubMessage): The message. delivery_attempt (int): The approximate number of times that Cloud Pub/Sub has @@ -827,9 +827,9 @@ class UpdateSubscriptionRequest(proto.Message): r"""Request for the UpdateSubscription method. Attributes: - subscription (~.pubsub.Subscription): + subscription (google.pubsub_v1.types.Subscription): Required. The updated subscription object. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Required. Indicates which fields in the provided subscription to update. Must be specified and non-empty. @@ -867,7 +867,7 @@ class ListSubscriptionsResponse(proto.Message): r"""Response for the ``ListSubscriptions`` method. Attributes: - subscriptions (Sequence[~.pubsub.Subscription]): + subscriptions (Sequence[google.pubsub_v1.types.Subscription]): The subscriptions that match the request. next_page_token (str): If not empty, indicates that there may be more subscriptions @@ -905,7 +905,7 @@ class ModifyPushConfigRequest(proto.Message): subscription (str): Required. The name of the subscription. Format is ``projects/{project}/subscriptions/{sub}``. - push_config (~.pubsub.PushConfig): + push_config (google.pubsub_v1.types.PushConfig): Required. The push configuration for future deliveries. An empty ``pushConfig`` indicates that the Pub/Sub system @@ -955,7 +955,7 @@ class PullResponse(proto.Message): r"""Response for the ``Pull`` method. Attributes: - received_messages (Sequence[~.pubsub.ReceivedMessage]): + received_messages (Sequence[google.pubsub_v1.types.ReceivedMessage]): Received Pub/Sub messages. The list will be empty if there are no more messages available in the backlog. For JSON, the response can be entirely empty. The Pub/Sub system may @@ -1122,7 +1122,7 @@ class StreamingPullResponse(proto.Message): stream messages from the server to the client. Attributes: - received_messages (Sequence[~.pubsub.ReceivedMessage]): + received_messages (Sequence[google.pubsub_v1.types.ReceivedMessage]): Received Pub/Sub messages. This will not be empty. """ @@ -1154,7 +1154,7 @@ class CreateSnapshotRequest(proto.Message): topic following the successful completion of the CreateSnapshot request. Format is ``projects/{project}/subscriptions/{sub}``. - labels (Sequence[~.pubsub.CreateSnapshotRequest.LabelsEntry]): + labels (Sequence[google.pubsub_v1.types.CreateSnapshotRequest.LabelsEntry]): See Creating and managing labels. @@ -1171,9 +1171,9 @@ class UpdateSnapshotRequest(proto.Message): r"""Request for the UpdateSnapshot method. Attributes: - snapshot (~.pubsub.Snapshot): + snapshot (google.pubsub_v1.types.Snapshot): Required. The updated snapshot object. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Required. Indicates which fields in the provided snapshot to update. Must be specified and non-empty. @@ -1197,7 +1197,7 @@ class Snapshot(proto.Message): topic (str): The name of the topic from which this snapshot is retaining messages. - expire_time (~.timestamp.Timestamp): + expire_time (google.protobuf.timestamp_pb2.Timestamp): The snapshot is guaranteed to exist up until this time. A newly-created snapshot expires no later than 7 days from the time of its creation. Its exact lifetime is determined at @@ -1211,7 +1211,7 @@ class Snapshot(proto.Message): expire in 4 days. The service will refuse to create a snapshot that would expire in less than 1 hour after creation. - labels (Sequence[~.pubsub.Snapshot.LabelsEntry]): + labels (Sequence[google.pubsub_v1.types.Snapshot.LabelsEntry]): See [Creating and managing labels] (https://cloud.google.com/pubsub/docs/labels). """ @@ -1264,7 +1264,7 @@ class ListSnapshotsResponse(proto.Message): r"""Response for the ``ListSnapshots`` method. Attributes: - snapshots (Sequence[~.pubsub.Snapshot]): + snapshots (Sequence[google.pubsub_v1.types.Snapshot]): The resulting snapshots. next_page_token (str): If not empty, indicates that there may be more snapshot that @@ -1299,7 +1299,7 @@ class SeekRequest(proto.Message): Attributes: subscription (str): Required. The subscription to affect. - time (~.timestamp.Timestamp): + time (google.protobuf.timestamp_pb2.Timestamp): The time to seek to. Messages retained in the subscription that were published before this time are marked as acknowledged, and messages retained in the subscription that diff --git a/google/pubsub_v1/types/schema.py b/google/pubsub_v1/types/schema.py index 2efa667c1..e4f71d132 100644 --- a/google/pubsub_v1/types/schema.py +++ b/google/pubsub_v1/types/schema.py @@ -60,7 +60,7 @@ class Schema(proto.Message): name (str): Required. Name of the schema. Format is ``projects/{project}/schemas/{schema}``. - type_ (~.gp_schema.Schema.Type): + type_ (google.pubsub_v1.types.Schema.Type): The type of the schema definition. definition (str): The definition of the schema. This should contain a string @@ -88,7 +88,7 @@ class CreateSchemaRequest(proto.Message): parent (str): Required. The name of the project in which to create the schema. Format is ``projects/{project-id}``. - schema (~.gp_schema.Schema): + schema (google.pubsub_v1.types.Schema): Required. The schema object to create. This schema's ``name`` parameter is ignored. The schema @@ -117,7 +117,7 @@ class GetSchemaRequest(proto.Message): name (str): Required. The name of the schema to get. Format is ``projects/{project}/schemas/{schema}``. - view (~.gp_schema.SchemaView): + view (google.pubsub_v1.types.SchemaView): The set of fields to return in the response. If not set, returns a Schema with ``name`` and ``type``, but not ``definition``. Set to ``FULL`` to retrieve all fields. @@ -135,7 +135,7 @@ class ListSchemasRequest(proto.Message): parent (str): Required. The name of the project in which to list schemas. Format is ``projects/{project-id}``. - view (~.gp_schema.SchemaView): + view (google.pubsub_v1.types.SchemaView): The set of Schema fields to return in the response. If not set, returns Schemas with ``name`` and ``type``, but not ``definition``. Set to ``FULL`` to retrieve all fields. @@ -161,7 +161,7 @@ class ListSchemasResponse(proto.Message): r"""Response for the ``ListSchemas`` method. Attributes: - schemas (Sequence[~.gp_schema.Schema]): + schemas (Sequence[google.pubsub_v1.types.Schema]): The resulting schemas. next_page_token (str): If not empty, indicates that there may be more schemas that @@ -197,7 +197,7 @@ class ValidateSchemaRequest(proto.Message): parent (str): Required. The name of the project in which to validate schemas. Format is ``projects/{project-id}``. - schema (~.gp_schema.Schema): + schema (google.pubsub_v1.types.Schema): Required. The schema object to validate. """ @@ -221,11 +221,11 @@ class ValidateMessageRequest(proto.Message): Name of the schema against which to validate. Format is ``projects/{project}/schemas/{schema}``. - schema (~.gp_schema.Schema): + schema (google.pubsub_v1.types.Schema): Ad-hoc schema against which to validate message (bytes): Message to validate against the provided ``schema_spec``. - encoding (~.gp_schema.Encoding): + encoding (google.pubsub_v1.types.Encoding): The encoding expected for messages """ diff --git a/noxfile.py b/noxfile.py index 913f271d5..6b178ad18 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,6 +30,17 @@ SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] +# 'docfx' is excluded since it only needs to run in 'docs-presubmit' +nox.options.sessions = [ + "unit", + "system", + "cover", + "lint", + "lint_setup_py", + "blacken", + "docs", +] + @nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): @@ -75,12 +86,14 @@ def default(session): session.install( "mock", "pytest", "pytest-cov", ) + session.install("-e", ".") # Run py.test against the unit tests. session.run( "py.test", "--quiet", + f"--junitxml=unit_{session.python}_sponge_log.xml", "--cov=google/cloud", "--cov=tests/unit", "--cov-append", @@ -127,9 +140,21 @@ def system(session): # Run py.test against the system tests. if system_test_exists: - session.run("py.test", "--quiet", system_test_path, *session.posargs) + session.run( + "py.test", + "--quiet", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_path, + *session.posargs, + ) if system_test_folder_exists: - session.run("py.test", "--quiet", system_test_folder_path, *session.posargs) + session.run( + "py.test", + "--quiet", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_folder_path, + *session.posargs, + ) @nox.session(python=DEFAULT_PYTHON_VERSION) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index bca0522ec..97bf7da80 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -85,7 +85,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to tested samples. -ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] +ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8", "3.9"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] diff --git a/synth.metadata b/synth.metadata index d4b5ca201..8d6a7126f 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,36 +4,36 @@ "git": { "name": ".", "remote": "git@github.com:plamut/python-pubsub.git", - "sha": "a4eab77decdd7ea0d421b56a784e8a673a5595ec" + "sha": "12307d8f12d96974130c7a49bd1eba4d62956f21" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "61ab0348bd228c942898aee291d677f0afdb888c", - "internalRef": "352069361" + "sha": "8b3d36daaf5561496b7d4075fba4f2c52d18ca1c", + "internalRef": "359285402" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" + "sha": "79ab0b44a2cc7d803d07c107f9faf07729fc4012" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" + "sha": "79ab0b44a2cc7d803d07c107f9faf07729fc4012" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "56ddc68f36b32341e9f22c2c59b4ce6aa3ba635f" + "sha": "79ab0b44a2cc7d803d07c107f9faf07729fc4012" } } ], diff --git a/synth.py b/synth.py index d2ade4b27..2c0bc5607 100644 --- a/synth.py +++ b/synth.py @@ -79,6 +79,50 @@ if count < 18: raise Exception("Expected replacements for gRPC channel options not made.") +# If the emulator is used, force an insecure gRPC channel to avoid SSL errors. +clients_to_patch = [ + "google/pubsub_v1/services/publisher/client.py", + "google/pubsub_v1/services/subscriber/client.py", +] +err_msg = "Expected replacements for gRPC channel to use with the emulator not made." + +count = s.replace( + clients_to_patch, + r"import os", + "import functools\n\g<0>" +) + +if count < len(clients_to_patch): + raise Exception(err_msg) + +count = s.replace( + clients_to_patch, + r"from google\.pubsub_v1\.types import pubsub", + "\g<0>\n\nimport grpc" +) + +if count < len(clients_to_patch): + raise Exception(err_msg) + +count = s.replace( + clients_to_patch, + r"Transport = type\(self\)\.get_transport_class\(transport\)", + """\g<0> + + emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") + if emulator_host: + if issubclass(Transport, type(self)._transport_registry["grpc"]): + channel = grpc.insecure_channel(target=emulator_host) + else: + channel = grpc.aio.insecure_channel(target=emulator_host) + Transport = functools.partial(Transport, channel=channel) + + """, +) + +if count < len(clients_to_patch): + raise Exception(err_msg) + # Monkey patch the streaming_pull() GAPIC method to disable pre-fetching stream # results. s.replace( diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index d1c34d474..cf24f5273 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -85,7 +85,20 @@ def test__get_default_mtls_endpoint(): assert PublisherClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [PublisherClient, PublisherAsyncClient]) +def test_publisher_client_from_service_account_info(): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = PublisherClient.from_service_account_info(info) + assert client.transport._credentials == creds + + assert client.transport._host == "pubsub.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [PublisherClient, PublisherAsyncClient,]) def test_publisher_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -103,7 +116,10 @@ def test_publisher_client_from_service_account_file(client_class): def test_publisher_client_get_transport_class(): transport = PublisherClient.get_transport_class() - assert transport == transports.PublisherGrpcTransport + available_transports = [ + transports.PublisherGrpcTransport, + ] + assert transport in available_transports transport = PublisherClient.get_transport_class("grpc") assert transport == transports.PublisherGrpcTransport @@ -150,7 +166,7 @@ def test_publisher_client_client_options(client_class, transport_class, transpor credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -166,7 +182,7 @@ def test_publisher_client_client_options(client_class, transport_class, transpor credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -182,7 +198,7 @@ def test_publisher_client_client_options(client_class, transport_class, transpor credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -210,7 +226,7 @@ def test_publisher_client_client_options(client_class, transport_class, transpor credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -259,29 +275,25 @@ def test_publisher_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -290,66 +302,53 @@ def test_publisher_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -375,7 +374,7 @@ def test_publisher_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -405,7 +404,7 @@ def test_publisher_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -422,7 +421,7 @@ def test_publisher_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -2492,7 +2491,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport], + [transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2612,6 +2611,52 @@ def test_publisher_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport], +) +def test_publisher_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/pubsub", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ("grpc.keepalive_time_ms", 30000), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_publisher_host_no_port(): client = PublisherClient( credentials=credentials.AnonymousCredentials(), @@ -2633,7 +2678,7 @@ def test_publisher_host_with_port(): def test_publisher_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.PublisherGrpcTransport( @@ -2645,7 +2690,7 @@ def test_publisher_grpc_transport_channel(): def test_publisher_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.PublisherGrpcAsyncIOTransport( @@ -2656,6 +2701,8 @@ def test_publisher_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport], @@ -2665,7 +2712,7 @@ def test_publisher_transport_channel_mtls_with_client_cert_source(transport_clas "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2707,6 +2754,8 @@ def test_publisher_transport_channel_mtls_with_client_cert_source(transport_clas assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport], @@ -2719,7 +2768,7 @@ def test_publisher_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 79cc71324..7f2f9d055 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -88,8 +88,21 @@ def test__get_default_mtls_endpoint(): ) +def test_schema_service_client_from_service_account_info(): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = SchemaServiceClient.from_service_account_info(info) + assert client.transport._credentials == creds + + assert client.transport._host == "pubsub.googleapis.com:443" + + @pytest.mark.parametrize( - "client_class", [SchemaServiceClient, SchemaServiceAsyncClient] + "client_class", [SchemaServiceClient, SchemaServiceAsyncClient,] ) def test_schema_service_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() @@ -108,7 +121,10 @@ def test_schema_service_client_from_service_account_file(client_class): def test_schema_service_client_get_transport_class(): transport = SchemaServiceClient.get_transport_class() - assert transport == transports.SchemaServiceGrpcTransport + available_transports = [ + transports.SchemaServiceGrpcTransport, + ] + assert transport in available_transports transport = SchemaServiceClient.get_transport_class("grpc") assert transport == transports.SchemaServiceGrpcTransport @@ -159,7 +175,7 @@ def test_schema_service_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -175,7 +191,7 @@ def test_schema_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -191,7 +207,7 @@ def test_schema_service_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -219,7 +235,7 @@ def test_schema_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -270,29 +286,25 @@ def test_schema_service_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -301,66 +313,53 @@ def test_schema_service_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -386,7 +385,7 @@ def test_schema_service_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -416,7 +415,7 @@ def test_schema_service_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -435,7 +434,7 @@ def test_schema_service_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -1847,6 +1846,55 @@ def test_schema_service_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.SchemaServiceGrpcTransport, + transports.SchemaServiceGrpcAsyncIOTransport, + ], +) +def test_schema_service_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/pubsub", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ("grpc.keepalive_time_ms", 30000), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_schema_service_host_no_port(): client = SchemaServiceClient( credentials=credentials.AnonymousCredentials(), @@ -1868,7 +1916,7 @@ def test_schema_service_host_with_port(): def test_schema_service_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SchemaServiceGrpcTransport( @@ -1880,7 +1928,7 @@ def test_schema_service_grpc_transport_channel(): def test_schema_service_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SchemaServiceGrpcAsyncIOTransport( @@ -1891,6 +1939,8 @@ def test_schema_service_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -1903,7 +1953,7 @@ def test_schema_service_transport_channel_mtls_with_client_cert_source(transport "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -1945,6 +1995,8 @@ def test_schema_service_transport_channel_mtls_with_client_cert_source(transport assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -1960,7 +2012,7 @@ def test_schema_service_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 8dec1c27d..2eb3503cd 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -87,7 +87,20 @@ def test__get_default_mtls_endpoint(): assert SubscriberClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [SubscriberClient, SubscriberAsyncClient]) +def test_subscriber_client_from_service_account_info(): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = SubscriberClient.from_service_account_info(info) + assert client.transport._credentials == creds + + assert client.transport._host == "pubsub.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [SubscriberClient, SubscriberAsyncClient,]) def test_subscriber_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -105,7 +118,10 @@ def test_subscriber_client_from_service_account_file(client_class): def test_subscriber_client_get_transport_class(): transport = SubscriberClient.get_transport_class() - assert transport == transports.SubscriberGrpcTransport + available_transports = [ + transports.SubscriberGrpcTransport, + ] + assert transport in available_transports transport = SubscriberClient.get_transport_class("grpc") assert transport == transports.SubscriberGrpcTransport @@ -154,7 +170,7 @@ def test_subscriber_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -170,7 +186,7 @@ def test_subscriber_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -186,7 +202,7 @@ def test_subscriber_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -214,7 +230,7 @@ def test_subscriber_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -263,29 +279,25 @@ def test_subscriber_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -294,66 +306,53 @@ def test_subscriber_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -379,7 +378,7 @@ def test_subscriber_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -409,7 +408,7 @@ def test_subscriber_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -426,7 +425,7 @@ def test_subscriber_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -3754,7 +3753,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.SubscriberGrpcTransport, transports.SubscriberGrpcAsyncIOTransport], + [transports.SubscriberGrpcTransport, transports.SubscriberGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -3881,6 +3880,52 @@ def test_subscriber_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.SubscriberGrpcTransport, transports.SubscriberGrpcAsyncIOTransport], +) +def test_subscriber_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/pubsub", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ("grpc.keepalive_time_ms", 30000), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_subscriber_host_no_port(): client = SubscriberClient( credentials=credentials.AnonymousCredentials(), @@ -3902,7 +3947,7 @@ def test_subscriber_host_with_port(): def test_subscriber_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SubscriberGrpcTransport( @@ -3914,7 +3959,7 @@ def test_subscriber_grpc_transport_channel(): def test_subscriber_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SubscriberGrpcAsyncIOTransport( @@ -3925,6 +3970,8 @@ def test_subscriber_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.SubscriberGrpcTransport, transports.SubscriberGrpcAsyncIOTransport], @@ -3934,7 +3981,7 @@ def test_subscriber_transport_channel_mtls_with_client_cert_source(transport_cla "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -3976,6 +4023,8 @@ def test_subscriber_transport_channel_mtls_with_client_cert_source(transport_cla assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.SubscriberGrpcTransport, transports.SubscriberGrpcAsyncIOTransport], @@ -3988,7 +4037,7 @@ def test_subscriber_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel