diff --git a/.flake8 b/.flake8
index ed931638..29227d4c 100644
--- a/.flake8
+++ b/.flake8
@@ -26,6 +26,7 @@ exclude =
*_pb2.py
# Standard linting exemptions.
+ **/.nox/**
__pycache__,
.git,
*.pyc,
diff --git a/.github/header-checker-lint.yml b/.github/header-checker-lint.yml
new file mode 100644
index 00000000..fc281c05
--- /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 efad5203..b4243ced 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,9 +50,10 @@ docs.metadata
# Virtual environment
env/
-venv/
+
+# 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 f98dd91c..1e7cc13e 100755
--- a/.kokoro/build.sh
+++ b/.kokoro/build.sh
@@ -15,7 +15,11 @@
set -eo pipefail
-cd github/python-pubsublite
+if [[ -z "${PROJECT_ROOT:-}" ]]; then
+ PROJECT_ROOT="github/python-pubsublite"
+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 11181078..1328c435 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-pubsublite/.kokoro/build.sh"
+}
+
+# Only run this nox session.
+env_vars: {
+ key: "NOX_SESSION"
+ value: "docs docfx"
+}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..32302e48
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,17 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v3.4.0
+ hooks:
+ - id: trailing-whitespace
+ - id: end-of-file-fixer
+ - id: check-yaml
+- repo: https://github.com/psf/black
+ rev: 19.10b0
+ hooks:
+ - id: black
+- repo: https://gitlab.com/pycqa/flake8
+ rev: 3.9.0
+ hooks:
+ - id: flake8
diff --git a/.trampolinerc b/.trampolinerc
index 995ee291..383b6ec8 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/CHANGELOG.md b/CHANGELOG.md
index 9a7b44dc..56c1acff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [0.4.0](https://www.github.com/googleapis/python-pubsublite/compare/v0.3.0...v0.4.0) (2021-03-22)
+
+
+### Features
+
+* adding ability to create subscriptions at HEAD ([#106](https://www.github.com/googleapis/python-pubsublite/issues/106)) ([4d03d3a](https://www.github.com/googleapis/python-pubsublite/commit/4d03d3a8ae8089fea87f5acd02a170697fa136fc))
+
## [0.3.0](https://www.github.com/googleapis/python-pubsublite/compare/v0.2.0...v0.3.0) (2021-03-09)
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index a6c4c49c..20aca1aa 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -21,8 +21,8 @@ In order to add a feature:
- The feature must be documented in both the API and narrative
documentation.
-- The feature must work fully on the following CPython versions: 2.7,
- 3.5, 3.6, 3.7 and 3.8 on both UNIX and Windows.
+- The feature must work fully on the following CPython versions:
+ 3.6, 3.7, 3.8 and 3.9 on both UNIX and Windows.
- The feature must not add unnecessary dependencies (where
"unnecessary" is of course subjective, but new dependencies should
@@ -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::
-- PEP8 compliance, with exceptions defined in the linter configuration.
+ $ nox -s blacken
+
+- 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::
@@ -111,6 +120,16 @@ Coding Style
should point to the official ``googleapis`` checkout and the
the branch should be the main branch on that remote (``master``).
+- This repository contains configuration for the
+ `pre-commit `__ tool, which automates checking
+ our linters during a commit. If you have it installed on your ``$PATH``,
+ you can enable enforcing those checks via:
+
+.. code-block:: bash
+
+ $ pre-commit install
+ pre-commit installed at .git/hooks/pre-commit
+
Exceptions to PEP8:
- Many unit tests use a helper method, ``_call_fut`` ("FUT" is short for
@@ -123,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
@@ -192,25 +216,24 @@ Supported Python Versions
We support:
-- `Python 3.5`_
- `Python 3.6`_
- `Python 3.7`_
- `Python 3.8`_
+- `Python 3.9`_
-.. _Python 3.5: https://docs.python.org/3.5/
.. _Python 3.6: https://docs.python.org/3.6/
.. _Python 3.7: https://docs.python.org/3.7/
.. _Python 3.8: https://docs.python.org/3.8/
+.. _Python 3.9: https://docs.python.org/3.9/
Supported versions can be found in our ``noxfile.py`` `config`_.
.. _config: https://github.com/googleapis/python-pubsublite/blob/master/noxfile.py
-Python 2.7 support is deprecated. All code changes should maintain Python 2.7 compatibility until January 1, 2020.
We also explicitly decided to support Python 3 beginning with version
-3.5. Reasons for this include:
+3.6. Reasons for this include:
- Encouraging use of newest versions of Python 3
- Taking the lead of `prominent`_ open-source `projects`_
diff --git a/LICENSE b/LICENSE
index a8ee855d..d6456956 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 e9e29d12..e783f4c6 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 0abaf229..bcd37bbd 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/docs/conf.py b/docs/conf.py
index ff0e2917..4ec5f60d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -345,8 +345,8 @@
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
- "python": ("https://python.readthedocs.io/en/latest/", None),
- "google-auth": ("https://google-auth.readthedocs.io/en/stable", None),
+ "python": ("https://python.readthedocs.org/en/latest/", None),
+ "google-auth": ("https://googleapis.dev/python/google-auth/latest/", None),
"google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,),
"grpc": ("https://grpc.github.io/grpc/python/", None),
"proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None),
diff --git a/google/cloud/pubsublite/admin_client.py b/google/cloud/pubsublite/admin_client.py
index d5e82197..7d7a8dd5 100644
--- a/google/cloud/pubsublite/admin_client.py
+++ b/google/cloud/pubsublite/admin_client.py
@@ -30,6 +30,7 @@
SubscriptionPath,
LocationPath,
TopicPath,
+ BacklogLocation,
)
from google.cloud.pubsublite_v1 import AdminServiceClient, Subscription, Topic
@@ -101,8 +102,12 @@ def list_topic_subscriptions(self, topic_path: TopicPath):
return self._impl.list_topic_subscriptions(topic_path)
@overrides
- def create_subscription(self, subscription: Subscription) -> Subscription:
- return self._impl.create_subscription(subscription)
+ def create_subscription(
+ self,
+ subscription: Subscription,
+ starting_offset: BacklogLocation = BacklogLocation.END,
+ ) -> Subscription:
+ return self._impl.create_subscription(subscription, starting_offset)
@overrides
def get_subscription(self, subscription_path: SubscriptionPath) -> Subscription:
diff --git a/google/cloud/pubsublite/admin_client_interface.py b/google/cloud/pubsublite/admin_client_interface.py
index 2c59a102..38b90ba3 100644
--- a/google/cloud/pubsublite/admin_client_interface.py
+++ b/google/cloud/pubsublite/admin_client_interface.py
@@ -20,6 +20,7 @@
TopicPath,
LocationPath,
SubscriptionPath,
+ BacklogLocation,
)
from google.cloud.pubsublite_v1 import Topic, Subscription
from google.protobuf.field_mask_pb2 import FieldMask
@@ -63,8 +64,14 @@ def list_topic_subscriptions(self, topic_path: TopicPath):
"""List the subscriptions that exist for a given topic."""
@abstractmethod
- def create_subscription(self, subscription: Subscription) -> Subscription:
- """Create a subscription, returns the created subscription."""
+ def create_subscription(
+ self,
+ subscription: Subscription,
+ starting_offset: BacklogLocation = BacklogLocation.END,
+ ) -> Subscription:
+ """Create a subscription, returns the created subscription. By default
+ a subscription will only receive messages published after the
+ subscription was created."""
@abstractmethod
def get_subscription(self, subscription_path: SubscriptionPath) -> Subscription:
diff --git a/google/cloud/pubsublite/internal/wire/admin_client_impl.py b/google/cloud/pubsublite/internal/wire/admin_client_impl.py
index 234916b9..ab5acba6 100644
--- a/google/cloud/pubsublite/internal/wire/admin_client_impl.py
+++ b/google/cloud/pubsublite/internal/wire/admin_client_impl.py
@@ -22,6 +22,7 @@
SubscriptionPath,
LocationPath,
TopicPath,
+ BacklogLocation,
)
from google.cloud.pubsublite_v1 import (
Subscription,
@@ -72,12 +73,19 @@ def list_topic_subscriptions(self, topic_path: TopicPath):
]
return [SubscriptionPath.parse(x) for x in subscription_strings]
- def create_subscription(self, subscription: Subscription) -> Subscription:
+ def create_subscription(
+ self,
+ subscription: Subscription,
+ starting_offset: BacklogLocation = BacklogLocation.END,
+ ) -> Subscription:
path = SubscriptionPath.parse(subscription.name)
return self._underlying.create_subscription(
- parent=str(path.to_location_path()),
- subscription=subscription,
- subscription_id=path.name,
+ request={
+ "parent": str(path.to_location_path()),
+ "subscription": subscription,
+ "subscription_id": path.name,
+ "skip_backlog": (starting_offset == BacklogLocation.END),
+ }
)
def get_subscription(self, subscription_path: SubscriptionPath) -> Subscription:
diff --git a/google/cloud/pubsublite/types/__init__.py b/google/cloud/pubsublite/types/__init__.py
index 6304fd3c..fbf5cc00 100644
--- a/google/cloud/pubsublite/types/__init__.py
+++ b/google/cloud/pubsublite/types/__init__.py
@@ -18,6 +18,7 @@
from .paths import LocationPath, TopicPath, SubscriptionPath
from .message_metadata import MessageMetadata
from .flow_control_settings import FlowControlSettings, DISABLED_FLOW_CONTROL
+from .backlog_location import BacklogLocation
__all__ = (
"CloudRegion",
@@ -28,4 +29,5 @@
"MessageMetadata",
"SubscriptionPath",
"TopicPath",
+ "BacklogLocation",
)
diff --git a/google/cloud/pubsublite/types/backlog_location.py b/google/cloud/pubsublite/types/backlog_location.py
new file mode 100644
index 00000000..42e64218
--- /dev/null
+++ b/google/cloud/pubsublite/types/backlog_location.py
@@ -0,0 +1,24 @@
+# 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
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import enum
+
+
+class BacklogLocation(enum.Enum):
+ """A location with respect to the message backlog. BEGINNING refers to the
+ location of the oldest retained message. END refers to the location past
+ all currently published messages, skipping the entire message backlog."""
+
+ BEGINNING = 0
+ END = 1
diff --git a/google/cloud/pubsublite_v1/__init__.py b/google/cloud/pubsublite_v1/__init__.py
index 40a270af..97f01058 100644
--- a/google/cloud/pubsublite_v1/__init__.py
+++ b/google/cloud/pubsublite_v1/__init__.py
@@ -72,6 +72,8 @@
from .types.subscriber import SeekResponse
from .types.subscriber import SubscribeRequest
from .types.subscriber import SubscribeResponse
+from .types.topic_stats import ComputeHeadCursorRequest
+from .types.topic_stats import ComputeHeadCursorResponse
from .types.topic_stats import ComputeMessageStatsRequest
from .types.topic_stats import ComputeMessageStatsResponse
@@ -81,6 +83,8 @@
"AttributeValues",
"CommitCursorRequest",
"CommitCursorResponse",
+ "ComputeHeadCursorRequest",
+ "ComputeHeadCursorResponse",
"ComputeMessageStatsRequest",
"ComputeMessageStatsResponse",
"CreateSubscriptionRequest",
@@ -114,6 +118,7 @@
"PartitionAssignment",
"PartitionAssignmentAck",
"PartitionAssignmentRequest",
+ "PartitionAssignmentServiceClient",
"PartitionCursor",
"PubSubMessage",
"PublishRequest",
@@ -128,12 +133,11 @@
"StreamingCommitCursorResponse",
"SubscribeRequest",
"SubscribeResponse",
- "SubscriberServiceClient",
"Subscription",
"Topic",
"TopicPartitions",
"TopicStatsServiceClient",
"UpdateSubscriptionRequest",
"UpdateTopicRequest",
- "PartitionAssignmentServiceClient",
+ "SubscriberServiceClient",
)
diff --git a/google/cloud/pubsublite_v1/services/admin_service/async_client.py b/google/cloud/pubsublite_v1/services/admin_service/async_client.py
index 1cdb674b..c063c75d 100644
--- a/google/cloud/pubsublite_v1/services/admin_service/async_client.py
+++ b/google/cloud/pubsublite_v1/services/admin_service/async_client.py
@@ -79,7 +79,36 @@ class AdminServiceAsyncClient:
AdminServiceClient.parse_common_location_path
)
- from_service_account_file = AdminServiceClient.from_service_account_file
+ @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:
+ AdminServiceAsyncClient: The constructed client.
+ """
+ return AdminServiceClient.from_service_account_info.__func__(AdminServiceAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ AdminServiceAsyncClient: The constructed client.
+ """
+ return AdminServiceClient.from_service_account_file.__func__(AdminServiceAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -157,18 +186,20 @@ async def create_topic(
r"""Creates a new topic.
Args:
- request (:class:`~.admin.CreateTopicRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.CreateTopicRequest`):
The request object. Request for CreateTopic.
parent (:class:`str`):
Required. The parent location in which to create the
topic. Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- topic (:class:`~.common.Topic`):
+ topic (:class:`google.cloud.pubsublite_v1.types.Topic`):
Required. Configuration of the topic to create. Its
``name`` field is ignored.
+
This corresponds to the ``topic`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -177,6 +208,7 @@ async def create_topic(
the final component of the topic's name.
This value is structured like: ``my-topic-name``.
+
This corresponds to the ``topic_id`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -188,7 +220,7 @@ async def create_topic(
sent along with the request as metadata.
Returns:
- ~.common.Topic:
+ google.cloud.pubsublite_v1.types.Topic:
Metadata about a topic resource.
"""
# Create or coerce a protobuf request object.
@@ -245,11 +277,12 @@ async def get_topic(
r"""Returns the topic configuration.
Args:
- request (:class:`~.admin.GetTopicRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.GetTopicRequest`):
The request object. Request for GetTopic.
name (:class:`str`):
Required. The name of the topic whose
configuration to return.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -261,7 +294,7 @@ async def get_topic(
sent along with the request as metadata.
Returns:
- ~.common.Topic:
+ google.cloud.pubsublite_v1.types.Topic:
Metadata about a topic resource.
"""
# Create or coerce a protobuf request object.
@@ -315,11 +348,12 @@ async def get_topic_partitions(
topic.
Args:
- request (:class:`~.admin.GetTopicPartitionsRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.GetTopicPartitionsRequest`):
The request object. Request for GetTopicPartitions.
name (:class:`str`):
Required. The topic whose partition
information to return.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -331,7 +365,7 @@ async def get_topic_partitions(
sent along with the request as metadata.
Returns:
- ~.admin.TopicPartitions:
+ google.cloud.pubsublite_v1.types.TopicPartitions:
Response for GetTopicPartitions.
"""
# Create or coerce a protobuf request object.
@@ -384,12 +418,13 @@ async def list_topics(
r"""Returns the list of topics for the given project.
Args:
- request (:class:`~.admin.ListTopicsRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.ListTopicsRequest`):
The request object. Request for ListTopics.
parent (:class:`str`):
Required. The parent whose topics are to be listed.
Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -401,7 +436,7 @@ async def list_topics(
sent along with the request as metadata.
Returns:
- ~.pagers.ListTopicsAsyncPager:
+ google.cloud.pubsublite_v1.services.admin_service.pagers.ListTopicsAsyncPager:
Response for ListTopics.
Iterating over this object will yield
results and resolve additional pages
@@ -465,17 +500,19 @@ async def update_topic(
r"""Updates properties of the specified topic.
Args:
- request (:class:`~.admin.UpdateTopicRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.UpdateTopicRequest`):
The request object. Request for UpdateTopic.
- topic (:class:`~.common.Topic`):
+ topic (:class:`google.cloud.pubsublite_v1.types.Topic`):
Required. The topic to update. Its ``name`` field must
be populated.
+
This corresponds to the ``topic`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- update_mask (:class:`~.field_mask.FieldMask`):
+ update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`):
Required. A mask specifying the topic
fields to change.
+
This corresponds to the ``update_mask`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -487,7 +524,7 @@ async def update_topic(
sent along with the request as metadata.
Returns:
- ~.common.Topic:
+ google.cloud.pubsublite_v1.types.Topic:
Metadata about a topic resource.
"""
# Create or coerce a protobuf request object.
@@ -544,11 +581,12 @@ async def delete_topic(
r"""Deletes the specified topic.
Args:
- request (:class:`~.admin.DeleteTopicRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.DeleteTopicRequest`):
The request object. Request for DeleteTopic.
name (:class:`str`):
Required. The name of the topic to
delete.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -609,11 +647,12 @@ async def list_topic_subscriptions(
topic.
Args:
- request (:class:`~.admin.ListTopicSubscriptionsRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.ListTopicSubscriptionsRequest`):
The request object. Request for ListTopicSubscriptions.
name (:class:`str`):
Required. The name of the topic whose
subscriptions to list.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -625,7 +664,7 @@ async def list_topic_subscriptions(
sent along with the request as metadata.
Returns:
- ~.pagers.ListTopicSubscriptionsAsyncPager:
+ google.cloud.pubsublite_v1.services.admin_service.pagers.ListTopicSubscriptionsAsyncPager:
Response for ListTopicSubscriptions.
Iterating over this object will yield
results and resolve additional pages
@@ -690,18 +729,20 @@ async def create_subscription(
r"""Creates a new subscription.
Args:
- request (:class:`~.admin.CreateSubscriptionRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.CreateSubscriptionRequest`):
The request object. Request for CreateSubscription.
parent (:class:`str`):
Required. The parent location in which to create the
subscription. Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- subscription (:class:`~.common.Subscription`):
+ subscription (:class:`google.cloud.pubsublite_v1.types.Subscription`):
Required. Configuration of the subscription to create.
Its ``name`` field is ignored.
+
This corresponds to the ``subscription`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -710,6 +751,7 @@ async def create_subscription(
become the final component of the subscription's name.
This value is structured like: ``my-sub-name``.
+
This corresponds to the ``subscription_id`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -721,7 +763,7 @@ async def create_subscription(
sent along with the request as metadata.
Returns:
- ~.common.Subscription:
+ google.cloud.pubsublite_v1.types.Subscription:
Metadata about a subscription
resource.
@@ -780,12 +822,13 @@ async def get_subscription(
r"""Returns the subscription configuration.
Args:
- request (:class:`~.admin.GetSubscriptionRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.GetSubscriptionRequest`):
The request object. Request for GetSubscription.
name (:class:`str`):
Required. The name of the
subscription whose configuration to
return.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -797,7 +840,7 @@ async def get_subscription(
sent along with the request as metadata.
Returns:
- ~.common.Subscription:
+ google.cloud.pubsublite_v1.types.Subscription:
Metadata about a subscription
resource.
@@ -853,12 +896,13 @@ async def list_subscriptions(
project.
Args:
- request (:class:`~.admin.ListSubscriptionsRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.ListSubscriptionsRequest`):
The request object. Request for ListSubscriptions.
parent (:class:`str`):
Required. The parent whose subscriptions are to be
listed. Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -870,7 +914,7 @@ async def list_subscriptions(
sent along with the request as metadata.
Returns:
- ~.pagers.ListSubscriptionsAsyncPager:
+ google.cloud.pubsublite_v1.services.admin_service.pagers.ListSubscriptionsAsyncPager:
Response for ListSubscriptions.
Iterating over this object will yield
results and resolve additional pages
@@ -934,17 +978,19 @@ async def update_subscription(
r"""Updates properties of the specified subscription.
Args:
- request (:class:`~.admin.UpdateSubscriptionRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.UpdateSubscriptionRequest`):
The request object. Request for UpdateSubscription.
- subscription (:class:`~.common.Subscription`):
+ subscription (:class:`google.cloud.pubsublite_v1.types.Subscription`):
Required. The subscription to update. Its ``name`` field
must be populated. Topic field must not be populated.
+
This corresponds to the ``subscription`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- update_mask (:class:`~.field_mask.FieldMask`):
+ update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`):
Required. A mask specifying the
subscription fields to change.
+
This corresponds to the ``update_mask`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -956,7 +1002,7 @@ async def update_subscription(
sent along with the request as metadata.
Returns:
- ~.common.Subscription:
+ google.cloud.pubsublite_v1.types.Subscription:
Metadata about a subscription
resource.
@@ -1015,11 +1061,12 @@ async def delete_subscription(
r"""Deletes the specified subscription.
Args:
- request (:class:`~.admin.DeleteSubscriptionRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.DeleteSubscriptionRequest`):
The request object. Request for DeleteSubscription.
name (:class:`str`):
Required. The name of the
subscription to delete.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
diff --git a/google/cloud/pubsublite_v1/services/admin_service/client.py b/google/cloud/pubsublite_v1/services/admin_service/client.py
index 159211ab..622d4cae 100644
--- a/google/cloud/pubsublite_v1/services/admin_service/client.py
+++ b/google/cloud/pubsublite_v1/services/admin_service/client.py
@@ -113,6 +113,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:
+ AdminServiceClient: 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
@@ -125,7 +141,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.
+ AdminServiceClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -249,10 +265,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, ~.AdminServiceTransport]): The
+ transport (Union[str, AdminServiceTransport]): 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
@@ -288,21 +304,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:
@@ -345,7 +357,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,
)
@@ -364,26 +376,29 @@ def create_topic(
r"""Creates a new topic.
Args:
- request (:class:`~.admin.CreateTopicRequest`):
+ request (google.cloud.pubsublite_v1.types.CreateTopicRequest):
The request object. Request for CreateTopic.
- parent (:class:`str`):
+ parent (str):
Required. The parent location in which to create the
topic. Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- topic (:class:`~.common.Topic`):
+ topic (google.cloud.pubsublite_v1.types.Topic):
Required. Configuration of the topic to create. Its
``name`` field is ignored.
+
This corresponds to the ``topic`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- topic_id (:class:`str`):
+ topic_id (str):
Required. The ID to use for the topic, which will become
the final component of the topic's name.
This value is structured like: ``my-topic-name``.
+
This corresponds to the ``topic_id`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -395,7 +410,7 @@ def create_topic(
sent along with the request as metadata.
Returns:
- ~.common.Topic:
+ google.cloud.pubsublite_v1.types.Topic:
Metadata about a topic resource.
"""
# Create or coerce a protobuf request object.
@@ -453,11 +468,12 @@ def get_topic(
r"""Returns the topic configuration.
Args:
- request (:class:`~.admin.GetTopicRequest`):
+ request (google.cloud.pubsublite_v1.types.GetTopicRequest):
The request object. Request for GetTopic.
- name (:class:`str`):
+ name (str):
Required. The name of the topic whose
configuration to return.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -469,7 +485,7 @@ def get_topic(
sent along with the request as metadata.
Returns:
- ~.common.Topic:
+ google.cloud.pubsublite_v1.types.Topic:
Metadata about a topic resource.
"""
# Create or coerce a protobuf request object.
@@ -524,11 +540,12 @@ def get_topic_partitions(
topic.
Args:
- request (:class:`~.admin.GetTopicPartitionsRequest`):
+ request (google.cloud.pubsublite_v1.types.GetTopicPartitionsRequest):
The request object. Request for GetTopicPartitions.
- name (:class:`str`):
+ name (str):
Required. The topic whose partition
information to return.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -540,7 +557,7 @@ def get_topic_partitions(
sent along with the request as metadata.
Returns:
- ~.admin.TopicPartitions:
+ google.cloud.pubsublite_v1.types.TopicPartitions:
Response for GetTopicPartitions.
"""
# Create or coerce a protobuf request object.
@@ -594,12 +611,13 @@ def list_topics(
r"""Returns the list of topics for the given project.
Args:
- request (:class:`~.admin.ListTopicsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListTopicsRequest):
The request object. Request for ListTopics.
- parent (:class:`str`):
+ parent (str):
Required. The parent whose topics are to be listed.
Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -611,7 +629,7 @@ def list_topics(
sent along with the request as metadata.
Returns:
- ~.pagers.ListTopicsPager:
+ google.cloud.pubsublite_v1.services.admin_service.pagers.ListTopicsPager:
Response for ListTopics.
Iterating over this object will yield
results and resolve additional pages
@@ -676,17 +694,19 @@ def update_topic(
r"""Updates properties of the specified topic.
Args:
- request (:class:`~.admin.UpdateTopicRequest`):
+ request (google.cloud.pubsublite_v1.types.UpdateTopicRequest):
The request object. Request for UpdateTopic.
- topic (:class:`~.common.Topic`):
+ topic (google.cloud.pubsublite_v1.types.Topic):
Required. The topic to update. Its ``name`` field must
be populated.
+
This corresponds to the ``topic`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- update_mask (:class:`~.field_mask.FieldMask`):
+ update_mask (google.protobuf.field_mask_pb2.FieldMask):
Required. A mask specifying the topic
fields to change.
+
This corresponds to the ``update_mask`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -698,7 +718,7 @@ def update_topic(
sent along with the request as metadata.
Returns:
- ~.common.Topic:
+ google.cloud.pubsublite_v1.types.Topic:
Metadata about a topic resource.
"""
# Create or coerce a protobuf request object.
@@ -756,11 +776,12 @@ def delete_topic(
r"""Deletes the specified topic.
Args:
- request (:class:`~.admin.DeleteTopicRequest`):
+ request (google.cloud.pubsublite_v1.types.DeleteTopicRequest):
The request object. Request for DeleteTopic.
- name (:class:`str`):
+ name (str):
Required. The name of the topic to
delete.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -822,11 +843,12 @@ def list_topic_subscriptions(
topic.
Args:
- request (:class:`~.admin.ListTopicSubscriptionsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListTopicSubscriptionsRequest):
The request object. Request for ListTopicSubscriptions.
- name (:class:`str`):
+ name (str):
Required. The name of the topic whose
subscriptions to list.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -838,7 +860,7 @@ def list_topic_subscriptions(
sent along with the request as metadata.
Returns:
- ~.pagers.ListTopicSubscriptionsPager:
+ google.cloud.pubsublite_v1.services.admin_service.pagers.ListTopicSubscriptionsPager:
Response for ListTopicSubscriptions.
Iterating over this object will yield
results and resolve additional pages
@@ -904,26 +926,29 @@ def create_subscription(
r"""Creates a new subscription.
Args:
- request (:class:`~.admin.CreateSubscriptionRequest`):
+ request (google.cloud.pubsublite_v1.types.CreateSubscriptionRequest):
The request object. Request for CreateSubscription.
- parent (:class:`str`):
+ parent (str):
Required. The parent location in which to create the
subscription. Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- subscription (:class:`~.common.Subscription`):
+ subscription (google.cloud.pubsublite_v1.types.Subscription):
Required. Configuration of the subscription to create.
Its ``name`` field is ignored.
+
This corresponds to the ``subscription`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- subscription_id (:class:`str`):
+ subscription_id (str):
Required. The ID to use for the subscription, which will
become the final component of the subscription's name.
This value is structured like: ``my-sub-name``.
+
This corresponds to the ``subscription_id`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -935,7 +960,7 @@ def create_subscription(
sent along with the request as metadata.
Returns:
- ~.common.Subscription:
+ google.cloud.pubsublite_v1.types.Subscription:
Metadata about a subscription
resource.
@@ -995,12 +1020,13 @@ def get_subscription(
r"""Returns the subscription configuration.
Args:
- request (:class:`~.admin.GetSubscriptionRequest`):
+ request (google.cloud.pubsublite_v1.types.GetSubscriptionRequest):
The request object. Request for GetSubscription.
- name (:class:`str`):
+ name (str):
Required. The name of the
subscription whose configuration to
return.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -1012,7 +1038,7 @@ def get_subscription(
sent along with the request as metadata.
Returns:
- ~.common.Subscription:
+ google.cloud.pubsublite_v1.types.Subscription:
Metadata about a subscription
resource.
@@ -1069,12 +1095,13 @@ def list_subscriptions(
project.
Args:
- request (:class:`~.admin.ListSubscriptionsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListSubscriptionsRequest):
The request object. Request for ListSubscriptions.
- parent (:class:`str`):
+ parent (str):
Required. The parent whose subscriptions are to be
listed. Structured like
``projects/{project_number}/locations/{location}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -1086,7 +1113,7 @@ def list_subscriptions(
sent along with the request as metadata.
Returns:
- ~.pagers.ListSubscriptionsPager:
+ google.cloud.pubsublite_v1.services.admin_service.pagers.ListSubscriptionsPager:
Response for ListSubscriptions.
Iterating over this object will yield
results and resolve additional pages
@@ -1151,17 +1178,19 @@ def update_subscription(
r"""Updates properties of the specified subscription.
Args:
- request (:class:`~.admin.UpdateSubscriptionRequest`):
+ request (google.cloud.pubsublite_v1.types.UpdateSubscriptionRequest):
The request object. Request for UpdateSubscription.
- subscription (:class:`~.common.Subscription`):
+ subscription (google.cloud.pubsublite_v1.types.Subscription):
Required. The subscription to update. Its ``name`` field
must be populated. Topic field must not be populated.
+
This corresponds to the ``subscription`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- update_mask (:class:`~.field_mask.FieldMask`):
+ update_mask (google.protobuf.field_mask_pb2.FieldMask):
Required. A mask specifying the
subscription fields to change.
+
This corresponds to the ``update_mask`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -1173,7 +1202,7 @@ def update_subscription(
sent along with the request as metadata.
Returns:
- ~.common.Subscription:
+ google.cloud.pubsublite_v1.types.Subscription:
Metadata about a subscription
resource.
@@ -1233,11 +1262,12 @@ def delete_subscription(
r"""Deletes the specified subscription.
Args:
- request (:class:`~.admin.DeleteSubscriptionRequest`):
+ request (google.cloud.pubsublite_v1.types.DeleteSubscriptionRequest):
The request object. Request for DeleteSubscription.
- name (:class:`str`):
+ name (str):
Required. The name of the
subscription to delete.
+
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
diff --git a/google/cloud/pubsublite_v1/services/admin_service/pagers.py b/google/cloud/pubsublite_v1/services/admin_service/pagers.py
index 8b37d64e..a0fe4d65 100644
--- a/google/cloud/pubsublite_v1/services/admin_service/pagers.py
+++ b/google/cloud/pubsublite_v1/services/admin_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.cloud.pubsublite_v1.types import admin
from google.cloud.pubsublite_v1.types import common
@@ -25,7 +34,7 @@ class ListTopicsPager:
"""A pager for iterating through ``list_topics`` requests.
This class thinly wraps an initial
- :class:`~.admin.ListTopicsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListTopicsResponse` object, and
provides an ``__iter__`` method to iterate through its
``topics`` field.
@@ -34,7 +43,7 @@ class ListTopicsPager:
through the ``topics`` field on the
corresponding responses.
- All the usual :class:`~.admin.ListTopicsResponse`
+ All the usual :class:`google.cloud.pubsublite_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.
"""
@@ -52,9 +61,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.admin.ListTopicsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListTopicsRequest):
The initial request object.
- response (:class:`~.admin.ListTopicsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListTopicsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -87,7 +96,7 @@ class ListTopicsAsyncPager:
"""A pager for iterating through ``list_topics`` requests.
This class thinly wraps an initial
- :class:`~.admin.ListTopicsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListTopicsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``topics`` field.
@@ -96,7 +105,7 @@ class ListTopicsAsyncPager:
through the ``topics`` field on the
corresponding responses.
- All the usual :class:`~.admin.ListTopicsResponse`
+ All the usual :class:`google.cloud.pubsublite_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.
"""
@@ -114,9 +123,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.admin.ListTopicsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListTopicsRequest):
The initial request object.
- response (:class:`~.admin.ListTopicsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListTopicsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -153,7 +162,7 @@ class ListTopicSubscriptionsPager:
"""A pager for iterating through ``list_topic_subscriptions`` requests.
This class thinly wraps an initial
- :class:`~.admin.ListTopicSubscriptionsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListTopicSubscriptionsResponse` object, and
provides an ``__iter__`` method to iterate through its
``subscriptions`` field.
@@ -162,7 +171,7 @@ class ListTopicSubscriptionsPager:
through the ``subscriptions`` field on the
corresponding responses.
- All the usual :class:`~.admin.ListTopicSubscriptionsResponse`
+ All the usual :class:`google.cloud.pubsublite_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.
"""
@@ -180,9 +189,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.admin.ListTopicSubscriptionsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListTopicSubscriptionsRequest):
The initial request object.
- response (:class:`~.admin.ListTopicSubscriptionsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListTopicSubscriptionsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -215,7 +224,7 @@ class ListTopicSubscriptionsAsyncPager:
"""A pager for iterating through ``list_topic_subscriptions`` requests.
This class thinly wraps an initial
- :class:`~.admin.ListTopicSubscriptionsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListTopicSubscriptionsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``subscriptions`` field.
@@ -224,7 +233,7 @@ class ListTopicSubscriptionsAsyncPager:
through the ``subscriptions`` field on the
corresponding responses.
- All the usual :class:`~.admin.ListTopicSubscriptionsResponse`
+ All the usual :class:`google.cloud.pubsublite_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.
"""
@@ -242,9 +251,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.admin.ListTopicSubscriptionsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListTopicSubscriptionsRequest):
The initial request object.
- response (:class:`~.admin.ListTopicSubscriptionsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListTopicSubscriptionsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -281,7 +290,7 @@ class ListSubscriptionsPager:
"""A pager for iterating through ``list_subscriptions`` requests.
This class thinly wraps an initial
- :class:`~.admin.ListSubscriptionsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListSubscriptionsResponse` object, and
provides an ``__iter__`` method to iterate through its
``subscriptions`` field.
@@ -290,7 +299,7 @@ class ListSubscriptionsPager:
through the ``subscriptions`` field on the
corresponding responses.
- All the usual :class:`~.admin.ListSubscriptionsResponse`
+ All the usual :class:`google.cloud.pubsublite_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.
"""
@@ -308,9 +317,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.admin.ListSubscriptionsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListSubscriptionsRequest):
The initial request object.
- response (:class:`~.admin.ListSubscriptionsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListSubscriptionsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -343,7 +352,7 @@ class ListSubscriptionsAsyncPager:
"""A pager for iterating through ``list_subscriptions`` requests.
This class thinly wraps an initial
- :class:`~.admin.ListSubscriptionsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListSubscriptionsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``subscriptions`` field.
@@ -352,7 +361,7 @@ class ListSubscriptionsAsyncPager:
through the ``subscriptions`` field on the
corresponding responses.
- All the usual :class:`~.admin.ListSubscriptionsResponse`
+ All the usual :class:`google.cloud.pubsublite_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.
"""
@@ -370,9 +379,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.admin.ListSubscriptionsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListSubscriptionsRequest):
The initial request object.
- response (:class:`~.admin.ListSubscriptionsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListSubscriptionsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
diff --git a/google/cloud/pubsublite_v1/services/admin_service/transports/__init__.py b/google/cloud/pubsublite_v1/services/admin_service/transports/__init__.py
index 4debddc7..aee0db06 100644
--- a/google/cloud/pubsublite_v1/services/admin_service/transports/__init__.py
+++ b/google/cloud/pubsublite_v1/services/admin_service/transports/__init__.py
@@ -28,7 +28,6 @@
_transport_registry["grpc"] = AdminServiceGrpcTransport
_transport_registry["grpc_asyncio"] = AdminServiceGrpcAsyncIOTransport
-
__all__ = (
"AdminServiceTransport",
"AdminServiceGrpcTransport",
diff --git a/google/cloud/pubsublite_v1/services/admin_service/transports/grpc.py b/google/cloud/pubsublite_v1/services/admin_service/transports/grpc.py
index d1778c5f..51f97260 100644
--- a/google/cloud/pubsublite_v1/services/admin_service/transports/grpc.py
+++ b/google/cloud/pubsublite_v1/services/admin_service/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
@@ -150,6 +155,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -160,14 +169,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._stubs = {} # type: Dict[str, Callable]
@@ -194,7 +213,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optionsl[str]): The host for the channel to use.
+ address (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
diff --git a/google/cloud/pubsublite_v1/services/admin_service/transports/grpc_asyncio.py b/google/cloud/pubsublite_v1/services/admin_service/transports/grpc_asyncio.py
index 03ffcea5..f35deee6 100644
--- a/google/cloud/pubsublite_v1/services/admin_service/transports/grpc_asyncio.py
+++ b/google/cloud/pubsublite_v1/services/admin_service/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
@@ -195,6 +200,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -205,14 +214,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
# Run the base constructor.
diff --git a/google/cloud/pubsublite_v1/services/cursor_service/async_client.py b/google/cloud/pubsublite_v1/services/cursor_service/async_client.py
index 8ad4b2ec..00077822 100644
--- a/google/cloud/pubsublite_v1/services/cursor_service/async_client.py
+++ b/google/cloud/pubsublite_v1/services/cursor_service/async_client.py
@@ -89,7 +89,36 @@ class CursorServiceAsyncClient:
CursorServiceClient.parse_common_location_path
)
- from_service_account_file = CursorServiceClient.from_service_account_file
+ @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:
+ CursorServiceAsyncClient: The constructed client.
+ """
+ return CursorServiceClient.from_service_account_info.__func__(CursorServiceAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ CursorServiceAsyncClient: The constructed client.
+ """
+ return CursorServiceClient.from_service_account_file.__func__(CursorServiceAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -165,7 +194,7 @@ def streaming_commit_cursor(
committed cursors.
Args:
- requests (AsyncIterator[`~.cursor.StreamingCommitCursorRequest`]):
+ requests (AsyncIterator[`google.cloud.pubsublite_v1.types.StreamingCommitCursorRequest`]):
The request object AsyncIterator. A request sent from the client to
the server on a stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -175,7 +204,7 @@ def streaming_commit_cursor(
sent along with the request as metadata.
Returns:
- AsyncIterable[~.cursor.StreamingCommitCursorResponse]:
+ AsyncIterable[google.cloud.pubsublite_v1.types.StreamingCommitCursorResponse]:
Response to a
StreamingCommitCursorRequest.
@@ -206,7 +235,7 @@ async def commit_cursor(
r"""Updates the committed cursor.
Args:
- request (:class:`~.cursor.CommitCursorRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.CommitCursorRequest`):
The request object. Request for CommitCursor.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -216,7 +245,7 @@ async def commit_cursor(
sent along with the request as metadata.
Returns:
- ~.cursor.CommitCursorResponse:
+ google.cloud.pubsublite_v1.types.CommitCursorResponse:
Response for CommitCursor.
"""
# Create or coerce a protobuf request object.
@@ -262,12 +291,13 @@ async def list_partition_cursors(
subscription.
Args:
- request (:class:`~.cursor.ListPartitionCursorsRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.ListPartitionCursorsRequest`):
The request object. Request for ListPartitionCursors.
parent (:class:`str`):
Required. The subscription for which to retrieve
cursors. Structured like
``projects/{project_number}/locations/{location}/subscriptions/{subscription_id}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -279,7 +309,7 @@ async def list_partition_cursors(
sent along with the request as metadata.
Returns:
- ~.pagers.ListPartitionCursorsAsyncPager:
+ google.cloud.pubsublite_v1.services.cursor_service.pagers.ListPartitionCursorsAsyncPager:
Response for ListPartitionCursors
Iterating over this object will yield
results and resolve additional pages
diff --git a/google/cloud/pubsublite_v1/services/cursor_service/client.py b/google/cloud/pubsublite_v1/services/cursor_service/client.py
index e0f89a2a..0f107803 100644
--- a/google/cloud/pubsublite_v1/services/cursor_service/client.py
+++ b/google/cloud/pubsublite_v1/services/cursor_service/client.py
@@ -122,6 +122,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:
+ CursorServiceClient: 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
@@ -134,7 +150,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.
+ CursorServiceClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -242,10 +258,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, ~.CursorServiceTransport]): The
+ transport (Union[str, CursorServiceTransport]): 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
@@ -281,21 +297,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:
@@ -338,7 +350,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,
)
@@ -355,7 +367,7 @@ def streaming_commit_cursor(
committed cursors.
Args:
- requests (Iterator[`~.cursor.StreamingCommitCursorRequest`]):
+ requests (Iterator[google.cloud.pubsublite_v1.types.StreamingCommitCursorRequest]):
The request object iterator. A request sent from the client to
the server on a stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -365,7 +377,7 @@ def streaming_commit_cursor(
sent along with the request as metadata.
Returns:
- Iterable[~.cursor.StreamingCommitCursorResponse]:
+ Iterable[google.cloud.pubsublite_v1.types.StreamingCommitCursorResponse]:
Response to a
StreamingCommitCursorRequest.
@@ -392,7 +404,7 @@ def commit_cursor(
r"""Updates the committed cursor.
Args:
- request (:class:`~.cursor.CommitCursorRequest`):
+ request (google.cloud.pubsublite_v1.types.CommitCursorRequest):
The request object. Request for CommitCursor.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -402,7 +414,7 @@ def commit_cursor(
sent along with the request as metadata.
Returns:
- ~.cursor.CommitCursorResponse:
+ google.cloud.pubsublite_v1.types.CommitCursorResponse:
Response for CommitCursor.
"""
# Create or coerce a protobuf request object.
@@ -437,12 +449,13 @@ def list_partition_cursors(
subscription.
Args:
- request (:class:`~.cursor.ListPartitionCursorsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListPartitionCursorsRequest):
The request object. Request for ListPartitionCursors.
- parent (:class:`str`):
+ parent (str):
Required. The subscription for which to retrieve
cursors. Structured like
``projects/{project_number}/locations/{location}/subscriptions/{subscription_id}``.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -454,7 +467,7 @@ def list_partition_cursors(
sent along with the request as metadata.
Returns:
- ~.pagers.ListPartitionCursorsPager:
+ google.cloud.pubsublite_v1.services.cursor_service.pagers.ListPartitionCursorsPager:
Response for ListPartitionCursors
Iterating over this object will yield
results and resolve additional pages
diff --git a/google/cloud/pubsublite_v1/services/cursor_service/pagers.py b/google/cloud/pubsublite_v1/services/cursor_service/pagers.py
index be5b3744..87d02908 100644
--- a/google/cloud/pubsublite_v1/services/cursor_service/pagers.py
+++ b/google/cloud/pubsublite_v1/services/cursor_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.cloud.pubsublite_v1.types import cursor
@@ -24,7 +33,7 @@ class ListPartitionCursorsPager:
"""A pager for iterating through ``list_partition_cursors`` requests.
This class thinly wraps an initial
- :class:`~.cursor.ListPartitionCursorsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListPartitionCursorsResponse` object, and
provides an ``__iter__`` method to iterate through its
``partition_cursors`` field.
@@ -33,7 +42,7 @@ class ListPartitionCursorsPager:
through the ``partition_cursors`` field on the
corresponding responses.
- All the usual :class:`~.cursor.ListPartitionCursorsResponse`
+ All the usual :class:`google.cloud.pubsublite_v1.types.ListPartitionCursorsResponse`
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:`~.cursor.ListPartitionCursorsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListPartitionCursorsRequest):
The initial request object.
- response (:class:`~.cursor.ListPartitionCursorsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListPartitionCursorsResponse):
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 ListPartitionCursorsAsyncPager:
"""A pager for iterating through ``list_partition_cursors`` requests.
This class thinly wraps an initial
- :class:`~.cursor.ListPartitionCursorsResponse` object, and
+ :class:`google.cloud.pubsublite_v1.types.ListPartitionCursorsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``partition_cursors`` field.
@@ -95,7 +104,7 @@ class ListPartitionCursorsAsyncPager:
through the ``partition_cursors`` field on the
corresponding responses.
- All the usual :class:`~.cursor.ListPartitionCursorsResponse`
+ All the usual :class:`google.cloud.pubsublite_v1.types.ListPartitionCursorsResponse`
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:`~.cursor.ListPartitionCursorsRequest`):
+ request (google.cloud.pubsublite_v1.types.ListPartitionCursorsRequest):
The initial request object.
- response (:class:`~.cursor.ListPartitionCursorsResponse`):
+ response (google.cloud.pubsublite_v1.types.ListPartitionCursorsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
diff --git a/google/cloud/pubsublite_v1/services/cursor_service/transports/__init__.py b/google/cloud/pubsublite_v1/services/cursor_service/transports/__init__.py
index cee721b1..16fd507b 100644
--- a/google/cloud/pubsublite_v1/services/cursor_service/transports/__init__.py
+++ b/google/cloud/pubsublite_v1/services/cursor_service/transports/__init__.py
@@ -28,7 +28,6 @@
_transport_registry["grpc"] = CursorServiceGrpcTransport
_transport_registry["grpc_asyncio"] = CursorServiceGrpcAsyncIOTransport
-
__all__ = (
"CursorServiceTransport",
"CursorServiceGrpcTransport",
diff --git a/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc.py b/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc.py
index cd45da36..c2a7c3ee 100644
--- a/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc.py
+++ b/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc.py
@@ -60,6 +60,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:
@@ -90,6 +91,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):
@@ -106,6 +111,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.
@@ -115,11 +125,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
@@ -149,6 +154,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -159,14 +168,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._stubs = {} # type: Dict[str, Callable]
@@ -193,7 +212,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optionsl[str]): The host for the channel to use.
+ address (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
diff --git a/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc_asyncio.py b/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc_asyncio.py
index 58b5d08b..9a4902f1 100644
--- a/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc_asyncio.py
+++ b/google/cloud/pubsublite_v1/services/cursor_service/transports/grpc_asyncio.py
@@ -104,6 +104,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:
@@ -135,6 +136,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):
@@ -151,6 +156,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.
@@ -160,11 +170,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
@@ -194,6 +199,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -204,14 +213,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
# Run the base constructor.
diff --git a/google/cloud/pubsublite_v1/services/partition_assignment_service/async_client.py b/google/cloud/pubsublite_v1/services/partition_assignment_service/async_client.py
index 37084819..1f014b21 100644
--- a/google/cloud/pubsublite_v1/services/partition_assignment_service/async_client.py
+++ b/google/cloud/pubsublite_v1/services/partition_assignment_service/async_client.py
@@ -89,9 +89,36 @@ class PartitionAssignmentServiceAsyncClient:
PartitionAssignmentServiceClient.parse_common_location_path
)
- from_service_account_file = (
- PartitionAssignmentServiceClient.from_service_account_file
- )
+ @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:
+ PartitionAssignmentServiceAsyncClient: The constructed client.
+ """
+ return PartitionAssignmentServiceClient.from_service_account_info.__func__(PartitionAssignmentServiceAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ PartitionAssignmentServiceAsyncClient: The constructed client.
+ """
+ return PartitionAssignmentServiceClient.from_service_account_file.__func__(PartitionAssignmentServiceAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -175,7 +202,7 @@ def assign_partitions(
the new assignment.
Args:
- requests (AsyncIterator[`~.subscriber.PartitionAssignmentRequest`]):
+ requests (AsyncIterator[`google.cloud.pubsublite_v1.types.PartitionAssignmentRequest`]):
The request object AsyncIterator. A request on the PartitionAssignment
stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -185,7 +212,7 @@ def assign_partitions(
sent along with the request as metadata.
Returns:
- AsyncIterable[~.subscriber.PartitionAssignment]:
+ AsyncIterable[google.cloud.pubsublite_v1.types.PartitionAssignment]:
PartitionAssignments should not race
with acknowledgements. There should be
exactly one unacknowledged
diff --git a/google/cloud/pubsublite_v1/services/partition_assignment_service/client.py b/google/cloud/pubsublite_v1/services/partition_assignment_service/client.py
index adf1095f..91aa5dbe 100644
--- a/google/cloud/pubsublite_v1/services/partition_assignment_service/client.py
+++ b/google/cloud/pubsublite_v1/services/partition_assignment_service/client.py
@@ -123,6 +123,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:
+ PartitionAssignmentServiceClient: 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
@@ -135,7 +151,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.
+ PartitionAssignmentServiceClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -227,10 +243,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, ~.PartitionAssignmentServiceTransport]): The
+ transport (Union[str, PartitionAssignmentServiceTransport]): 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
@@ -266,21 +282,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:
@@ -323,7 +335,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,
)
@@ -347,7 +359,7 @@ def assign_partitions(
the new assignment.
Args:
- requests (Iterator[`~.subscriber.PartitionAssignmentRequest`]):
+ requests (Iterator[google.cloud.pubsublite_v1.types.PartitionAssignmentRequest]):
The request object iterator. A request on the PartitionAssignment
stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -357,7 +369,7 @@ def assign_partitions(
sent along with the request as metadata.
Returns:
- Iterable[~.subscriber.PartitionAssignment]:
+ Iterable[google.cloud.pubsublite_v1.types.PartitionAssignment]:
PartitionAssignments should not race
with acknowledgements. There should be
exactly one unacknowledged
diff --git a/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/__init__.py b/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/__init__.py
index 29c8d8be..10ec3e4b 100644
--- a/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/__init__.py
+++ b/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/__init__.py
@@ -30,7 +30,6 @@
_transport_registry["grpc"] = PartitionAssignmentServiceGrpcTransport
_transport_registry["grpc_asyncio"] = PartitionAssignmentServiceGrpcAsyncIOTransport
-
__all__ = (
"PartitionAssignmentServiceTransport",
"PartitionAssignmentServiceGrpcTransport",
diff --git a/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc.py b/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc.py
index 4fb66ce3..5b9cbad7 100644
--- a/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc.py
+++ b/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc.py
@@ -58,6 +58,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:
@@ -88,6 +89,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):
@@ -104,6 +109,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.
@@ -113,11 +123,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
@@ -147,6 +152,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -157,14 +166,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._stubs = {} # type: Dict[str, Callable]
@@ -191,7 +210,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optionsl[str]): The host for the channel to use.
+ address (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
diff --git a/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc_asyncio.py b/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc_asyncio.py
index 7a6b1cf0..5b9674ea 100644
--- a/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc_asyncio.py
+++ b/google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc_asyncio.py
@@ -104,6 +104,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:
@@ -135,6 +136,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):
@@ -151,6 +156,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.
@@ -160,11 +170,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
@@ -194,6 +199,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -204,14 +213,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
# Run the base constructor.
diff --git a/google/cloud/pubsublite_v1/services/publisher_service/async_client.py b/google/cloud/pubsublite_v1/services/publisher_service/async_client.py
index 57c02aa3..901b2fdb 100644
--- a/google/cloud/pubsublite_v1/services/publisher_service/async_client.py
+++ b/google/cloud/pubsublite_v1/services/publisher_service/async_client.py
@@ -86,7 +86,36 @@ class PublisherServiceAsyncClient:
PublisherServiceClient.parse_common_location_path
)
- from_service_account_file = PublisherServiceClient.from_service_account_file
+ @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:
+ PublisherServiceAsyncClient: The constructed client.
+ """
+ return PublisherServiceClient.from_service_account_info.__func__(PublisherServiceAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ PublisherServiceAsyncClient: The constructed client.
+ """
+ return PublisherServiceClient.from_service_account_file.__func__(PublisherServiceAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -169,7 +198,7 @@ def publish(
they are sent by the client on a given stream.
Args:
- requests (AsyncIterator[`~.publisher.PublishRequest`]):
+ requests (AsyncIterator[`google.cloud.pubsublite_v1.types.PublishRequest`]):
The request object AsyncIterator. Request sent from the client to the
server on a stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -179,7 +208,7 @@ def publish(
sent along with the request as metadata.
Returns:
- AsyncIterable[~.publisher.PublishResponse]:
+ AsyncIterable[google.cloud.pubsublite_v1.types.PublishResponse]:
Response to a PublishRequest.
"""
diff --git a/google/cloud/pubsublite_v1/services/publisher_service/client.py b/google/cloud/pubsublite_v1/services/publisher_service/client.py
index 1d55b422..202c0782 100644
--- a/google/cloud/pubsublite_v1/services/publisher_service/client.py
+++ b/google/cloud/pubsublite_v1/services/publisher_service/client.py
@@ -124,6 +124,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:
+ PublisherServiceClient: 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
@@ -136,7 +152,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.
+ PublisherServiceClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -228,10 +244,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, ~.PublisherServiceTransport]): The
+ transport (Union[str, PublisherServiceTransport]): 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
@@ -267,21 +283,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:
@@ -324,7 +336,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,
)
@@ -348,7 +360,7 @@ def publish(
they are sent by the client on a given stream.
Args:
- requests (Iterator[`~.publisher.PublishRequest`]):
+ requests (Iterator[google.cloud.pubsublite_v1.types.PublishRequest]):
The request object iterator. Request sent from the client to the
server on a stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -358,7 +370,7 @@ def publish(
sent along with the request as metadata.
Returns:
- Iterable[~.publisher.PublishResponse]:
+ Iterable[google.cloud.pubsublite_v1.types.PublishResponse]:
Response to a PublishRequest.
"""
diff --git a/google/cloud/pubsublite_v1/services/publisher_service/transports/__init__.py b/google/cloud/pubsublite_v1/services/publisher_service/transports/__init__.py
index f46d8c16..485b0b7c 100644
--- a/google/cloud/pubsublite_v1/services/publisher_service/transports/__init__.py
+++ b/google/cloud/pubsublite_v1/services/publisher_service/transports/__init__.py
@@ -28,7 +28,6 @@
_transport_registry["grpc"] = PublisherServiceGrpcTransport
_transport_registry["grpc_asyncio"] = PublisherServiceGrpcAsyncIOTransport
-
__all__ = (
"PublisherServiceTransport",
"PublisherServiceGrpcTransport",
diff --git a/google/cloud/pubsublite_v1/services/publisher_service/transports/grpc.py b/google/cloud/pubsublite_v1/services/publisher_service/transports/grpc.py
index 553e89cc..bd6b44ac 100644
--- a/google/cloud/pubsublite_v1/services/publisher_service/transports/grpc.py
+++ b/google/cloud/pubsublite_v1/services/publisher_service/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
@@ -150,6 +155,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -160,14 +169,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._stubs = {} # type: Dict[str, Callable]
@@ -194,7 +213,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optionsl[str]): The host for the channel to use.
+ address (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
diff --git a/google/cloud/pubsublite_v1/services/publisher_service/transports/grpc_asyncio.py b/google/cloud/pubsublite_v1/services/publisher_service/transports/grpc_asyncio.py
index 783353c6..a0fa51b7 100644
--- a/google/cloud/pubsublite_v1/services/publisher_service/transports/grpc_asyncio.py
+++ b/google/cloud/pubsublite_v1/services/publisher_service/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
@@ -195,6 +200,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -205,14 +214,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
# Run the base constructor.
diff --git a/google/cloud/pubsublite_v1/services/subscriber_service/async_client.py b/google/cloud/pubsublite_v1/services/subscriber_service/async_client.py
index 64cf2318..fa2c66b5 100644
--- a/google/cloud/pubsublite_v1/services/subscriber_service/async_client.py
+++ b/google/cloud/pubsublite_v1/services/subscriber_service/async_client.py
@@ -83,7 +83,36 @@ class SubscriberServiceAsyncClient:
SubscriberServiceClient.parse_common_location_path
)
- from_service_account_file = SubscriberServiceClient.from_service_account_file
+ @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:
+ SubscriberServiceAsyncClient: The constructed client.
+ """
+ return SubscriberServiceClient.from_service_account_info.__func__(SubscriberServiceAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ SubscriberServiceAsyncClient: The constructed client.
+ """
+ return SubscriberServiceClient.from_service_account_file.__func__(SubscriberServiceAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -159,7 +188,7 @@ def subscribe(
messages.
Args:
- requests (AsyncIterator[`~.subscriber.SubscribeRequest`]):
+ requests (AsyncIterator[`google.cloud.pubsublite_v1.types.SubscribeRequest`]):
The request object AsyncIterator. A request sent from the client to
the server on a stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -169,7 +198,7 @@ def subscribe(
sent along with the request as metadata.
Returns:
- AsyncIterable[~.subscriber.SubscribeResponse]:
+ AsyncIterable[google.cloud.pubsublite_v1.types.SubscribeResponse]:
Response to SubscribeRequest.
"""
diff --git a/google/cloud/pubsublite_v1/services/subscriber_service/client.py b/google/cloud/pubsublite_v1/services/subscriber_service/client.py
index 8f1b5a3c..6fc767ad 100644
--- a/google/cloud/pubsublite_v1/services/subscriber_service/client.py
+++ b/google/cloud/pubsublite_v1/services/subscriber_service/client.py
@@ -123,6 +123,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:
+ SubscriberServiceClient: 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
@@ -135,7 +151,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.
+ SubscriberServiceClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -227,10 +243,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, ~.SubscriberServiceTransport]): The
+ transport (Union[str, SubscriberServiceTransport]): 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
@@ -266,21 +282,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:
@@ -323,7 +335,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,
)
@@ -340,7 +352,7 @@ def subscribe(
messages.
Args:
- requests (Iterator[`~.subscriber.SubscribeRequest`]):
+ requests (Iterator[google.cloud.pubsublite_v1.types.SubscribeRequest]):
The request object iterator. A request sent from the client to
the server on a stream.
retry (google.api_core.retry.Retry): Designation of what errors, if any,
@@ -350,7 +362,7 @@ def subscribe(
sent along with the request as metadata.
Returns:
- Iterable[~.subscriber.SubscribeResponse]:
+ Iterable[google.cloud.pubsublite_v1.types.SubscribeResponse]:
Response to SubscribeRequest.
"""
diff --git a/google/cloud/pubsublite_v1/services/subscriber_service/transports/__init__.py b/google/cloud/pubsublite_v1/services/subscriber_service/transports/__init__.py
index cd8b0bd2..a1da8b59 100644
--- a/google/cloud/pubsublite_v1/services/subscriber_service/transports/__init__.py
+++ b/google/cloud/pubsublite_v1/services/subscriber_service/transports/__init__.py
@@ -28,7 +28,6 @@
_transport_registry["grpc"] = SubscriberServiceGrpcTransport
_transport_registry["grpc_asyncio"] = SubscriberServiceGrpcAsyncIOTransport
-
__all__ = (
"SubscriberServiceTransport",
"SubscriberServiceGrpcTransport",
diff --git a/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc.py b/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc.py
index aaaaeb3d..87f56ced 100644
--- a/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc.py
+++ b/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc.py
@@ -58,6 +58,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:
@@ -88,6 +89,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):
@@ -104,6 +109,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.
@@ -113,11 +123,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
@@ -147,6 +152,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -157,14 +166,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._stubs = {} # type: Dict[str, Callable]
@@ -191,7 +210,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optionsl[str]): The host for the channel to use.
+ address (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
diff --git a/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc_asyncio.py b/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc_asyncio.py
index 5f51cdcb..71f6a9a4 100644
--- a/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc_asyncio.py
+++ b/google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc_asyncio.py
@@ -102,6 +102,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:
@@ -133,6 +134,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):
@@ -149,6 +154,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.
@@ -158,11 +168,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
@@ -192,6 +197,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -202,14 +211,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
# Run the base constructor.
diff --git a/google/cloud/pubsublite_v1/services/topic_stats_service/async_client.py b/google/cloud/pubsublite_v1/services/topic_stats_service/async_client.py
index 21ce7f6f..1b8cf299 100644
--- a/google/cloud/pubsublite_v1/services/topic_stats_service/async_client.py
+++ b/google/cloud/pubsublite_v1/services/topic_stats_service/async_client.py
@@ -28,6 +28,7 @@
from google.auth import credentials # type: ignore
from google.oauth2 import service_account # type: ignore
+from google.cloud.pubsublite_v1.types import common
from google.cloud.pubsublite_v1.types import topic_stats
from google.protobuf import timestamp_pb2 as timestamp # type: ignore
@@ -78,7 +79,36 @@ class TopicStatsServiceAsyncClient:
TopicStatsServiceClient.parse_common_location_path
)
- from_service_account_file = TopicStatsServiceClient.from_service_account_file
+ @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:
+ TopicStatsServiceAsyncClient: The constructed client.
+ """
+ return TopicStatsServiceClient.from_service_account_info.__func__(TopicStatsServiceAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ TopicStatsServiceAsyncClient: The constructed client.
+ """
+ return TopicStatsServiceClient.from_service_account_file.__func__(TopicStatsServiceAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -154,7 +184,7 @@ async def compute_message_stats(
given topic and partition.
Args:
- request (:class:`~.topic_stats.ComputeMessageStatsRequest`):
+ request (:class:`google.cloud.pubsublite_v1.types.ComputeMessageStatsRequest`):
The request object. Compute statistics about a range of
messages in a given topic and partition.
@@ -165,7 +195,7 @@ async def compute_message_stats(
sent along with the request as metadata.
Returns:
- ~.topic_stats.ComputeMessageStatsResponse:
+ google.cloud.pubsublite_v1.types.ComputeMessageStatsResponse:
Response containing stats for
messages in the requested topic and
partition.
@@ -207,6 +237,63 @@ async def compute_message_stats(
# Done; return the response.
return response
+ async def compute_head_cursor(
+ self,
+ request: topic_stats.ComputeHeadCursorRequest = None,
+ *,
+ retry: retries.Retry = gapic_v1.method.DEFAULT,
+ timeout: float = None,
+ metadata: Sequence[Tuple[str, str]] = (),
+ ) -> topic_stats.ComputeHeadCursorResponse:
+ r"""Compute the head cursor for the partition.
+ The head cursor's offset is guaranteed to be less than
+ or equal to all messages which have not yet been
+ acknowledged as published, and greater than the offset
+ of any message whose publish has already been
+ acknowledged. It is zero if there have never been
+ messages in the partition.
+
+ Args:
+ request (:class:`google.cloud.pubsublite_v1.types.ComputeHeadCursorRequest`):
+ The request object. Compute the current head cursor for
+ a partition.
+
+ retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ should be retried.
+ timeout (float): The timeout for this request.
+ metadata (Sequence[Tuple[str, str]]): Strings which should be
+ sent along with the request as metadata.
+
+ Returns:
+ google.cloud.pubsublite_v1.types.ComputeHeadCursorResponse:
+ Response containing the head cursor
+ for the requested topic and partition.
+
+ """
+ # Create or coerce a protobuf request object.
+
+ request = topic_stats.ComputeHeadCursorRequest(request)
+
+ # Wrap the RPC method; this adds retry and timeout information,
+ # and friendly error handling.
+ rpc = gapic_v1.method_async.wrap_method(
+ self._client._transport.compute_head_cursor,
+ default_timeout=None,
+ client_info=DEFAULT_CLIENT_INFO,
+ )
+
+ # Certain fields should be provided within the metadata header;
+ # add these here.
+ metadata = tuple(metadata) + (
+ gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)),
+ )
+
+ # Send the request.
+ response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,)
+
+ # Done; return the response.
+ return response
+
try:
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
diff --git a/google/cloud/pubsublite_v1/services/topic_stats_service/client.py b/google/cloud/pubsublite_v1/services/topic_stats_service/client.py
index a66963fe..57018295 100644
--- a/google/cloud/pubsublite_v1/services/topic_stats_service/client.py
+++ b/google/cloud/pubsublite_v1/services/topic_stats_service/client.py
@@ -32,6 +32,7 @@
from google.auth.exceptions import MutualTLSChannelError # type: ignore
from google.oauth2 import service_account # type: ignore
+from google.cloud.pubsublite_v1.types import common
from google.cloud.pubsublite_v1.types import topic_stats
from google.protobuf import timestamp_pb2 as timestamp # type: ignore
@@ -114,6 +115,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:
+ TopicStatsServiceClient: 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 +143,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.
+ TopicStatsServiceClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -234,10 +251,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, ~.TopicStatsServiceTransport]): The
+ transport (Union[str, TopicStatsServiceTransport]): 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
@@ -273,21 +290,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:
@@ -330,7 +343,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,
)
@@ -347,7 +360,7 @@ def compute_message_stats(
given topic and partition.
Args:
- request (:class:`~.topic_stats.ComputeMessageStatsRequest`):
+ request (google.cloud.pubsublite_v1.types.ComputeMessageStatsRequest):
The request object. Compute statistics about a range of
messages in a given topic and partition.
@@ -358,7 +371,7 @@ def compute_message_stats(
sent along with the request as metadata.
Returns:
- ~.topic_stats.ComputeMessageStatsResponse:
+ google.cloud.pubsublite_v1.types.ComputeMessageStatsResponse:
Response containing stats for
messages in the requested topic and
partition.
@@ -389,6 +402,64 @@ def compute_message_stats(
# Done; return the response.
return response
+ def compute_head_cursor(
+ self,
+ request: topic_stats.ComputeHeadCursorRequest = None,
+ *,
+ retry: retries.Retry = gapic_v1.method.DEFAULT,
+ timeout: float = None,
+ metadata: Sequence[Tuple[str, str]] = (),
+ ) -> topic_stats.ComputeHeadCursorResponse:
+ r"""Compute the head cursor for the partition.
+ The head cursor's offset is guaranteed to be less than
+ or equal to all messages which have not yet been
+ acknowledged as published, and greater than the offset
+ of any message whose publish has already been
+ acknowledged. It is zero if there have never been
+ messages in the partition.
+
+ Args:
+ request (google.cloud.pubsublite_v1.types.ComputeHeadCursorRequest):
+ The request object. Compute the current head cursor for
+ a partition.
+
+ retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ should be retried.
+ timeout (float): The timeout for this request.
+ metadata (Sequence[Tuple[str, str]]): Strings which should be
+ sent along with the request as metadata.
+
+ Returns:
+ google.cloud.pubsublite_v1.types.ComputeHeadCursorResponse:
+ Response containing the head cursor
+ for the requested topic and partition.
+
+ """
+ # Create or coerce a protobuf request object.
+
+ # Minor optimization to avoid making a copy if the user passes
+ # in a topic_stats.ComputeHeadCursorRequest.
+ # There's no risk of modifying the input as we've already verified
+ # there are no flattened fields.
+ if not isinstance(request, topic_stats.ComputeHeadCursorRequest):
+ request = topic_stats.ComputeHeadCursorRequest(request)
+
+ # Wrap the RPC method; this adds retry and timeout information,
+ # and friendly error handling.
+ rpc = self._transport._wrapped_methods[self._transport.compute_head_cursor]
+
+ # Certain fields should be provided within the metadata header;
+ # add these here.
+ metadata = tuple(metadata) + (
+ gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)),
+ )
+
+ # Send the request.
+ response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,)
+
+ # Done; return the response.
+ return response
+
try:
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
diff --git a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/__init__.py b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/__init__.py
index 83891172..91ce8fb5 100644
--- a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/__init__.py
+++ b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/__init__.py
@@ -28,7 +28,6 @@
_transport_registry["grpc"] = TopicStatsServiceGrpcTransport
_transport_registry["grpc_asyncio"] = TopicStatsServiceGrpcAsyncIOTransport
-
__all__ = (
"TopicStatsServiceTransport",
"TopicStatsServiceGrpcTransport",
diff --git a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/base.py b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/base.py
index f7c4146f..1eda2715 100644
--- a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/base.py
+++ b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/base.py
@@ -123,6 +123,9 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=600.0,
client_info=client_info,
),
+ self.compute_head_cursor: gapic_v1.method.wrap_method(
+ self.compute_head_cursor, default_timeout=None, client_info=client_info,
+ ),
}
@property
@@ -137,5 +140,17 @@ def compute_message_stats(
]:
raise NotImplementedError()
+ @property
+ def compute_head_cursor(
+ self,
+ ) -> typing.Callable[
+ [topic_stats.ComputeHeadCursorRequest],
+ typing.Union[
+ topic_stats.ComputeHeadCursorResponse,
+ typing.Awaitable[topic_stats.ComputeHeadCursorResponse],
+ ],
+ ]:
+ raise NotImplementedError()
+
__all__ = ("TopicStatsServiceTransport",)
diff --git a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc.py b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc.py
index 3e25decb..ff57fb65 100644
--- a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc.py
+++ b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc.py
@@ -58,6 +58,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:
@@ -88,6 +89,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):
@@ -104,6 +109,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.
@@ -113,11 +123,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
@@ -147,6 +152,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -157,14 +166,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._stubs = {} # type: Dict[str, Callable]
@@ -191,7 +210,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optionsl[str]): The host for the channel to use.
+ address (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
@@ -260,5 +279,39 @@ def compute_message_stats(
)
return self._stubs["compute_message_stats"]
+ @property
+ def compute_head_cursor(
+ self,
+ ) -> Callable[
+ [topic_stats.ComputeHeadCursorRequest], topic_stats.ComputeHeadCursorResponse
+ ]:
+ r"""Return a callable for the compute head cursor method over gRPC.
+
+ Compute the head cursor for the partition.
+ The head cursor's offset is guaranteed to be less than
+ or equal to all messages which have not yet been
+ acknowledged as published, and greater than the offset
+ of any message whose publish has already been
+ acknowledged. It is zero if there have never been
+ messages in the partition.
+
+ Returns:
+ Callable[[~.ComputeHeadCursorRequest],
+ ~.ComputeHeadCursorResponse]:
+ A function that, when called, will call the underlying RPC
+ on the server.
+ """
+ # Generate a "stub function" on-the-fly which will actually make
+ # the request.
+ # gRPC handles serialization and deserialization, so we just need
+ # to pass in the functions for each.
+ if "compute_head_cursor" not in self._stubs:
+ self._stubs["compute_head_cursor"] = self.grpc_channel.unary_unary(
+ "/google.cloud.pubsublite.v1.TopicStatsService/ComputeHeadCursor",
+ request_serializer=topic_stats.ComputeHeadCursorRequest.serialize,
+ response_deserializer=topic_stats.ComputeHeadCursorResponse.deserialize,
+ )
+ return self._stubs["compute_head_cursor"]
+
__all__ = ("TopicStatsServiceGrpcTransport",)
diff --git a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc_asyncio.py b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc_asyncio.py
index ee787ba5..93d938a3 100644
--- a/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc_asyncio.py
+++ b/google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc_asyncio.py
@@ -102,6 +102,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:
@@ -133,6 +134,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):
@@ -149,6 +154,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.
@@ -158,11 +168,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
@@ -192,6 +197,10 @@ def __init__(
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
quota_project_id=quota_project_id,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
self._ssl_channel_credentials = ssl_credentials
else:
@@ -202,14 +211,24 @@ 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=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
# Run the base constructor.
@@ -264,5 +283,40 @@ def compute_message_stats(
)
return self._stubs["compute_message_stats"]
+ @property
+ def compute_head_cursor(
+ self,
+ ) -> Callable[
+ [topic_stats.ComputeHeadCursorRequest],
+ Awaitable[topic_stats.ComputeHeadCursorResponse],
+ ]:
+ r"""Return a callable for the compute head cursor method over gRPC.
+
+ Compute the head cursor for the partition.
+ The head cursor's offset is guaranteed to be less than
+ or equal to all messages which have not yet been
+ acknowledged as published, and greater than the offset
+ of any message whose publish has already been
+ acknowledged. It is zero if there have never been
+ messages in the partition.
+
+ Returns:
+ Callable[[~.ComputeHeadCursorRequest],
+ Awaitable[~.ComputeHeadCursorResponse]]:
+ A function that, when called, will call the underlying RPC
+ on the server.
+ """
+ # Generate a "stub function" on-the-fly which will actually make
+ # the request.
+ # gRPC handles serialization and deserialization, so we just need
+ # to pass in the functions for each.
+ if "compute_head_cursor" not in self._stubs:
+ self._stubs["compute_head_cursor"] = self.grpc_channel.unary_unary(
+ "/google.cloud.pubsublite.v1.TopicStatsService/ComputeHeadCursor",
+ request_serializer=topic_stats.ComputeHeadCursorRequest.serialize,
+ response_deserializer=topic_stats.ComputeHeadCursorResponse.deserialize,
+ )
+ return self._stubs["compute_head_cursor"]
+
__all__ = ("TopicStatsServiceGrpcAsyncIOTransport",)
diff --git a/google/cloud/pubsublite_v1/types/__init__.py b/google/cloud/pubsublite_v1/types/__init__.py
index 957e9223..15ed9aeb 100644
--- a/google/cloud/pubsublite_v1/types/__init__.py
+++ b/google/cloud/pubsublite_v1/types/__init__.py
@@ -15,44 +15,44 @@
# limitations under the License.
#
-from .common import (
- AttributeValues,
- PubSubMessage,
- Cursor,
- SequencedMessage,
- Topic,
- Subscription,
-)
from .admin import (
+ CreateSubscriptionRequest,
CreateTopicRequest,
- GetTopicRequest,
+ DeleteSubscriptionRequest,
+ DeleteTopicRequest,
+ GetSubscriptionRequest,
GetTopicPartitionsRequest,
- TopicPartitions,
+ GetTopicRequest,
+ ListSubscriptionsRequest,
+ ListSubscriptionsResponse,
ListTopicsRequest,
ListTopicsResponse,
- UpdateTopicRequest,
- DeleteTopicRequest,
ListTopicSubscriptionsRequest,
ListTopicSubscriptionsResponse,
- CreateSubscriptionRequest,
- GetSubscriptionRequest,
- ListSubscriptionsRequest,
- ListSubscriptionsResponse,
+ TopicPartitions,
UpdateSubscriptionRequest,
- DeleteSubscriptionRequest,
+ UpdateTopicRequest,
+)
+from .common import (
+ AttributeValues,
+ Cursor,
+ PubSubMessage,
+ SequencedMessage,
+ Subscription,
+ Topic,
)
from .cursor import (
+ CommitCursorRequest,
+ CommitCursorResponse,
InitialCommitCursorRequest,
InitialCommitCursorResponse,
+ ListPartitionCursorsRequest,
+ ListPartitionCursorsResponse,
+ PartitionCursor,
SequencedCommitCursorRequest,
SequencedCommitCursorResponse,
StreamingCommitCursorRequest,
StreamingCommitCursorResponse,
- CommitCursorRequest,
- CommitCursorResponse,
- ListPartitionCursorsRequest,
- PartitionCursor,
- ListPartitionCursorsResponse,
)
from .publisher import (
InitialPublishRequest,
@@ -63,77 +63,80 @@
PublishResponse,
)
from .subscriber import (
+ FlowControlRequest,
+ InitialPartitionAssignmentRequest,
InitialSubscribeRequest,
InitialSubscribeResponse,
- SeekRequest,
- SeekResponse,
- FlowControlRequest,
- SubscribeRequest,
MessageResponse,
- SubscribeResponse,
- InitialPartitionAssignmentRequest,
PartitionAssignment,
PartitionAssignmentAck,
PartitionAssignmentRequest,
+ SeekRequest,
+ SeekResponse,
+ SubscribeRequest,
+ SubscribeResponse,
)
from .topic_stats import (
+ ComputeHeadCursorRequest,
+ ComputeHeadCursorResponse,
ComputeMessageStatsRequest,
ComputeMessageStatsResponse,
)
-
__all__ = (
- "AttributeValues",
- "PubSubMessage",
- "Cursor",
- "SequencedMessage",
- "Topic",
- "Subscription",
+ "CreateSubscriptionRequest",
"CreateTopicRequest",
- "GetTopicRequest",
+ "DeleteSubscriptionRequest",
+ "DeleteTopicRequest",
+ "GetSubscriptionRequest",
"GetTopicPartitionsRequest",
- "TopicPartitions",
+ "GetTopicRequest",
+ "ListSubscriptionsRequest",
+ "ListSubscriptionsResponse",
"ListTopicsRequest",
"ListTopicsResponse",
- "UpdateTopicRequest",
- "DeleteTopicRequest",
"ListTopicSubscriptionsRequest",
"ListTopicSubscriptionsResponse",
- "CreateSubscriptionRequest",
- "GetSubscriptionRequest",
- "ListSubscriptionsRequest",
- "ListSubscriptionsResponse",
+ "TopicPartitions",
"UpdateSubscriptionRequest",
- "DeleteSubscriptionRequest",
+ "UpdateTopicRequest",
+ "AttributeValues",
+ "Cursor",
+ "PubSubMessage",
+ "SequencedMessage",
+ "Subscription",
+ "Topic",
+ "CommitCursorRequest",
+ "CommitCursorResponse",
"InitialCommitCursorRequest",
"InitialCommitCursorResponse",
+ "ListPartitionCursorsRequest",
+ "ListPartitionCursorsResponse",
+ "PartitionCursor",
"SequencedCommitCursorRequest",
"SequencedCommitCursorResponse",
"StreamingCommitCursorRequest",
"StreamingCommitCursorResponse",
- "CommitCursorRequest",
- "CommitCursorResponse",
- "ListPartitionCursorsRequest",
- "PartitionCursor",
- "ListPartitionCursorsResponse",
"InitialPublishRequest",
"InitialPublishResponse",
"MessagePublishRequest",
"MessagePublishResponse",
"PublishRequest",
"PublishResponse",
+ "FlowControlRequest",
+ "InitialPartitionAssignmentRequest",
"InitialSubscribeRequest",
"InitialSubscribeResponse",
- "SeekRequest",
- "SeekResponse",
- "FlowControlRequest",
- "SubscribeRequest",
"MessageResponse",
- "SubscribeResponse",
- "InitialPartitionAssignmentRequest",
"PartitionAssignment",
"PartitionAssignmentAck",
"PartitionAssignmentRequest",
+ "SeekRequest",
+ "SeekResponse",
+ "SubscribeRequest",
+ "SubscribeResponse",
+ "ComputeHeadCursorRequest",
+ "ComputeHeadCursorResponse",
"ComputeMessageStatsRequest",
"ComputeMessageStatsResponse",
)
diff --git a/google/cloud/pubsublite_v1/types/admin.py b/google/cloud/pubsublite_v1/types/admin.py
index 55b8d626..c8585e92 100644
--- a/google/cloud/pubsublite_v1/types/admin.py
+++ b/google/cloud/pubsublite_v1/types/admin.py
@@ -53,7 +53,7 @@ class CreateTopicRequest(proto.Message):
Required. The parent location in which to create the topic.
Structured like
``projects/{project_number}/locations/{location}``.
- topic (~.common.Topic):
+ topic (google.cloud.pubsublite_v1.types.Topic):
Required. Configuration of the topic to create. Its ``name``
field is ignored.
topic_id (str):
@@ -138,7 +138,7 @@ class ListTopicsResponse(proto.Message):
r"""Response for ListTopics.
Attributes:
- topics (Sequence[~.common.Topic]):
+ topics (Sequence[google.cloud.pubsublite_v1.types.Topic]):
The list of topic in the requested parent.
The order of the topics is unspecified.
next_page_token (str):
@@ -160,10 +160,10 @@ class UpdateTopicRequest(proto.Message):
r"""Request for UpdateTopic.
Attributes:
- topic (~.common.Topic):
+ topic (google.cloud.pubsublite_v1.types.Topic):
Required. The topic to update. Its ``name`` field must be
populated.
- update_mask (~.field_mask.FieldMask):
+ update_mask (google.protobuf.field_mask_pb2.FieldMask):
Required. A mask specifying the topic fields
to change.
"""
@@ -244,7 +244,7 @@ class CreateSubscriptionRequest(proto.Message):
Required. The parent location in which to create the
subscription. Structured like
``projects/{project_number}/locations/{location}``.
- subscription (~.common.Subscription):
+ subscription (google.cloud.pubsublite_v1.types.Subscription):
Required. Configuration of the subscription to create. Its
``name`` field is ignored.
subscription_id (str):
@@ -252,6 +252,12 @@ class CreateSubscriptionRequest(proto.Message):
become the final component of the subscription's name.
This value is structured like: ``my-sub-name``.
+ skip_backlog (bool):
+ If true, the newly created subscription will
+ only receive messages published after the
+ subscription was created. Otherwise, the entire
+ message backlog will be received on the
+ subscription. Defaults to false.
"""
parent = proto.Field(proto.STRING, number=1)
@@ -260,6 +266,8 @@ class CreateSubscriptionRequest(proto.Message):
subscription_id = proto.Field(proto.STRING, number=3)
+ skip_backlog = proto.Field(proto.BOOL, number=4)
+
class GetSubscriptionRequest(proto.Message):
r"""Request for GetSubscription.
@@ -306,7 +314,7 @@ class ListSubscriptionsResponse(proto.Message):
r"""Response for ListSubscriptions.
Attributes:
- subscriptions (Sequence[~.common.Subscription]):
+ subscriptions (Sequence[google.cloud.pubsublite_v1.types.Subscription]):
The list of subscriptions in the requested
parent. The order of the subscriptions is
unspecified.
@@ -331,10 +339,10 @@ class UpdateSubscriptionRequest(proto.Message):
r"""Request for UpdateSubscription.
Attributes:
- subscription (~.common.Subscription):
+ subscription (google.cloud.pubsublite_v1.types.Subscription):
Required. The subscription to update. Its ``name`` field
must be populated. Topic field must not be populated.
- update_mask (~.field_mask.FieldMask):
+ update_mask (google.protobuf.field_mask_pb2.FieldMask):
Required. A mask specifying the subscription
fields to change.
"""
diff --git a/google/cloud/pubsublite_v1/types/common.py b/google/cloud/pubsublite_v1/types/common.py
index 6d1c605e..0b94b4b8 100644
--- a/google/cloud/pubsublite_v1/types/common.py
+++ b/google/cloud/pubsublite_v1/types/common.py
@@ -58,10 +58,10 @@ class PubSubMessage(proto.Message):
the message is routed to an arbitrary partition.
data (bytes):
The payload of the message.
- attributes (Sequence[~.common.PubSubMessage.AttributesEntry]):
+ attributes (Sequence[google.cloud.pubsublite_v1.types.PubSubMessage.AttributesEntry]):
Optional attributes that can be used for
message metadata/headers.
- event_time (~.timestamp.Timestamp):
+ event_time (google.protobuf.timestamp_pb2.Timestamp):
An optional, user-specified event time.
"""
@@ -94,13 +94,13 @@ class SequencedMessage(proto.Message):
Lite system.
Attributes:
- cursor (~.common.Cursor):
+ cursor (google.cloud.pubsublite_v1.types.Cursor):
The position of a message within the
partition where it is stored.
- publish_time (~.timestamp.Timestamp):
+ publish_time (google.protobuf.timestamp_pb2.Timestamp):
The time when the message was received by the
server when it was first published.
- message (~.common.PubSubMessage):
+ message (google.cloud.pubsublite_v1.types.PubSubMessage):
The user message.
size_bytes (int):
The size in bytes of this message for flow
@@ -123,9 +123,9 @@ class Topic(proto.Message):
name (str):
The name of the topic. Structured like:
projects/{project_number}/locations/{location}/topics/{topic_id}
- partition_config (~.common.Topic.PartitionConfig):
+ partition_config (google.cloud.pubsublite_v1.types.Topic.PartitionConfig):
The settings for this topic's partitions.
- retention_config (~.common.Topic.RetentionConfig):
+ retention_config (google.cloud.pubsublite_v1.types.Topic.RetentionConfig):
The settings for this topic's message
retention.
"""
@@ -135,8 +135,12 @@ class PartitionConfig(proto.Message):
Attributes:
count (int):
- The number of partitions in the topic. Must
- be at least 1.
+ The number of partitions in the topic. Must be at least 1.
+
+ Once a topic has been created the number of partitions can
+ be increased but not decreased. Message ordering is not
+ guaranteed across a topic resize. For more information see
+ https://cloud.google.com/pubsub/lite/docs/topics#scaling_capacity
scale (int):
DEPRECATED: Use capacity instead which can express a
superset of configurations.
@@ -147,7 +151,7 @@ class PartitionConfig(proto.Message):
this topic; a topic with ``scale`` of 2 and count of 10 is
charged for 20 partitions. This value must be in the range
[1,4].
- capacity (~.common.Topic.PartitionConfig.Capacity):
+ capacity (google.cloud.pubsublite_v1.types.Topic.PartitionConfig.Capacity):
The capacity configuration.
"""
@@ -188,7 +192,7 @@ class RetentionConfig(proto.Message):
grows beyond this value, older messages will be dropped to
make room for newer ones, regardless of the value of
``period``.
- period (~.duration.Duration):
+ period (google.protobuf.duration_pb2.Duration):
How long a published message is retained. If unset, messages
will be retained as long as the bytes retained for each
partition is below ``per_partition_bytes``.
@@ -216,7 +220,7 @@ class Subscription(proto.Message):
The name of the topic this subscription is attached to.
Structured like:
projects/{project_number}/locations/{location}/topics/{topic_id}
- delivery_config (~.common.Subscription.DeliveryConfig):
+ delivery_config (google.cloud.pubsublite_v1.types.Subscription.DeliveryConfig):
The settings for this subscription's message
delivery.
"""
@@ -225,7 +229,7 @@ class DeliveryConfig(proto.Message):
r"""The settings for a subscription's message delivery.
Attributes:
- delivery_requirement (~.common.Subscription.DeliveryConfig.DeliveryRequirement):
+ delivery_requirement (google.cloud.pubsublite_v1.types.Subscription.DeliveryConfig.DeliveryRequirement):
The DeliveryRequirement for this
subscription.
"""
diff --git a/google/cloud/pubsublite_v1/types/cursor.py b/google/cloud/pubsublite_v1/types/cursor.py
index 1f31c8d9..094f972a 100644
--- a/google/cloud/pubsublite_v1/types/cursor.py
+++ b/google/cloud/pubsublite_v1/types/cursor.py
@@ -68,7 +68,7 @@ class SequencedCommitCursorRequest(proto.Message):
SequencedCommitCursorRequests override outstanding ones.
Attributes:
- cursor (~.common.Cursor):
+ cursor (google.cloud.pubsublite_v1.types.Cursor):
The new value for the committed cursor.
"""
@@ -94,9 +94,9 @@ class StreamingCommitCursorRequest(proto.Message):
r"""A request sent from the client to the server on a stream.
Attributes:
- initial (~.gcp_cursor.InitialCommitCursorRequest):
+ initial (google.cloud.pubsublite_v1.types.InitialCommitCursorRequest):
Initial request on the stream.
- commit (~.gcp_cursor.SequencedCommitCursorRequest):
+ commit (google.cloud.pubsublite_v1.types.SequencedCommitCursorRequest):
Request to commit a new cursor value.
"""
@@ -116,9 +116,9 @@ class StreamingCommitCursorResponse(proto.Message):
r"""Response to a StreamingCommitCursorRequest.
Attributes:
- initial (~.gcp_cursor.InitialCommitCursorResponse):
+ initial (google.cloud.pubsublite_v1.types.InitialCommitCursorResponse):
Initial response on the stream.
- commit (~.gcp_cursor.SequencedCommitCursorResponse):
+ commit (google.cloud.pubsublite_v1.types.SequencedCommitCursorResponse):
Response to committing a new cursor value.
"""
@@ -145,7 +145,7 @@ class CommitCursorRequest(proto.Message):
The partition for which to update the cursor. Partitions are
zero indexed, so ``partition`` must be in the range [0,
topic.num_partitions).
- cursor (~.common.Cursor):
+ cursor (google.cloud.pubsublite_v1.types.Cursor):
The new value for the committed cursor.
"""
@@ -196,7 +196,7 @@ class PartitionCursor(proto.Message):
Attributes:
partition (int):
The partition this is for.
- cursor (~.common.Cursor):
+ cursor (google.cloud.pubsublite_v1.types.Cursor):
The value of the cursor.
"""
@@ -209,7 +209,7 @@ class ListPartitionCursorsResponse(proto.Message):
r"""Response for ListPartitionCursors
Attributes:
- partition_cursors (Sequence[~.gcp_cursor.PartitionCursor]):
+ partition_cursors (Sequence[google.cloud.pubsublite_v1.types.PartitionCursor]):
The partition cursors from this request.
next_page_token (str):
A token, which can be sent as ``page_token`` to retrieve the
diff --git a/google/cloud/pubsublite_v1/types/publisher.py b/google/cloud/pubsublite_v1/types/publisher.py
index d8129236..d8ca68cb 100644
--- a/google/cloud/pubsublite_v1/types/publisher.py
+++ b/google/cloud/pubsublite_v1/types/publisher.py
@@ -59,7 +59,7 @@ class MessagePublishRequest(proto.Message):
r"""Request to publish messages to the topic.
Attributes:
- messages (Sequence[~.common.PubSubMessage]):
+ messages (Sequence[google.cloud.pubsublite_v1.types.PubSubMessage]):
The messages to publish.
"""
@@ -72,7 +72,7 @@ class MessagePublishResponse(proto.Message):
r"""Response to a MessagePublishRequest.
Attributes:
- start_cursor (~.common.Cursor):
+ start_cursor (google.cloud.pubsublite_v1.types.Cursor):
The cursor of the first published message in
the batch. The cursors for any remaining
messages in the batch are guaranteed to be
@@ -86,9 +86,9 @@ class PublishRequest(proto.Message):
r"""Request sent from the client to the server on a stream.
Attributes:
- initial_request (~.publisher.InitialPublishRequest):
+ initial_request (google.cloud.pubsublite_v1.types.InitialPublishRequest):
Initial request on the stream.
- message_publish_request (~.publisher.MessagePublishRequest):
+ message_publish_request (google.cloud.pubsublite_v1.types.MessagePublishRequest):
Request to publish messages.
"""
@@ -105,9 +105,9 @@ class PublishResponse(proto.Message):
r"""Response to a PublishRequest.
Attributes:
- initial_response (~.publisher.InitialPublishResponse):
+ initial_response (google.cloud.pubsublite_v1.types.InitialPublishResponse):
Initial response on the stream.
- message_response (~.publisher.MessagePublishResponse):
+ message_response (google.cloud.pubsublite_v1.types.MessagePublishResponse):
Response to publishing messages.
"""
diff --git a/google/cloud/pubsublite_v1/types/subscriber.py b/google/cloud/pubsublite_v1/types/subscriber.py
index b2e53c71..1f5a7799 100644
--- a/google/cloud/pubsublite_v1/types/subscriber.py
+++ b/google/cloud/pubsublite_v1/types/subscriber.py
@@ -64,7 +64,7 @@ class InitialSubscribeResponse(proto.Message):
r"""Response to an InitialSubscribeRequest.
Attributes:
- cursor (~.common.Cursor):
+ cursor (google.cloud.pubsublite_v1.types.Cursor):
The cursor from which the subscriber will
start receiving messages once flow control
tokens become available.
@@ -81,9 +81,9 @@ class SeekRequest(proto.Message):
stream. SeekRequests past head result in stream breakage.
Attributes:
- named_target (~.subscriber.SeekRequest.NamedTarget):
+ named_target (google.cloud.pubsublite_v1.types.SeekRequest.NamedTarget):
A named target.
- cursor (~.common.Cursor):
+ cursor (google.cloud.pubsublite_v1.types.Cursor):
A target corresponding to the cursor,
pointing to anywhere in the topic partition.
"""
@@ -107,7 +107,7 @@ class SeekResponse(proto.Message):
r"""Response to a SeekRequest.
Attributes:
- cursor (~.common.Cursor):
+ cursor (google.cloud.pubsublite_v1.types.Cursor):
The new delivery cursor for the current
stream.
"""
@@ -137,12 +137,12 @@ class SubscribeRequest(proto.Message):
r"""A request sent from the client to the server on a stream.
Attributes:
- initial (~.subscriber.InitialSubscribeRequest):
+ initial (google.cloud.pubsublite_v1.types.InitialSubscribeRequest):
Initial request on the stream.
- seek (~.subscriber.SeekRequest):
+ seek (google.cloud.pubsublite_v1.types.SeekRequest):
Request to update the stream's delivery
cursor.
- flow_control (~.subscriber.FlowControlRequest):
+ flow_control (google.cloud.pubsublite_v1.types.FlowControlRequest):
Request to grant tokens to the server,
"""
@@ -167,7 +167,7 @@ class MessageResponse(proto.Message):
available to the server.
Attributes:
- messages (Sequence[~.common.SequencedMessage]):
+ messages (Sequence[google.cloud.pubsublite_v1.types.SequencedMessage]):
Messages from the topic partition.
"""
@@ -180,11 +180,11 @@ class SubscribeResponse(proto.Message):
r"""Response to SubscribeRequest.
Attributes:
- initial (~.subscriber.InitialSubscribeResponse):
+ initial (google.cloud.pubsublite_v1.types.InitialSubscribeResponse):
Initial response on the stream.
- seek (~.subscriber.SeekResponse):
+ seek (google.cloud.pubsublite_v1.types.SeekResponse):
Response to a Seek operation.
- messages (~.subscriber.MessageResponse):
+ messages (google.cloud.pubsublite_v1.types.MessageResponse):
Response containing messages from the topic
partition.
"""
@@ -258,9 +258,9 @@ class PartitionAssignmentRequest(proto.Message):
r"""A request on the PartitionAssignment stream.
Attributes:
- initial (~.subscriber.InitialPartitionAssignmentRequest):
+ initial (google.cloud.pubsublite_v1.types.InitialPartitionAssignmentRequest):
Initial request on the stream.
- ack (~.subscriber.PartitionAssignmentAck):
+ ack (google.cloud.pubsublite_v1.types.PartitionAssignmentAck):
Acknowledgement of a partition assignment.
"""
diff --git a/google/cloud/pubsublite_v1/types/topic_stats.py b/google/cloud/pubsublite_v1/types/topic_stats.py
index 54098b9d..003c2f3a 100644
--- a/google/cloud/pubsublite_v1/types/topic_stats.py
+++ b/google/cloud/pubsublite_v1/types/topic_stats.py
@@ -24,7 +24,12 @@
__protobuf__ = proto.module(
package="google.cloud.pubsublite.v1",
- manifest={"ComputeMessageStatsRequest", "ComputeMessageStatsResponse",},
+ manifest={
+ "ComputeMessageStatsRequest",
+ "ComputeMessageStatsResponse",
+ "ComputeHeadCursorRequest",
+ "ComputeHeadCursorResponse",
+ },
)
@@ -39,9 +44,9 @@ class ComputeMessageStatsRequest(proto.Message):
partition (int):
Required. The partition for which we should
compute message stats.
- start_cursor (~.common.Cursor):
+ start_cursor (google.cloud.pubsublite_v1.types.Cursor):
The inclusive start of the range.
- end_cursor (~.common.Cursor):
+ end_cursor (google.cloud.pubsublite_v1.types.Cursor):
The exclusive end of the range. The range is empty if
end_cursor <= start_cursor. Specifying a start_cursor before
the first message and an end_cursor after the last message
@@ -67,12 +72,13 @@ class ComputeMessageStatsResponse(proto.Message):
message_bytes (int):
The number of quota bytes accounted to these
messages.
- minimum_publish_time (~.timestamp.Timestamp):
+ minimum_publish_time (google.protobuf.timestamp_pb2.Timestamp):
The minimum publish timestamp across these
messages. Note that publish timestamps within a
- partition are non-decreasing. The timestamp will
- be unset if there are no messages.
- minimum_event_time (~.timestamp.Timestamp):
+ partition are not guaranteed to be non-
+ decreasing. The timestamp will be unset if there
+ are no messages.
+ minimum_event_time (google.protobuf.timestamp_pb2.Timestamp):
The minimum event timestamp across these
messages. For the purposes of this computation,
if a message does not have an event time, we use
@@ -93,4 +99,33 @@ class ComputeMessageStatsResponse(proto.Message):
)
+class ComputeHeadCursorRequest(proto.Message):
+ r"""Compute the current head cursor for a partition.
+
+ Attributes:
+ topic (str):
+ Required. The topic for which we should
+ compute the head cursor.
+ partition (int):
+ Required. The partition for which we should
+ compute the head cursor.
+ """
+
+ topic = proto.Field(proto.STRING, number=1)
+
+ partition = proto.Field(proto.INT64, number=2)
+
+
+class ComputeHeadCursorResponse(proto.Message):
+ r"""Response containing the head cursor for the requested topic
+ and partition.
+
+ Attributes:
+ head_cursor (google.cloud.pubsublite_v1.types.Cursor):
+ The head cursor.
+ """
+
+ head_cursor = proto.Field(proto.MESSAGE, number=1, message=common.Cursor,)
+
+
__all__ = tuple(sorted(__protobuf__.manifest))
diff --git a/noxfile.py b/noxfile.py
index 776269dd..afdeb126 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -26,10 +26,21 @@
BLACK_VERSION = "black==19.10b0"
BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"]
-DEFAULT_PYTHON_VERSION = "3.6"
+DEFAULT_PYTHON_VERSION = "3.8"
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):
@@ -45,7 +56,7 @@ def lint(session):
session.run("flake8", "google", "tests")
-@nox.session(python=DEFAULT_PYTHON_VERSION)
+@nox.session(python="3.6")
def blacken(session):
"""Run black.
@@ -73,15 +84,16 @@ def default(session):
session.install("asyncmock", "pytest-asyncio")
session.install("mock", "pytest", "pytest-cov", "asynctest")
+
session.install("-e", ".")
# Run py.test against the unit tests.
session.run(
"py.test",
"--quiet",
- "--cov=google.cloud.pubsublite",
- "--cov=google.cloud",
- "--cov=tests.unit",
+ f"--junitxml=unit_{session.python}_sponge_log.xml",
+ "--cov=google/cloud",
+ "--cov=tests/unit",
"--cov-append",
"--cov-config=.coveragerc",
"--cov-report=",
@@ -109,6 +121,9 @@ def system(session):
# Sanity check: Only run tests if the environment variable is set.
if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", ""):
session.skip("Credentials must be set via environment variable")
+ # Install pyopenssl for mTLS testing.
+ if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true":
+ session.install("pyopenssl")
system_test_exists = os.path.exists(system_test_path)
system_test_folder_exists = os.path.exists(system_test_folder_path)
@@ -126,9 +141,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/requirements.txt b/samples/snippets/requirements.txt
index b42844d6..77fd6823 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -1 +1 @@
-google-cloud-pubsublite==0.2.0
\ No newline at end of file
+google-cloud-pubsublite==0.3.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
index bdd97869..1829756b 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@
import os
import setuptools # type: ignore
-version = "0.3.0"
+version = "0.4.0"
package_root = os.path.abspath(os.path.dirname(__file__))
diff --git a/synth.metadata b/synth.metadata
index e4207efc..68419ef3 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -3,30 +3,30 @@
{
"git": {
"name": ".",
- "remote": "git@github.com:googleapis/python-pubsublite.git",
- "sha": "78345176c1eaf07cd143cee941c665b3c99dbc6c"
+ "remote": "https://github.com/googleapis/python-pubsublite.git",
+ "sha": "f913ed00453d3fe924e75caccd2486b24e12a6ae"
}
},
{
"git": {
"name": "googleapis",
"remote": "https://github.com/googleapis/googleapis.git",
- "sha": "6a69c750c3f01a69017662395f90515bbf1fe1ff",
- "internalRef": "342721036"
+ "sha": "d652c6370bf66e325da6ac9ad82989fe7ee7bb4b",
+ "internalRef": "362183999"
}
},
{
"git": {
"name": "synthtool",
"remote": "https://github.com/googleapis/synthtool.git",
- "sha": "d5fc0bcf9ea9789c5b0e3154a9e3b29e5cea6116"
+ "sha": "0780323da96d5a53925fe0547757181fe76e8f1e"
}
},
{
"git": {
"name": "synthtool",
"remote": "https://github.com/googleapis/synthtool.git",
- "sha": "d5fc0bcf9ea9789c5b0e3154a9e3b29e5cea6116"
+ "sha": "0780323da96d5a53925fe0547757181fe76e8f1e"
}
}
],
@@ -40,5 +40,135 @@
"generator": "bazel"
}
}
+ ],
+ "generatedFiles": [
+ ".coveragerc",
+ ".flake8",
+ ".github/CONTRIBUTING.md",
+ ".github/ISSUE_TEMPLATE/bug_report.md",
+ ".github/ISSUE_TEMPLATE/feature_request.md",
+ ".github/ISSUE_TEMPLATE/support_request.md",
+ ".github/PULL_REQUEST_TEMPLATE.md",
+ ".github/header-checker-lint.yml",
+ ".github/release-please.yml",
+ ".github/snippet-bot.yml",
+ ".gitignore",
+ ".kokoro/build.sh",
+ ".kokoro/continuous/common.cfg",
+ ".kokoro/continuous/continuous.cfg",
+ ".kokoro/docker/docs/Dockerfile",
+ ".kokoro/docker/docs/fetch_gpg_keys.sh",
+ ".kokoro/docs/common.cfg",
+ ".kokoro/docs/docs-presubmit.cfg",
+ ".kokoro/docs/docs.cfg",
+ ".kokoro/populate-secrets.sh",
+ ".kokoro/presubmit/common.cfg",
+ ".kokoro/presubmit/presubmit.cfg",
+ ".kokoro/publish-docs.sh",
+ ".kokoro/release.sh",
+ ".kokoro/release/common.cfg",
+ ".kokoro/release/release.cfg",
+ ".kokoro/samples/lint/common.cfg",
+ ".kokoro/samples/lint/continuous.cfg",
+ ".kokoro/samples/lint/periodic.cfg",
+ ".kokoro/samples/lint/presubmit.cfg",
+ ".kokoro/samples/python3.6/common.cfg",
+ ".kokoro/samples/python3.6/continuous.cfg",
+ ".kokoro/samples/python3.6/periodic.cfg",
+ ".kokoro/samples/python3.6/presubmit.cfg",
+ ".kokoro/samples/python3.7/common.cfg",
+ ".kokoro/samples/python3.7/continuous.cfg",
+ ".kokoro/samples/python3.7/periodic.cfg",
+ ".kokoro/samples/python3.7/presubmit.cfg",
+ ".kokoro/samples/python3.8/common.cfg",
+ ".kokoro/samples/python3.8/continuous.cfg",
+ ".kokoro/samples/python3.8/periodic.cfg",
+ ".kokoro/samples/python3.8/presubmit.cfg",
+ ".kokoro/test-samples.sh",
+ ".kokoro/trampoline.sh",
+ ".kokoro/trampoline_v2.sh",
+ ".pre-commit-config.yaml",
+ ".trampolinerc",
+ "CODE_OF_CONDUCT.md",
+ "CONTRIBUTING.rst",
+ "LICENSE",
+ "MANIFEST.in",
+ "docs/_static/custom.css",
+ "docs/_templates/layout.html",
+ "docs/conf.py",
+ "google/cloud/pubsublite/py.typed",
+ "google/cloud/pubsublite_v1/__init__.py",
+ "google/cloud/pubsublite_v1/py.typed",
+ "google/cloud/pubsublite_v1/services/__init__.py",
+ "google/cloud/pubsublite_v1/services/admin_service/__init__.py",
+ "google/cloud/pubsublite_v1/services/admin_service/async_client.py",
+ "google/cloud/pubsublite_v1/services/admin_service/client.py",
+ "google/cloud/pubsublite_v1/services/admin_service/pagers.py",
+ "google/cloud/pubsublite_v1/services/admin_service/transports/__init__.py",
+ "google/cloud/pubsublite_v1/services/admin_service/transports/base.py",
+ "google/cloud/pubsublite_v1/services/admin_service/transports/grpc.py",
+ "google/cloud/pubsublite_v1/services/admin_service/transports/grpc_asyncio.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/__init__.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/async_client.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/client.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/pagers.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/transports/__init__.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/transports/base.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/transports/grpc.py",
+ "google/cloud/pubsublite_v1/services/cursor_service/transports/grpc_asyncio.py",
+ "google/cloud/pubsublite_v1/services/partition_assignment_service/__init__.py",
+ "google/cloud/pubsublite_v1/services/partition_assignment_service/async_client.py",
+ "google/cloud/pubsublite_v1/services/partition_assignment_service/client.py",
+ "google/cloud/pubsublite_v1/services/partition_assignment_service/transports/__init__.py",
+ "google/cloud/pubsublite_v1/services/partition_assignment_service/transports/base.py",
+ "google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc.py",
+ "google/cloud/pubsublite_v1/services/partition_assignment_service/transports/grpc_asyncio.py",
+ "google/cloud/pubsublite_v1/services/publisher_service/__init__.py",
+ "google/cloud/pubsublite_v1/services/publisher_service/async_client.py",
+ "google/cloud/pubsublite_v1/services/publisher_service/client.py",
+ "google/cloud/pubsublite_v1/services/publisher_service/transports/__init__.py",
+ "google/cloud/pubsublite_v1/services/publisher_service/transports/base.py",
+ "google/cloud/pubsublite_v1/services/publisher_service/transports/grpc.py",
+ "google/cloud/pubsublite_v1/services/publisher_service/transports/grpc_asyncio.py",
+ "google/cloud/pubsublite_v1/services/subscriber_service/__init__.py",
+ "google/cloud/pubsublite_v1/services/subscriber_service/async_client.py",
+ "google/cloud/pubsublite_v1/services/subscriber_service/client.py",
+ "google/cloud/pubsublite_v1/services/subscriber_service/transports/__init__.py",
+ "google/cloud/pubsublite_v1/services/subscriber_service/transports/base.py",
+ "google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc.py",
+ "google/cloud/pubsublite_v1/services/subscriber_service/transports/grpc_asyncio.py",
+ "google/cloud/pubsublite_v1/services/topic_stats_service/__init__.py",
+ "google/cloud/pubsublite_v1/services/topic_stats_service/async_client.py",
+ "google/cloud/pubsublite_v1/services/topic_stats_service/client.py",
+ "google/cloud/pubsublite_v1/services/topic_stats_service/transports/__init__.py",
+ "google/cloud/pubsublite_v1/services/topic_stats_service/transports/base.py",
+ "google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc.py",
+ "google/cloud/pubsublite_v1/services/topic_stats_service/transports/grpc_asyncio.py",
+ "google/cloud/pubsublite_v1/types/__init__.py",
+ "google/cloud/pubsublite_v1/types/admin.py",
+ "google/cloud/pubsublite_v1/types/common.py",
+ "google/cloud/pubsublite_v1/types/cursor.py",
+ "google/cloud/pubsublite_v1/types/publisher.py",
+ "google/cloud/pubsublite_v1/types/subscriber.py",
+ "google/cloud/pubsublite_v1/types/topic_stats.py",
+ "mypy.ini",
+ "noxfile.py",
+ "renovate.json",
+ "scripts/decrypt-secrets.sh",
+ "scripts/readme-gen/readme_gen.py",
+ "scripts/readme-gen/templates/README.tmpl.rst",
+ "scripts/readme-gen/templates/auth.tmpl.rst",
+ "scripts/readme-gen/templates/auth_api_key.tmpl.rst",
+ "scripts/readme-gen/templates/install_deps.tmpl.rst",
+ "scripts/readme-gen/templates/install_portaudio.tmpl.rst",
+ "setup.cfg",
+ "testing/.gitignore",
+ "tests/unit/gapic/pubsublite_v1/__init__.py",
+ "tests/unit/gapic/pubsublite_v1/test_admin_service.py",
+ "tests/unit/gapic/pubsublite_v1/test_cursor_service.py",
+ "tests/unit/gapic/pubsublite_v1/test_partition_assignment_service.py",
+ "tests/unit/gapic/pubsublite_v1/test_publisher_service.py",
+ "tests/unit/gapic/pubsublite_v1/test_subscriber_service.py",
+ "tests/unit/gapic/pubsublite_v1/test_topic_stats_service.py"
]
}
\ No newline at end of file
diff --git a/synth.py b/synth.py
index 367d403c..9eb6b85a 100644
--- a/synth.py
+++ b/synth.py
@@ -32,11 +32,12 @@
)
excludes = [
+ "docs/pubsublite_v1", # generated GAPIC docs should be ignored
+ "docs/index.rst",
+ "google/cloud/pubsublite/__init__.py",
+ "README.rst",
"scripts/fixup*.py", # new libraries do not need the keyword fixup script
"setup.py",
- "README.rst",
- "docs/index.rst",
- "google/cloud/pubsublite/__init__.py"
]
s.move(library, excludes=excludes)
@@ -46,15 +47,17 @@
templated_files = common.py_library(
cov_level=70,
microgenerator=True,
- system_test_external_dependencies = ['asynctest'],
- unit_test_external_dependencies = ['asynctest'],
+ system_test_external_dependencies=["asynctest"],
+ unit_test_external_dependencies=["asynctest"],
)
s.move(
- templated_files, excludes=[".coveragerc"]
-) # the microgenerator has a good coveragerc file
+ templated_files,
+ excludes=[
+ ".coveragerc", # the microgenerator has a good coveragerc file
+ "docs/multiprocessing.rst", # exclude multiprocessing note
+ ]
+)
s.shell.run(["nox", "-s", "blacken"], hide_output=False)
-
-
diff --git a/tests/unit/gapic/pubsublite_v1/__init__.py b/tests/unit/gapic/pubsublite_v1/__init__.py
index 8b137891..42ffdf2b 100644
--- a/tests/unit/gapic/pubsublite_v1/__init__.py
+++ b/tests/unit/gapic/pubsublite_v1/__init__.py
@@ -1 +1,16 @@
+# -*- 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
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/tests/unit/gapic/pubsublite_v1/test_admin_service.py b/tests/unit/gapic/pubsublite_v1/test_admin_service.py
index 0685d58b..7ea93dd7 100644
--- a/tests/unit/gapic/pubsublite_v1/test_admin_service.py
+++ b/tests/unit/gapic/pubsublite_v1/test_admin_service.py
@@ -84,7 +84,22 @@ def test__get_default_mtls_endpoint():
assert AdminServiceClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi
-@pytest.mark.parametrize("client_class", [AdminServiceClient, AdminServiceAsyncClient])
+@pytest.mark.parametrize("client_class", [AdminServiceClient, AdminServiceAsyncClient,])
+def test_admin_service_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "pubsublite.googleapis.com:443"
+
+
+@pytest.mark.parametrize("client_class", [AdminServiceClient, AdminServiceAsyncClient,])
def test_admin_service_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
with mock.patch.object(
@@ -93,16 +108,21 @@ def test_admin_service_client_from_service_account_file(client_class):
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "pubsublite.googleapis.com:443"
def test_admin_service_client_get_transport_class():
transport = AdminServiceClient.get_transport_class()
- assert transport == transports.AdminServiceGrpcTransport
+ available_transports = [
+ transports.AdminServiceGrpcTransport,
+ ]
+ assert transport in available_transports
transport = AdminServiceClient.get_transport_class("grpc")
assert transport == transports.AdminServiceGrpcTransport
@@ -151,7 +171,7 @@ def test_admin_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,
)
@@ -167,7 +187,7 @@ def test_admin_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,
)
@@ -183,7 +203,7 @@ def test_admin_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,
)
@@ -211,7 +231,7 @@ def test_admin_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,
)
@@ -260,29 +280,25 @@ def test_admin_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.
@@ -291,66 +307,53 @@ def test_admin_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",
@@ -376,7 +379,7 @@ def test_admin_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,
)
@@ -406,7 +409,7 @@ def test_admin_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,
)
@@ -423,7 +426,7 @@ def test_admin_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,
)
@@ -462,6 +465,22 @@ def test_create_topic_from_dict():
test_create_topic(request_type=dict)
+def test_create_topic_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.create_topic), "__call__") as call:
+ client.create_topic()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.CreateTopicRequest()
+
+
@pytest.mark.asyncio
async def test_create_topic_async(
transport: str = "grpc_asyncio", request_type=admin.CreateTopicRequest
@@ -669,6 +688,22 @@ def test_get_topic_from_dict():
test_get_topic(request_type=dict)
+def test_get_topic_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.get_topic), "__call__") as call:
+ client.get_topic()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.GetTopicRequest()
+
+
@pytest.mark.asyncio
async def test_get_topic_async(
transport: str = "grpc_asyncio", request_type=admin.GetTopicRequest
@@ -858,6 +893,24 @@ def test_get_topic_partitions_from_dict():
test_get_topic_partitions(request_type=dict)
+def test_get_topic_partitions_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.get_topic_partitions), "__call__"
+ ) as call:
+ client.get_topic_partitions()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.GetTopicPartitionsRequest()
+
+
@pytest.mark.asyncio
async def test_get_topic_partitions_async(
transport: str = "grpc_asyncio", request_type=admin.GetTopicPartitionsRequest
@@ -1059,6 +1112,22 @@ def test_list_topics_from_dict():
test_list_topics(request_type=dict)
+def test_list_topics_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.list_topics), "__call__") as call:
+ client.list_topics()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.ListTopicsRequest()
+
+
@pytest.mark.asyncio
async def test_list_topics_async(
transport: str = "grpc_asyncio", request_type=admin.ListTopicsRequest
@@ -1354,6 +1423,22 @@ def test_update_topic_from_dict():
test_update_topic(request_type=dict)
+def test_update_topic_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.update_topic), "__call__") as call:
+ client.update_topic()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.UpdateTopicRequest()
+
+
@pytest.mark.asyncio
async def test_update_topic_async(
transport: str = "grpc_asyncio", request_type=admin.UpdateTopicRequest
@@ -1550,6 +1635,22 @@ def test_delete_topic_from_dict():
test_delete_topic(request_type=dict)
+def test_delete_topic_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.delete_topic), "__call__") as call:
+ client.delete_topic()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.DeleteTopicRequest()
+
+
@pytest.mark.asyncio
async def test_delete_topic_async(
transport: str = "grpc_asyncio", request_type=admin.DeleteTopicRequest
@@ -1740,6 +1841,24 @@ def test_list_topic_subscriptions_from_dict():
test_list_topic_subscriptions(request_type=dict)
+def test_list_topic_subscriptions_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_topic_subscriptions), "__call__"
+ ) as call:
+ client.list_topic_subscriptions()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.ListTopicSubscriptionsRequest()
+
+
@pytest.mark.asyncio
async def test_list_topic_subscriptions_async(
transport: str = "grpc_asyncio", request_type=admin.ListTopicSubscriptionsRequest
@@ -2076,6 +2195,24 @@ def test_create_subscription_from_dict():
test_create_subscription(request_type=dict)
+def test_create_subscription_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.create_subscription), "__call__"
+ ) as call:
+ client.create_subscription()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.CreateSubscriptionRequest()
+
+
@pytest.mark.asyncio
async def test_create_subscription_async(
transport: str = "grpc_asyncio", request_type=admin.CreateSubscriptionRequest
@@ -2299,6 +2436,22 @@ def test_get_subscription_from_dict():
test_get_subscription(request_type=dict)
+def test_get_subscription_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.get_subscription), "__call__") as call:
+ client.get_subscription()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.GetSubscriptionRequest()
+
+
@pytest.mark.asyncio
async def test_get_subscription_async(
transport: str = "grpc_asyncio", request_type=admin.GetSubscriptionRequest
@@ -2492,6 +2645,24 @@ def test_list_subscriptions_from_dict():
test_list_subscriptions(request_type=dict)
+def test_list_subscriptions_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_subscriptions), "__call__"
+ ) as call:
+ client.list_subscriptions()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.ListSubscriptionsRequest()
+
+
@pytest.mark.asyncio
async def test_list_subscriptions_async(
transport: str = "grpc_asyncio", request_type=admin.ListSubscriptionsRequest
@@ -2843,6 +3014,24 @@ def test_update_subscription_from_dict():
test_update_subscription(request_type=dict)
+def test_update_subscription_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.update_subscription), "__call__"
+ ) as call:
+ client.update_subscription()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.UpdateSubscriptionRequest()
+
+
@pytest.mark.asyncio
async def test_update_subscription_async(
transport: str = "grpc_asyncio", request_type=admin.UpdateSubscriptionRequest
@@ -3061,6 +3250,24 @@ def test_delete_subscription_from_dict():
test_delete_subscription(request_type=dict)
+def test_delete_subscription_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = AdminServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.delete_subscription), "__call__"
+ ) as call:
+ client.delete_subscription()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == admin.DeleteSubscriptionRequest()
+
+
@pytest.mark.asyncio
async def test_delete_subscription_async(
transport: str = "grpc_asyncio", request_type=admin.DeleteSubscriptionRequest
@@ -3275,7 +3482,10 @@ def test_transport_get_channel():
@pytest.mark.parametrize(
"transport_class",
- [transports.AdminServiceGrpcTransport, transports.AdminServiceGrpcAsyncIOTransport],
+ [
+ transports.AdminServiceGrpcTransport,
+ transports.AdminServiceGrpcAsyncIOTransport,
+ ],
)
def test_transport_adc(transport_class):
# Test default credentials are used if not provided.
@@ -3386,6 +3596,48 @@ def test_admin_service_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [transports.AdminServiceGrpcTransport, transports.AdminServiceGrpcAsyncIOTransport],
+)
+def test_admin_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",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # 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_admin_service_host_no_port():
client = AdminServiceClient(
credentials=credentials.AnonymousCredentials(),
@@ -3407,7 +3659,7 @@ def test_admin_service_host_with_port():
def test_admin_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.AdminServiceGrpcTransport(
@@ -3419,7 +3671,7 @@ def test_admin_service_grpc_transport_channel():
def test_admin_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.AdminServiceGrpcAsyncIOTransport(
@@ -3430,6 +3682,8 @@ def test_admin_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",
[transports.AdminServiceGrpcTransport, transports.AdminServiceGrpcAsyncIOTransport],
@@ -3439,7 +3693,7 @@ def test_admin_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
@@ -3468,11 +3722,17 @@ def test_admin_service_transport_channel_mtls_with_client_cert_source(transport_
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
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.AdminServiceGrpcTransport, transports.AdminServiceGrpcAsyncIOTransport],
@@ -3485,7 +3745,7 @@ def test_admin_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
@@ -3506,6 +3766,10 @@ def test_admin_service_transport_channel_mtls_with_adc(transport_class):
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
diff --git a/tests/unit/gapic/pubsublite_v1/test_cursor_service.py b/tests/unit/gapic/pubsublite_v1/test_cursor_service.py
index f4981ca4..181b141f 100644
--- a/tests/unit/gapic/pubsublite_v1/test_cursor_service.py
+++ b/tests/unit/gapic/pubsublite_v1/test_cursor_service.py
@@ -86,7 +86,24 @@ def test__get_default_mtls_endpoint():
@pytest.mark.parametrize(
- "client_class", [CursorServiceClient, CursorServiceAsyncClient]
+ "client_class", [CursorServiceClient, CursorServiceAsyncClient,]
+)
+def test_cursor_service_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "pubsublite.googleapis.com:443"
+
+
+@pytest.mark.parametrize(
+ "client_class", [CursorServiceClient, CursorServiceAsyncClient,]
)
def test_cursor_service_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
@@ -96,16 +113,21 @@ def test_cursor_service_client_from_service_account_file(client_class):
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "pubsublite.googleapis.com:443"
def test_cursor_service_client_get_transport_class():
transport = CursorServiceClient.get_transport_class()
- assert transport == transports.CursorServiceGrpcTransport
+ available_transports = [
+ transports.CursorServiceGrpcTransport,
+ ]
+ assert transport in available_transports
transport = CursorServiceClient.get_transport_class("grpc")
assert transport == transports.CursorServiceGrpcTransport
@@ -156,7 +178,7 @@ def test_cursor_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,
)
@@ -172,7 +194,7 @@ def test_cursor_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,
)
@@ -188,7 +210,7 @@ def test_cursor_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,
)
@@ -216,7 +238,7 @@ def test_cursor_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,
)
@@ -267,29 +289,25 @@ def test_cursor_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.
@@ -298,66 +316,53 @@ def test_cursor_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",
@@ -383,7 +388,7 @@ def test_cursor_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,
)
@@ -413,7 +418,7 @@ def test_cursor_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,
)
@@ -432,7 +437,7 @@ def test_cursor_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,
)
@@ -550,6 +555,22 @@ def test_commit_cursor_from_dict():
test_commit_cursor(request_type=dict)
+def test_commit_cursor_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = CursorServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.commit_cursor), "__call__") as call:
+ client.commit_cursor()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == cursor.CommitCursorRequest()
+
+
@pytest.mark.asyncio
async def test_commit_cursor_async(
transport: str = "grpc_asyncio", request_type=cursor.CommitCursorRequest
@@ -625,6 +646,24 @@ def test_list_partition_cursors_from_dict():
test_list_partition_cursors(request_type=dict)
+def test_list_partition_cursors_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = CursorServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_partition_cursors), "__call__"
+ ) as call:
+ client.list_partition_cursors()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == cursor.ListPartitionCursorsRequest()
+
+
@pytest.mark.asyncio
async def test_list_partition_cursors_async(
transport: str = "grpc_asyncio", request_type=cursor.ListPartitionCursorsRequest
@@ -1108,6 +1147,51 @@ def test_cursor_service_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.CursorServiceGrpcTransport,
+ transports.CursorServiceGrpcAsyncIOTransport,
+ ],
+)
+def test_cursor_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",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # 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_cursor_service_host_no_port():
client = CursorServiceClient(
credentials=credentials.AnonymousCredentials(),
@@ -1129,7 +1213,7 @@ def test_cursor_service_host_with_port():
def test_cursor_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.CursorServiceGrpcTransport(
@@ -1141,7 +1225,7 @@ def test_cursor_service_grpc_transport_channel():
def test_cursor_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.CursorServiceGrpcAsyncIOTransport(
@@ -1152,6 +1236,8 @@ def test_cursor_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",
[
@@ -1164,7 +1250,7 @@ def test_cursor_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
@@ -1193,11 +1279,17 @@ def test_cursor_service_transport_channel_mtls_with_client_cert_source(transport
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
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",
[
@@ -1213,7 +1305,7 @@ def test_cursor_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
@@ -1234,6 +1326,10 @@ def test_cursor_service_transport_channel_mtls_with_adc(transport_class):
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
diff --git a/tests/unit/gapic/pubsublite_v1/test_partition_assignment_service.py b/tests/unit/gapic/pubsublite_v1/test_partition_assignment_service.py
index 35bf8481..45ac59a9 100644
--- a/tests/unit/gapic/pubsublite_v1/test_partition_assignment_service.py
+++ b/tests/unit/gapic/pubsublite_v1/test_partition_assignment_service.py
@@ -92,7 +92,25 @@ def test__get_default_mtls_endpoint():
@pytest.mark.parametrize(
"client_class",
- [PartitionAssignmentServiceClient, PartitionAssignmentServiceAsyncClient],
+ [PartitionAssignmentServiceClient, PartitionAssignmentServiceAsyncClient,],
+)
+def test_partition_assignment_service_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "pubsublite.googleapis.com:443"
+
+
+@pytest.mark.parametrize(
+ "client_class",
+ [PartitionAssignmentServiceClient, PartitionAssignmentServiceAsyncClient,],
)
def test_partition_assignment_service_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
@@ -102,16 +120,21 @@ def test_partition_assignment_service_client_from_service_account_file(client_cl
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "pubsublite.googleapis.com:443"
def test_partition_assignment_service_client_get_transport_class():
transport = PartitionAssignmentServiceClient.get_transport_class()
- assert transport == transports.PartitionAssignmentServiceGrpcTransport
+ available_transports = [
+ transports.PartitionAssignmentServiceGrpcTransport,
+ ]
+ assert transport in available_transports
transport = PartitionAssignmentServiceClient.get_transport_class("grpc")
assert transport == transports.PartitionAssignmentServiceGrpcTransport
@@ -170,7 +193,7 @@ def test_partition_assignment_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,
)
@@ -186,7 +209,7 @@ def test_partition_assignment_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,
)
@@ -202,7 +225,7 @@ def test_partition_assignment_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,
)
@@ -230,7 +253,7 @@ def test_partition_assignment_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,
)
@@ -291,29 +314,25 @@ def test_partition_assignment_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.
@@ -322,66 +341,53 @@ def test_partition_assignment_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",
@@ -411,7 +417,7 @@ def test_partition_assignment_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,
)
@@ -445,7 +451,7 @@ def test_partition_assignment_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,
)
@@ -464,7 +470,7 @@ def test_partition_assignment_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,
)
@@ -710,6 +716,53 @@ def test_partition_assignment_service_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.PartitionAssignmentServiceGrpcTransport,
+ transports.PartitionAssignmentServiceGrpcAsyncIOTransport,
+ ],
+)
+def test_partition_assignment_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",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # 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_partition_assignment_service_host_no_port():
client = PartitionAssignmentServiceClient(
credentials=credentials.AnonymousCredentials(),
@@ -731,7 +784,7 @@ def test_partition_assignment_service_host_with_port():
def test_partition_assignment_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.PartitionAssignmentServiceGrpcTransport(
@@ -743,7 +796,7 @@ def test_partition_assignment_service_grpc_transport_channel():
def test_partition_assignment_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.PartitionAssignmentServiceGrpcAsyncIOTransport(
@@ -754,6 +807,8 @@ def test_partition_assignment_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",
[
@@ -768,7 +823,7 @@ def test_partition_assignment_service_transport_channel_mtls_with_client_cert_so
"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
@@ -797,11 +852,17 @@ def test_partition_assignment_service_transport_channel_mtls_with_client_cert_so
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
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",
[
@@ -817,7 +878,7 @@ def test_partition_assignment_service_transport_channel_mtls_with_adc(transport_
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
@@ -838,6 +899,10 @@ def test_partition_assignment_service_transport_channel_mtls_with_adc(transport_
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
diff --git a/tests/unit/gapic/pubsublite_v1/test_publisher_service.py b/tests/unit/gapic/pubsublite_v1/test_publisher_service.py
index c85679a9..abc1ff8c 100644
--- a/tests/unit/gapic/pubsublite_v1/test_publisher_service.py
+++ b/tests/unit/gapic/pubsublite_v1/test_publisher_service.py
@@ -89,7 +89,24 @@ def test__get_default_mtls_endpoint():
@pytest.mark.parametrize(
- "client_class", [PublisherServiceClient, PublisherServiceAsyncClient]
+ "client_class", [PublisherServiceClient, PublisherServiceAsyncClient,]
+)
+def test_publisher_service_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "pubsublite.googleapis.com:443"
+
+
+@pytest.mark.parametrize(
+ "client_class", [PublisherServiceClient, PublisherServiceAsyncClient,]
)
def test_publisher_service_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
@@ -99,16 +116,21 @@ def test_publisher_service_client_from_service_account_file(client_class):
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "pubsublite.googleapis.com:443"
def test_publisher_service_client_get_transport_class():
transport = PublisherServiceClient.get_transport_class()
- assert transport == transports.PublisherServiceGrpcTransport
+ available_transports = [
+ transports.PublisherServiceGrpcTransport,
+ ]
+ assert transport in available_transports
transport = PublisherServiceClient.get_transport_class("grpc")
assert transport == transports.PublisherServiceGrpcTransport
@@ -159,7 +181,7 @@ def test_publisher_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 +197,7 @@ def test_publisher_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 +213,7 @@ def test_publisher_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 +241,7 @@ def test_publisher_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,
)
@@ -280,29 +302,25 @@ def test_publisher_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.
@@ -311,66 +329,53 @@ def test_publisher_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",
@@ -396,7 +401,7 @@ def test_publisher_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,
)
@@ -426,7 +431,7 @@ def test_publisher_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,
)
@@ -445,7 +450,7 @@ def test_publisher_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,
)
@@ -681,6 +686,51 @@ def test_publisher_service_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.PublisherServiceGrpcTransport,
+ transports.PublisherServiceGrpcAsyncIOTransport,
+ ],
+)
+def test_publisher_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",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # 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_service_host_no_port():
client = PublisherServiceClient(
credentials=credentials.AnonymousCredentials(),
@@ -702,7 +752,7 @@ def test_publisher_service_host_with_port():
def test_publisher_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.PublisherServiceGrpcTransport(
@@ -714,7 +764,7 @@ def test_publisher_service_grpc_transport_channel():
def test_publisher_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.PublisherServiceGrpcAsyncIOTransport(
@@ -725,6 +775,8 @@ def test_publisher_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",
[
@@ -739,7 +791,7 @@ def test_publisher_service_transport_channel_mtls_with_client_cert_source(
"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
@@ -768,11 +820,17 @@ def test_publisher_service_transport_channel_mtls_with_client_cert_source(
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
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",
[
@@ -788,7 +846,7 @@ def test_publisher_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
@@ -809,6 +867,10 @@ def test_publisher_service_transport_channel_mtls_with_adc(transport_class):
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
diff --git a/tests/unit/gapic/pubsublite_v1/test_subscriber_service.py b/tests/unit/gapic/pubsublite_v1/test_subscriber_service.py
index ca5464dc..6118ade5 100644
--- a/tests/unit/gapic/pubsublite_v1/test_subscriber_service.py
+++ b/tests/unit/gapic/pubsublite_v1/test_subscriber_service.py
@@ -90,7 +90,24 @@ def test__get_default_mtls_endpoint():
@pytest.mark.parametrize(
- "client_class", [SubscriberServiceClient, SubscriberServiceAsyncClient]
+ "client_class", [SubscriberServiceClient, SubscriberServiceAsyncClient,]
+)
+def test_subscriber_service_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "pubsublite.googleapis.com:443"
+
+
+@pytest.mark.parametrize(
+ "client_class", [SubscriberServiceClient, SubscriberServiceAsyncClient,]
)
def test_subscriber_service_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
@@ -100,16 +117,21 @@ def test_subscriber_service_client_from_service_account_file(client_class):
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "pubsublite.googleapis.com:443"
def test_subscriber_service_client_get_transport_class():
transport = SubscriberServiceClient.get_transport_class()
- assert transport == transports.SubscriberServiceGrpcTransport
+ available_transports = [
+ transports.SubscriberServiceGrpcTransport,
+ ]
+ assert transport in available_transports
transport = SubscriberServiceClient.get_transport_class("grpc")
assert transport == transports.SubscriberServiceGrpcTransport
@@ -160,7 +182,7 @@ def test_subscriber_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,
)
@@ -176,7 +198,7 @@ def test_subscriber_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,
)
@@ -192,7 +214,7 @@ def test_subscriber_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,
)
@@ -220,7 +242,7 @@ def test_subscriber_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,
)
@@ -281,29 +303,25 @@ def test_subscriber_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.
@@ -312,66 +330,53 @@ def test_subscriber_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",
@@ -397,7 +402,7 @@ def test_subscriber_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,
)
@@ -427,7 +432,7 @@ def test_subscriber_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,
)
@@ -446,7 +451,7 @@ def test_subscriber_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,
)
@@ -682,6 +687,51 @@ def test_subscriber_service_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.SubscriberServiceGrpcTransport,
+ transports.SubscriberServiceGrpcAsyncIOTransport,
+ ],
+)
+def test_subscriber_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",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # 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_service_host_no_port():
client = SubscriberServiceClient(
credentials=credentials.AnonymousCredentials(),
@@ -703,7 +753,7 @@ def test_subscriber_service_host_with_port():
def test_subscriber_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.SubscriberServiceGrpcTransport(
@@ -715,7 +765,7 @@ def test_subscriber_service_grpc_transport_channel():
def test_subscriber_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.SubscriberServiceGrpcAsyncIOTransport(
@@ -726,6 +776,8 @@ def test_subscriber_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",
[
@@ -740,7 +792,7 @@ def test_subscriber_service_transport_channel_mtls_with_client_cert_source(
"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
@@ -769,11 +821,17 @@ def test_subscriber_service_transport_channel_mtls_with_client_cert_source(
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
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",
[
@@ -789,7 +847,7 @@ def test_subscriber_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
@@ -810,6 +868,10 @@ def test_subscriber_service_transport_channel_mtls_with_adc(transport_class):
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
diff --git a/tests/unit/gapic/pubsublite_v1/test_topic_stats_service.py b/tests/unit/gapic/pubsublite_v1/test_topic_stats_service.py
index cd0f54e1..5c53bbb8 100644
--- a/tests/unit/gapic/pubsublite_v1/test_topic_stats_service.py
+++ b/tests/unit/gapic/pubsublite_v1/test_topic_stats_service.py
@@ -91,7 +91,24 @@ def test__get_default_mtls_endpoint():
@pytest.mark.parametrize(
- "client_class", [TopicStatsServiceClient, TopicStatsServiceAsyncClient]
+ "client_class", [TopicStatsServiceClient, TopicStatsServiceAsyncClient,]
+)
+def test_topic_stats_service_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "pubsublite.googleapis.com:443"
+
+
+@pytest.mark.parametrize(
+ "client_class", [TopicStatsServiceClient, TopicStatsServiceAsyncClient,]
)
def test_topic_stats_service_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
@@ -101,16 +118,21 @@ def test_topic_stats_service_client_from_service_account_file(client_class):
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "pubsublite.googleapis.com:443"
def test_topic_stats_service_client_get_transport_class():
transport = TopicStatsServiceClient.get_transport_class()
- assert transport == transports.TopicStatsServiceGrpcTransport
+ available_transports = [
+ transports.TopicStatsServiceGrpcTransport,
+ ]
+ assert transport in available_transports
transport = TopicStatsServiceClient.get_transport_class("grpc")
assert transport == transports.TopicStatsServiceGrpcTransport
@@ -161,7 +183,7 @@ def test_topic_stats_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,
)
@@ -177,7 +199,7 @@ def test_topic_stats_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,
)
@@ -193,7 +215,7 @@ def test_topic_stats_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,
)
@@ -221,7 +243,7 @@ def test_topic_stats_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,
)
@@ -282,29 +304,25 @@ def test_topic_stats_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.
@@ -313,66 +331,53 @@ def test_topic_stats_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",
@@ -398,7 +403,7 @@ def test_topic_stats_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,
)
@@ -428,7 +433,7 @@ def test_topic_stats_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,
)
@@ -447,7 +452,7 @@ def test_topic_stats_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,
)
@@ -494,6 +499,24 @@ def test_compute_message_stats_from_dict():
test_compute_message_stats(request_type=dict)
+def test_compute_message_stats_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = TopicStatsServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.compute_message_stats), "__call__"
+ ) as call:
+ client.compute_message_stats()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == topic_stats.ComputeMessageStatsRequest()
+
+
@pytest.mark.asyncio
async def test_compute_message_stats_async(
transport: str = "grpc_asyncio", request_type=topic_stats.ComputeMessageStatsRequest
@@ -595,6 +618,154 @@ async def test_compute_message_stats_field_headers_async():
assert ("x-goog-request-params", "topic=topic/value",) in kw["metadata"]
+def test_compute_head_cursor(
+ transport: str = "grpc", request_type=topic_stats.ComputeHeadCursorRequest
+):
+ client = TopicStatsServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport=transport,
+ )
+
+ # Everything is optional in proto3 as far as the runtime is concerned,
+ # and we are mocking out the actual API, so just send an empty request.
+ request = request_type()
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.compute_head_cursor), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = topic_stats.ComputeHeadCursorResponse()
+
+ response = client.compute_head_cursor(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert len(call.mock_calls) == 1
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == topic_stats.ComputeHeadCursorRequest()
+
+ # Establish that the response is the type that we expect.
+
+ assert isinstance(response, topic_stats.ComputeHeadCursorResponse)
+
+
+def test_compute_head_cursor_from_dict():
+ test_compute_head_cursor(request_type=dict)
+
+
+def test_compute_head_cursor_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = TopicStatsServiceClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.compute_head_cursor), "__call__"
+ ) as call:
+ client.compute_head_cursor()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == topic_stats.ComputeHeadCursorRequest()
+
+
+@pytest.mark.asyncio
+async def test_compute_head_cursor_async(
+ transport: str = "grpc_asyncio", request_type=topic_stats.ComputeHeadCursorRequest
+):
+ client = TopicStatsServiceAsyncClient(
+ credentials=credentials.AnonymousCredentials(), transport=transport,
+ )
+
+ # Everything is optional in proto3 as far as the runtime is concerned,
+ # and we are mocking out the actual API, so just send an empty request.
+ request = request_type()
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.compute_head_cursor), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ topic_stats.ComputeHeadCursorResponse()
+ )
+
+ response = await client.compute_head_cursor(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert len(call.mock_calls)
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == topic_stats.ComputeHeadCursorRequest()
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, topic_stats.ComputeHeadCursorResponse)
+
+
+@pytest.mark.asyncio
+async def test_compute_head_cursor_async_from_dict():
+ await test_compute_head_cursor_async(request_type=dict)
+
+
+def test_compute_head_cursor_field_headers():
+ client = TopicStatsServiceClient(credentials=credentials.AnonymousCredentials(),)
+
+ # Any value that is part of the HTTP/1.1 URI should be sent as
+ # a field header. Set these to a non-empty value.
+ request = topic_stats.ComputeHeadCursorRequest()
+ request.topic = "topic/value"
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.compute_head_cursor), "__call__"
+ ) as call:
+ call.return_value = topic_stats.ComputeHeadCursorResponse()
+
+ client.compute_head_cursor(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert len(call.mock_calls) == 1
+ _, args, _ = call.mock_calls[0]
+ assert args[0] == request
+
+ # Establish that the field header was sent.
+ _, _, kw = call.mock_calls[0]
+ assert ("x-goog-request-params", "topic=topic/value",) in kw["metadata"]
+
+
+@pytest.mark.asyncio
+async def test_compute_head_cursor_field_headers_async():
+ client = TopicStatsServiceAsyncClient(
+ credentials=credentials.AnonymousCredentials(),
+ )
+
+ # Any value that is part of the HTTP/1.1 URI should be sent as
+ # a field header. Set these to a non-empty value.
+ request = topic_stats.ComputeHeadCursorRequest()
+ request.topic = "topic/value"
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.compute_head_cursor), "__call__"
+ ) as call:
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ topic_stats.ComputeHeadCursorResponse()
+ )
+
+ await client.compute_head_cursor(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert len(call.mock_calls)
+ _, args, _ = call.mock_calls[0]
+ assert args[0] == request
+
+ # Establish that the field header was sent.
+ _, _, kw = call.mock_calls[0]
+ assert ("x-goog-request-params", "topic=topic/value",) in kw["metadata"]
+
+
def test_credentials_transport_error():
# It is an error to provide credentials and a transport instance.
transport = transports.TopicStatsServiceGrpcTransport(
@@ -691,7 +862,10 @@ def test_topic_stats_service_base_transport():
# Every method on the transport should just blindly
# raise NotImplementedError.
- methods = ("compute_message_stats",)
+ methods = (
+ "compute_message_stats",
+ "compute_head_cursor",
+ )
for method in methods:
with pytest.raises(NotImplementedError):
getattr(transport, method)(request=object())
@@ -752,6 +926,53 @@ def test_topic_stats_service_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.TopicStatsServiceGrpcTransport,
+ transports.TopicStatsServiceGrpcAsyncIOTransport,
+ ],
+)
+def test_topic_stats_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",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # 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_topic_stats_service_host_no_port():
client = TopicStatsServiceClient(
credentials=credentials.AnonymousCredentials(),
@@ -773,7 +994,7 @@ def test_topic_stats_service_host_with_port():
def test_topic_stats_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.TopicStatsServiceGrpcTransport(
@@ -785,7 +1006,7 @@ def test_topic_stats_service_grpc_transport_channel():
def test_topic_stats_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.TopicStatsServiceGrpcAsyncIOTransport(
@@ -796,6 +1017,8 @@ def test_topic_stats_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",
[
@@ -810,7 +1033,7 @@ def test_topic_stats_service_transport_channel_mtls_with_client_cert_source(
"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
@@ -839,11 +1062,17 @@ def test_topic_stats_service_transport_channel_mtls_with_client_cert_source(
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel
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",
[
@@ -859,7 +1088,7 @@ def test_topic_stats_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
@@ -880,6 +1109,10 @@ def test_topic_stats_service_transport_channel_mtls_with_adc(transport_class):
scopes=("https://www.googleapis.com/auth/cloud-platform",),
ssl_credentials=mock_ssl_cred,
quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
)
assert transport.grpc_channel == mock_grpc_channel