diff --git a/.flake8 b/.flake8
index 32986c792..90316de21 100644
--- a/.flake8
+++ b/.flake8
@@ -1,28 +1,29 @@
# -*- coding: utf-8 -*-
-#
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# https://www.apache.org/licenses/LICENSE-2.0
+# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-# Generated by synthtool. DO NOT EDIT!
+#
[flake8]
+# TODO(https://github.com/googleapis/gapic-generator-python/issues/2333):
+# Resolve flake8 lint issues
ignore = E203, E231, E266, E501, W503
exclude =
- # Exclude generated code.
- **/proto/**
+ # TODO(https://github.com/googleapis/gapic-generator-python/issues/2333):
+ # Ensure that generated code passes flake8 lint
**/gapic/**
**/services/**
**/types/**
+ # Exclude Protobuf gencode
*_pb2.py
# Standard linting exemptions.
diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
deleted file mode 100644
index f30cb3775..000000000
--- a/.github/.OwlBot.lock.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2024 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.
-docker:
- image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
- digest: sha256:52210e0e0559f5ea8c52be148b33504022e1faef4e95fbe4b32d68022af2fa7e
-# created: 2024-07-08T19:25:35.862283192Z
diff --git a/.github/.OwlBot.yaml b/.github/.OwlBot.yaml
deleted file mode 100644
index 0bfe82f74..000000000
--- a/.github/.OwlBot.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2021 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.
-
-docker:
- image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-
-deep-remove-regex:
- - /owl-bot-staging
-
-deep-copy-regex:
- - source: /google/pubsub/(v.*)/.*-py/(.*)
- dest: /owl-bot-staging/$1/$2
-
-begin-after-commit-hash: 40278112d2922ec917140dcb5cc6d5ef2923aeb2
-
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index f1b33465e..a01d0971f 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -5,8 +5,8 @@
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax
# Note: This file is autogenerated. To make changes to the codeowner team, please update .repo-metadata.json.
-# @googleapis/yoshi-python @googleapis/api-pubsub are the default owners for changes in this repo
-* @googleapis/yoshi-python @googleapis/api-pubsub
+# @googleapis/cloud-sdk-python-team @googleapis/pubsub-team are the default owners for changes in this repo
+* @googleapis/cloud-sdk-python-team @googleapis/pubsub-team
-# @googleapis/python-samples-reviewers @googleapis/api-pubsub are the default owners for samples changes
-/samples/ @googleapis/python-samples-reviewers @googleapis/api-pubsub
+# @googleapis/python-samples-reviewers @googleapis/pubsub-team are the default owners for samples changes
+/samples/ @googleapis/python-samples-reviewers @googleapis/pubsub-team
diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml
index 3408b580a..ac5c87339 100644
--- a/.github/blunderbuss.yml
+++ b/.github/blunderbuss.yml
@@ -4,14 +4,14 @@
# Note: This file is autogenerated. To make changes to the assignee
# team, please update `codeowner_team` in `.repo-metadata.json`.
assign_issues:
- - mukund-ananthu
+ - abbrowne126
assign_issues_by:
- labels:
- "samples"
to:
- googleapis/python-samples-reviewers
- - mukund-ananthu
+ - abbrowne126
assign_prs:
- - mukund-ananthu
+ - abbrowne126
diff --git a/.github/release-please.yml b/.github/release-please.yml
deleted file mode 100644
index fe749ff6b..000000000
--- a/.github/release-please.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-releaseType: python
-handleGHRelease: true
-manifest: true
-# NOTE: this section is generated by synthtool.languages.python
-# See https://github.com/googleapis/synthtool/blob/master/synthtool/languages/python.py
-branches:
-- branch: v1
- handleGHRelease: true
- releaseType: python
-- branch: v0
- handleGHRelease: true
- releaseType: python
diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml
deleted file mode 100644
index d4ca94189..000000000
--- a/.github/release-trigger.yml
+++ /dev/null
@@ -1 +0,0 @@
-enabled: true
diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml
index 77c1a4fb5..ecc31984d 100644
--- a/.github/sync-repo-settings.yaml
+++ b/.github/sync-repo-settings.yaml
@@ -11,8 +11,6 @@ branchProtectionRules:
- 'Kokoro - Against Pub/Sub Lite samples'
- 'cla/google'
- 'Samples - Lint'
- - 'Samples - Python 3.7'
- - 'Samples - Python 3.8'
- 'Samples - Python 3.9'
- 'Samples - Python 3.10'
- 'Samples - Python 3.11'
@@ -21,10 +19,10 @@ branchProtectionRules:
- 'docs'
- 'docfx'
- 'lint'
- - 'unit (3.7)'
- - 'unit (3.8)'
- 'unit (3.9)'
- 'unit (3.10)'
- 'unit (3.11)'
- 'unit (3.12)'
+ - 'unit (3.13)'
+ - 'unit (3.14)'
- 'cover'
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 698fbc5c9..c5ee98837 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -8,11 +8,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
- python-version: "3.9"
+ python-version: "3.10"
- name: Install nox
run: |
python -m pip install --upgrade setuptools pip wheel
@@ -24,9 +24,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: "3.10"
- name: Install nox
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 4866193af..a52933488 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -8,11 +8,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
- python-version: "3.8"
+ python-version: "3.14"
- name: Install nox
run: |
python -m pip install --upgrade setuptools pip wheel
diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml
index f4a337c49..9fb410b81 100644
--- a/.github/workflows/unittest.yml
+++ b/.github/workflows/unittest.yml
@@ -5,10 +5,13 @@ on:
name: unittest
jobs:
unit:
- runs-on: ubuntu-latest
+ # TODO(https://github.com/googleapis/gapic-generator-python/issues/2303): use `ubuntu-latest` once this bug is fixed.
+ # Use ubuntu-22.04 until Python 3.7 is removed from the test matrix
+ # https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories
+ runs-on: ubuntu-22.04
strategy:
matrix:
- python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
+ python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -30,6 +33,7 @@ jobs:
with:
name: coverage-artifact-${{ matrix.python }}
path: .coverage-${{ matrix.python }}
+ include-hidden-files: true
cover:
runs-on: ubuntu-latest
@@ -41,7 +45,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
- python-version: "3.8"
+ python-version: "3.14"
- name: Install coverage
run: |
python -m pip install --upgrade setuptools pip wheel
@@ -54,4 +58,4 @@ jobs:
run: |
find .coverage-results -type f -name '*.zip' -exec unzip {} \;
coverage combine .coverage-results/**/.coverage*
- coverage report --show-missing --fail-under=100
+ coverage report --show-missing --fail-under=99
diff --git a/.kokoro/build.sh b/.kokoro/build.sh
index 90e690e7a..d41b45aa1 100755
--- a/.kokoro/build.sh
+++ b/.kokoro/build.sh
@@ -15,11 +15,13 @@
set -eo pipefail
+CURRENT_DIR=$(dirname "${BASH_SOURCE[0]}")
+
if [[ -z "${PROJECT_ROOT:-}" ]]; then
- PROJECT_ROOT="github/python-pubsub"
+ PROJECT_ROOT=$(realpath "${CURRENT_DIR}/..")
fi
-cd "${PROJECT_ROOT}"
+pushd "${PROJECT_ROOT}"
# Disable buffering, so that the logs stream through.
export PYTHONUNBUFFERED=1
@@ -28,10 +30,16 @@ export PYTHONUNBUFFERED=1
env | grep KOKORO
# Setup service account credentials.
-export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json
+if [[ -f "${KOKORO_GFILE_DIR}/service-account.json" ]]
+then
+ export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json
+fi
# Setup project id.
-export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json")
+if [[ -f "${KOKORO_GFILE_DIR}/project-id.json" ]]
+then
+ export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json")
+fi
# If this is a continuous build, send the test log to the FlakyBot.
# See https://github.com/googleapis/repo-automation-bots/tree/main/packages/flakybot.
@@ -46,7 +54,7 @@ fi
# If NOX_SESSION is set, it only runs the specified session,
# otherwise run all the sessions.
if [[ -n "${NOX_SESSION:-}" ]]; then
- python3 -m nox -s ${NOX_SESSION:-}
+ python3 -m nox -s ${NOX_SESSION:-}
else
- python3 -m nox
+ python3 -m nox
fi
diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile
deleted file mode 100644
index 5205308b3..000000000
--- a/.kokoro/docker/docs/Dockerfile
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright 2024 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from ubuntu:24.04
-
-ENV DEBIAN_FRONTEND noninteractive
-
-# Ensure local Python is preferred over distribution Python.
-ENV PATH /usr/local/bin:$PATH
-
-# Install dependencies.
-RUN apt-get update \
- && apt-get install -y --no-install-recommends \
- apt-transport-https \
- build-essential \
- ca-certificates \
- curl \
- dirmngr \
- git \
- gpg-agent \
- graphviz \
- libbz2-dev \
- libdb5.3-dev \
- libexpat1-dev \
- libffi-dev \
- liblzma-dev \
- libreadline-dev \
- libsnappy-dev \
- libssl-dev \
- libsqlite3-dev \
- portaudio19-dev \
- redis-server \
- software-properties-common \
- ssh \
- sudo \
- tcl \
- tcl-dev \
- tk \
- tk-dev \
- uuid-dev \
- wget \
- zlib1g-dev \
- && add-apt-repository universe \
- && apt-get update \
- && apt-get -y install jq \
- && apt-get clean autoclean \
- && apt-get autoremove -y \
- && rm -rf /var/lib/apt/lists/* \
- && rm -f /var/cache/apt/archives/*.deb
-
-
-###################### Install python 3.10.14 for docs/docfx session
-
-# Download python 3.10.14
-RUN wget https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tgz
-
-# Extract files
-RUN tar -xvf Python-3.10.14.tgz
-
-# Install python 3.10.14
-RUN ./Python-3.10.14/configure --enable-optimizations
-RUN make altinstall
-
-RUN python3.10 -m venv /venv
-ENV PATH /venv/bin:$PATH
-
-###################### Install pip
-RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \
- && python3 /tmp/get-pip.py \
- && rm /tmp/get-pip.py
-
-# Test pip
-RUN python3 -m pip
-
-# Install build requirements
-COPY requirements.txt /requirements.txt
-RUN python3 -m pip install --require-hashes -r requirements.txt
-
-CMD ["python3.10"]
diff --git a/.kokoro/docker/docs/requirements.in b/.kokoro/docker/docs/requirements.in
deleted file mode 100644
index 816817c67..000000000
--- a/.kokoro/docker/docs/requirements.in
+++ /dev/null
@@ -1 +0,0 @@
-nox
diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt
deleted file mode 100644
index 7129c7715..000000000
--- a/.kokoro/docker/docs/requirements.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.9
-# by the following command:
-#
-# pip-compile --allow-unsafe --generate-hashes requirements.in
-#
-argcomplete==3.4.0 \
- --hash=sha256:69a79e083a716173e5532e0fa3bef45f793f4e61096cf52b5a42c0211c8b8aa5 \
- --hash=sha256:c2abcdfe1be8ace47ba777d4fce319eb13bf8ad9dace8d085dcad6eded88057f
- # via nox
-colorlog==6.8.2 \
- --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \
- --hash=sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33
- # via nox
-distlib==0.3.8 \
- --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \
- --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64
- # via virtualenv
-filelock==3.15.4 \
- --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \
- --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7
- # via virtualenv
-nox==2024.4.15 \
- --hash=sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565 \
- --hash=sha256:ecf6700199cdfa9e5ea0a41ff5e6ef4641d09508eda6edb89d9987864115817f
- # via -r requirements.in
-packaging==24.1 \
- --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \
- --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124
- # via nox
-platformdirs==4.2.2 \
- --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
- --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
- # via virtualenv
-tomli==2.0.1 \
- --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
- --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
- # via nox
-virtualenv==20.26.3 \
- --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \
- --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589
- # via nox
diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg
deleted file mode 100644
index 63ce88a82..000000000
--- a/.kokoro/docs/common.cfg
+++ /dev/null
@@ -1,66 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Build logs will be here
-action {
- define_artifacts {
- regex: "**/*sponge_log.xml"
- }
-}
-
-# Download trampoline resources.
-gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
-
-# Use the trampoline script to run in docker.
-build_file: "python-pubsub/.kokoro/trampoline_v2.sh"
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/python-lib-docs"
-}
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/python-pubsub/.kokoro/publish-docs.sh"
-}
-
-env_vars: {
- key: "STAGING_BUCKET"
- value: "docs-staging"
-}
-
-env_vars: {
- key: "V2_STAGING_BUCKET"
- # Push google cloud library docs to the Cloud RAD bucket `docs-staging-v2`
- value: "docs-staging-v2"
-}
-
-# It will upload the docker image after successful builds.
-env_vars: {
- key: "TRAMPOLINE_IMAGE_UPLOAD"
- value: "true"
-}
-
-# It will always build the docker image.
-env_vars: {
- key: "TRAMPOLINE_DOCKERFILE"
- value: ".kokoro/docker/docs/Dockerfile"
-}
-
-# Fetch the token needed for reporting release status to GitHub
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "yoshi-automation-github-key"
- }
- }
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "docuploader_service_account"
- }
- }
-}
\ No newline at end of file
diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg
deleted file mode 100644
index 2c532d9db..000000000
--- a/.kokoro/docs/docs-presubmit.cfg
+++ /dev/null
@@ -1,28 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-env_vars: {
- key: "STAGING_BUCKET"
- value: "gcloud-python-test"
-}
-
-env_vars: {
- key: "V2_STAGING_BUCKET"
- value: "gcloud-python-test"
-}
-
-# We only upload the image in the main `docs` build.
-env_vars: {
- key: "TRAMPOLINE_IMAGE_UPLOAD"
- value: "false"
-}
-
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/python-pubsub/.kokoro/build.sh"
-}
-
-# Only run this nox session.
-env_vars: {
- key: "NOX_SESSION"
- value: "docs docfx"
-}
diff --git a/.kokoro/docs/docs.cfg b/.kokoro/docs/docs.cfg
deleted file mode 100644
index 8f43917d9..000000000
--- a/.kokoro/docs/docs.cfg
+++ /dev/null
@@ -1 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
\ No newline at end of file
diff --git a/.kokoro/presubmit/presubmit.cfg b/.kokoro/presubmit/presubmit.cfg
index 8f43917d9..227ccdf47 100644
--- a/.kokoro/presubmit/presubmit.cfg
+++ b/.kokoro/presubmit/presubmit.cfg
@@ -1 +1,6 @@
-# Format: //devtools/kokoro/config/proto/build.proto
\ No newline at end of file
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "NOX_SESSION"
+ value: "system-3.12 blacken mypy format"
+}
diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh
deleted file mode 100755
index 38f083f05..000000000
--- a/.kokoro/publish-docs.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-# Copyright 2024 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -eo pipefail
-
-# Disable buffering, so that the logs stream through.
-export PYTHONUNBUFFERED=1
-
-export PATH="${HOME}/.local/bin:${PATH}"
-
-# Install nox
-python3 -m pip install --require-hashes -r .kokoro/requirements.txt
-python3 -m nox --version
-
-# build docs
-nox -s docs
-
-# create metadata
-python3 -m docuploader create-metadata \
- --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \
- --version=$(python3 setup.py --version) \
- --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \
- --distribution-name=$(python3 setup.py --name) \
- --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \
- --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \
- --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json)
-
-cat docs.metadata
-
-# upload docs
-python3 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket "${STAGING_BUCKET}"
-
-
-# docfx yaml files
-nox -s docfx
-
-# create metadata.
-python3 -m docuploader create-metadata \
- --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \
- --version=$(python3 setup.py --version) \
- --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \
- --distribution-name=$(python3 setup.py --name) \
- --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \
- --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \
- --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json)
-
-cat docs.metadata
-
-# upload docs
-python3 -m docuploader upload docs/_build/html/docfx_yaml --metadata-file docs.metadata --destination-prefix docfx --staging-bucket "${V2_STAGING_BUCKET}"
diff --git a/.kokoro/release.sh b/.kokoro/release.sh
deleted file mode 100755
index 9b17b081a..000000000
--- a/.kokoro/release.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-# Copyright 2024 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -eo pipefail
-
-# Start the releasetool reporter
-python3 -m pip install --require-hashes -r github/python-pubsub/.kokoro/requirements.txt
-python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script
-
-# Disable buffering, so that the logs stream through.
-export PYTHONUNBUFFERED=1
-
-# Move into the package, build the distribution and upload.
-TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-1")
-cd github/python-pubsub
-python3 setup.py sdist bdist_wheel
-twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/*
diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg
deleted file mode 100644
index 5b1bbe360..000000000
--- a/.kokoro/release/common.cfg
+++ /dev/null
@@ -1,49 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Build logs will be here
-action {
- define_artifacts {
- regex: "**/*sponge_log.xml"
- }
-}
-
-# Download trampoline resources.
-gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
-
-# Use the trampoline script to run in docker.
-build_file: "python-pubsub/.kokoro/trampoline.sh"
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/python-multi"
-}
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/python-pubsub/.kokoro/release.sh"
-}
-
-# Fetch PyPI password
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "google-cloud-pypi-token-keystore-1"
- }
- }
-}
-
-# Tokens needed to report release status back to GitHub
-env_vars: {
- key: "SECRET_MANAGER_KEYS"
- value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem"
-}
-
-# Store the packages we uploaded to PyPI. That way, we have a record of exactly
-# what we published, which we can use to generate SBOMs and attestations.
-action {
- define_artifacts {
- regex: "github/python-pubsub/**/*.tar.gz"
- strip_prefix: "github/python-pubsub"
- }
-}
diff --git a/.kokoro/release/release.cfg b/.kokoro/release/release.cfg
deleted file mode 100644
index 8f43917d9..000000000
--- a/.kokoro/release/release.cfg
+++ /dev/null
@@ -1 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
\ No newline at end of file
diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in
deleted file mode 100644
index fff4d9ce0..000000000
--- a/.kokoro/requirements.in
+++ /dev/null
@@ -1,11 +0,0 @@
-gcp-docuploader
-gcp-releasetool>=2 # required for compatibility with cryptography>=42.x
-importlib-metadata
-typing-extensions
-twine
-wheel
-setuptools
-nox>=2022.11.21 # required to remove dependency on py
-charset-normalizer<3
-click<8.1.0
-cryptography>=42.0.5
diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt
deleted file mode 100644
index 9622baf0b..000000000
--- a/.kokoro/requirements.txt
+++ /dev/null
@@ -1,537 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.9
-# by the following command:
-#
-# pip-compile --allow-unsafe --generate-hashes requirements.in
-#
-argcomplete==3.4.0 \
- --hash=sha256:69a79e083a716173e5532e0fa3bef45f793f4e61096cf52b5a42c0211c8b8aa5 \
- --hash=sha256:c2abcdfe1be8ace47ba777d4fce319eb13bf8ad9dace8d085dcad6eded88057f
- # via nox
-attrs==23.2.0 \
- --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \
- --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1
- # via gcp-releasetool
-backports-tarfile==1.2.0 \
- --hash=sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34 \
- --hash=sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991
- # via jaraco-context
-cachetools==5.3.3 \
- --hash=sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 \
- --hash=sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105
- # via google-auth
-certifi==2024.7.4 \
- --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \
- --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90
- # via requests
-cffi==1.16.0 \
- --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \
- --hash=sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a \
- --hash=sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417 \
- --hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \
- --hash=sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520 \
- --hash=sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36 \
- --hash=sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743 \
- --hash=sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8 \
- --hash=sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed \
- --hash=sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684 \
- --hash=sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56 \
- --hash=sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324 \
- --hash=sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d \
- --hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \
- --hash=sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e \
- --hash=sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088 \
- --hash=sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000 \
- --hash=sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7 \
- --hash=sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e \
- --hash=sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673 \
- --hash=sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c \
- --hash=sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe \
- --hash=sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2 \
- --hash=sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098 \
- --hash=sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8 \
- --hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \
- --hash=sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0 \
- --hash=sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b \
- --hash=sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896 \
- --hash=sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e \
- --hash=sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9 \
- --hash=sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2 \
- --hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \
- --hash=sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6 \
- --hash=sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404 \
- --hash=sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f \
- --hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \
- --hash=sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4 \
- --hash=sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc \
- --hash=sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936 \
- --hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \
- --hash=sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872 \
- --hash=sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb \
- --hash=sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614 \
- --hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1 \
- --hash=sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d \
- --hash=sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969 \
- --hash=sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b \
- --hash=sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4 \
- --hash=sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627 \
- --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \
- --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357
- # via cryptography
-charset-normalizer==2.1.1 \
- --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \
- --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f
- # via
- # -r requirements.in
- # requests
-click==8.0.4 \
- --hash=sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1 \
- --hash=sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb
- # via
- # -r requirements.in
- # gcp-docuploader
- # gcp-releasetool
-colorlog==6.8.2 \
- --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \
- --hash=sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33
- # via
- # gcp-docuploader
- # nox
-cryptography==42.0.8 \
- --hash=sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad \
- --hash=sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583 \
- --hash=sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b \
- --hash=sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c \
- --hash=sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1 \
- --hash=sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648 \
- --hash=sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949 \
- --hash=sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba \
- --hash=sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c \
- --hash=sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9 \
- --hash=sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d \
- --hash=sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c \
- --hash=sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e \
- --hash=sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2 \
- --hash=sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d \
- --hash=sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7 \
- --hash=sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70 \
- --hash=sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2 \
- --hash=sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7 \
- --hash=sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14 \
- --hash=sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe \
- --hash=sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e \
- --hash=sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71 \
- --hash=sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961 \
- --hash=sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7 \
- --hash=sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c \
- --hash=sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28 \
- --hash=sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842 \
- --hash=sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902 \
- --hash=sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801 \
- --hash=sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a \
- --hash=sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e
- # via
- # -r requirements.in
- # gcp-releasetool
- # secretstorage
-distlib==0.3.8 \
- --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \
- --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64
- # via virtualenv
-docutils==0.21.2 \
- --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \
- --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2
- # via readme-renderer
-filelock==3.15.4 \
- --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \
- --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7
- # via virtualenv
-gcp-docuploader==0.6.5 \
- --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \
- --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea
- # via -r requirements.in
-gcp-releasetool==2.0.1 \
- --hash=sha256:34314a910c08e8911d9c965bd44f8f2185c4f556e737d719c33a41f6a610de96 \
- --hash=sha256:b0d5863c6a070702b10883d37c4bdfd74bf930fe417f36c0c965d3b7c779ae62
- # via -r requirements.in
-google-api-core==2.19.1 \
- --hash=sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125 \
- --hash=sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd
- # via
- # google-cloud-core
- # google-cloud-storage
-google-auth==2.31.0 \
- --hash=sha256:042c4702efa9f7d3c48d3a69341c209381b125faa6dbf3ebe56bc7e40ae05c23 \
- --hash=sha256:87805c36970047247c8afe614d4e3af8eceafc1ebba0c679fe75ddd1d575e871
- # via
- # gcp-releasetool
- # google-api-core
- # google-cloud-core
- # google-cloud-storage
-google-cloud-core==2.4.1 \
- --hash=sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073 \
- --hash=sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61
- # via google-cloud-storage
-google-cloud-storage==2.17.0 \
- --hash=sha256:49378abff54ef656b52dca5ef0f2eba9aa83dc2b2c72c78714b03a1a95fe9388 \
- --hash=sha256:5b393bc766b7a3bc6f5407b9e665b2450d36282614b7945e570b3480a456d1e1
- # via gcp-docuploader
-google-crc32c==1.5.0 \
- --hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
- --hash=sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876 \
- --hash=sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c \
- --hash=sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289 \
- --hash=sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298 \
- --hash=sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02 \
- --hash=sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f \
- --hash=sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2 \
- --hash=sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a \
- --hash=sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb \
- --hash=sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210 \
- --hash=sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5 \
- --hash=sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee \
- --hash=sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c \
- --hash=sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a \
- --hash=sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314 \
- --hash=sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd \
- --hash=sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65 \
- --hash=sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37 \
- --hash=sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4 \
- --hash=sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13 \
- --hash=sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894 \
- --hash=sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31 \
- --hash=sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e \
- --hash=sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709 \
- --hash=sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740 \
- --hash=sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc \
- --hash=sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d \
- --hash=sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c \
- --hash=sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c \
- --hash=sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d \
- --hash=sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906 \
- --hash=sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61 \
- --hash=sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57 \
- --hash=sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c \
- --hash=sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a \
- --hash=sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438 \
- --hash=sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946 \
- --hash=sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7 \
- --hash=sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96 \
- --hash=sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091 \
- --hash=sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae \
- --hash=sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d \
- --hash=sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88 \
- --hash=sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2 \
- --hash=sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd \
- --hash=sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541 \
- --hash=sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728 \
- --hash=sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178 \
- --hash=sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968 \
- --hash=sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346 \
- --hash=sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8 \
- --hash=sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93 \
- --hash=sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7 \
- --hash=sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273 \
- --hash=sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462 \
- --hash=sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94 \
- --hash=sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd \
- --hash=sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e \
- --hash=sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57 \
- --hash=sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b \
- --hash=sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9 \
- --hash=sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a \
- --hash=sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100 \
- --hash=sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325 \
- --hash=sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183 \
- --hash=sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556 \
- --hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
- # via
- # google-cloud-storage
- # google-resumable-media
-google-resumable-media==2.7.1 \
- --hash=sha256:103ebc4ba331ab1bfdac0250f8033627a2cd7cde09e7ccff9181e31ba4315b2c \
- --hash=sha256:eae451a7b2e2cdbaaa0fd2eb00cc8a1ee5e95e16b55597359cbc3d27d7d90e33
- # via google-cloud-storage
-googleapis-common-protos==1.63.2 \
- --hash=sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945 \
- --hash=sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87
- # via google-api-core
-idna==3.7 \
- --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
- --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0
- # via requests
-importlib-metadata==8.0.0 \
- --hash=sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f \
- --hash=sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812
- # via
- # -r requirements.in
- # keyring
- # twine
-jaraco-classes==3.4.0 \
- --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \
- --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790
- # via keyring
-jaraco-context==5.3.0 \
- --hash=sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266 \
- --hash=sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2
- # via keyring
-jaraco-functools==4.0.1 \
- --hash=sha256:3b24ccb921d6b593bdceb56ce14799204f473976e2a9d4b15b04d0f2c2326664 \
- --hash=sha256:d33fa765374c0611b52f8b3a795f8900869aa88c84769d4d1746cd68fb28c3e8
- # via keyring
-jeepney==0.8.0 \
- --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \
- --hash=sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755
- # via
- # keyring
- # secretstorage
-jinja2==3.1.4 \
- --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \
- --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d
- # via gcp-releasetool
-keyring==25.2.1 \
- --hash=sha256:2458681cdefc0dbc0b7eb6cf75d0b98e59f9ad9b2d4edd319d18f68bdca95e50 \
- --hash=sha256:daaffd42dbda25ddafb1ad5fec4024e5bbcfe424597ca1ca452b299861e49f1b
- # via
- # gcp-releasetool
- # twine
-markdown-it-py==3.0.0 \
- --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \
- --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb
- # via rich
-markupsafe==2.1.5 \
- --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \
- --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \
- --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \
- --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \
- --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \
- --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \
- --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \
- --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \
- --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \
- --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \
- --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \
- --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \
- --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \
- --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \
- --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \
- --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \
- --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \
- --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \
- --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \
- --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \
- --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \
- --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \
- --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \
- --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \
- --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \
- --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \
- --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \
- --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \
- --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \
- --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \
- --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \
- --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \
- --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \
- --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \
- --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \
- --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \
- --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \
- --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \
- --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \
- --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \
- --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \
- --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \
- --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \
- --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \
- --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \
- --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \
- --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \
- --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \
- --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \
- --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \
- --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \
- --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \
- --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \
- --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \
- --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \
- --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \
- --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \
- --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \
- --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \
- --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68
- # via jinja2
-mdurl==0.1.2 \
- --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
- --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
- # via markdown-it-py
-more-itertools==10.3.0 \
- --hash=sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463 \
- --hash=sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320
- # via
- # jaraco-classes
- # jaraco-functools
-nh3==0.2.18 \
- --hash=sha256:0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164 \
- --hash=sha256:14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86 \
- --hash=sha256:19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b \
- --hash=sha256:34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad \
- --hash=sha256:36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204 \
- --hash=sha256:3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a \
- --hash=sha256:42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200 \
- --hash=sha256:5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189 \
- --hash=sha256:6955369e4d9f48f41e3f238a9e60f9410645db7e07435e62c6a9ea6135a4907f \
- --hash=sha256:7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811 \
- --hash=sha256:8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844 \
- --hash=sha256:94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4 \
- --hash=sha256:a7f1b5b2c15866f2db413a3649a8fe4fd7b428ae58be2c0f6bca5eefd53ca2be \
- --hash=sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50 \
- --hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \
- --hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe
- # via readme-renderer
-nox==2024.4.15 \
- --hash=sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565 \
- --hash=sha256:ecf6700199cdfa9e5ea0a41ff5e6ef4641d09508eda6edb89d9987864115817f
- # via -r requirements.in
-packaging==24.1 \
- --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \
- --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124
- # via
- # gcp-releasetool
- # nox
-pkginfo==1.10.0 \
- --hash=sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297 \
- --hash=sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097
- # via twine
-platformdirs==4.2.2 \
- --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
- --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
- # via virtualenv
-proto-plus==1.24.0 \
- --hash=sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445 \
- --hash=sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12
- # via google-api-core
-protobuf==5.27.2 \
- --hash=sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505 \
- --hash=sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b \
- --hash=sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38 \
- --hash=sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863 \
- --hash=sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470 \
- --hash=sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6 \
- --hash=sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce \
- --hash=sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca \
- --hash=sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5 \
- --hash=sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e \
- --hash=sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714
- # via
- # gcp-docuploader
- # gcp-releasetool
- # google-api-core
- # googleapis-common-protos
- # proto-plus
-pyasn1==0.6.0 \
- --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \
- --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473
- # via
- # pyasn1-modules
- # rsa
-pyasn1-modules==0.4.0 \
- --hash=sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6 \
- --hash=sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b
- # via google-auth
-pycparser==2.22 \
- --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
- --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
- # via cffi
-pygments==2.18.0 \
- --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \
- --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a
- # via
- # readme-renderer
- # rich
-pyjwt==2.8.0 \
- --hash=sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de \
- --hash=sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320
- # via gcp-releasetool
-pyperclip==1.9.0 \
- --hash=sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310
- # via gcp-releasetool
-python-dateutil==2.9.0.post0 \
- --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
- --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
- # via gcp-releasetool
-readme-renderer==44.0 \
- --hash=sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151 \
- --hash=sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1
- # via twine
-requests==2.32.3 \
- --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
- --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
- # via
- # gcp-releasetool
- # google-api-core
- # google-cloud-storage
- # requests-toolbelt
- # twine
-requests-toolbelt==1.0.0 \
- --hash=sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6 \
- --hash=sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06
- # via twine
-rfc3986==2.0.0 \
- --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \
- --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c
- # via twine
-rich==13.7.1 \
- --hash=sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222 \
- --hash=sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432
- # via twine
-rsa==4.9 \
- --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \
- --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21
- # via google-auth
-secretstorage==3.3.3 \
- --hash=sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77 \
- --hash=sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99
- # via keyring
-six==1.16.0 \
- --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
- --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
- # via
- # gcp-docuploader
- # python-dateutil
-tomli==2.0.1 \
- --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
- --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
- # via nox
-twine==5.1.1 \
- --hash=sha256:215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997 \
- --hash=sha256:9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db
- # via -r requirements.in
-typing-extensions==4.12.2 \
- --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
- --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
- # via -r requirements.in
-urllib3==2.2.2 \
- --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
- --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
- # via
- # requests
- # twine
-virtualenv==20.26.3 \
- --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \
- --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589
- # via nox
-wheel==0.43.0 \
- --hash=sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85 \
- --hash=sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81
- # via -r requirements.in
-zipp==3.19.2 \
- --hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \
- --hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c
- # via importlib-metadata
-
-# The following packages are considered to be unsafe in a requirements file:
-setuptools==70.2.0 \
- --hash=sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05 \
- --hash=sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1
- # via -r requirements.in
diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.13/common.cfg
similarity index 88%
rename from .kokoro/samples/python3.7/common.cfg
rename to .kokoro/samples/python3.13/common.cfg
index 9156c5975..96783769b 100644
--- a/.kokoro/samples/python3.7/common.cfg
+++ b/.kokoro/samples/python3.13/common.cfg
@@ -10,13 +10,13 @@ action {
# Specify which tests to run
env_vars: {
key: "RUN_TESTS_SESSION"
- value: "py-3.7"
+ value: "py-3.13"
}
# Declare build specific Cloud project.
env_vars: {
key: "BUILD_SPECIFIC_GCLOUD_PROJECT"
- value: "python-docs-samples-tests-py37"
+ value: "python-docs-samples-tests-313"
}
env_vars: {
@@ -37,4 +37,4 @@ gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples"
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
# Use the trampoline script to run in docker.
-build_file: "python-pubsub/.kokoro/trampoline_v2.sh"
\ No newline at end of file
+build_file: "python-pubsub/.kokoro/trampoline_v2.sh"
diff --git a/.kokoro/samples/python3.7/continuous.cfg b/.kokoro/samples/python3.13/continuous.cfg
similarity index 100%
rename from .kokoro/samples/python3.7/continuous.cfg
rename to .kokoro/samples/python3.13/continuous.cfg
diff --git a/.kokoro/samples/python3.7/periodic-head.cfg b/.kokoro/samples/python3.13/periodic-head.cfg
similarity index 100%
rename from .kokoro/samples/python3.7/periodic-head.cfg
rename to .kokoro/samples/python3.13/periodic-head.cfg
diff --git a/.kokoro/samples/python3.7/periodic.cfg b/.kokoro/samples/python3.13/periodic.cfg
similarity index 100%
rename from .kokoro/samples/python3.7/periodic.cfg
rename to .kokoro/samples/python3.13/periodic.cfg
diff --git a/.kokoro/samples/python3.7/presubmit.cfg b/.kokoro/samples/python3.13/presubmit.cfg
similarity index 100%
rename from .kokoro/samples/python3.7/presubmit.cfg
rename to .kokoro/samples/python3.13/presubmit.cfg
diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.14/common.cfg
similarity index 88%
rename from .kokoro/samples/python3.8/common.cfg
rename to .kokoro/samples/python3.14/common.cfg
index 5922bef07..f6feff705 100644
--- a/.kokoro/samples/python3.8/common.cfg
+++ b/.kokoro/samples/python3.14/common.cfg
@@ -10,13 +10,13 @@ action {
# Specify which tests to run
env_vars: {
key: "RUN_TESTS_SESSION"
- value: "py-3.8"
+ value: "py-3.14"
}
# Declare build specific Cloud project.
env_vars: {
key: "BUILD_SPECIFIC_GCLOUD_PROJECT"
- value: "python-docs-samples-tests-py38"
+ value: "python-docs-samples-tests-314"
}
env_vars: {
@@ -37,4 +37,4 @@ gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples"
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
# Use the trampoline script to run in docker.
-build_file: "python-pubsub/.kokoro/trampoline_v2.sh"
\ No newline at end of file
+build_file: "python-pubsub/.kokoro/trampoline_v2.sh"
diff --git a/.kokoro/samples/python3.8/continuous.cfg b/.kokoro/samples/python3.14/continuous.cfg
similarity index 98%
rename from .kokoro/samples/python3.8/continuous.cfg
rename to .kokoro/samples/python3.14/continuous.cfg
index a1c8d9759..b19681787 100644
--- a/.kokoro/samples/python3.8/continuous.cfg
+++ b/.kokoro/samples/python3.14/continuous.cfg
@@ -3,4 +3,4 @@
env_vars: {
key: "INSTALL_LIBRARY_FROM_SOURCE"
value: "True"
-}
\ No newline at end of file
+}
diff --git a/.kokoro/samples/python3.8/periodic-head.cfg b/.kokoro/samples/python3.14/periodic-head.cfg
similarity index 100%
rename from .kokoro/samples/python3.8/periodic-head.cfg
rename to .kokoro/samples/python3.14/periodic-head.cfg
diff --git a/.kokoro/samples/python3.8/periodic.cfg b/.kokoro/samples/python3.14/periodic.cfg
similarity index 100%
rename from .kokoro/samples/python3.8/periodic.cfg
rename to .kokoro/samples/python3.14/periodic.cfg
diff --git a/.kokoro/samples/python3.8/presubmit.cfg b/.kokoro/samples/python3.14/presubmit.cfg
similarity index 98%
rename from .kokoro/samples/python3.8/presubmit.cfg
rename to .kokoro/samples/python3.14/presubmit.cfg
index a1c8d9759..b19681787 100644
--- a/.kokoro/samples/python3.8/presubmit.cfg
+++ b/.kokoro/samples/python3.14/presubmit.cfg
@@ -3,4 +3,4 @@
env_vars: {
key: "INSTALL_LIBRARY_FROM_SOURCE"
value: "True"
-}
\ No newline at end of file
+}
diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh
index 55910c8ba..53e365bc4 100755
--- a/.kokoro/test-samples-impl.sh
+++ b/.kokoro/test-samples-impl.sh
@@ -33,7 +33,8 @@ export PYTHONUNBUFFERED=1
env | grep KOKORO
# Install nox
-python3.9 -m pip install --upgrade --quiet nox
+# `virtualenv==20.26.6` is added for Python 3.7 compatibility
+python3.9 -m pip install --upgrade --quiet nox virtualenv==20.26.6
# Use secrets acessor service account to get secrets
if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then
diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh
index 35fa52923..d03f92dfc 100755
--- a/.kokoro/trampoline_v2.sh
+++ b/.kokoro/trampoline_v2.sh
@@ -26,8 +26,8 @@
# To run this script, first download few files from gcs to /dev/shm.
# (/dev/shm is passed into the container as KOKORO_GFILE_DIR).
#
-# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm
-# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm
+# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm
+# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm
#
# Then run the script.
# .kokoro/trampoline_v2.sh
diff --git a/.librarian/config.yaml b/.librarian/config.yaml
new file mode 100644
index 000000000..111f94dd5
--- /dev/null
+++ b/.librarian/config.yaml
@@ -0,0 +1,6 @@
+global_files_allowlist:
+ # Allow the container to read and write the root `CHANGELOG.md`
+ # file during the `release` step to update the latest client library
+ # versions which are hardcoded in the file.
+ - path: "CHANGELOG.md"
+ permissions: "read-write"
diff --git a/.librarian/generator-input/.repo-metadata.json b/.librarian/generator-input/.repo-metadata.json
new file mode 100644
index 000000000..8d12e4cc0
--- /dev/null
+++ b/.librarian/generator-input/.repo-metadata.json
@@ -0,0 +1,18 @@
+{
+ "name": "pubsub",
+ "name_pretty": "Google Cloud Pub/Sub",
+ "product_documentation": "https://cloud.google.com/pubsub/docs/",
+ "client_documentation": "https://cloud.google.com/python/docs/reference/pubsub/latest",
+ "issue_tracker": "https://issuetracker.google.com/savedsearches/559741",
+ "release_level": "stable",
+ "language": "python",
+ "repo": "googleapis/python-pubsub",
+ "distribution_name": "google-cloud-pubsub",
+ "api_id": "pubsub.googleapis.com",
+ "requires_billing": true,
+ "default_version": "v1",
+ "codeowner_team": "@googleapis/api-pubsub",
+ "api_shortname": "pubsub",
+ "library_type": "GAPIC_COMBO",
+ "api_description": "is designed to provide reliable, many-to-many, asynchronous messaging between applications. Publisher applications can send messages to a topic and other applications can subscribe to that topic to receive the messages. By decoupling senders and receivers, Google Cloud Pub/Sub allows developers to communicate between independently written applications."
+}
diff --git a/owlbot.py b/.librarian/generator-input/librarian.py
similarity index 77%
rename from owlbot.py
rename to .librarian/generator-input/librarian.py
index 204b30ba5..5263c2285 100644
--- a/owlbot.py
+++ b/.librarian/generator-input/librarian.py
@@ -99,6 +99,8 @@
if count < len(clients_to_patch):
raise Exception(err_msg)
+ # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator
+ # code below to test files.
count = s.replace(
clients_to_patch,
r"# initialize with the provided callable or the passed in class",
@@ -106,7 +108,7 @@
emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST")
if emulator_host:
- if issubclass(transport_init, type(self)._transport_registry["grpc"]):
+ if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore
channel = grpc.insecure_channel(target=emulator_host)
else:
channel = grpc.aio.insecure_channel(target=emulator_host)
@@ -324,7 +326,7 @@
if count < 1:
raise Exception(".coveragerc replacement failed.")
- s.move([library], excludes=["**/gapic_version.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"])
+ s.move([library], excludes=["noxfile.py", "README.rst", "docs/**/*", "setup.py"])
s.remove_staging_dirs()
# ----------------------------------------------------------------------------
@@ -334,105 +336,14 @@
templated_files = gcp.CommonTemplates().py_library(
microgenerator=True,
samples=True,
- cov_level=100,
+ cov_level=99,
versions=gcp.common.detect_versions(path="./google", default_first=True),
- unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"],
+ unit_test_python_versions=["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"],
+ unit_test_dependencies=["flaky"],
system_test_python_versions=["3.12"],
- system_test_external_dependencies=["psutil","flaky"],
+ system_test_external_dependencies=["psutil", "flaky"],
)
-s.move(templated_files, excludes=[".coveragerc", ".github/release-please.yml", "README.rst", "docs/index.rst"])
-
-# ----------------------------------------------------------------------------
-# Add mypy nox session.
-# ----------------------------------------------------------------------------
-s.replace(
- "noxfile.py",
- r"LINT_PATHS = \[.*?\]",
- '\g<0>\n\nMYPY_VERSION = "mypy==1.10.0"',
-)
-s.replace(
- "noxfile.py", r'"blacken",', '\g<0>\n "mypy",',
-)
-s.replace(
- "noxfile.py",
- r"nox\.options\.error_on_missing_interpreters = True",
- textwrap.dedent(
- ''' \g<0>
-
-
- @nox.session(python=DEFAULT_PYTHON_VERSION)
- def mypy(session):
- """Run type checks with mypy."""
- session.install("-e", ".[all]")
- session.install(MYPY_VERSION)
-
- # Version 2.1.1 of google-api-core version is the first type-checked release.
- # Version 2.2.0 of google-cloud-core version is the first type-checked release.
- session.install(
- "google-api-core[grpc]>=2.1.1",
- "google-cloud-core>=2.2.0",
- )
-
- # Just install the type info directly, since "mypy --install-types" might
- # require an additional pass.
- # Exclude types-protobuf==4.24.0.20240106
- # See https://github.com/python/typeshed/issues/11254
- session.install("types-protobuf!=4.24.0.20240106", "types-setuptools")
-
- # TODO: Only check the hand-written layer, the generated code does not pass
- # mypy checks yet.
- # https://github.com/googleapis/gapic-generator-python/issues/1092
- session.run("mypy", "-p", "google.cloud")'''
- ),
-)
-
-
-# ----------------------------------------------------------------------------
-# Add mypy_samples nox session.
-# ----------------------------------------------------------------------------
-s.replace(
- "noxfile.py",
- r' "mypy",',
- '\g<0>\n # https://github.com/googleapis/python-pubsub/pull/552#issuecomment-1016256936'
- '\n # "mypy_samples", # TODO: uncomment when the check passes',
-)
-s.replace(
- "noxfile.py",
- r'session\.run\("mypy", "-p", "google.cloud"\)',
- textwrap.dedent(
- ''' \g<0>
-
-
- @nox.session(python=DEFAULT_PYTHON_VERSION)
- def mypy_samples(session):
- """Run type checks with mypy."""
-
- session.install("-e", ".[all]")
-
- session.install("pytest")
- session.install(MYPY_VERSION)
-
- # Just install the type info directly, since "mypy --install-types" might
- # require an additional pass.
- session.install("types-mock", "types-protobuf", "types-setuptools")
-
- session.run(
- "mypy",
- "--config-file",
- str(CURRENT_DIRECTORY / "samples" / "snippets" / "mypy.ini"),
- "--no-incremental", # Required by warn-unused-configs from mypy.ini to work
- "samples/",
- )'''
- ),
-)
-
-
-# Only consider the hand-written layer when assessing the test coverage.
-s.replace(
- "noxfile.py", "--cov=google", "--cov=google/cloud",
-)
-
-s.replace(".github/blunderbuss.yml", "googleapis/api-pubsub", "mukund-ananthu")
+s.move(templated_files, excludes=[".coveragerc", ".github/**", "README.rst", "docs/**", ".kokoro/**"])
python.py_samples(skip_readmes=True)
diff --git a/.librarian/generator-input/noxfile.py b/.librarian/generator-input/noxfile.py
new file mode 100644
index 000000000..170360d6f
--- /dev/null
+++ b/.librarian/generator-input/noxfile.py
@@ -0,0 +1,554 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generated by synthtool. DO NOT EDIT!
+
+from __future__ import absolute_import
+
+import os
+import pathlib
+import re
+import shutil
+from typing import Dict, List
+import warnings
+
+import nox
+
+FLAKE8_VERSION = "flake8==6.1.0"
+BLACK_VERSION = "black[jupyter]==23.7.0"
+ISORT_VERSION = "isort==5.11.0"
+LINT_PATHS = ["google", "tests", "noxfile.py", "setup.py"]
+
+MYPY_VERSION = "mypy==1.10.0"
+
+DEFAULT_PYTHON_VERSION = "3.14"
+
+UNIT_TEST_PYTHON_VERSIONS: List[str] = [
+ "3.9",
+ "3.10",
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+]
+UNIT_TEST_STANDARD_DEPENDENCIES = [
+ "mock",
+ "asyncmock",
+ "pytest",
+ "pytest-cov",
+ "pytest-asyncio",
+]
+UNIT_TEST_EXTERNAL_DEPENDENCIES: List[str] = []
+UNIT_TEST_LOCAL_DEPENDENCIES: List[str] = []
+UNIT_TEST_DEPENDENCIES: List[str] = [
+ "flaky",
+]
+UNIT_TEST_EXTRAS: List[str] = []
+UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {}
+
+SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"]
+SYSTEM_TEST_STANDARD_DEPENDENCIES: List[str] = [
+ "mock",
+ "pytest",
+ "google-cloud-testutils",
+]
+SYSTEM_TEST_EXTERNAL_DEPENDENCIES: List[str] = [
+ "psutil",
+ "flaky",
+]
+SYSTEM_TEST_LOCAL_DEPENDENCIES: List[str] = []
+SYSTEM_TEST_DEPENDENCIES: List[str] = []
+SYSTEM_TEST_EXTRAS: List[str] = []
+SYSTEM_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {}
+
+CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
+
+nox.options.sessions = [
+ "unit",
+ "system",
+ "cover",
+ "lint",
+ "lint_setup_py",
+ "blacken",
+ "mypy",
+ # https://github.com/googleapis/python-pubsub/pull/552#issuecomment-1016256936
+ # "mypy_samples", # TODO: uncomment when the check passes
+ "docs",
+ "docfx",
+ "format",
+]
+
+# Error if a python version is missing
+nox.options.error_on_missing_interpreters = True
+
+
+@nox.session(python=DEFAULT_PYTHON_VERSION)
+def mypy(session):
+ """Run type checks with mypy."""
+ session.install("-e", ".[all]")
+ session.install(MYPY_VERSION)
+
+ # Version 2.1.1 of google-api-core version is the first type-checked release.
+ # Version 2.2.0 of google-cloud-core version is the first type-checked release.
+ session.install(
+ "google-api-core[grpc]>=2.1.1", "google-cloud-core>=2.2.0", "types-requests"
+ )
+
+ # Just install the type info directly, since "mypy --install-types" might
+ # require an additional pass.
+ # Exclude types-protobuf==4.24.0.20240106
+ # See https://github.com/python/typeshed/issues/11254
+ session.install("types-protobuf!=4.24.0.20240106", "types-setuptools")
+
+ # TODO: Only check the hand-written layer, the generated code does not pass
+ # mypy checks yet.
+ # https://github.com/googleapis/gapic-generator-python/issues/1092
+ # TODO: Re-enable mypy checks once we merge, since incremental checks are failing due to protobuf upgrade
+ # session.run("mypy", "-p", "google.cloud", "--exclude", "google/pubsub_v1/")
+
+
+@nox.session(python=DEFAULT_PYTHON_VERSION)
+def mypy_samples(session):
+ """Run type checks with mypy."""
+
+ session.install("-e", ".[all]")
+
+ session.install("pytest")
+ session.install(MYPY_VERSION)
+
+ # Just install the type info directly, since "mypy --install-types" might
+ # require an additional pass.
+ session.install(
+ "types-mock", "types-protobuf", "types-setuptools", "types-requests"
+ )
+
+ session.run(
+ "mypy",
+ "--config-file",
+ str(CURRENT_DIRECTORY / "samples" / "snippets" / "mypy.ini"),
+ "--no-incremental", # Required by warn-unused-configs from mypy.ini to work
+ "samples/",
+ )
+
+
+@nox.session(python=DEFAULT_PYTHON_VERSION)
+def lint(session):
+ """Run linters.
+
+ Returns a failure if the linters find linting errors or sufficiently
+ serious code quality issues.
+ """
+ session.install(FLAKE8_VERSION, BLACK_VERSION)
+ session.run(
+ "black",
+ "--check",
+ *LINT_PATHS,
+ )
+ session.run("flake8", "google", "tests")
+
+
+@nox.session(python=DEFAULT_PYTHON_VERSION)
+def blacken(session):
+ """Run black. Format code to uniform standard."""
+ session.install(BLACK_VERSION)
+ session.run(
+ "black",
+ *LINT_PATHS,
+ )
+
+
+@nox.session(python=DEFAULT_PYTHON_VERSION)
+def format(session):
+ """
+ Run isort to sort imports. Then run black
+ to format code to uniform standard.
+ """
+ session.install(BLACK_VERSION, ISORT_VERSION)
+ # Use the --fss option to sort imports using strict alphabetical order.
+ # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections
+ session.run(
+ "isort",
+ "--fss",
+ *LINT_PATHS,
+ )
+ session.run(
+ "black",
+ *LINT_PATHS,
+ )
+
+
+@nox.session(python=DEFAULT_PYTHON_VERSION)
+def lint_setup_py(session):
+ """Verify that setup.py is valid (including RST check)."""
+ session.install("setuptools", "docutils", "pygments")
+ session.run("python", "setup.py", "check", "--restructuredtext", "--strict")
+
+
+def install_unittest_dependencies(session, *constraints):
+ standard_deps = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_DEPENDENCIES
+ session.install(*standard_deps, *constraints)
+
+ if UNIT_TEST_EXTERNAL_DEPENDENCIES:
+ warnings.warn(
+ "'unit_test_external_dependencies' is deprecated. Instead, please "
+ "use 'unit_test_dependencies' or 'unit_test_local_dependencies'.",
+ DeprecationWarning,
+ )
+ session.install(*UNIT_TEST_EXTERNAL_DEPENDENCIES, *constraints)
+
+ if UNIT_TEST_LOCAL_DEPENDENCIES:
+ session.install(*UNIT_TEST_LOCAL_DEPENDENCIES, *constraints)
+
+ if UNIT_TEST_EXTRAS_BY_PYTHON:
+ extras = UNIT_TEST_EXTRAS_BY_PYTHON.get(session.python, [])
+ elif UNIT_TEST_EXTRAS:
+ extras = UNIT_TEST_EXTRAS
+ else:
+ extras = []
+
+ if extras:
+ session.install("-e", f".[{','.join(extras)}]", *constraints)
+ else:
+ session.install("-e", ".", *constraints)
+
+
+@nox.session(python=UNIT_TEST_PYTHON_VERSIONS)
+@nox.parametrize(
+ "protobuf_implementation",
+ ["python", "upb", "cpp"],
+)
+def unit(session, protobuf_implementation):
+ # Install all test dependencies, then install this package in-place.
+
+ if protobuf_implementation == "cpp" and session.python in (
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+ ):
+ session.skip("cpp implementation is not supported in python 3.11+")
+
+ constraints_path = str(
+ CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
+ )
+ install_unittest_dependencies(session, "-c", constraints_path)
+
+ # TODO(https://github.com/googleapis/synthtool/issues/1976):
+ # Remove the 'cpp' implementation once support for Protobuf 3.x is dropped.
+ # The 'cpp' implementation requires Protobuf<4.
+ if protobuf_implementation == "cpp":
+ session.install("protobuf<4")
+
+ # Run py.test against the unit tests.
+ session.run(
+ "py.test",
+ "--quiet",
+ f"--junitxml=unit_{session.python}_sponge_log.xml",
+ "--cov=google/cloud",
+ "--cov=tests/unit",
+ "--cov-append",
+ "--cov-config=.coveragerc",
+ "--cov-report=",
+ "--cov-fail-under=0",
+ os.path.join("tests", "unit"),
+ *session.posargs,
+ env={
+ "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
+ },
+ )
+
+
+def install_systemtest_dependencies(session, *constraints):
+ # Use pre-release gRPC for system tests.
+ # Exclude version 1.52.0rc1 which has a known issue.
+ # See https://github.com/grpc/grpc/issues/32163
+ session.install("--pre", "grpcio!=1.52.0rc1")
+
+ session.install(*SYSTEM_TEST_STANDARD_DEPENDENCIES, *constraints)
+
+ if SYSTEM_TEST_EXTERNAL_DEPENDENCIES:
+ session.install(*SYSTEM_TEST_EXTERNAL_DEPENDENCIES, *constraints)
+
+ if SYSTEM_TEST_LOCAL_DEPENDENCIES:
+ session.install("-e", *SYSTEM_TEST_LOCAL_DEPENDENCIES, *constraints)
+
+ if SYSTEM_TEST_DEPENDENCIES:
+ session.install("-e", *SYSTEM_TEST_DEPENDENCIES, *constraints)
+
+ if SYSTEM_TEST_EXTRAS_BY_PYTHON:
+ extras = SYSTEM_TEST_EXTRAS_BY_PYTHON.get(session.python, [])
+ elif SYSTEM_TEST_EXTRAS:
+ extras = SYSTEM_TEST_EXTRAS
+ else:
+ extras = []
+
+ if extras:
+ session.install("-e", f".[{','.join(extras)}]", *constraints)
+ else:
+ session.install("-e", ".", *constraints)
+
+
+@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
+def system(session):
+ """Run the system test suite."""
+ constraints_path = str(
+ CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
+ )
+ system_test_path = os.path.join("tests", "system.py")
+ system_test_folder_path = os.path.join("tests", "system")
+
+ # Check the value of `RUN_SYSTEM_TESTS` env var. It defaults to true.
+ if os.environ.get("RUN_SYSTEM_TESTS", "true") == "false":
+ session.skip("RUN_SYSTEM_TESTS is set to false, skipping")
+ # 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)
+ # Sanity check: only run tests if found.
+ if not system_test_exists and not system_test_folder_exists:
+ session.skip("System tests were not found")
+
+ install_systemtest_dependencies(session, "-c", constraints_path)
+
+ # Run py.test against the system tests.
+ if system_test_exists:
+ session.run(
+ "py.test",
+ "--verbose",
+ f"--junitxml=system_{session.python}_sponge_log.xml",
+ system_test_path,
+ *session.posargs,
+ )
+ if os.path.exists(system_test_folder_path):
+ session.run(
+ "py.test",
+ "--verbose",
+ f"--junitxml=system_{session.python}_sponge_log.xml",
+ system_test_folder_path,
+ *session.posargs,
+ )
+
+
+@nox.session(python=DEFAULT_PYTHON_VERSION)
+def cover(session):
+ """Run the final coverage report.
+
+ This outputs the coverage report aggregating coverage from the unit
+ test runs (not system test runs), and then erases coverage data.
+ """
+ session.install("coverage", "pytest-cov")
+ session.run("coverage", "report", "--show-missing", "--fail-under=99")
+
+ session.run("coverage", "erase")
+
+
+# py > 3.10 not supported yet
+@nox.session(python="3.10")
+def docs(session):
+ """Build the docs for this library."""
+
+ session.install("-e", ".")
+ session.install(
+ # We need to pin to specific versions of the `sphinxcontrib-*` packages
+ # which still support sphinx 4.x.
+ # See https://github.com/googleapis/sphinx-docfx-yaml/issues/344
+ # and https://github.com/googleapis/sphinx-docfx-yaml/issues/345.
+ "sphinxcontrib-applehelp==1.0.4",
+ "sphinxcontrib-devhelp==1.0.2",
+ "sphinxcontrib-htmlhelp==2.0.1",
+ "sphinxcontrib-qthelp==1.0.3",
+ "sphinxcontrib-serializinghtml==1.1.5",
+ "sphinx==4.5.0",
+ "alabaster",
+ "recommonmark",
+ )
+
+ shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True)
+ session.run(
+ "sphinx-build",
+ "-W", # warnings as errors
+ "-T", # show full traceback on exception
+ "-N", # no colors
+ "-b",
+ "html",
+ "-d",
+ os.path.join("docs", "_build", "doctrees", ""),
+ os.path.join("docs", ""),
+ os.path.join("docs", "_build", "html", ""),
+ )
+
+
+# py > 3.10 not supported yet
+@nox.session(python="3.10")
+def docfx(session):
+ """Build the docfx yaml files for this library."""
+
+ session.install("-e", ".")
+ session.install(
+ # We need to pin to specific versions of the `sphinxcontrib-*` packages
+ # which still support sphinx 4.x.
+ # See https://github.com/googleapis/sphinx-docfx-yaml/issues/344
+ # and https://github.com/googleapis/sphinx-docfx-yaml/issues/345.
+ "sphinxcontrib-applehelp==1.0.4",
+ "sphinxcontrib-devhelp==1.0.2",
+ "sphinxcontrib-htmlhelp==2.0.1",
+ "sphinxcontrib-qthelp==1.0.3",
+ "sphinxcontrib-serializinghtml==1.1.5",
+ "gcp-sphinx-docfx-yaml",
+ "alabaster",
+ "recommonmark",
+ )
+
+ shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True)
+ session.run(
+ "sphinx-build",
+ "-T", # show full traceback on exception
+ "-N", # no colors
+ "-D",
+ (
+ "extensions=sphinx.ext.autodoc,"
+ "sphinx.ext.autosummary,"
+ "docfx_yaml.extension,"
+ "sphinx.ext.intersphinx,"
+ "sphinx.ext.coverage,"
+ "sphinx.ext.napoleon,"
+ "sphinx.ext.todo,"
+ "sphinx.ext.viewcode,"
+ "recommonmark"
+ ),
+ "-b",
+ "html",
+ "-d",
+ os.path.join("docs", "_build", "doctrees", ""),
+ os.path.join("docs", ""),
+ os.path.join("docs", "_build", "html", ""),
+ )
+
+
+@nox.session(python="3.14")
+@nox.parametrize(
+ "protobuf_implementation",
+ ["python", "upb", "cpp"],
+)
+def prerelease_deps(session, protobuf_implementation):
+ """Run all tests with prerelease versions of dependencies installed."""
+
+ if protobuf_implementation == "cpp" and session.python in (
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+ ):
+ session.skip("cpp implementation is not supported in python 3.11+")
+
+ # Install all dependencies
+ session.install("-e", ".[all, tests, tracing]")
+ unit_deps_all = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_EXTERNAL_DEPENDENCIES
+ session.install(*unit_deps_all)
+ system_deps_all = (
+ SYSTEM_TEST_STANDARD_DEPENDENCIES + SYSTEM_TEST_EXTERNAL_DEPENDENCIES
+ )
+ session.install(*system_deps_all)
+
+ # Because we test minimum dependency versions on the minimum Python
+ # version, the first version we test with in the unit tests sessions has a
+ # constraints file containing all dependencies and extras.
+ with open(
+ CURRENT_DIRECTORY
+ / "testing"
+ / f"constraints-{UNIT_TEST_PYTHON_VERSIONS[0]}.txt",
+ encoding="utf-8",
+ ) as constraints_file:
+ constraints_text = constraints_file.read()
+
+ # Ignore leading whitespace and comment lines.
+ constraints_deps = [
+ match.group(1)
+ for match in re.finditer(
+ r"^\s*(\S+)(?===\S+)", constraints_text, flags=re.MULTILINE
+ )
+ ]
+
+ session.install(*constraints_deps)
+
+ prerel_deps = [
+ "protobuf",
+ # dependency of grpc
+ "six",
+ "grpc-google-iam-v1",
+ "googleapis-common-protos",
+ "grpcio",
+ "grpcio-status",
+ "google-api-core",
+ "google-auth",
+ "proto-plus",
+ "google-cloud-testutils",
+ # dependencies of google-cloud-testutils"
+ "click",
+ ]
+
+ for dep in prerel_deps:
+ session.install("--pre", "--no-deps", "--upgrade", dep)
+
+ # Remaining dependencies
+ other_deps = [
+ "requests",
+ ]
+ session.install(*other_deps)
+
+ # Print out prerelease package versions
+ session.run(
+ "python", "-c", "import google.protobuf; print(google.protobuf.__version__)"
+ )
+ session.run("python", "-c", "import grpc; print(grpc.__version__)")
+ session.run("python", "-c", "import google.auth; print(google.auth.__version__)")
+
+ session.run(
+ "py.test",
+ "tests/unit",
+ env={
+ "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
+ },
+ )
+
+ system_test_path = os.path.join("tests", "system.py")
+ system_test_folder_path = os.path.join("tests", "system")
+
+ # Only run system tests if found.
+ if os.path.exists(system_test_path):
+ session.run(
+ "py.test",
+ "--verbose",
+ f"--junitxml=system_{session.python}_sponge_log.xml",
+ system_test_path,
+ *session.posargs,
+ env={
+ "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
+ },
+ )
+ if os.path.exists(system_test_folder_path):
+ session.run(
+ "py.test",
+ "--verbose",
+ f"--junitxml=system_{session.python}_sponge_log.xml",
+ system_test_folder_path,
+ *session.posargs,
+ env={
+ "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
+ },
+ )
diff --git a/.librarian/generator-input/setup.py b/.librarian/generator-input/setup.py
new file mode 100644
index 000000000..761bc90ce
--- /dev/null
+++ b/.librarian/generator-input/setup.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import io
+import os
+
+import setuptools # type: ignore
+
+package_root = os.path.abspath(os.path.dirname(__file__))
+
+name = "google-cloud-pubsub"
+
+
+description = "Google Cloud Pub/Sub API client library"
+
+version = {}
+with open(os.path.join(package_root, "google/pubsub/gapic_version.py")) as fp:
+ exec(fp.read(), version)
+version = version["__version__"]
+
+if version[0] == "0":
+ release_status = "Development Status :: 4 - Beta"
+else:
+ release_status = "Development Status :: 5 - Production/Stable"
+
+dependencies = [
+ "grpcio >= 1.51.3, < 2.0.0; python_version < '3.14'", # https://github.com/googleapis/python-pubsub/issues/609
+ "grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
+ # google-api-core >= 1.34.0 is allowed in order to support google-api-core 1.x
+ "google-auth >= 2.14.1, <3.0.0",
+ "google-api-core[grpc] >= 1.34.0, <3.0.0,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*",
+ "proto-plus >= 1.22.0, <2.0.0",
+ "proto-plus >= 1.22.2, <2.0.0; python_version>='3.11'",
+ "proto-plus >= 1.25.0, < 2.0.0; python_version >= '3.13'",
+ "protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5",
+ "grpc-google-iam-v1 >= 0.12.4, < 1.0.0",
+ "grpcio-status >= 1.33.2",
+ "opentelemetry-api >= 1.27.0",
+ "opentelemetry-sdk >= 1.27.0",
+]
+extras = {"libcst": "libcst >= 0.3.10"}
+url = "https://github.com/googleapis/python-pubsub"
+
+package_root = os.path.abspath(os.path.dirname(__file__))
+
+readme_filename = os.path.join(package_root, "README.rst")
+with io.open(readme_filename, encoding="utf-8") as readme_file:
+ readme = readme_file.read()
+
+packages = [
+ package
+ for package in setuptools.find_namespace_packages()
+ if package.startswith("google")
+]
+
+setuptools.setup(
+ name=name,
+ version=version,
+ description=description,
+ long_description=readme,
+ author="Google LLC",
+ author_email="googleapis-packages@google.com",
+ license="Apache 2.0",
+ url=url,
+ classifiers=[
+ release_status,
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Apache Software License",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+ "Operating System :: OS Independent",
+ "Topic :: Internet",
+ ],
+ platforms="Posix; MacOS X; Windows",
+ packages=packages,
+ install_requires=dependencies,
+ extras_require=extras,
+ python_requires=">=3.9",
+ include_package_data=True,
+ zip_safe=False,
+)
diff --git a/.librarian/state.yaml b/.librarian/state.yaml
new file mode 100644
index 000000000..2d5b23ac6
--- /dev/null
+++ b/.librarian/state.yaml
@@ -0,0 +1,37 @@
+image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
+libraries:
+ - id: google-cloud-pubsub
+ version: 2.35.0
+ last_generated_commit: 2d0b3a154fb4f56993487c5409850ad3431a2690
+ apis:
+ - path: google/pubsub/v1
+ service_config: pubsub_v1.yaml
+ source_roots:
+ - .
+ preserve_regex: []
+ remove_regex:
+ - ^google/pubsub
+ - ^google/pubsub_v1
+ - ^tests/unit/gapic
+ - ^tests/__init__.py
+ - ^tests/unit/__init__.py
+ - ^.coveragerc
+ - ^.flake8
+ - ^.pre-commit-config.yaml
+ - ^.repo-metadata.json
+ - ^.trampolinerc
+ - ^LICENSE
+ - ^MANIFEST.in
+ - ^SECURITY.md
+ - ^mypy.ini
+ - ^noxfile.py
+ - ^owlbot.py
+ - ^renovate.json
+ - ^samples/AUTHORING_GUIDE.md
+ - ^samples/CONTRIBUTING.md
+ - ^samples/generated_samples
+ - ^scripts/fixup_pubsub_v1_keywords.py
+ - ^setup.py
+ - ^testing/constraints-3.9
+ - ^testing/constraints-3.1
+ tag_format: v{version}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
deleted file mode 100644
index b6be5b944..000000000
--- a/.release-please-manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- ".": "2.23.0"
-}
-
\ No newline at end of file
diff --git a/.repo-metadata.json b/.repo-metadata.json
index 8d12e4cc0..f65318dba 100644
--- a/.repo-metadata.json
+++ b/.repo-metadata.json
@@ -11,7 +11,7 @@
"api_id": "pubsub.googleapis.com",
"requires_billing": true,
"default_version": "v1",
- "codeowner_team": "@googleapis/api-pubsub",
+ "codeowner_team": "@googleapis/cloud-sdk-python-team @googleapis/pubsub-team",
"api_shortname": "pubsub",
"library_type": "GAPIC_COMBO",
"api_description": "is designed to provide reliable, many-to-many, asynchronous messaging between applications. Publisher applications can send messages to a topic and other applications can subscribe to that topic to receive the messages. By decoupling senders and receivers, Google Cloud Pub/Sub allows developers to communicate between independently written applications."
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a410d0dc4..bef0c4c9e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,228 @@
[1]: https://pypi.org/project/google-cloud-pubsub/#history
+## [2.35.0](https://github.com/googleapis/python-pubsub/compare/v2.34.0...v2.35.0) (2026-02-05)
+
+
+### Documentation
+
+* A comment for field `topic` in message `.google.pubsub.v1.Subscription` is updated ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46))
+* A comment for field `analytics_hub_subscription_info` in message `.google.pubsub.v1.Subscription` is updated ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46))
+* A comment for field `subscription` in message `.google.pubsub.v1.CreateSnapshotRequest` is updated ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46))
+* add tags documentation links to Pub/Sub resource tags fields ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46))
+* Add the IDENTIFIER field behavior annotation to fields of Cloud Pub/Sub methods that represent a specific identity and need to be sourced with additional care ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46))
+
+
+### Features
+
+* Add AIInference MessageTransform type ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46))
+
+## [2.34.0](https://github.com/googleapis/python-pubsub/compare/v2.33.0...v2.34.0) (2025-12-16)
+
+
+### Features
+
+* support mTLS certificates when available (#1566) ([24761a2fedeb17f5af98a72a62306ad59306a553](https://github.com/googleapis/python-pubsub/commit/24761a2fedeb17f5af98a72a62306ad59306a553))
+
+
+## [2.33.0](https://github.com/googleapis/python-pubsub/compare/v2.32.0...v2.33.0) (2025-10-30)
+
+
+### Features
+
+* Add AwsKinesisFailureReason.ApiViolationReason ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19))
+* Add tags to Subscription, Topic, and CreateSnapshotRequest messages for use in CreateSubscription, CreateTopic, and CreateSnapshot requests respectively ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19))
+* Annotate some resource fields with their corresponding API types ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19))
+
+
+### Bug Fixes
+
+* Deprecate credentials_file argument ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19))
+
+
+### Documentation
+
+* A comment for field `received_messages` in message `.google.pubsub.v1.StreamingPullResponse` is changed ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19))
+
+## [2.32.0](https://github.com/googleapis/python-pubsub/compare/v2.31.1...v2.32.0) (2025-10-28)
+
+
+### Features
+
+* Adds Python 3.14 support ([#1512](https://github.com/googleapis/python-pubsub/issues/1512)) ([95a2690](https://github.com/googleapis/python-pubsub/commit/95a26907efecfa5d56b140b7f833640b7fbb21d7))
+* Debug logs ([#1460](https://github.com/googleapis/python-pubsub/issues/1460)) ([b5d4a45](https://github.com/googleapis/python-pubsub/commit/b5d4a458ca9319bebbe3142a1f05d4d4471c8d4d))
+* Support the protocol version in StreamingPullRequest ([#1455](https://github.com/googleapis/python-pubsub/issues/1455)) ([e6294a1](https://github.com/googleapis/python-pubsub/commit/e6294a1883abf9809cb56d5cd4ad25cc501bc994))
+
+
+### Bug Fixes
+
+* Ignore future warnings on python versions ([#1546](https://github.com/googleapis/python-pubsub/issues/1546)) ([8e28dea](https://github.com/googleapis/python-pubsub/commit/8e28dea5b68fc940266d0b1a9f2a07a7b5f10b34))
+
+## [2.31.1](https://github.com/googleapis/python-pubsub/compare/v2.31.0...v2.31.1) (2025-07-28)
+
+
+### Bug Fixes
+
+* Change Log Severities for Terminated Streams ([#1433](https://github.com/googleapis/python-pubsub/issues/1433)) ([3a3aa79](https://github.com/googleapis/python-pubsub/commit/3a3aa79040d656a3391a153386ec662d002f9368))
+* Propagate Otel Context to Subscriber Callback if Provided ([#1429](https://github.com/googleapis/python-pubsub/issues/1429)) ([b0f6f49](https://github.com/googleapis/python-pubsub/commit/b0f6f49f65752e88523f9c4209366d2a18140416))
+
+## [2.31.0](https://github.com/googleapis/python-pubsub/compare/v2.30.0...v2.31.0) (2025-06-26)
+
+
+### Features
+
+* Add MessageTransformationFailureReason to IngestionFailureEvent ([#1427](https://github.com/googleapis/python-pubsub/issues/1427)) ([8ab13e1](https://github.com/googleapis/python-pubsub/commit/8ab13e1b71c151f0146548e7224dd38c9d719a88))
+
+
+### Bug Fixes
+
+* Surface Fatal Stream Errors to Future; Adjust Retryable Error Codes ([#1422](https://github.com/googleapis/python-pubsub/issues/1422)) ([e081beb](https://github.com/googleapis/python-pubsub/commit/e081beb29056035304d365ec9c50fa7ffbac6886))
+
+## [2.30.0](https://github.com/googleapis/python-pubsub/compare/v2.29.1...v2.30.0) (2025-06-07)
+
+
+### Features
+
+* Add SchemaViolationReason to IngestionFailureEvent ([#1411](https://github.com/googleapis/python-pubsub/issues/1411)) ([c046ca2](https://github.com/googleapis/python-pubsub/commit/c046ca22e9bddff6b50f7670bf6b9b9470bf78e8))
+
+## [2.29.1](https://github.com/googleapis/python-pubsub/compare/v2.29.0...v2.29.1) (2025-05-23)
+
+
+### Bug Fixes
+
+* Remove setup.cfg configuration for creating universal wheels ([#1376](https://github.com/googleapis/python-pubsub/issues/1376)) ([60639c4](https://github.com/googleapis/python-pubsub/commit/60639c4928105ae8a72c8e37b1f48f75cc2ffcc3))
+
+
+### Documentation
+
+* **sample:** Add samples for topic and subscription SMTs ([#1386](https://github.com/googleapis/python-pubsub/issues/1386)) ([4d072e0](https://github.com/googleapis/python-pubsub/commit/4d072e088b59f692dc3d59c3197a2993c125917e))
+* Update documentation for JavaScriptUDF to indicate that the `message_id` metadata field is optional instead of required ([#1380](https://github.com/googleapis/python-pubsub/issues/1380)) ([be90054](https://github.com/googleapis/python-pubsub/commit/be9005412fea06bea917c8b6861546b7e6c62a1e))
+* Update readme links ([#1409](https://github.com/googleapis/python-pubsub/issues/1409)) ([77ba05d](https://github.com/googleapis/python-pubsub/commit/77ba05d4ba5b84a25c1a07c5397bbc184fa6041d))
+
+## [2.29.0](https://github.com/googleapis/python-pubsub/compare/v2.28.0...v2.29.0) (2025-03-19)
+
+
+### Features
+
+* Add REST Interceptors which support reading metadata ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063))
+* Add support for opt-in debug logging ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063))
+* Deprecate `enabled` field for message transforms and add `disabled` field ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063))
+
+
+### Bug Fixes
+
+* Allow logs to propagate upstream for caplog testing ([#1374](https://github.com/googleapis/python-pubsub/issues/1374)) ([fa39b0e](https://github.com/googleapis/python-pubsub/commit/fa39b0e87695da40036c1daec1b3108374672d61))
+* Allow Protobuf 6.x ([#1369](https://github.com/googleapis/python-pubsub/issues/1369)) ([c95b7a5](https://github.com/googleapis/python-pubsub/commit/c95b7a5bad7138a70e56c278970f5b54939a68f8))
+* Fix typing issue with gRPC metadata when key ends in -bin ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063))
+
+
+### Documentation
+
+* A comment for field `code` in message `.google.pubsub.v1.JavaScriptUDF` is changed ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063))
+* Add samples and test for ingestion from Kafka sources ([#1354](https://github.com/googleapis/python-pubsub/issues/1354)) ([820f986](https://github.com/googleapis/python-pubsub/commit/820f986104ca39fd0c92ba6816319e939be1ed63))
+* Deprecate `enabled` field for message transforms and add `disabled` field ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063))
+* **samples:** Increase example max_bytes setting for cloud storage subscriptions to encourage more performant subscribe ([#1324](https://github.com/googleapis/python-pubsub/issues/1324)) ([cb760a7](https://github.com/googleapis/python-pubsub/commit/cb760a71cd4ad035d0c2c4c0f7b66bf52f18808c))
+
+## [2.28.0](https://github.com/googleapis/python-pubsub/compare/v2.27.3...v2.28.0) (2025-01-30)
+
+
+### Features
+
+* Add support for message transforms to Topic and Subscription ([#1274](https://github.com/googleapis/python-pubsub/issues/1274)) ([e5e2f3f](https://github.com/googleapis/python-pubsub/commit/e5e2f3f732f451d14dfb4c37ae979e5c04045305))
+
+
+### Bug Fixes
+
+* Get channel target for a gRPC request ([#1339](https://github.com/googleapis/python-pubsub/issues/1339)) ([16ea766](https://github.com/googleapis/python-pubsub/commit/16ea76611d121700a3f3119d18919063d12c81c1))
+* Set creds only if transport not provided ([#1348](https://github.com/googleapis/python-pubsub/issues/1348)) ([59965a4](https://github.com/googleapis/python-pubsub/commit/59965a4804a434467a47815cdbdd5ce31bbb3662))
+
+## [2.27.3](https://github.com/googleapis/python-pubsub/compare/v2.27.2...v2.27.3) (2025-01-24)
+
+
+### Bug Fixes
+
+* Stop using api_core default timeouts in publish since they are broken ([#1326](https://github.com/googleapis/python-pubsub/issues/1326)) ([ba2c2ee](https://github.com/googleapis/python-pubsub/commit/ba2c2eef7da89a3c14c14d9b6191cd8738c30341))
+
+## [2.27.2](https://github.com/googleapis/python-pubsub/compare/v2.27.1...v2.27.2) (2025-01-06)
+
+
+### Bug Fixes
+
+* Handle TransportError Exceptions thrown from gapic_publish ([#1318](https://github.com/googleapis/python-pubsub/issues/1318)) ([0e058c7](https://github.com/googleapis/python-pubsub/commit/0e058c73487384100847adcb2f4ab95a61c072c4))
+
+## [2.27.1](https://github.com/googleapis/python-pubsub/compare/v2.27.0...v2.27.1) (2024-11-08)
+
+
+### Bug Fixes
+
+* Add support for Python3.13 ([#1302](https://github.com/googleapis/python-pubsub/issues/1302)) ([ab22e27](https://github.com/googleapis/python-pubsub/commit/ab22e27954450b4e06ec98fe2e3458056aa8ca60))
+
+## [2.27.0](https://github.com/googleapis/python-pubsub/compare/v2.26.1...v2.27.0) (2024-11-02)
+
+
+### Features
+
+* Add support for Python 3.13 ([#1281](https://github.com/googleapis/python-pubsub/issues/1281)) ([0b46a33](https://github.com/googleapis/python-pubsub/commit/0b46a3321d6f19cd72e4f2ccdba73d062c7bd832))
+
+
+### Bug Fixes
+
+* Mark test_streaming_pull_max_messages flaky ([#1288](https://github.com/googleapis/python-pubsub/issues/1288)) ([d6635a0](https://github.com/googleapis/python-pubsub/commit/d6635a00dc2c614dd8608ef32ad4e79f9124e040))
+
+## [2.26.1](https://github.com/googleapis/python-pubsub/compare/v2.26.0...v2.26.1) (2024-10-10)
+
+
+### Documentation
+
+* Add ingestion from GCS sample ([#1273](https://github.com/googleapis/python-pubsub/issues/1273)) ([b59cc8d](https://github.com/googleapis/python-pubsub/commit/b59cc8d4fae593eb7592455a1696d7ab996a53dd))
+
+## [2.26.0](https://github.com/googleapis/python-pubsub/compare/v2.25.2...v2.26.0) (2024-10-09)
+
+
+### Features
+
+* Add ingestion Cloud Storage fields and Platform Logging fields to Topic ([#1248](https://github.com/googleapis/python-pubsub/issues/1248)) ([a7a4caa](https://github.com/googleapis/python-pubsub/commit/a7a4caaa5a73e9b15369471dc892688e24bf52e0))
+
+## [2.25.2](https://github.com/googleapis/python-pubsub/compare/v2.25.1...v2.25.2) (2024-09-30)
+
+
+### Documentation
+
+* Add command line args for OpenTelemetry Subscribe sample ([#1265](https://github.com/googleapis/python-pubsub/issues/1265)) ([0ff7f2a](https://github.com/googleapis/python-pubsub/commit/0ff7f2a64b5aa1b0e014e0933e4edaef0fb3f222))
+
+## [2.25.1](https://github.com/googleapis/python-pubsub/compare/v2.25.0...v2.25.1) (2024-09-29)
+
+
+### Bug Fixes
+
+* Update the requirements.txt for samples directory ([#1263](https://github.com/googleapis/python-pubsub/issues/1263)) ([5cce8b1](https://github.com/googleapis/python-pubsub/commit/5cce8b103ab7085613b7ee0efb5c8342d41ebae1))
+
+## [2.25.0](https://github.com/googleapis/python-pubsub/compare/v2.24.0...v2.25.0) (2024-09-28)
+
+
+### Features
+
+* Add OpenTelemetry publish sample ([#1258](https://github.com/googleapis/python-pubsub/issues/1258)) ([bc13ff0](https://github.com/googleapis/python-pubsub/commit/bc13ff05c3d1104c17169c360bdc09340430da37))
+
+## [2.24.0](https://github.com/googleapis/python-pubsub/compare/v2.23.1...v2.24.0) (2024-09-24)
+
+
+### Features
+
+* Add OpenTelemetry support for Subscribe Side ([#1252](https://github.com/googleapis/python-pubsub/issues/1252)) ([1b6f3d2](https://github.com/googleapis/python-pubsub/commit/1b6f3d284095e138943576de8551df263f73a506))
+* Open Telemetry Publish Side Support ([#1241](https://github.com/googleapis/python-pubsub/issues/1241)) ([bb5f3d1](https://github.com/googleapis/python-pubsub/commit/bb5f3d1a7df2d661cccc336edc8eceb2161c6921))
+
+
+### Bug Fixes
+
+* Fix flaky test ([#1254](https://github.com/googleapis/python-pubsub/issues/1254)) ([1ae49de](https://github.com/googleapis/python-pubsub/commit/1ae49de09996a5cf19f592f996c46e0222d540fc))
+
+## [2.23.1](https://github.com/googleapis/python-pubsub/compare/v2.23.0...v2.23.1) (2024-09-09)
+
+
+### Bug Fixes
+
+* Replace asserts with None checks for graceful shutdown ([#1244](https://github.com/googleapis/python-pubsub/issues/1244)) ([ced4f52](https://github.com/googleapis/python-pubsub/commit/ced4f527c7f918a87d1b89c2b5da59dbdf00e2c3))
## [2.23.0](https://github.com/googleapis/python-pubsub/compare/v2.22.0...v2.23.0) (2024-07-29)
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 727b5ec7f..4e926536b 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -22,7 +22,7 @@ In order to add a feature:
documentation.
- The feature must work fully on the following CPython versions:
- 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12 on both UNIX and Windows.
+ 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows.
- The feature must not add unnecessary dependencies (where
"unnecessary" is of course subjective, but new dependencies should
@@ -72,7 +72,7 @@ We use `nox `__ to instrument our tests.
- To run a single unit test::
- $ nox -s unit-3.12 -- -k
+ $ nox -s unit-3.13 -- -k
.. note::
@@ -195,11 +195,11 @@ configure them just like the System Tests.
# Run all tests in a folder
$ cd samples/snippets
- $ nox -s py-3.8
+ $ nox -s py-3.14
# Run a single sample test
$ cd samples/snippets
- $ nox -s py-3.8 -- -k
+ $ nox -s py-3.14 -- -k
********************************************
Note About ``README`` as it pertains to PyPI
@@ -221,19 +221,19 @@ Supported Python Versions
We support:
-- `Python 3.7`_
-- `Python 3.8`_
- `Python 3.9`_
- `Python 3.10`_
- `Python 3.11`_
- `Python 3.12`_
+- `Python 3.13`_
+- `Python 3.14`_
-.. _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/
.. _Python 3.10: https://docs.python.org/3.10/
.. _Python 3.11: https://docs.python.org/3.11/
.. _Python 3.12: https://docs.python.org/3.12/
+.. _Python 3.13: https://docs.python.org/3.13/
+.. _Python 3.14: https://docs.python.org/3.14/
Supported versions can be found in our ``noxfile.py`` `config`_.
diff --git a/MANIFEST.in b/MANIFEST.in
index d6814cd60..dae249ec8 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,25 +1,20 @@
# -*- coding: utf-8 -*-
-#
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# https://www.apache.org/licenses/LICENSE-2.0
+# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-# Generated by synthtool. DO NOT EDIT!
+#
include README.rst LICENSE
-recursive-include google *.json *.proto py.typed
+recursive-include google *.py *.pyi *.json *.proto py.typed
recursive-include tests *
global-exclude *.py[co]
global-exclude __pycache__
-
-# Exclude scripts for samples readmegen
-prune scripts/readme-gen
diff --git a/README.rst b/README.rst
index dd3032e00..bf78f1635 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,12 @@
+:**NOTE**: **This github repository is archived. The repository contents and history have moved to** `google-cloud-python`_.
+
+.. _google-cloud-python: https://github.com/googleapis/google-cloud-python/tree/main/packages/google-cloud-pubsub
+
+
Python Client for Google Cloud Pub / Sub
========================================
-|GA| |pypi| |versions|
+|GA| |pypi| |versions|
`Google Cloud Pub / Sub`_ is a fully-managed real-time messaging service that
allows you to send and receive messages between independent applications. You
@@ -27,7 +32,7 @@ independently written applications.
:target: https://pypi.org/project/google-cloud-pubsub/
.. _Google Cloud Pub / Sub: https://cloud.google.com/pubsub/
.. _Product Documentation: https://cloud.google.com/pubsub/docs
-.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/pubsub/latest
+.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/summary_overview
Quick Start
-----------
@@ -60,11 +65,13 @@ dependencies.
Supported Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^
-Python >= 3.7
+Python >= 3.9
Deprecated Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^^
-Python <= 3.6.
+Python < 3.9
+
+The last version of this library compatible with Python 3.7 and 3.8 is google-cloud-pubsub==2.34.0.
The last version of this library compatible with Python 2.7 is google-cloud-pubsub==1.7.0.
@@ -116,7 +123,7 @@ messages to it
To learn more, consult the `publishing documentation`_.
-.. _publishing documentation: https://cloud.google.com/python/docs/reference/pubsub/latest
+.. _publishing documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/google.cloud.pubsub_v1.publisher.client.Client
Subscribing
@@ -146,7 +153,7 @@ the topic, and subscribe to that, passing a callback function.
with pubsub_v1.SubscriberClient() as subscriber:
subscriber.create_subscription(
- name=subscription_name, topic=topic_name)
+ name=subscription_name, topic=topic_name)
future = subscriber.subscribe(subscription_name, callback)
The future returned by the call to ``subscriber.subscribe`` can be used to
@@ -162,7 +169,7 @@ block the current thread until a given condition obtains:
It is also possible to pull messages in a synchronous (blocking) fashion. To
learn more about subscribing, consult the `subscriber documentation`_.
-.. _subscriber documentation: https://cloud.google.com/python/docs/reference/pubsub/latest
+.. _subscriber documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/google.cloud.pubsub_v1.subscriber.client.Client
Authentication
@@ -190,7 +197,7 @@ For example, to use JSON Web Tokens, provide a `google.auth.jwt.Credentials`_ in
# The same for the publisher, except that the "audience" claim needs to be adjusted
publisher_audience = "https://pubsub.googleapis.com/google.pubsub.v1.Publisher"
- credentials_pub = credentials.with_claims(audience=publisher_audience)
+ credentials_pub = credentials.with_claims(audience=publisher_audience)
publisher = pubsub_v1.PublisherClient(credentials=credentials_pub)
.. _Credentials: https://google-auth.readthedocs.io/en/latest/reference/google.auth.credentials.html#google.auth.credentials.Credentials
diff --git a/setup.cfg b/google/cloud/pubsub_v1/open_telemetry/__init__.py
similarity index 74%
rename from setup.cfg
rename to google/cloud/pubsub_v1/open_telemetry/__init__.py
index 052350089..e88bb5dbb 100644
--- a/setup.cfg
+++ b/google/cloud/pubsub_v1/open_telemetry/__init__.py
@@ -1,19 +1,13 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright 2023 Google LLC
+# Copyright 2024, Google LLC All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# https://www.apache.org/licenses/LICENSE-2.0
+# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-# Generated by synthtool. DO NOT EDIT!
-[bdist_wheel]
-universal = 1
diff --git a/google/cloud/pubsub_v1/open_telemetry/context_propagation.py b/google/cloud/pubsub_v1/open_telemetry/context_propagation.py
new file mode 100644
index 000000000..bfa1aa638
--- /dev/null
+++ b/google/cloud/pubsub_v1/open_telemetry/context_propagation.py
@@ -0,0 +1,55 @@
+# Copyright 2024, Google LLC All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Optional, List
+
+from opentelemetry.propagators.textmap import Setter, Getter
+
+from google.pubsub_v1 import PubsubMessage
+
+
+class OpenTelemetryContextSetter(Setter):
+ """
+ Used by Open Telemetry for context propagation.
+ """
+
+ def set(self, carrier: PubsubMessage, key: str, value: str) -> None:
+ """
+ Injects trace context into Pub/Sub message attributes with
+ "googclient_" prefix.
+
+ Args:
+ carrier(PubsubMessage): The Pub/Sub message which is the carrier of Open Telemetry
+ data.
+ key(str): The key for which the Open Telemetry context data needs to be set.
+ value(str): The Open Telemetry context value to be set.
+
+ Returns:
+ None
+ """
+ carrier.attributes["googclient_" + key] = value
+
+
+class OpenTelemetryContextGetter(Getter):
+ """
+ Used by Open Telemetry for context propagation.
+ """
+
+ def get(self, carrier: PubsubMessage, key: str) -> Optional[List[str]]:
+ if ("googclient_" + key) not in carrier.attributes:
+ return None
+ return [carrier.attributes["googclient_" + key]]
+
+ def keys(self, carrier: PubsubMessage) -> List[str]:
+ return list(map(str, carrier.attributes.keys()))
diff --git a/google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py b/google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py
new file mode 100644
index 000000000..e03a8f800
--- /dev/null
+++ b/google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py
@@ -0,0 +1,142 @@
+# Copyright 2017, Google LLC All rights reserved.
+#
+# 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 sys
+from datetime import datetime
+from typing import Optional
+
+from opentelemetry import trace
+from opentelemetry.trace.propagation import set_span_in_context
+from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
+
+from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.context_propagation import (
+ OpenTelemetryContextSetter,
+)
+
+
+class PublishMessageWrapper:
+ _OPEN_TELEMETRY_TRACER_NAME: str = "google.cloud.pubsub_v1"
+ _OPEN_TELEMETRY_MESSAGING_SYSTEM: str = "gcp_pubsub"
+ _OPEN_TELEMETRY_PUBLISHER_BATCHING = "publisher batching"
+
+ _PUBLISH_START_EVENT: str = "publish start"
+ _PUBLISH_FLOW_CONTROL: str = "publisher flow control"
+
+ def __init__(self, message: gapic_types.PubsubMessage):
+ self._message: gapic_types.PubsubMessage = message
+ self._create_span: Optional[trace.Span] = None
+ self._flow_control_span: Optional[trace.Span] = None
+ self._batching_span: Optional[trace.Span] = None
+
+ @property
+ def message(self):
+ return self._message
+
+ @message.setter # type: ignore[no-redef] # resetting message value is intentional here
+ def message(self, message: gapic_types.PubsubMessage):
+ self._message = message
+
+ @property
+ def create_span(self):
+ return self._create_span
+
+ def __eq__(self, other): # pragma: NO COVER
+ """Used for pytest asserts to compare two PublishMessageWrapper objects with the same message"""
+ if isinstance(self, other.__class__):
+ return self.message == other.message
+ return False
+
+ def start_create_span(self, topic: str, ordering_key: str) -> None:
+ tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME)
+ assert len(topic.split("/")) == 4
+ topic_short_name = topic.split("/")[3]
+ with tracer.start_as_current_span(
+ name=f"{topic_short_name} create",
+ attributes={
+ "messaging.system": self._OPEN_TELEMETRY_MESSAGING_SYSTEM,
+ "messaging.destination.name": topic_short_name,
+ "code.function": "publish",
+ "messaging.gcp_pubsub.message.ordering_key": ordering_key,
+ "messaging.operation": "create",
+ "gcp.project_id": topic.split("/")[1],
+ "messaging.message.body.size": sys.getsizeof(
+ self._message.data
+ ), # sys.getsizeof() used since the attribute expects size of message body in bytes
+ },
+ kind=trace.SpanKind.PRODUCER,
+ end_on_exit=False,
+ ) as create_span:
+ create_span.add_event(
+ name=self._PUBLISH_START_EVENT,
+ attributes={
+ "timestamp": str(datetime.now()),
+ },
+ )
+ self._create_span = create_span
+ TraceContextTextMapPropagator().inject(
+ carrier=self._message,
+ setter=OpenTelemetryContextSetter(),
+ )
+
+ def end_create_span(self, exc: Optional[BaseException] = None) -> None:
+ assert self._create_span is not None
+ if exc:
+ self._create_span.record_exception(exception=exc)
+ self._create_span.set_status(
+ trace.Status(status_code=trace.StatusCode.ERROR)
+ )
+ self._create_span.end()
+
+ def start_publisher_flow_control_span(self) -> None:
+ tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME)
+ assert self._create_span is not None
+ with tracer.start_as_current_span(
+ name=self._PUBLISH_FLOW_CONTROL,
+ kind=trace.SpanKind.INTERNAL,
+ context=set_span_in_context(self._create_span),
+ end_on_exit=False,
+ ) as flow_control_span:
+ self._flow_control_span = flow_control_span
+
+ def end_publisher_flow_control_span(
+ self, exc: Optional[BaseException] = None
+ ) -> None:
+ assert self._flow_control_span is not None
+ if exc:
+ self._flow_control_span.record_exception(exception=exc)
+ self._flow_control_span.set_status(
+ trace.Status(status_code=trace.StatusCode.ERROR)
+ )
+ self._flow_control_span.end()
+
+ def start_publisher_batching_span(self) -> None:
+ assert self._create_span is not None
+ tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME)
+ with tracer.start_as_current_span(
+ name=self._OPEN_TELEMETRY_PUBLISHER_BATCHING,
+ kind=trace.SpanKind.INTERNAL,
+ context=set_span_in_context(self._create_span),
+ end_on_exit=False,
+ ) as batching_span:
+ self._batching_span = batching_span
+
+ def end_publisher_batching_span(self, exc: Optional[BaseException] = None) -> None:
+ assert self._batching_span is not None
+ if exc:
+ self._batching_span.record_exception(exception=exc)
+ self._batching_span.set_status(
+ trace.Status(status_code=trace.StatusCode.ERROR)
+ )
+ self._batching_span.end()
diff --git a/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py b/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py
new file mode 100644
index 000000000..5a6abd21b
--- /dev/null
+++ b/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py
@@ -0,0 +1,288 @@
+# Copyright 2024, Google LLC All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Optional, List
+from datetime import datetime
+
+from opentelemetry import trace, context
+from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
+from opentelemetry.trace.propagation import set_span_in_context
+
+from google.cloud.pubsub_v1.open_telemetry.context_propagation import (
+ OpenTelemetryContextGetter,
+)
+from google.pubsub_v1.types import PubsubMessage
+
+_OPEN_TELEMETRY_TRACER_NAME: str = "google.cloud.pubsub_v1"
+_OPEN_TELEMETRY_MESSAGING_SYSTEM: str = "gcp_pubsub"
+
+
+class SubscribeOpenTelemetry:
+ def __init__(self, message: PubsubMessage):
+ self._message: PubsubMessage = message
+
+ # subscribe span will be initialized by the `start_subscribe_span`
+ # method.
+ self._subscribe_span: Optional[trace.Span] = None
+
+ # subscriber concurrency control span will be initialized by the
+ # `start_subscribe_concurrency_control_span` method.
+ self._concurrency_control_span: Optional[trace.Span] = None
+
+ # scheduler span will be initialized by the
+ # `start_subscribe_scheduler_span` method.
+ self._scheduler_span: Optional[trace.Span] = None
+
+ # This will be set by `start_subscribe_span` method and will be used
+ # for other spans, such as process span.
+ self._subscription_id: Optional[str] = None
+
+ # This will be set by `start_process_span` method.
+ self._process_span: Optional[trace.Span] = None
+
+ # This will be set by `start_subscribe_span` method, if a publisher create span
+ # context was extracted from trace propagation. And will be used by spans like
+ # proces span to add links to the publisher create span.
+ self._publisher_create_span_context: Optional[context.Context] = None
+
+ # This will be set by `start_subscribe_span` method and will be used
+ # for other spans, such as modack span.
+ self._project_id: Optional[str] = None
+
+ @property
+ def subscription_id(self) -> Optional[str]:
+ return self._subscription_id
+
+ @property
+ def project_id(self) -> Optional[str]:
+ return self._project_id
+
+ @property
+ def subscribe_span(self) -> Optional[trace.Span]:
+ return self._subscribe_span
+
+ def start_subscribe_span(
+ self,
+ subscription: str,
+ exactly_once_enabled: bool,
+ ack_id: str,
+ delivery_attempt: int,
+ ) -> None:
+ tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME)
+ parent_span_context = TraceContextTextMapPropagator().extract(
+ carrier=self._message,
+ getter=OpenTelemetryContextGetter(),
+ )
+ self._publisher_create_span_context = parent_span_context
+ split_subscription: List[str] = subscription.split("/")
+ assert len(split_subscription) == 4
+ subscription_short_name = split_subscription[3]
+ self._project_id = split_subscription[1]
+ self._subscription_id = subscription_short_name
+ with tracer.start_as_current_span(
+ name=f"{subscription_short_name} subscribe",
+ context=parent_span_context if parent_span_context else None,
+ kind=trace.SpanKind.CONSUMER,
+ attributes={
+ "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM,
+ "messaging.destination.name": subscription_short_name,
+ "gcp.project_id": subscription.split("/")[1],
+ "messaging.message.id": self._message.message_id,
+ "messaging.message.body.size": len(self._message.data),
+ "messaging.gcp_pubsub.message.ack_id": ack_id,
+ "messaging.gcp_pubsub.message.ordering_key": self._message.ordering_key,
+ "messaging.gcp_pubsub.message.exactly_once_delivery": exactly_once_enabled,
+ "code.function": "_on_response",
+ "messaging.gcp_pubsub.message.delivery_attempt": delivery_attempt,
+ },
+ end_on_exit=False,
+ ) as subscribe_span:
+ self._subscribe_span = subscribe_span
+
+ def add_subscribe_span_event(self, event: str) -> None:
+ assert self._subscribe_span is not None
+ self._subscribe_span.add_event(
+ name=event,
+ attributes={
+ "timestamp": str(datetime.now()),
+ },
+ )
+
+ def end_subscribe_span(self) -> None:
+ assert self._subscribe_span is not None
+ self._subscribe_span.end()
+
+ def set_subscribe_span_result(self, result: str) -> None:
+ assert self._subscribe_span is not None
+ self._subscribe_span.set_attribute(
+ key="messaging.gcp_pubsub.result",
+ value=result,
+ )
+
+ def start_subscribe_concurrency_control_span(self) -> None:
+ assert self._subscribe_span is not None
+ tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME)
+ with tracer.start_as_current_span(
+ name="subscriber concurrency control",
+ kind=trace.SpanKind.INTERNAL,
+ context=set_span_in_context(self._subscribe_span),
+ end_on_exit=False,
+ ) as concurrency_control_span:
+ self._concurrency_control_span = concurrency_control_span
+
+ def end_subscribe_concurrency_control_span(self) -> None:
+ assert self._concurrency_control_span is not None
+ self._concurrency_control_span.end()
+
+ def start_subscribe_scheduler_span(self) -> None:
+ assert self._subscribe_span is not None
+ tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME)
+ with tracer.start_as_current_span(
+ name="subscriber scheduler",
+ kind=trace.SpanKind.INTERNAL,
+ context=set_span_in_context(self._subscribe_span),
+ end_on_exit=False,
+ ) as scheduler_span:
+ self._scheduler_span = scheduler_span
+
+ def end_subscribe_scheduler_span(self) -> None:
+ assert self._scheduler_span is not None
+ self._scheduler_span.end()
+
+ def start_process_span(self) -> trace.Span:
+ assert self._subscribe_span is not None
+ tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME)
+ publish_create_span_link: Optional[trace.Link] = None
+ if self._publisher_create_span_context:
+ publish_create_span: trace.Span = trace.get_current_span(
+ self._publisher_create_span_context
+ )
+ span_context: Optional[
+ trace.SpanContext
+ ] = publish_create_span.get_span_context()
+ publish_create_span_link = (
+ trace.Link(span_context) if span_context else None
+ )
+
+ with tracer.start_as_current_span(
+ name=f"{self._subscription_id} process",
+ attributes={
+ "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM,
+ },
+ kind=trace.SpanKind.INTERNAL,
+ context=set_span_in_context(self._subscribe_span),
+ links=[publish_create_span_link] if publish_create_span_link else None,
+ end_on_exit=False,
+ ) as process_span:
+ self._process_span = process_span
+ return process_span
+
+ def end_process_span(self) -> None:
+ assert self._process_span is not None
+ self._process_span.end()
+
+ def add_process_span_event(self, event: str) -> None:
+ assert self._process_span is not None
+ self._process_span.add_event(
+ name=event,
+ attributes={
+ "timestamp": str(datetime.now()),
+ },
+ )
+
+ def __enter__(self) -> trace.Span:
+ return self.start_process_span()
+
+ def __exit__(self, exc_type, exc_val, traceback):
+ if self._process_span:
+ self.end_process_span()
+
+
+def start_modack_span(
+ subscribe_span_links: List[trace.Link],
+ subscription_id: Optional[str],
+ message_count: int,
+ deadline: float,
+ project_id: Optional[str],
+ code_function: str,
+ receipt_modack: bool,
+) -> trace.Span:
+ assert subscription_id is not None
+ assert project_id is not None
+ tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME)
+ with tracer.start_as_current_span(
+ name=f"{subscription_id} modack",
+ attributes={
+ "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM,
+ "messaging.batch.message_count": message_count,
+ "messaging.gcp_pubsub.message.ack_deadline": deadline,
+ "messaging.destination.name": subscription_id,
+ "gcp.project_id": project_id,
+ "messaging.operation.name": "modack",
+ "code.function": code_function,
+ "messaging.gcp_pubsub.is_receipt_modack": receipt_modack,
+ },
+ links=subscribe_span_links,
+ kind=trace.SpanKind.CLIENT,
+ end_on_exit=False,
+ ) as modack_span:
+ return modack_span
+
+
+def start_ack_span(
+ subscription_id: str,
+ message_count: int,
+ project_id: str,
+ links: List[trace.Link],
+) -> trace.Span:
+ tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME)
+ with tracer.start_as_current_span(
+ name=f"{subscription_id} ack",
+ attributes={
+ "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM,
+ "messaging.batch.message_count": message_count,
+ "messaging.operation": "ack",
+ "gcp.project_id": project_id,
+ "messaging.destination.name": subscription_id,
+ "code.function": "ack",
+ },
+ kind=trace.SpanKind.CLIENT,
+ links=links,
+ end_on_exit=False,
+ ) as ack_span:
+ return ack_span
+
+
+def start_nack_span(
+ subscription_id: str,
+ message_count: int,
+ project_id: str,
+ links: List[trace.Link],
+) -> trace.Span:
+ tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME)
+ with tracer.start_as_current_span(
+ name=f"{subscription_id} nack",
+ attributes={
+ "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM,
+ "messaging.batch.message_count": message_count,
+ "messaging.operation": "nack",
+ "gcp.project_id": project_id,
+ "messaging.destination.name": subscription_id,
+ "code.function": "modify_ack_deadline",
+ },
+ kind=trace.SpanKind.CLIENT,
+ links=links,
+ end_on_exit=False,
+ ) as nack_span:
+ return nack_span
diff --git a/google/cloud/pubsub_v1/publisher/_batch/base.py b/google/cloud/pubsub_v1/publisher/_batch/base.py
index 52505996b..c91e0a444 100644
--- a/google/cloud/pubsub_v1/publisher/_batch/base.py
+++ b/google/cloud/pubsub_v1/publisher/_batch/base.py
@@ -19,6 +19,10 @@
import typing
from typing import Optional, Sequence
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
+
if typing.TYPE_CHECKING: # pragma: NO COVER
from google.cloud import pubsub_v1
@@ -54,7 +58,7 @@ class Batch(metaclass=abc.ABCMeta):
def __len__(self):
"""Return the number of messages currently in the batch."""
- return len(self.messages)
+ return len(self.message_wrappers)
@staticmethod
@abc.abstractmethod
@@ -68,7 +72,7 @@ def make_lock(): # pragma: NO COVER
@property
@abc.abstractmethod
- def messages(self) -> Sequence["gapic_types.PubsubMessage"]: # pragma: NO COVER
+ def message_wrappers(self) -> Sequence[PublishMessageWrapper]: # pragma: NO COVER
"""Return the messages currently in the batch.
Returns:
diff --git a/google/cloud/pubsub_v1/publisher/_batch/thread.py b/google/cloud/pubsub_v1/publisher/_batch/thread.py
index 1617f8c90..2afbe3761 100644
--- a/google/cloud/pubsub_v1/publisher/_batch/thread.py
+++ b/google/cloud/pubsub_v1/publisher/_batch/thread.py
@@ -19,13 +19,20 @@
import time
import typing
from typing import Any, Callable, List, Optional, Sequence
+from datetime import datetime
+from opentelemetry import trace
import google.api_core.exceptions
from google.api_core import gapic_v1
+from google.auth import exceptions as auth_exceptions
+
from google.cloud.pubsub_v1.publisher import exceptions
from google.cloud.pubsub_v1.publisher import futures
from google.cloud.pubsub_v1.publisher._batch import base
from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
if typing.TYPE_CHECKING: # pragma: NO COVER
from google.cloud import pubsub_v1
@@ -85,6 +92,9 @@ class Batch(base.Batch):
timeout is used.
"""
+ _OPEN_TELEMETRY_TRACER_NAME: str = "google.cloud.pubsub_v1"
+ _OPEN_TELEMETRY_MESSAGING_SYSTEM: str = "gcp_pubsub"
+
def __init__(
self,
client: "PublisherClient",
@@ -108,7 +118,7 @@ def __init__(
# status changed from ACCEPTING_MESSAGES to any other
# in order to avoid race conditions
self._futures: List[futures.Future] = []
- self._messages: List[gapic_types.PubsubMessage] = []
+ self._message_wrappers: List[PublishMessageWrapper] = []
self._status = base.BatchStatus.ACCEPTING_MESSAGES
# The initial size is not zero, we need to account for the size overhead
@@ -119,6 +129,10 @@ def __init__(
self._commit_retry = commit_retry
self._commit_timeout = commit_timeout
+ # Publish RPC Span that will be set by method `_start_publish_rpc_span`
+ # if Open Telemetry is enabled.
+ self._rpc_span: Optional[trace.Span] = None
+
@staticmethod
def make_lock() -> threading.Lock:
"""Return a threading lock.
@@ -134,9 +148,9 @@ def client(self) -> "PublisherClient":
return self._client
@property
- def messages(self) -> Sequence[gapic_types.PubsubMessage]:
- """The messages currently in the batch."""
- return self._messages
+ def message_wrappers(self) -> Sequence[PublishMessageWrapper]:
+ """The message wrappers currently in the batch."""
+ return self._message_wrappers
@property
def settings(self) -> "types.BatchSettings":
@@ -226,6 +240,38 @@ def _start_commit_thread(self) -> None:
)
commit_thread.start()
+ def _start_publish_rpc_span(self) -> None:
+ tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME)
+ links = []
+
+ for wrapper in self._message_wrappers:
+ span = wrapper.create_span
+ # Add links only for sampled spans.
+ if span.get_span_context().trace_flags.sampled:
+ links.append(trace.Link(span.get_span_context()))
+ assert len(self._topic.split("/")) == 4
+ topic_short_name = self._topic.split("/")[3]
+ with tracer.start_as_current_span(
+ name=f"{topic_short_name} publish",
+ attributes={
+ "messaging.system": self._OPEN_TELEMETRY_MESSAGING_SYSTEM,
+ "messaging.destination.name": topic_short_name,
+ "gcp.project_id": self._topic.split("/")[1],
+ "messaging.batch.message_count": len(self._message_wrappers),
+ "messaging.operation": "publish",
+ "code.function": "_commit",
+ },
+ links=links,
+ kind=trace.SpanKind.CLIENT,
+ end_on_exit=False,
+ ) as rpc_span:
+ ctx = rpc_span.get_span_context()
+ for wrapper in self._message_wrappers:
+ span = wrapper.create_span
+ if span.get_span_context().trace_flags.sampled:
+ span.add_link(ctx)
+ self._rpc_span = rpc_span
+
def _commit(self) -> None:
"""Actually publish all of the messages on the active batch.
@@ -259,7 +305,7 @@ def _commit(self) -> None:
# https://github.com/googleapis/google-cloud-python/issues/8036
# Sanity check: If there are no messages, no-op.
- if not self._messages:
+ if not self._message_wrappers:
_LOGGER.debug("No messages to publish, exiting commit")
self._status = base.BatchStatus.SUCCESS
return
@@ -270,18 +316,54 @@ def _commit(self) -> None:
batch_transport_succeeded = True
try:
+ if self._client.open_telemetry_enabled:
+ self._start_publish_rpc_span()
+
# Performs retries for errors defined by the retry configuration.
response = self._client._gapic_publish(
topic=self._topic,
- messages=self._messages,
+ messages=[wrapper.message for wrapper in self._message_wrappers],
retry=self._commit_retry,
timeout=self._commit_timeout,
)
- except google.api_core.exceptions.GoogleAPIError as exc:
+
+ if self._client.open_telemetry_enabled:
+ assert self._rpc_span is not None
+ self._rpc_span.end()
+ end_time = str(datetime.now())
+ for message_id, wrapper in zip(
+ response.message_ids, self._message_wrappers
+ ):
+ span = wrapper.create_span
+ span.add_event(
+ name="publish end",
+ attributes={
+ "timestamp": end_time,
+ },
+ )
+ span.set_attribute(key="messaging.message.id", value=message_id)
+ wrapper.end_create_span()
+ except (
+ google.api_core.exceptions.GoogleAPIError,
+ auth_exceptions.TransportError,
+ ) as exc:
# We failed to publish, even after retries, so set the exception on
# all futures and exit.
self._status = base.BatchStatus.ERROR
+ if self._client.open_telemetry_enabled:
+ if self._rpc_span:
+ self._rpc_span.record_exception(
+ exception=exc,
+ )
+ self._rpc_span.set_status(
+ trace.Status(status_code=trace.StatusCode.ERROR)
+ )
+ self._rpc_span.end()
+
+ for wrapper in self._message_wrappers:
+ wrapper.end_create_span(exc=exc)
+
batch_transport_succeeded = False
if self._batch_done_callback is not None:
# Failed to publish batch.
@@ -326,7 +408,8 @@ def _commit(self) -> None:
self._batch_done_callback(batch_transport_succeeded)
def publish(
- self, message: gapic_types.PubsubMessage
+ self,
+ wrapper: PublishMessageWrapper,
) -> Optional["pubsub_v1.publisher.futures.Future"]:
"""Publish a single message.
@@ -338,7 +421,7 @@ def publish(
This method is called by :meth:`~.PublisherClient.publish`.
Args:
- message: The Pub/Sub message.
+ wrapper: The Pub/Sub message wrapper.
Returns:
An object conforming to the :class:`~concurrent.futures.Future` interface
@@ -351,12 +434,14 @@ def publish(
"""
# Coerce the type, just in case.
- if not isinstance(message, gapic_types.PubsubMessage):
+ if not isinstance(
+ wrapper.message, gapic_types.PubsubMessage
+ ): # pragma: NO COVER
# For performance reasons, the message should be constructed by directly
# using the raw protobuf class, and only then wrapping it into the
# higher-level PubsubMessage class.
- vanilla_pb = _raw_proto_pubbsub_message(**message)
- message = gapic_types.PubsubMessage.wrap(vanilla_pb)
+ vanilla_pb = _raw_proto_pubbsub_message(**wrapper.message)
+ wrapper.message = gapic_types.PubsubMessage.wrap(vanilla_pb)
future = None
@@ -369,7 +454,7 @@ def publish(
return None
size_increase = gapic_types.PublishRequest(
- messages=[message]
+ messages=[wrapper.message]
)._pb.ByteSize()
if (self._base_request_size + size_increase) > _SERVER_PUBLISH_MAX_BYTES:
@@ -381,14 +466,14 @@ def publish(
raise exceptions.MessageTooLargeError(err_msg)
new_size = self._size + size_increase
- new_count = len(self._messages) + 1
+ new_count = len(self._message_wrappers) + 1
size_limit = min(self.settings.max_bytes, _SERVER_PUBLISH_MAX_BYTES)
overflow = new_size > size_limit or new_count >= self.settings.max_messages
- if not self._messages or not overflow:
+ if not self._message_wrappers or not overflow:
# Store the actual message in the batch's message queue.
- self._messages.append(message)
+ self._message_wrappers.append(wrapper)
self._size = new_size
# Track the future on this batch (so that the result of the
diff --git a/google/cloud/pubsub_v1/publisher/_sequencer/base.py b/google/cloud/pubsub_v1/publisher/_sequencer/base.py
index 58ec5a571..daaacaa33 100644
--- a/google/cloud/pubsub_v1/publisher/_sequencer/base.py
+++ b/google/cloud/pubsub_v1/publisher/_sequencer/base.py
@@ -53,8 +53,8 @@ def unpause(self) -> None: # pragma: NO COVER
def publish(
self,
message: gapic_types.PubsubMessage,
- retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
- timeout: gapic_types.TimeoutType = gapic_v1.method.DEFAULT,
+ retry: "OptionalRetry" = gapic_v1.method.DEFAULT, # type: ignore
+ timeout: gapic_types.TimeoutType = gapic_v1.method.DEFAULT, # type: ignore
) -> "futures.Future": # pragma: NO COVER
"""Publish message for this ordering key.
diff --git a/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py b/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py
index 30c76a44f..9644a1fa2 100644
--- a/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py
+++ b/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py
@@ -23,7 +23,9 @@
from google.cloud.pubsub_v1.publisher import exceptions
from google.cloud.pubsub_v1.publisher._sequencer import base as sequencer_base
from google.cloud.pubsub_v1.publisher._batch import base as batch_base
-from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
if typing.TYPE_CHECKING: # pragma: NO COVER
from google.cloud.pubsub_v1 import types
@@ -262,15 +264,15 @@ def _create_batch(
def publish(
self,
- message: gapic_types.PubsubMessage,
+ wrapper: PublishMessageWrapper,
retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT,
) -> futures.Future:
"""Publish message for this ordering key.
Args:
- message:
- The Pub/Sub message.
+ wrapper:
+ The Pub/Sub message wrapper.
retry:
The retry settings to apply when publishing the message.
timeout:
@@ -317,11 +319,11 @@ def publish(
self._ordered_batches.append(new_batch)
batch = self._ordered_batches[-1]
- future = batch.publish(message)
+ future = batch.publish(wrapper)
while future is None:
batch = self._create_batch(commit_retry=retry, commit_timeout=timeout)
self._ordered_batches.append(batch)
- future = batch.publish(message)
+ future = batch.publish(wrapper)
return future
diff --git a/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py b/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py
index 7d57aa821..7dbd3f084 100644
--- a/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py
+++ b/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py
@@ -18,7 +18,9 @@
from google.api_core import gapic_v1
from google.cloud.pubsub_v1.publisher._sequencer import base
-from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
if typing.TYPE_CHECKING: # pragma: NO COVER
from google.cloud.pubsub_v1.publisher import _batch
@@ -115,15 +117,15 @@ def _create_batch(
def publish(
self,
- message: gapic_types.PubsubMessage,
+ wrapper: PublishMessageWrapper,
retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT,
) -> "futures.Future":
"""Batch message into existing or new batch.
Args:
- message:
- The Pub/Sub message.
+ wrapper:
+ The Pub/Sub message wrapper.
retry:
The retry settings to apply when publishing the message.
timeout:
@@ -151,7 +153,7 @@ def publish(
future = None
while future is None:
# Might throw MessageTooLargeError
- future = batch.publish(message)
+ future = batch.publish(wrapper)
# batch is full, triggering commit_when_full
if future is None:
batch = self._create_batch(commit_retry=retry, commit_timeout=timeout)
diff --git a/google/cloud/pubsub_v1/publisher/client.py b/google/cloud/pubsub_v1/publisher/client.py
index 54b353276..0740e3185 100644
--- a/google/cloud/pubsub_v1/publisher/client.py
+++ b/google/cloud/pubsub_v1/publisher/client.py
@@ -22,6 +22,7 @@
import typing
from typing import Any, Dict, Optional, Sequence, Tuple, Type, Union
import warnings
+import sys
from google.api_core import gapic_v1
from google.auth.credentials import AnonymousCredentials # type: ignore
@@ -37,6 +38,9 @@
from google.pubsub_v1 import gapic_version as package_version
from google.pubsub_v1 import types as gapic_types
from google.pubsub_v1.services.publisher import client as publisher_client
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
__version__ = package_version.__version__
@@ -124,11 +128,15 @@ def __init__(
# Sanity check: Is our goal to use the emulator?
# If so, create a grpc insecure channel with the emulator host
# as the target.
+ # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator
+ # code below to test files.
if os.environ.get("PUBSUB_EMULATOR_HOST"):
kwargs["client_options"] = {
"api_endpoint": os.environ.get("PUBSUB_EMULATOR_HOST")
}
- kwargs["credentials"] = AnonymousCredentials()
+ # Configure credentials directly to transport, if provided.
+ if "transport" not in kwargs:
+ kwargs["credentials"] = AnonymousCredentials()
# For a transient failure, retry publishing the message infinitely.
self.publisher_options = types.PublisherOptions(*publisher_options)
@@ -153,6 +161,22 @@ def __init__(
# The object controlling the message publishing flow
self._flow_controller = FlowController(self.publisher_options.flow_control)
+ self._open_telemetry_enabled = (
+ self.publisher_options.enable_open_telemetry_tracing
+ )
+ # OpenTelemetry features used by the library are not supported in Python versions <= 3.7.
+ # Refer https://github.com/open-telemetry/opentelemetry-python/issues/3993#issuecomment-2211976389
+ if (
+ self.publisher_options.enable_open_telemetry_tracing
+ and sys.version_info.major == 3
+ and sys.version_info.minor < 8
+ ):
+ warnings.warn(
+ message="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.",
+ category=RuntimeWarning,
+ )
+ self._open_telemetry_enabled = False
+
@classmethod
def from_service_account_file( # type: ignore[override]
cls,
@@ -209,6 +233,10 @@ def api(self):
warnings.warn(msg, category=DeprecationWarning)
return super()
+ @property
+ def open_telemetry_enabled(self) -> bool:
+ return self._open_telemetry_enabled
+
def _get_or_create_sequencer(self, topic: str, ordering_key: str) -> SequencerType:
"""Get an existing sequencer or create a new one given the (topic,
ordering_key) pair.
@@ -368,11 +396,41 @@ def publish( # type: ignore[override]
)
message = gapic_types.PubsubMessage.wrap(vanilla_pb)
+ wrapper: PublishMessageWrapper = PublishMessageWrapper(message)
+ if self._open_telemetry_enabled:
+ wrapper.start_create_span(topic=topic, ordering_key=ordering_key)
+
# Messages should go through flow control to prevent excessive
# queuing on the client side (depending on the settings).
try:
+ if self._open_telemetry_enabled:
+ if wrapper:
+ wrapper.start_publisher_flow_control_span()
+ else: # pragma: NO COVER
+ warnings.warn(
+ message="PubSubMessageWrapper is None. Not starting publisher flow control span.",
+ category=RuntimeWarning,
+ )
self._flow_controller.add(message)
+ if self._open_telemetry_enabled:
+ if wrapper:
+ wrapper.end_publisher_flow_control_span()
+ else: # pragma: NO COVER
+ warnings.warn(
+ message="PubSubMessageWrapper is None. Not ending publisher flow control span.",
+ category=RuntimeWarning,
+ )
except exceptions.FlowControlLimitError as exc:
+ if self._open_telemetry_enabled:
+ if wrapper:
+ wrapper.end_publisher_flow_control_span(exc)
+ wrapper.end_create_span(exc)
+ else: # pragma: NO COVER
+ warnings.warn(
+ message="PubSubMessageWrapper is None. Not ending publisher create and flow control spans on FlowControlLimitError.",
+ category=RuntimeWarning,
+ )
+
future = futures.Future()
future.set_exception(exc)
return future
@@ -386,31 +444,68 @@ def on_publish_done(future):
if timeout is gapic_v1.method.DEFAULT: # if custom timeout not passed in
timeout = self.publisher_options.timeout
+ if self._open_telemetry_enabled:
+ if wrapper:
+ wrapper.start_publisher_batching_span()
+ else: # pragma: NO COVER
+ warnings.warn(
+ message="PublishMessageWrapper is None. Hence, not starting publisher batching span",
+ category=RuntimeWarning,
+ )
with self._batch_lock:
- if self._is_stopped:
- raise RuntimeError("Cannot publish on a stopped publisher.")
-
- # Set retry timeout to "infinite" when message ordering is enabled.
- # Note that this then also impacts messages added with an empty
- # ordering key.
- if self._enable_message_ordering:
- if retry is gapic_v1.method.DEFAULT:
- # use the default retry for the publish GRPC method as a base
- transport = self._transport
- base_retry = transport._wrapped_methods[transport.publish]._retry
- retry = base_retry.with_deadline(2.0**32)
- # timeout needs to be overridden and set to infinite in
- # addition to the retry deadline since both determine
- # the duration for which retries are attempted.
- timeout = 2.0**32
- elif retry is not None:
- retry = retry.with_deadline(2.0**32)
- timeout = 2.0**32
-
- # Delegate the publishing to the sequencer.
- sequencer = self._get_or_create_sequencer(topic, ordering_key)
- future = sequencer.publish(message, retry=retry, timeout=timeout)
- future.add_done_callback(on_publish_done)
+ try:
+ if self._is_stopped:
+ raise RuntimeError("Cannot publish on a stopped publisher.")
+
+ # Set retry timeout to "infinite" when message ordering is enabled.
+ # Note that this then also impacts messages added with an empty
+ # ordering key.
+ if self._enable_message_ordering:
+ if retry is gapic_v1.method.DEFAULT:
+ # use the default retry for the publish GRPC method as a base
+ transport = self._transport
+ base_retry = transport._wrapped_methods[
+ transport.publish
+ ]._retry
+ retry = base_retry.with_deadline(2.0**32)
+ # timeout needs to be overridden and set to infinite in
+ # addition to the retry deadline since both determine
+ # the duration for which retries are attempted.
+ timeout = 2.0**32
+ elif retry is not None:
+ retry = retry.with_deadline(2.0**32)
+ timeout = 2.0**32
+
+ # Delegate the publishing to the sequencer.
+ sequencer = self._get_or_create_sequencer(topic, ordering_key)
+ future = sequencer.publish(
+ wrapper=wrapper, retry=retry, timeout=timeout
+ )
+ future.add_done_callback(on_publish_done)
+ except BaseException as be:
+ # Exceptions can be thrown when attempting to add messages to
+ # the batch. If they're thrown, record them in publisher
+ # batching and create span, end the spans and bubble the
+ # exception up.
+ if self._open_telemetry_enabled:
+ if wrapper:
+ wrapper.end_publisher_batching_span(be)
+ wrapper.end_create_span(be)
+ else: # pragma: NO COVER
+ warnings.warn(
+ message="PublishMessageWrapper is None. Hence, not recording exception and ending publisher batching span and create span",
+ category=RuntimeWarning,
+ )
+ raise be
+
+ if self._open_telemetry_enabled:
+ if wrapper:
+ wrapper.end_publisher_batching_span()
+ else: # pragma: NO COVER
+ warnings.warn(
+ message="PublishMessageWrapper is None. Hence, not ending publisher batching span",
+ category=RuntimeWarning,
+ )
# Create a timer thread if necessary to enforce the batching
# timeout.
diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py b/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py
index 15ad4abb3..fe3771432 100644
--- a/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py
+++ b/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py
@@ -26,11 +26,17 @@
import warnings
from google.api_core.retry import exponential_sleep_generator
+from opentelemetry import trace
+
from google.cloud.pubsub_v1.subscriber._protocol import helper_threads
from google.cloud.pubsub_v1.subscriber._protocol import requests
from google.cloud.pubsub_v1.subscriber.exceptions import (
AcknowledgeStatus,
)
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ start_ack_span,
+ start_nack_span,
+)
if typing.TYPE_CHECKING: # pragma: NO COVER
import queue
@@ -232,16 +238,69 @@ def ack(self, items: Sequence[requests.AckRequest]) -> None:
items_gen = iter(items)
ack_ids_gen = (item.ack_id for item in items)
total_chunks = int(math.ceil(len(items) / _ACK_IDS_BATCH_SIZE))
+ subscription_id: Optional[str] = None
+ project_id: Optional[str] = None
+ for item in items:
+ if item.opentelemetry_data:
+ item.opentelemetry_data.add_subscribe_span_event("ack start")
+ if subscription_id is None:
+ subscription_id = item.opentelemetry_data.subscription_id
+ if project_id is None:
+ project_id = item.opentelemetry_data.project_id
for _ in range(total_chunks):
ack_reqs_dict = {
req.ack_id: req
for req in itertools.islice(items_gen, _ACK_IDS_BATCH_SIZE)
}
+
+ subscribe_links: List[trace.Link] = []
+ subscribe_spans: List[trace.Span] = []
+ for ack_req in ack_reqs_dict.values():
+ if ack_req.opentelemetry_data:
+ subscribe_span: Optional[
+ trace.Span
+ ] = ack_req.opentelemetry_data.subscribe_span
+ if (
+ subscribe_span
+ and subscribe_span.get_span_context().trace_flags.sampled
+ ):
+ subscribe_links.append(
+ trace.Link(subscribe_span.get_span_context())
+ )
+ subscribe_spans.append(subscribe_span)
+ ack_span: Optional[trace.Span] = None
+ if subscription_id and project_id:
+ ack_span = start_ack_span(
+ subscription_id,
+ len(ack_reqs_dict),
+ project_id,
+ subscribe_links,
+ )
+ if (
+ ack_span and ack_span.get_span_context().trace_flags.sampled
+ ): # pragma: NO COVER
+ ack_span_context: trace.SpanContext = ack_span.get_span_context()
+ for subscribe_span in subscribe_spans:
+ subscribe_span.add_link(
+ context=ack_span_context,
+ attributes={
+ "messaging.operation.name": "ack",
+ },
+ )
+
requests_completed, requests_to_retry = self._manager.send_unary_ack(
ack_ids=list(itertools.islice(ack_ids_gen, _ACK_IDS_BATCH_SIZE)),
ack_reqs_dict=ack_reqs_dict,
)
+ if ack_span:
+ ack_span.end()
+
+ for completed_ack in requests_completed:
+ if completed_ack.opentelemetry_data:
+ completed_ack.opentelemetry_data.add_subscribe_span_event("ack end")
+ completed_ack.opentelemetry_data.set_subscribe_span_result("acked")
+ completed_ack.opentelemetry_data.end_subscribe_span()
# Remove the completed messages from lease management.
self.drop(requests_completed)
@@ -267,7 +326,7 @@ def _start_retry_thread(self, thread_name, thread_target):
# a back-end timeout error or other permanent failure.
retry_thread.start()
- def _retry_acks(self, requests_to_retry):
+ def _retry_acks(self, requests_to_retry: List[requests.AckRequest]):
retry_delay_gen = exponential_sleep_generator(
initial=_MIN_EXACTLY_ONCE_DELIVERY_ACK_MODACK_RETRY_DURATION_SECS,
maximum=_MAX_EXACTLY_ONCE_DELIVERY_ACK_MODACK_RETRY_DURATION_SECS,
@@ -282,10 +341,62 @@ def _retry_acks(self, requests_to_retry):
time.sleep(time_to_wait)
ack_reqs_dict = {req.ack_id: req for req in requests_to_retry}
+ subscription_id: Optional[str] = None
+ project_id: Optional[str] = None
+ subscribe_links: List[trace.Link] = []
+ subscribe_spans: List[trace.Span] = []
+ for req in requests_to_retry:
+ if req.opentelemetry_data:
+ req.opentelemetry_data.add_subscribe_span_event("ack start")
+ if subscription_id is None:
+ subscription_id = req.opentelemetry_data.subscription_id
+ if project_id is None:
+ project_id = req.opentelemetry_data.project_id
+ subscribe_span: Optional[
+ trace.Span
+ ] = req.opentelemetry_data.subscribe_span
+ if (
+ subscribe_span
+ and subscribe_span.get_span_context().trace_flags.sampled
+ ):
+ subscribe_links.append(
+ trace.Link(subscribe_span.get_span_context())
+ )
+ subscribe_spans.append(subscribe_span)
+ ack_span: Optional[trace.Span] = None
+ if subscription_id and project_id:
+ ack_span = start_ack_span(
+ subscription_id,
+ len(ack_reqs_dict),
+ project_id,
+ subscribe_links,
+ )
+ if (
+ ack_span and ack_span.get_span_context().trace_flags.sampled
+ ): # pragma: NO COVER
+ ack_span_context: trace.SpanContext = ack_span.get_span_context()
+ for subscribe_span in subscribe_spans:
+ subscribe_span.add_link(
+ context=ack_span_context,
+ attributes={
+ "messaging.operation.name": "ack",
+ },
+ )
+
requests_completed, requests_to_retry = self._manager.send_unary_ack(
ack_ids=[req.ack_id for req in requests_to_retry],
ack_reqs_dict=ack_reqs_dict,
)
+
+ if ack_span:
+ ack_span.end()
+
+ for completed_ack in requests_completed:
+ if completed_ack.opentelemetry_data:
+ completed_ack.opentelemetry_data.add_subscribe_span_event("ack end")
+ completed_ack.opentelemetry_data.set_subscribe_span_result("acked")
+ completed_ack.opentelemetry_data.end_subscribe_span()
+
assert (
len(requests_to_retry) <= _ACK_IDS_BATCH_SIZE
), "Too many requests to be retried."
@@ -336,15 +447,63 @@ def modify_ack_deadline(
deadline_seconds_gen = (item.seconds for item in items)
total_chunks = int(math.ceil(len(items) / _ACK_IDS_BATCH_SIZE))
+ subscription_id: Optional[str] = None
+ project_id: Optional[str] = None
+
+ for item in items:
+ if item.opentelemetry_data:
+ if math.isclose(item.seconds, 0):
+ item.opentelemetry_data.add_subscribe_span_event("nack start")
+ if subscription_id is None:
+ subscription_id = item.opentelemetry_data.subscription_id
+ if project_id is None:
+ project_id = item.opentelemetry_data.project_id
+ else:
+ item.opentelemetry_data.add_subscribe_span_event("modack start")
for _ in range(total_chunks):
ack_reqs_dict = {
req.ack_id: req
for req in itertools.islice(items_gen, _ACK_IDS_BATCH_SIZE)
}
+ subscribe_links: List[trace.Link] = []
+ subscribe_spans: List[trace.Span] = []
+ for ack_req in ack_reqs_dict.values():
+ if ack_req.opentelemetry_data and math.isclose(ack_req.seconds, 0):
+ subscribe_span: Optional[
+ trace.Span
+ ] = ack_req.opentelemetry_data.subscribe_span
+ if (
+ subscribe_span
+ and subscribe_span.get_span_context().trace_flags.sampled
+ ):
+ subscribe_links.append(
+ trace.Link(subscribe_span.get_span_context())
+ )
+ subscribe_spans.append(subscribe_span)
+ nack_span: Optional[trace.Span] = None
+ if subscription_id and project_id:
+ nack_span = start_nack_span(
+ subscription_id,
+ len(ack_reqs_dict),
+ project_id,
+ subscribe_links,
+ )
+ if (
+ nack_span and nack_span.get_span_context().trace_flags.sampled
+ ): # pragma: NO COVER
+ nack_span_context: trace.SpanContext = nack_span.get_span_context()
+ for subscribe_span in subscribe_spans:
+ subscribe_span.add_link(
+ context=nack_span_context,
+ attributes={
+ "messaging.operation.name": "nack",
+ },
+ )
requests_to_retry: List[requests.ModAckRequest]
+ requests_completed: Optional[List[requests.ModAckRequest]] = None
if default_deadline is None:
# no further work needs to be done for `requests_to_retry`
- _, requests_to_retry = self._manager.send_unary_modack(
+ requests_completed, requests_to_retry = self._manager.send_unary_modack(
modify_deadline_ack_ids=list(
itertools.islice(ack_ids_gen, _ACK_IDS_BATCH_SIZE)
),
@@ -355,7 +514,7 @@ def modify_ack_deadline(
default_deadline=None,
)
else:
- _, requests_to_retry = self._manager.send_unary_modack(
+ requests_completed, requests_to_retry = self._manager.send_unary_modack(
modify_deadline_ack_ids=itertools.islice(
ack_ids_gen, _ACK_IDS_BATCH_SIZE
),
@@ -363,10 +522,28 @@ def modify_ack_deadline(
ack_reqs_dict=ack_reqs_dict,
default_deadline=default_deadline,
)
+ if nack_span:
+ nack_span.end()
assert (
len(requests_to_retry) <= _ACK_IDS_BATCH_SIZE
), "Too many requests to be retried."
+ for completed_modack in requests_completed:
+ if completed_modack.opentelemetry_data:
+ # nack is a modack with 0 extension seconds.
+ if math.isclose(completed_modack.seconds, 0):
+ completed_modack.opentelemetry_data.set_subscribe_span_result(
+ "nacked"
+ )
+ completed_modack.opentelemetry_data.add_subscribe_span_event(
+ "nack end"
+ )
+ completed_modack.opentelemetry_data.end_subscribe_span()
+ else:
+ completed_modack.opentelemetry_data.add_subscribe_span_event(
+ "modack end"
+ )
+
# Retry on a separate thread so the dispatcher thread isn't blocked
# by sleeps.
if requests_to_retry:
@@ -390,11 +567,67 @@ def _retry_modacks(self, requests_to_retry):
time.sleep(time_to_wait)
ack_reqs_dict = {req.ack_id: req for req in requests_to_retry}
+
+ subscription_id = None
+ project_id = None
+ subscribe_links = []
+ subscribe_spans = []
+ for ack_req in ack_reqs_dict.values():
+ if ack_req.opentelemetry_data and math.isclose(ack_req.seconds, 0):
+ if subscription_id is None:
+ subscription_id = ack_req.opentelemetry_data.subscription_id
+ if project_id is None:
+ project_id = ack_req.opentelemetry_data.project_id
+ subscribe_span = ack_req.opentelemetry_data.subscribe_span
+ if (
+ subscribe_span
+ and subscribe_span.get_span_context().trace_flags.sampled
+ ):
+ subscribe_links.append(
+ trace.Link(subscribe_span.get_span_context())
+ )
+ subscribe_spans.append(subscribe_span)
+ nack_span = None
+ if subscription_id and project_id:
+ nack_span = start_nack_span(
+ subscription_id,
+ len(ack_reqs_dict),
+ project_id,
+ subscribe_links,
+ )
+ if (
+ nack_span and nack_span.get_span_context().trace_flags.sampled
+ ): # pragma: NO COVER
+ nack_span_context: trace.SpanContext = nack_span.get_span_context()
+ for subscribe_span in subscribe_spans:
+ subscribe_span.add_link(
+ context=nack_span_context,
+ attributes={
+ "messaging.operation.name": "nack",
+ },
+ )
requests_completed, requests_to_retry = self._manager.send_unary_modack(
modify_deadline_ack_ids=[req.ack_id for req in requests_to_retry],
modify_deadline_seconds=[req.seconds for req in requests_to_retry],
ack_reqs_dict=ack_reqs_dict,
)
+ if nack_span:
+ nack_span.end()
+ for completed_modack in requests_completed:
+ if completed_modack.opentelemetry_data:
+ # nack is a modack with 0 extension seconds.
+ if math.isclose(completed_modack.seconds, 0):
+ completed_modack.opentelemetry_data.set_subscribe_span_result(
+ "nacked"
+ )
+ completed_modack.opentelemetry_data.add_subscribe_span_event(
+ "nack end"
+ )
+ completed_modack.opentelemetry_data.end_subscribe_span()
+ else:
+ completed_modack.opentelemetry_data.add_subscribe_span_event(
+ "modack end"
+ )
def nack(self, items: Sequence[requests.NackRequest]) -> None:
"""Explicitly deny receipt of messages.
@@ -405,7 +638,10 @@ def nack(self, items: Sequence[requests.NackRequest]) -> None:
self.modify_ack_deadline(
[
requests.ModAckRequest(
- ack_id=item.ack_id, seconds=0, future=item.future
+ ack_id=item.ack_id,
+ seconds=0,
+ future=item.future,
+ opentelemetry_data=item.opentelemetry_data,
)
for item in items
]
diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py b/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py
index 16018e384..5abdb7081 100644
--- a/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py
+++ b/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py
@@ -23,6 +23,9 @@
from typing import Dict, Iterable, Optional, Union
from google.cloud.pubsub_v1.subscriber._protocol.dispatcher import _MAX_BATCH_LATENCY
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
try:
from collections.abc import KeysView
@@ -50,6 +53,7 @@ class _LeasedMessage(typing.NamedTuple):
size: int
ordering_key: Optional[str]
+ opentelemetry_data: Optional[SubscribeOpenTelemetry]
class Leaser(object):
@@ -98,6 +102,7 @@ def add(self, items: Iterable[requests.LeaseRequest]) -> None:
sent_time=float("inf"),
size=item.byte_size,
ordering_key=item.ordering_key,
+ opentelemetry_data=item.opentelemetry_data,
)
self._bytes += item.byte_size
else:
@@ -175,6 +180,17 @@ def maintain_leases(self) -> None:
"Dropping %s items because they were leased too long.", len(to_drop)
)
assert self._manager.dispatcher is not None
+ for drop_msg in to_drop:
+ leased_message = leased_messages.get(drop_msg.ack_id)
+ if leased_message and leased_message.opentelemetry_data:
+ leased_message.opentelemetry_data.add_process_span_event(
+ "expired"
+ )
+ leased_message.opentelemetry_data.end_process_span()
+ leased_message.opentelemetry_data.set_subscribe_span_result(
+ "expired"
+ )
+ leased_message.opentelemetry_data.end_subscribe_span()
self._manager.dispatcher.drop(to_drop)
# Remove dropped items from our copy of the leased messages (they
@@ -198,14 +214,28 @@ def maintain_leases(self) -> None:
# is inactive.
assert self._manager.dispatcher is not None
ack_id_gen = (ack_id for ack_id in ack_ids)
+ opentelemetry_data = [
+ message.opentelemetry_data
+ for message in list(leased_messages.values())
+ if message.opentelemetry_data
+ ]
expired_ack_ids = self._manager._send_lease_modacks(
- ack_id_gen, deadline
+ ack_id_gen,
+ deadline,
+ opentelemetry_data,
)
start_time = time.time()
# If exactly once delivery is enabled, we should drop all expired ack_ids from lease management.
if self._manager._exactly_once_delivery_enabled() and len(expired_ack_ids):
assert self._manager.dispatcher is not None
+ for ack_id in expired_ack_ids:
+ msg = leased_messages.get(ack_id)
+ if msg and msg.opentelemetry_data:
+ msg.opentelemetry_data.add_process_span_event("expired")
+ msg.opentelemetry_data.end_process_span()
+ msg.opentelemetry_data.set_subscribe_span_result("expired")
+ msg.opentelemetry_data.end_subscribe_span()
self._manager.dispatcher.drop(
[
requests.DropRequest(
diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py b/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py
index 63c2edbfa..3d4c2a392 100644
--- a/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py
+++ b/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py
@@ -100,6 +100,8 @@ def put(self, message: "subscriber.message.Message") -> None:
Args:
message: The message to put on hold.
"""
+ if message.opentelemetry_data:
+ message.opentelemetry_data.start_subscribe_scheduler_span()
self._messages_on_hold.append(message)
self._size = self._size + 1
diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/requests.py b/google/cloud/pubsub_v1/subscriber/_protocol/requests.py
index 9cd387545..9a0ba5a50 100644
--- a/google/cloud/pubsub_v1/subscriber/_protocol/requests.py
+++ b/google/cloud/pubsub_v1/subscriber/_protocol/requests.py
@@ -15,6 +15,10 @@
import typing
from typing import NamedTuple, Optional
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
+
if typing.TYPE_CHECKING: # pragma: NO COVER
from google.cloud.pubsub_v1.subscriber import futures
@@ -27,6 +31,8 @@ class AckRequest(NamedTuple):
time_to_ack: float
ordering_key: Optional[str]
future: Optional["futures.Future"]
+ opentelemetry_data: Optional[SubscribeOpenTelemetry] = None
+ message_id: Optional[str] = None
class DropRequest(NamedTuple):
@@ -39,12 +45,15 @@ class LeaseRequest(NamedTuple):
ack_id: str
byte_size: int
ordering_key: Optional[str]
+ opentelemetry_data: Optional[SubscribeOpenTelemetry] = None
class ModAckRequest(NamedTuple):
ack_id: str
seconds: float
future: Optional["futures.Future"]
+ opentelemetry_data: Optional[SubscribeOpenTelemetry] = None
+ message_id: Optional[str] = None
class NackRequest(NamedTuple):
@@ -52,3 +61,4 @@ class NackRequest(NamedTuple):
byte_size: int
ordering_key: Optional[str]
future: Optional["futures.Future"]
+ opentelemetry_data: Optional[SubscribeOpenTelemetry] = None
diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py
index b8531db17..5132456a2 100644
--- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py
+++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py
@@ -16,13 +16,24 @@
import collections
import functools
+import inspect
import itertools
import logging
import threading
import typing
-from typing import Any, Dict, Callable, Iterable, List, Optional, Set, Tuple
+from typing import (
+ Any,
+ Dict,
+ Callable,
+ Iterable,
+ List,
+ Optional,
+ Set,
+ Tuple,
+)
import uuid
+from opentelemetry import trace
import grpc # type: ignore
from google.api_core import bidi
@@ -38,6 +49,9 @@
AcknowledgeError,
AcknowledgeStatus,
)
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
import google.cloud.pubsub_v1.subscriber.message
from google.cloud.pubsub_v1.subscriber import futures
from google.cloud.pubsub_v1.subscriber.scheduler import ThreadScheduler
@@ -46,23 +60,40 @@
from google.rpc.error_details_pb2 import ErrorInfo # type: ignore
from google.rpc import code_pb2 # type: ignore
from google.rpc import status_pb2
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ start_modack_span,
+)
if typing.TYPE_CHECKING: # pragma: NO COVER
from google.cloud.pubsub_v1 import subscriber
_LOGGER = logging.getLogger(__name__)
+_SLOW_ACK_LOGGER = logging.getLogger("slow-ack")
+_STREAMS_LOGGER = logging.getLogger("subscriber-streams")
+_FLOW_CONTROL_LOGGER = logging.getLogger("subscriber-flow-control")
+_CALLBACK_DELIVERY_LOGGER = logging.getLogger("callback-delivery")
+_CALLBACK_EXCEPTION_LOGGER = logging.getLogger("callback-exceptions")
+_EXPIRY_LOGGER = logging.getLogger("expiry")
_REGULAR_SHUTDOWN_THREAD_NAME = "Thread-RegularStreamShutdown"
_RPC_ERROR_THREAD_NAME = "Thread-OnRpcTerminated"
_RETRYABLE_STREAM_ERRORS = (
+ exceptions.Aborted,
exceptions.DeadlineExceeded,
- exceptions.ServiceUnavailable,
+ exceptions.GatewayTimeout,
exceptions.InternalServerError,
+ exceptions.ResourceExhausted,
+ exceptions.ServiceUnavailable,
exceptions.Unknown,
- exceptions.GatewayTimeout,
- exceptions.Aborted,
)
-_TERMINATING_STREAM_ERRORS = (exceptions.Cancelled,)
+_TERMINATING_STREAM_ERRORS = (
+ exceptions.Cancelled,
+ exceptions.InvalidArgument,
+ exceptions.NotFound,
+ exceptions.PermissionDenied,
+ exceptions.Unauthenticated,
+ exceptions.Unauthorized,
+)
_MAX_LOAD = 1.0
"""The load threshold above which to pause the incoming message stream."""
@@ -91,6 +122,13 @@
code_pb2.UNAVAILABLE,
}
+# `on_fatal_exception` was added in `google-api-core v2.25.1``, which allows us to inform
+# callers on unrecoverable errors. We can only pass this arg if it's available in the
+# `BackgroundConsumer` spec.
+_SHOULD_USE_ON_FATAL_ERROR_CALLBACK = "on_fatal_exception" in inspect.getfullargspec(
+ bidi.BackgroundConsumer
+)
+
def _wrap_as_exception(maybe_exception: Any) -> BaseException:
"""Wrap an object as a Python exception, if needed.
@@ -122,15 +160,34 @@ def _wrap_callback_errors(
callback: The user callback.
message: The Pub/Sub message.
"""
+ _CALLBACK_DELIVERY_LOGGER.debug(
+ "Message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=%s) received by subscriber callback",
+ message.message_id,
+ message.ack_id,
+ message.ordering_key,
+ message.exactly_once_enabled,
+ )
+
try:
- callback(message)
+ if message.opentelemetry_data:
+ message.opentelemetry_data.end_subscribe_concurrency_control_span()
+ with message.opentelemetry_data:
+ callback(message)
+ else:
+ callback(message)
except BaseException as exc:
# Note: the likelihood of this failing is extremely low. This just adds
# a message to a queue, so if this doesn't work the world is in an
# unrecoverable state and this thread should just bail.
- _LOGGER.exception(
- "Top-level exception occurred in callback while processing a message"
+
+ _CALLBACK_EXCEPTION_LOGGER.exception(
+ "Message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=%s)'s callback threw exception, nacking message.",
+ message.message_id,
+ message.ack_id,
+ message.ordering_key,
+ message.exactly_once_enabled,
)
+
message.nack()
on_callback_error(exc)
@@ -171,6 +228,9 @@ def _process_requests(
error_status: Optional["status_pb2.Status"],
ack_reqs_dict: Dict[str, requests.AckRequest],
errors_dict: Optional[Dict[str, str]],
+ ack_histogram: Optional[histogram.Histogram] = None,
+ # TODO - Change this param to a Union of Literals when we drop p3.7 support
+ req_type: str = "ack",
):
"""Process requests when exactly-once delivery is enabled by referring to
error_status and errors_dict.
@@ -181,28 +241,40 @@ def _process_requests(
"""
requests_completed = []
requests_to_retry = []
- for ack_id in ack_reqs_dict:
+ for ack_id, ack_request in ack_reqs_dict.items():
+ # Debug logging: slow acks
+ if (
+ req_type == "ack"
+ and ack_histogram
+ and ack_request.time_to_ack > ack_histogram.percentile(percent=99)
+ ):
+ _SLOW_ACK_LOGGER.debug(
+ "Message (id=%s, ack_id=%s) ack duration of %s s is higher than the p99 ack duration",
+ ack_request.message_id,
+ ack_request.ack_id,
+ )
+
# Handle special errors returned for ack/modack RPCs via the ErrorInfo
# sidecar metadata when exactly-once delivery is enabled.
if errors_dict and ack_id in errors_dict:
exactly_once_error = errors_dict[ack_id]
if exactly_once_error.startswith("TRANSIENT_"):
- requests_to_retry.append(ack_reqs_dict[ack_id])
+ requests_to_retry.append(ack_request)
else:
if exactly_once_error == "PERMANENT_FAILURE_INVALID_ACK_ID":
exc = AcknowledgeError(AcknowledgeStatus.INVALID_ACK_ID, info=None)
else:
exc = AcknowledgeError(AcknowledgeStatus.OTHER, exactly_once_error)
- future = ack_reqs_dict[ack_id].future
+ future = ack_request.future
if future is not None:
future.set_exception(exc)
- requests_completed.append(ack_reqs_dict[ack_id])
+ requests_completed.append(ack_request)
# Temporary GRPC errors are retried
elif (
error_status
and error_status.code in _EXACTLY_ONCE_DELIVERY_TEMPORARY_RETRY_ERRORS
):
- requests_to_retry.append(ack_reqs_dict[ack_id])
+ requests_to_retry.append(ack_request)
# Other GRPC errors are NOT retried
elif error_status:
if error_status.code == code_pb2.PERMISSION_DENIED:
@@ -211,20 +283,20 @@ def _process_requests(
exc = AcknowledgeError(AcknowledgeStatus.FAILED_PRECONDITION, info=None)
else:
exc = AcknowledgeError(AcknowledgeStatus.OTHER, str(error_status))
- future = ack_reqs_dict[ack_id].future
+ future = ack_request.future
if future is not None:
future.set_exception(exc)
- requests_completed.append(ack_reqs_dict[ack_id])
+ requests_completed.append(ack_request)
# Since no error occurred, requests with futures are completed successfully.
- elif ack_reqs_dict[ack_id].future:
- future = ack_reqs_dict[ack_id].future
+ elif ack_request.future:
+ future = ack_request.future
# success
assert future is not None
future.set_result(AcknowledgeStatus.SUCCESS)
- requests_completed.append(ack_reqs_dict[ack_id])
+ requests_completed.append(ack_request)
# All other requests are considered completed.
else:
- requests_completed.append(ack_reqs_dict[ack_id])
+ requests_completed.append(ack_request)
return requests_completed, requests_to_retry
@@ -386,7 +458,7 @@ def dispatcher(self) -> Optional[dispatcher.Dispatcher]:
return self._dispatcher
@property
- def leaser(self) -> Optional[leaser.Leaser]:
+ def leaser(self) -> Optional["leaser.Leaser"]:
"""The leaser helper."""
return self._leaser
@@ -532,8 +604,10 @@ def maybe_pause_consumer(self) -> None:
with self._pause_resume_lock:
if self.load >= _MAX_LOAD:
if self._consumer is not None and not self._consumer.is_paused:
- _LOGGER.debug(
- "Message backlog over load at %.2f, pausing.", self.load
+ _FLOW_CONTROL_LOGGER.debug(
+ "Message backlog over load at %.2f (threshold %.2f), initiating client-side flow control",
+ self.load,
+ _RESUME_THRESHOLD,
)
self._consumer.pause()
@@ -560,10 +634,18 @@ def maybe_resume_consumer(self) -> None:
self._maybe_release_messages()
if self.load < _RESUME_THRESHOLD:
- _LOGGER.debug("Current load is %.2f, resuming consumer.", self.load)
+ _FLOW_CONTROL_LOGGER.debug(
+ "Current load is %.2f (threshold %.2f), suspending client-side flow control.",
+ self.load,
+ _RESUME_THRESHOLD,
+ )
self._consumer.resume()
else:
- _LOGGER.debug("Did not resume, current load is %.2f.", self.load)
+ _FLOW_CONTROL_LOGGER.debug(
+ "Current load is %.2f (threshold %.2f), retaining client-side flow control.",
+ self.load,
+ _RESUME_THRESHOLD,
+ )
def _maybe_release_messages(self) -> None:
"""Release (some of) the held messages if the current load allows for it.
@@ -582,7 +664,8 @@ def _maybe_release_messages(self) -> None:
msg = self._messages_on_hold.get()
if not msg:
break
-
+ if msg.opentelemetry_data:
+ msg.opentelemetry_data.end_subscribe_scheduler_span()
self._schedule_message_on_hold(msg)
released_ack_ids.append(msg.ack_id)
@@ -618,6 +701,8 @@ def _schedule_message_on_hold(
)
assert self._scheduler is not None
assert self._callback is not None
+ if msg.opentelemetry_data:
+ msg.opentelemetry_data.start_subscribe_concurrency_control_span()
self._scheduler.schedule(self._callback, msg)
def send_unary_ack(
@@ -671,7 +756,7 @@ def send_unary_ack(
if self._exactly_once_delivery_enabled():
requests_completed, requests_to_retry = _process_requests(
- error_status, ack_reqs_dict, ack_errors_dict
+ error_status, ack_reqs_dict, ack_errors_dict, self.ack_histogram, "ack"
)
else:
requests_completed = []
@@ -765,7 +850,11 @@ def send_unary_modack(
if self._exactly_once_delivery_enabled():
requests_completed, requests_to_retry = _process_requests(
- error_status, ack_reqs_dict, modack_errors_dict
+ error_status,
+ ack_reqs_dict,
+ modack_errors_dict,
+ self.ack_histogram,
+ "modack",
)
else:
requests_completed = []
@@ -863,7 +952,18 @@ def open(
assert self._scheduler is not None
scheduler_queue = self._scheduler.queue
self._dispatcher = dispatcher.Dispatcher(self, scheduler_queue)
- self._consumer = bidi.BackgroundConsumer(self._rpc, self._on_response)
+
+ # `on_fatal_exception` is only available in more recent library versions.
+ # For backwards compatibility reasons, we only pass it when `google-api-core` supports it.
+ if _SHOULD_USE_ON_FATAL_ERROR_CALLBACK:
+ self._consumer = bidi.BackgroundConsumer(
+ self._rpc,
+ self._on_response,
+ on_fatal_exception=self._on_fatal_exception,
+ )
+ else:
+ self._consumer = bidi.BackgroundConsumer(self._rpc, self._on_response)
+
self._leaser = leaser.Leaser(self)
self._heartbeater = heartbeater.Heartbeater(self)
@@ -1007,22 +1107,85 @@ def _get_initial_request(
return request
def _send_lease_modacks(
- self, ack_ids: Iterable[str], ack_deadline: float, warn_on_invalid=True
+ self,
+ ack_ids: Iterable[str],
+ ack_deadline: float,
+ opentelemetry_data: List[SubscribeOpenTelemetry],
+ warn_on_invalid=True,
+ receipt_modack: bool = False,
) -> Set[str]:
exactly_once_enabled = False
+
+ modack_span: Optional[trace.Span] = None
+ if self._client.open_telemetry_enabled:
+ subscribe_span_links: List[trace.Link] = []
+ subscribe_spans: List[trace.Span] = []
+ subscription_split: List[str] = self._subscription.split("/")
+ assert len(subscription_split) == 4
+ subscription_id: str = subscription_split[3]
+ project_id: str = subscription_split[1]
+ for data in opentelemetry_data:
+ subscribe_span: Optional[trace.Span] = data.subscribe_span
+ if (
+ subscribe_span
+ and subscribe_span.get_span_context().trace_flags.sampled
+ ):
+ subscribe_span_links.append(
+ trace.Link(subscribe_span.get_span_context())
+ )
+ subscribe_spans.append(subscribe_span)
+ modack_span = start_modack_span(
+ subscribe_span_links,
+ subscription_id,
+ len(opentelemetry_data),
+ ack_deadline,
+ project_id,
+ "_send_lease_modacks",
+ receipt_modack,
+ )
+ if (
+ modack_span and modack_span.get_span_context().trace_flags.sampled
+ ): # pragma: NO COVER
+ modack_span_context: trace.SpanContext = modack_span.get_span_context()
+ for subscribe_span in subscribe_spans:
+ subscribe_span.add_link(
+ context=modack_span_context,
+ attributes={
+ "messaging.operation.name": "modack",
+ },
+ )
+
with self._exactly_once_enabled_lock:
exactly_once_enabled = self._exactly_once_enabled
if exactly_once_enabled:
- items = [
- requests.ModAckRequest(ack_id, ack_deadline, futures.Future())
- for ack_id in ack_ids
- ]
+ eod_items: List[requests.ModAckRequest] = []
+ if self._client.open_telemetry_enabled:
+ for ack_id, data in zip(
+ ack_ids, opentelemetry_data
+ ): # pragma: NO COVER # Identical code covered in the same function below
+ assert data is not None
+ eod_items.append(
+ requests.ModAckRequest(
+ ack_id,
+ ack_deadline,
+ futures.Future(),
+ data,
+ )
+ )
+ else:
+ eod_items = [
+ requests.ModAckRequest(ack_id, ack_deadline, futures.Future())
+ for ack_id in ack_ids
+ ]
assert self._dispatcher is not None
- self._dispatcher.modify_ack_deadline(items, ack_deadline)
-
+ self._dispatcher.modify_ack_deadline(eod_items, ack_deadline)
+ if (
+ modack_span
+ ): # pragma: NO COVER # Identical code covered in the same function below
+ modack_span.end()
expired_ack_ids = set()
- for req in items:
+ for req in eod_items:
try:
assert req.future is not None
req.future.result()
@@ -1039,12 +1202,27 @@ def _send_lease_modacks(
expired_ack_ids.add(req.ack_id)
return expired_ack_ids
else:
- items = [
- requests.ModAckRequest(ack_id, self.ack_deadline, None)
- for ack_id in ack_ids
- ]
+ items: List[requests.ModAckRequest] = []
+ if self._client.open_telemetry_enabled:
+ for ack_id, data in zip(ack_ids, opentelemetry_data):
+ assert data is not None
+ items.append(
+ requests.ModAckRequest(
+ ack_id,
+ self.ack_deadline,
+ None,
+ data,
+ )
+ )
+ else:
+ items = [
+ requests.ModAckRequest(ack_id, self.ack_deadline, None)
+ for ack_id in ack_ids
+ ]
assert self._dispatcher is not None
self._dispatcher.modify_ack_deadline(items, ack_deadline)
+ if modack_span:
+ modack_span.end()
return set()
def _exactly_once_delivery_enabled(self) -> bool:
@@ -1075,6 +1253,18 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None:
# protobuf message to significantly gain on attribute access performance.
received_messages = response._pb.received_messages
+ subscribe_opentelemetry: List[SubscribeOpenTelemetry] = []
+ if self._client.open_telemetry_enabled:
+ for received_message in received_messages:
+ opentelemetry_data = SubscribeOpenTelemetry(received_message.message)
+ opentelemetry_data.start_subscribe_span(
+ self._subscription,
+ response.subscription_properties.exactly_once_delivery_enabled,
+ received_message.ack_id,
+ received_message.delivery_attempt,
+ )
+ subscribe_opentelemetry.append(opentelemetry_data)
+
_LOGGER.debug(
"Processing %s received message(s), currently on hold %s (bytes %s).",
len(received_messages),
@@ -1100,13 +1290,26 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None:
# received them.
ack_id_gen = (message.ack_id for message in received_messages)
expired_ack_ids = self._send_lease_modacks(
- ack_id_gen, self.ack_deadline, warn_on_invalid=False
+ ack_id_gen,
+ self.ack_deadline,
+ subscribe_opentelemetry,
+ warn_on_invalid=False,
+ receipt_modack=True,
)
+ if len(expired_ack_ids):
+ _EXPIRY_LOGGER.debug(
+ "ack ids %s were dropped as they have already expired.", expired_ack_ids
+ )
+
with self._pause_resume_lock:
- assert self._scheduler is not None
- assert self._leaser is not None
+ if self._scheduler is None or self._leaser is None:
+ _LOGGER.debug(
+ f"self._scheduler={self._scheduler} or self._leaser={self._leaser} is None. Stopping further processing."
+ )
+ return
+ i: int = 0
for received_message in received_messages:
if (
not self._exactly_once_delivery_enabled()
@@ -1119,12 +1322,16 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None:
self._scheduler.queue,
self._exactly_once_delivery_enabled,
)
+ if self._client.open_telemetry_enabled:
+ message.opentelemetry_data = subscribe_opentelemetry[i]
+ i = i + 1
self._messages_on_hold.put(message)
self._on_hold_bytes += message.size
req = requests.LeaseRequest(
ack_id=message.ack_id,
byte_size=message.size,
ordering_key=message.ordering_key,
+ opentelemetry_data=message.opentelemetry_data,
)
self._leaser.add([req])
@@ -1132,6 +1339,17 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None:
self.maybe_pause_consumer()
+ def _on_fatal_exception(self, exception: BaseException) -> None:
+ """
+ Called whenever `self.consumer` receives a non-retryable exception.
+ We close the manager on such non-retryable cases.
+ """
+ _LOGGER.info(
+ "Streaming pull terminating after receiving non-recoverable error: %s",
+ exception,
+ )
+ self.close(exception)
+
def _should_recover(self, exception: BaseException) -> bool:
"""Determine if an error on the RPC stream should be recovered.
@@ -1149,9 +1367,13 @@ def _should_recover(self, exception: BaseException) -> bool:
# If this is in the list of idempotent exceptions, then we want to
# recover.
if isinstance(exception, _RETRYABLE_STREAM_ERRORS):
- _LOGGER.debug("Observed recoverable stream error %s", exception)
+ _STREAMS_LOGGER.debug(
+ "Observed recoverable stream error %s, reopening stream", exception
+ )
return True
- _LOGGER.debug("Observed non-recoverable stream error %s", exception)
+ _STREAMS_LOGGER.debug(
+ "Observed non-recoverable stream error %s, shutting down stream", exception
+ )
return False
def _should_terminate(self, exception: BaseException) -> bool:
@@ -1168,10 +1390,16 @@ def _should_terminate(self, exception: BaseException) -> bool:
in a list of terminating exceptions.
"""
exception = _wrap_as_exception(exception)
- if isinstance(exception, _TERMINATING_STREAM_ERRORS):
- _LOGGER.debug("Observed terminating stream error %s", exception)
+ is_api_error = isinstance(exception, exceptions.GoogleAPICallError)
+ # Terminate any non-API errors, or non-retryable errors (permission denied, unauthorized, etc.)
+ if not is_api_error or isinstance(exception, _TERMINATING_STREAM_ERRORS):
+ _STREAMS_LOGGER.debug(
+ "Observed terminating stream error %s, shutting down stream", exception
+ )
return True
- _LOGGER.debug("Observed non-terminating stream error %s", exception)
+ _STREAMS_LOGGER.debug(
+ "Observed non-terminating stream error %s, attempting to reopen", exception
+ )
return False
def _on_rpc_done(self, future: Any) -> None:
diff --git a/google/cloud/pubsub_v1/subscriber/client.py b/google/cloud/pubsub_v1/subscriber/client.py
index 0d0d36a0c..41277e5e1 100644
--- a/google/cloud/pubsub_v1/subscriber/client.py
+++ b/google/cloud/pubsub_v1/subscriber/client.py
@@ -14,6 +14,7 @@
from __future__ import absolute_import
+import sys
import os
import typing
from typing import cast, Any, Callable, Optional, Sequence, Union
@@ -67,21 +68,60 @@ class Client(subscriber_client.SubscriberClient):
)
"""
- def __init__(self, **kwargs: Any):
+ def __init__(
+ self,
+ subscriber_options: Union[types.SubscriberOptions, Sequence] = (),
+ **kwargs: Any
+ ):
+ assert (
+ isinstance(subscriber_options, types.SubscriberOptions)
+ or len(subscriber_options) == 0
+ ), "subscriber_options must be of type SubscriberOptions or an empty sequence."
+
# Sanity check: Is our goal to use the emulator?
# If so, create a grpc insecure channel with the emulator host
# as the target.
+ # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator
+ # code below to test files.
if os.environ.get("PUBSUB_EMULATOR_HOST"):
kwargs["client_options"] = {
"api_endpoint": os.environ.get("PUBSUB_EMULATOR_HOST")
}
- kwargs["credentials"] = AnonymousCredentials()
+ # Configure credentials directly to transport, if provided.
+ if "transport" not in kwargs:
+ kwargs["credentials"] = AnonymousCredentials()
# Instantiate the underlying GAPIC client.
super().__init__(**kwargs)
self._target = self._transport._host
self._closed = False
+ self.subscriber_options = types.SubscriberOptions(*subscriber_options)
+
+ # Set / override Open Telemetry option.
+ self._open_telemetry_enabled = (
+ self.subscriber_options.enable_open_telemetry_tracing
+ )
+ # OpenTelemetry features used by the library are not supported in Python versions <= 3.7.
+ # Refer https://github.com/open-telemetry/opentelemetry-python/issues/3993#issuecomment-2211976389
+ if (
+ self.subscriber_options.enable_open_telemetry_tracing
+ and sys.version_info.major == 3
+ and sys.version_info.minor < 8
+ ):
+ warnings.warn(
+ message="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.",
+ category=RuntimeWarning,
+ )
+ self._open_telemetry_enabled = False
+
+ @property
+ def open_telemetry_enabled(self) -> bool:
+ """
+ Returns True if Open Telemetry is enabled. False otherwise.
+ """
+ return self._open_telemetry_enabled # pragma: NO COVER
+
@classmethod
def from_service_account_file( # type: ignore[override]
cls, filename: str, **kwargs: Any
diff --git a/google/cloud/pubsub_v1/subscriber/message.py b/google/cloud/pubsub_v1/subscriber/message.py
index f744966a2..aa715ac67 100644
--- a/google/cloud/pubsub_v1/subscriber/message.py
+++ b/google/cloud/pubsub_v1/subscriber/message.py
@@ -16,6 +16,7 @@
import datetime as dt
import json
+import logging
import math
import time
import typing
@@ -24,6 +25,9 @@
from google.cloud.pubsub_v1.subscriber._protocol import requests
from google.cloud.pubsub_v1.subscriber import futures
from google.cloud.pubsub_v1.subscriber.exceptions import AcknowledgeStatus
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
if typing.TYPE_CHECKING: # pragma: NO COVER
@@ -40,6 +44,8 @@
attributes: {}
}}"""
+_ACK_NACK_LOGGER = logging.getLogger("ack-nack")
+
_SUCCESS_FUTURE = futures.Future()
_SUCCESS_FUTURE.set_result(AcknowledgeStatus.SUCCESS)
@@ -85,6 +91,8 @@ class Message(object):
information on this type.
publish_time (google.protobuf.timestamp_pb2.Timestamp):
The time that this message was originally published.
+ opentelemetry_data (google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry.SubscribeOpenTelemetry)
+ Open Telemetry data associated with this message. None if Open Telemetry is not enabled.
"""
def __init__(
@@ -144,6 +152,9 @@ def __init__(
self._ordering_key = message.ordering_key
self._size = message.ByteSize()
+ # None if Open Telemetry is disabled. Else contains OpenTelemetry data.
+ self._opentelemetry_data: Optional[SubscribeOpenTelemetry] = None
+
def __repr__(self):
# Get an abbreviated version of the data.
abbv_data = self._message.data
@@ -158,6 +169,14 @@ def __repr__(self):
pretty_attrs = pretty_attrs.lstrip()
return _MESSAGE_REPR.format(abbv_data, str(self.ordering_key), pretty_attrs)
+ @property
+ def opentelemetry_data(self):
+ return self._opentelemetry_data # pragma: NO COVER
+
+ @opentelemetry_data.setter
+ def opentelemetry_data(self, data):
+ self._opentelemetry_data = data # pragma: NO COVER
+
@property
def attributes(self) -> "containers.ScalarMap":
"""Return the attributes of the underlying Pub/Sub Message.
@@ -252,16 +271,27 @@ def ack(self) -> None:
https://cloud.google.com/pubsub/docs/exactly-once-delivery."
"""
+ if self.opentelemetry_data:
+ self.opentelemetry_data.add_process_span_event("ack called")
+ self.opentelemetry_data.end_process_span()
time_to_ack = math.ceil(time.time() - self._received_timestamp)
self._request_queue.put(
requests.AckRequest(
+ message_id=self.message_id,
ack_id=self._ack_id,
byte_size=self.size,
time_to_ack=time_to_ack,
ordering_key=self.ordering_key,
future=None,
+ opentelemetry_data=self.opentelemetry_data,
)
)
+ _ACK_NACK_LOGGER.debug(
+ "Called ack for message (id=%s, ack_id=%s, ordering_key=%s)",
+ self.message_id,
+ self.ack_id,
+ self.ordering_key,
+ )
def ack_with_response(self) -> "futures.Future":
"""Acknowledge the given message.
@@ -302,6 +332,15 @@ def ack_with_response(self) -> "futures.Future":
pubsub_v1.subscriber.exceptions.AcknowledgeError exception
will be thrown.
"""
+ _ACK_NACK_LOGGER.debug(
+ "Called ack for message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=True)",
+ self.message_id,
+ self.ack_id,
+ self.ordering_key,
+ )
+ if self.opentelemetry_data:
+ self.opentelemetry_data.add_process_span_event("ack called")
+ self.opentelemetry_data.end_process_span()
req_future: Optional[futures.Future]
if self._exactly_once_delivery_enabled_func():
future = futures.Future()
@@ -312,11 +351,13 @@ def ack_with_response(self) -> "futures.Future":
time_to_ack = math.ceil(time.time() - self._received_timestamp)
self._request_queue.put(
requests.AckRequest(
+ message_id=self.message_id,
ack_id=self._ack_id,
byte_size=self.size,
time_to_ack=time_to_ack,
ordering_key=self.ordering_key,
future=req_future,
+ opentelemetry_data=self.opentelemetry_data,
)
)
return future
@@ -357,7 +398,13 @@ def modify_ack_deadline(self, seconds: int) -> None:
against.
"""
self._request_queue.put(
- requests.ModAckRequest(ack_id=self._ack_id, seconds=seconds, future=None)
+ requests.ModAckRequest(
+ message_id=self.message_id,
+ ack_id=self._ack_id,
+ seconds=seconds,
+ future=None,
+ opentelemetry_data=self.opentelemetry_data,
+ )
)
def modify_ack_deadline_with_response(self, seconds: int) -> "futures.Future":
@@ -416,7 +463,11 @@ def modify_ack_deadline_with_response(self, seconds: int) -> "futures.Future":
self._request_queue.put(
requests.ModAckRequest(
- ack_id=self._ack_id, seconds=seconds, future=req_future
+ message_id=self.message_id,
+ ack_id=self._ack_id,
+ seconds=seconds,
+ future=req_future,
+ opentelemetry_data=self.opentelemetry_data,
)
)
@@ -429,12 +480,23 @@ def nack(self) -> None:
may take place immediately or after a delay, and may arrive at this subscriber
or another.
"""
+ _ACK_NACK_LOGGER.debug(
+ "Called nack for message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=%s)",
+ self.message_id,
+ self.ack_id,
+ self.ordering_key,
+ self._exactly_once_delivery_enabled_func(),
+ )
+ if self.opentelemetry_data:
+ self.opentelemetry_data.add_process_span_event("nack called")
+ self.opentelemetry_data.end_process_span()
self._request_queue.put(
requests.NackRequest(
ack_id=self._ack_id,
byte_size=self.size,
ordering_key=self.ordering_key,
future=None,
+ opentelemetry_data=self.opentelemetry_data,
)
)
@@ -472,6 +534,9 @@ def nack_with_response(self) -> "futures.Future":
will be thrown.
"""
+ if self.opentelemetry_data:
+ self.opentelemetry_data.add_process_span_event("nack called")
+ self.opentelemetry_data.end_process_span()
req_future: Optional[futures.Future]
if self._exactly_once_delivery_enabled_func():
future = futures.Future()
@@ -486,7 +551,12 @@ def nack_with_response(self) -> "futures.Future":
byte_size=self.size,
ordering_key=self.ordering_key,
future=req_future,
+ opentelemetry_data=self.opentelemetry_data,
)
)
return future
+
+ @property
+ def exactly_once_enabled(self):
+ return self._exactly_once_delivery_enabled_func()
diff --git a/google/cloud/pubsub_v1/subscriber/scheduler.py b/google/cloud/pubsub_v1/subscriber/scheduler.py
index a3b3c88e1..cc3393bd7 100644
--- a/google/cloud/pubsub_v1/subscriber/scheduler.py
+++ b/google/cloud/pubsub_v1/subscriber/scheduler.py
@@ -21,6 +21,7 @@
import abc
import concurrent.futures
import queue
+import sys
import typing
from typing import Callable, List, Optional
import warnings
@@ -37,7 +38,7 @@ class Scheduler(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
- def queue(self) -> queue.Queue: # pragma: NO COVER
+ def queue(self) -> "queue.Queue": # pragma: NO COVER
"""Queue: A concurrency-safe queue specific to the underlying
concurrency implementation.
@@ -162,7 +163,25 @@ def shutdown(
work_item = self._executor._work_queue.get(block=False)
if work_item is None: # Exceutor in shutdown mode.
continue
- dropped_messages.append(work_item.args[0]) # type: ignore[index]
+
+ dropped_message = None
+ if sys.version_info < (3, 14):
+ # For Python < 3.14, work_item.args is a tuple of positional arguments.
+ # The message is expected to be the first argument.
+ if hasattr(work_item, "args") and work_item.args:
+ dropped_message = work_item.args[0] # type: ignore[index]
+ else:
+ # For Python >= 3.14, work_item.task is (fn, args, kwargs).
+ # The message is expected to be the first item in the args tuple (task[1]).
+ if (
+ hasattr(work_item, "task")
+ and len(work_item.task) == 3
+ and work_item.task[1]
+ ):
+ dropped_message = work_item.task[1][0]
+
+ if dropped_message is not None:
+ dropped_messages.append(dropped_message)
except queue.Empty:
pass
diff --git a/google/cloud/pubsub_v1/types.py b/google/cloud/pubsub_v1/types.py
index 3d071a189..6746e141a 100644
--- a/google/cloud/pubsub_v1/types.py
+++ b/google/cloud/pubsub_v1/types.py
@@ -35,6 +35,7 @@
from google.protobuf import timestamp_pb2
from google.api_core.protobuf_helpers import get_messages
+from google.api_core.timeout import ConstantTimeout
from google.pubsub_v1.types import pubsub as pubsub_gapic_types
@@ -131,6 +132,29 @@ class PublishFlowControl(NamedTuple):
"""The action to take when publish flow control limits are exceeded."""
+# Define the default subscriber options.
+#
+# This class is used when creating a subscriber client to pass in options
+# to enable/disable features.
+class SubscriberOptions(NamedTuple):
+ """
+ Options for the subscriber client.
+ Attributes:
+ enable_open_telemetry_tracing (bool):
+ Whether to enable OpenTelemetry tracing. Defaults to False.
+ """
+
+ enable_open_telemetry_tracing: bool = False
+ """
+ Whether to enable OpenTelemetry tracing.
+
+ Warning: traces are subject to change. The name and attributes of a span might
+ change without notice. Only use run traces interactively. Don't use in
+ automation. Running non-interactive traces can cause problems if the underlying
+ trace architecture changes without notice.
+ """
+
+
# Define the default publisher options.
#
# This class is used when creating a publisher client to pass in options
@@ -168,12 +192,25 @@ class PublisherOptions(NamedTuple):
"an instance of :class:`google.api_core.retry.Retry`."
)
- timeout: "OptionalTimeout" = gapic_v1.method.DEFAULT # use api_core default
+ # Use ConstantTimeout instead of api_core default because the default
+ # value results in retries with zero deadline.
+ # Refer https://github.com/googleapis/python-api-core/issues/654
+ timeout: "OptionalTimeout" = ConstantTimeout(60)
(
"Timeout settings for message publishing by the client. It should be "
"compatible with :class:`~.pubsub_v1.types.TimeoutType`."
)
+ enable_open_telemetry_tracing: bool = False # disabled by default
+ """
+ Open Telemetry tracing is enabled if this is set to True.
+
+ Warning: traces are subject to change. The name and attributes of a span might
+ change without notice. Only use run traces interactively. Don't use in
+ automation. Running non-interactive traces can cause problems if the underlying
+ trace architecture changes without notice.
+ """
+
# Define the type class and default values for flow control settings.
#
diff --git a/google/pubsub/__init__.py b/google/pubsub/__init__.py
index d6d0a00ff..b61343e55 100644
--- a/google/pubsub/__init__.py
+++ b/google/pubsub/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
from google.pubsub_v1.services.subscriber.async_client import SubscriberAsyncClient
from google.pubsub_v1.types.pubsub import AcknowledgeRequest
+from google.pubsub_v1.types.pubsub import AIInference
from google.pubsub_v1.types.pubsub import BigQueryConfig
from google.pubsub_v1.types.pubsub import CloudStorageConfig
from google.pubsub_v1.types.pubsub import CreateSnapshotRequest
@@ -42,6 +43,8 @@
from google.pubsub_v1.types.pubsub import GetSubscriptionRequest
from google.pubsub_v1.types.pubsub import GetTopicRequest
from google.pubsub_v1.types.pubsub import IngestionDataSourceSettings
+from google.pubsub_v1.types.pubsub import IngestionFailureEvent
+from google.pubsub_v1.types.pubsub import JavaScriptUDF
from google.pubsub_v1.types.pubsub import ListSnapshotsRequest
from google.pubsub_v1.types.pubsub import ListSnapshotsResponse
from google.pubsub_v1.types.pubsub import ListSubscriptionsRequest
@@ -53,8 +56,10 @@
from google.pubsub_v1.types.pubsub import ListTopicSubscriptionsRequest
from google.pubsub_v1.types.pubsub import ListTopicSubscriptionsResponse
from google.pubsub_v1.types.pubsub import MessageStoragePolicy
+from google.pubsub_v1.types.pubsub import MessageTransform
from google.pubsub_v1.types.pubsub import ModifyAckDeadlineRequest
from google.pubsub_v1.types.pubsub import ModifyPushConfigRequest
+from google.pubsub_v1.types.pubsub import PlatformLogsSettings
from google.pubsub_v1.types.pubsub import PublishRequest
from google.pubsub_v1.types.pubsub import PublishResponse
from google.pubsub_v1.types.pubsub import PubsubMessage
@@ -100,6 +105,7 @@
"SubscriberClient",
"SubscriberAsyncClient",
"AcknowledgeRequest",
+ "AIInference",
"BigQueryConfig",
"CloudStorageConfig",
"CreateSnapshotRequest",
@@ -114,6 +120,8 @@
"GetSubscriptionRequest",
"GetTopicRequest",
"IngestionDataSourceSettings",
+ "IngestionFailureEvent",
+ "JavaScriptUDF",
"ListSnapshotsRequest",
"ListSnapshotsResponse",
"ListSubscriptionsRequest",
@@ -125,8 +133,10 @@
"ListTopicSubscriptionsRequest",
"ListTopicSubscriptionsResponse",
"MessageStoragePolicy",
+ "MessageTransform",
"ModifyAckDeadlineRequest",
"ModifyPushConfigRequest",
+ "PlatformLogsSettings",
"PublishRequest",
"PublishResponse",
"PubsubMessage",
diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py
index f01e1d3a5..6d72a226d 100644
--- a/google/pubsub/gapic_version.py
+++ b/google/pubsub/gapic_version.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2022 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-__version__ = "2.23.0" # {x-release-please-version}
+__version__ = "2.35.0" # {x-release-please-version}
diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py
index 61b89e6b1..5d7a6518c 100644
--- a/google/pubsub_v1/__init__.py
+++ b/google/pubsub_v1/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,8 +15,18 @@
#
from google.pubsub_v1 import gapic_version as package_version
+import google.api_core as api_core
+import sys
+
__version__ = package_version.__version__
+if sys.version_info >= (3, 8): # pragma: NO COVER
+ from importlib import metadata
+else: # pragma: NO COVER
+ # TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
+ # this code path once we drop support for Python 3.7
+ import importlib_metadata as metadata
+
from .services.publisher import PublisherClient
from .services.publisher import PublisherAsyncClient
@@ -26,6 +36,7 @@
from .services.subscriber import SubscriberAsyncClient
from .types.pubsub import AcknowledgeRequest
+from .types.pubsub import AIInference
from .types.pubsub import BigQueryConfig
from .types.pubsub import CloudStorageConfig
from .types.pubsub import CreateSnapshotRequest
@@ -40,6 +51,8 @@
from .types.pubsub import GetSubscriptionRequest
from .types.pubsub import GetTopicRequest
from .types.pubsub import IngestionDataSourceSettings
+from .types.pubsub import IngestionFailureEvent
+from .types.pubsub import JavaScriptUDF
from .types.pubsub import ListSnapshotsRequest
from .types.pubsub import ListSnapshotsResponse
from .types.pubsub import ListSubscriptionsRequest
@@ -51,8 +64,10 @@
from .types.pubsub import ListTopicSubscriptionsRequest
from .types.pubsub import ListTopicSubscriptionsResponse
from .types.pubsub import MessageStoragePolicy
+from .types.pubsub import MessageTransform
from .types.pubsub import ModifyAckDeadlineRequest
from .types.pubsub import ModifyPushConfigRequest
+from .types.pubsub import PlatformLogsSettings
from .types.pubsub import PublishRequest
from .types.pubsub import PublishResponse
from .types.pubsub import PubsubMessage
@@ -90,10 +105,105 @@
from .types.schema import Encoding
from .types.schema import SchemaView
+if hasattr(api_core, "check_python_version") and hasattr(
+ api_core, "check_dependency_versions"
+): # pragma: NO COVER
+ api_core.check_python_version("google.pubsub_v1") # type: ignore
+ api_core.check_dependency_versions("google.pubsub_v1") # type: ignore
+else: # pragma: NO COVER
+ # An older version of api_core is installed which does not define the
+ # functions above. We do equivalent checks manually.
+ try:
+ import warnings
+ import sys
+
+ _py_version_str = sys.version.split()[0]
+ _package_label = "google.pubsub_v1"
+ if sys.version_info < (3, 9):
+ warnings.warn(
+ "You are using a non-supported Python version "
+ + f"({_py_version_str}). Google will not post any further "
+ + f"updates to {_package_label} supporting this Python version. "
+ + "Please upgrade to the latest Python version, or at "
+ + f"least to Python 3.9, and then update {_package_label}.",
+ FutureWarning,
+ )
+ if sys.version_info[:2] == (3, 9):
+ warnings.warn(
+ f"You are using a Python version ({_py_version_str}) "
+ + f"which Google will stop supporting in {_package_label} in "
+ + "January 2026. Please "
+ + "upgrade to the latest Python version, or at "
+ + "least to Python 3.10, before then, and "
+ + f"then update {_package_label}.",
+ FutureWarning,
+ )
+
+ def parse_version_to_tuple(version_string: str):
+ """Safely converts a semantic version string to a comparable tuple of integers.
+ Example: "4.25.8" -> (4, 25, 8)
+ Ignores non-numeric parts and handles common version formats.
+ Args:
+ version_string: Version string in the format "x.y.z" or "x.y.z"
+ Returns:
+ Tuple of integers for the parsed version string.
+ """
+ parts = []
+ for part in version_string.split("."):
+ try:
+ parts.append(int(part))
+ except ValueError:
+ # If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
+ # This is a simplification compared to 'packaging.parse_version', but sufficient
+ # for comparing strictly numeric semantic versions.
+ break
+ return tuple(parts)
+
+ def _get_version(dependency_name):
+ try:
+ version_string: str = metadata.version(dependency_name)
+ parsed_version = parse_version_to_tuple(version_string)
+ return (parsed_version, version_string)
+ except Exception:
+ # Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
+ # or errors during parse_version_to_tuple
+ return (None, "--")
+
+ _dependency_package = "google.protobuf"
+ _next_supported_version = "4.25.8"
+ _next_supported_version_tuple = (4, 25, 8)
+ _recommendation = " (we recommend 6.x)"
+ (_version_used, _version_used_string) = _get_version(_dependency_package)
+ if _version_used and _version_used < _next_supported_version_tuple:
+ warnings.warn(
+ f"Package {_package_label} depends on "
+ + f"{_dependency_package}, currently installed at version "
+ + f"{_version_used_string}. Future updates to "
+ + f"{_package_label} will require {_dependency_package} at "
+ + f"version {_next_supported_version} or higher{_recommendation}."
+ + " Please ensure "
+ + "that either (a) your Python environment doesn't pin the "
+ + f"version of {_dependency_package}, so that updates to "
+ + f"{_package_label} can require the higher version, or "
+ + "(b) you manually update your Python environment to use at "
+ + f"least version {_next_supported_version} of "
+ + f"{_dependency_package}.",
+ FutureWarning,
+ )
+ except Exception:
+ warnings.warn(
+ "Could not determine the version of Python "
+ + "currently being used. To continue receiving "
+ + "updates for {_package_label}, ensure you are "
+ + "using a supported version of Python; see "
+ + "https://devguide.python.org/versions/"
+ )
+
__all__ = (
"PublisherAsyncClient",
"SchemaServiceAsyncClient",
"SubscriberAsyncClient",
+ "AIInference",
"AcknowledgeRequest",
"BigQueryConfig",
"CloudStorageConfig",
@@ -115,6 +225,8 @@
"GetSubscriptionRequest",
"GetTopicRequest",
"IngestionDataSourceSettings",
+ "IngestionFailureEvent",
+ "JavaScriptUDF",
"ListSchemaRevisionsRequest",
"ListSchemaRevisionsResponse",
"ListSchemasRequest",
@@ -130,8 +242,10 @@
"ListTopicsRequest",
"ListTopicsResponse",
"MessageStoragePolicy",
+ "MessageTransform",
"ModifyAckDeadlineRequest",
"ModifyPushConfigRequest",
+ "PlatformLogsSettings",
"PublishRequest",
"PublishResponse",
"PublisherClient",
diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py
index f01e1d3a5..6d72a226d 100644
--- a/google/pubsub_v1/gapic_version.py
+++ b/google/pubsub_v1/gapic_version.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2022 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-__version__ = "2.23.0" # {x-release-please-version}
+__version__ = "2.35.0" # {x-release-please-version}
diff --git a/google/pubsub_v1/services/__init__.py b/google/pubsub_v1/services/__init__.py
index 8f6cf0682..cbf94b283 100644
--- a/google/pubsub_v1/services/__init__.py
+++ b/google/pubsub_v1/services/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/google/pubsub_v1/services/publisher/__init__.py b/google/pubsub_v1/services/publisher/__init__.py
index 7e1e8e5ce..6c1355801 100644
--- a/google/pubsub_v1/services/publisher/__init__.py
+++ b/google/pubsub_v1/services/publisher/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py
index eda259ad5..9f52347d4 100644
--- a/google/pubsub_v1/services/publisher/async_client.py
+++ b/google/pubsub_v1/services/publisher/async_client.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging as std_logging
from collections import OrderedDict
import re
from typing import (
@@ -36,6 +37,7 @@
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
try:
@@ -54,6 +56,15 @@
from .transports.grpc_asyncio import PublisherGrpcAsyncIOTransport
from .client import PublisherClient
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
class PublisherAsyncClient:
"""The service that an application uses to manipulate topics,
@@ -69,8 +80,12 @@ class PublisherAsyncClient:
_DEFAULT_ENDPOINT_TEMPLATE = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE
_DEFAULT_UNIVERSE = PublisherClient._DEFAULT_UNIVERSE
+ crypto_key_path = staticmethod(PublisherClient.crypto_key_path)
+ parse_crypto_key_path = staticmethod(PublisherClient.parse_crypto_key_path)
schema_path = staticmethod(PublisherClient.schema_path)
parse_schema_path = staticmethod(PublisherClient.parse_schema_path)
+ snapshot_path = staticmethod(PublisherClient.snapshot_path)
+ parse_snapshot_path = staticmethod(PublisherClient.parse_snapshot_path)
subscription_path = staticmethod(PublisherClient.subscription_path)
parse_subscription_path = staticmethod(PublisherClient.parse_subscription_path)
topic_path = staticmethod(PublisherClient.topic_path)
@@ -259,6 +274,28 @@ def __init__(
client_info=client_info,
)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.pubsub_v1.PublisherAsyncClient`.",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "universeDomain": getattr(
+ self._client._transport._credentials, "universe_domain", ""
+ ),
+ "credentialsType": f"{type(self._client._transport._credentials).__module__}.{type(self._client._transport._credentials).__qualname__}",
+ "credentialsInfo": getattr(
+ self.transport._credentials, "get_cred_info", lambda: None
+ )(),
+ }
+ if hasattr(self._client._transport, "_credentials")
+ else {
+ "serviceName": "google.pubsub.v1.Publisher",
+ "credentialsType": None,
+ },
+ )
+
async def create_topic(
self,
request: Optional[Union[pubsub.Topic, dict]] = None,
@@ -266,7 +303,7 @@ async def create_topic(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Creates the given topic with the given name. See the [resource
name rules]
@@ -302,14 +339,14 @@ async def sample_create_topic():
request (Optional[Union[google.pubsub_v1.types.Topic, dict]]):
The request object. A topic resource.
name (:class:`str`):
- Required. The name of the topic. It must have the format
- ``"projects/{project}/topics/{topic}"``. ``{topic}``
- must start with a letter, and contain only letters
- (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``),
- underscores (``_``), periods (``.``), tildes (``~``),
- plus (``+``) or percent signs (``%``). It must be
- between 3 and 255 characters in length, and it must not
- start with ``"goog"``.
+ Required. Identifier. The name of the topic. It must
+ have the format ``"projects/{project}/topics/{topic}"``.
+ ``{topic}`` must start with a letter, and contain only
+ letters (``[A-Za-z]``), numbers (``[0-9]``), dashes
+ (``-``), underscores (``_``), periods (``.``), tildes
+ (``~``), plus (``+``) or percent signs (``%``). It must
+ be between 3 and 255 characters in length, and it must
+ not start with ``"goog"``.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -318,8 +355,10 @@ async def sample_create_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Topic:
@@ -328,7 +367,10 @@ async def sample_create_topic():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -379,7 +421,7 @@ async def update_topic(
update_mask: Optional[field_mask_pb2.FieldMask] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Updates an existing topic by updating the fields
specified in the update mask. Note that certain
@@ -438,8 +480,10 @@ async def sample_update_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Topic:
@@ -448,7 +492,10 @@ async def sample_update_topic():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic, update_mask])
+ flattened_params = [topic, update_mask]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -503,7 +550,7 @@ async def publish(
messages: Optional[MutableSequence[pubsub.PubsubMessage]] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.PublishResponse:
r"""Adds one or more messages to the topic. Returns ``NOT_FOUND`` if
the topic does not exist.
@@ -554,8 +601,10 @@ async def sample_publish():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.PublishResponse:
@@ -564,7 +613,10 @@ async def sample_publish():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic, messages])
+ flattened_params = [topic, messages]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -614,7 +666,7 @@ async def get_topic(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Gets the configuration of a topic.
@@ -658,8 +710,10 @@ async def sample_get_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Topic:
@@ -668,7 +722,10 @@ async def sample_get_topic():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -718,7 +775,7 @@ async def list_topics(
project: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListTopicsAsyncPager:
r"""Lists matching topics.
@@ -763,8 +820,10 @@ async def sample_list_topics():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.publisher.pagers.ListTopicsAsyncPager:
@@ -777,7 +836,10 @@ async def sample_list_topics():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([project])
+ flattened_params = [project]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -838,7 +900,7 @@ async def list_topic_subscriptions(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListTopicSubscriptionsAsyncPager:
r"""Lists the names of the attached subscriptions on this
topic.
@@ -885,8 +947,10 @@ async def sample_list_topic_subscriptions():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsAsyncPager:
@@ -899,7 +963,10 @@ async def sample_list_topic_subscriptions():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -960,7 +1027,7 @@ async def list_topic_snapshots(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListTopicSnapshotsAsyncPager:
r"""Lists the names of the snapshots on this topic. Snapshots are
used in
@@ -1011,8 +1078,10 @@ async def sample_list_topic_snapshots():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsAsyncPager:
@@ -1025,7 +1094,10 @@ async def sample_list_topic_snapshots():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1086,7 +1158,7 @@ async def delete_topic(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Deletes the topic with the given name. Returns ``NOT_FOUND`` if
the topic does not exist. After a topic is deleted, a new topic
@@ -1132,13 +1204,18 @@ async def sample_delete_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1184,7 +1261,7 @@ async def detach_subscription(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.DetachSubscriptionResponse:
r"""Detaches a subscription from this topic. All messages retained
in the subscription are dropped. Subsequent ``Pull`` and
@@ -1226,8 +1303,10 @@ async def sample_detach_subscription():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.DetachSubscriptionResponse:
@@ -1275,22 +1354,24 @@ async def set_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Sets the IAM access control policy on the specified function.
Replaces any existing policy.
Args:
- request (:class:`~.policy_pb2.SetIamPolicyRequest`):
+ request (:class:`~.iam_policy_pb2.SetIamPolicyRequest`):
The request object. Request message for `SetIamPolicy`
method.
retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -1310,6 +1391,7 @@ async def set_iam_policy(
**JSON Example**
::
+
{
"bindings": [
{
@@ -1366,11 +1448,7 @@ async def set_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.set_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[self._client._transport.set_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1398,23 +1476,25 @@ async def get_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Gets the IAM access control policy for a function.
- Returns an empty policy if the function exists and does
- not have a policy set.
+ Returns an empty policy if the function exists and does not have a
+ policy set.
Args:
request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`):
The request object. Request message for `GetIamPolicy`
method.
- retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
- should be retried.
+ retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if
+ any, should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -1491,11 +1571,7 @@ async def get_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.get_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[self._client._transport.get_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1523,26 +1599,28 @@ async def test_iam_permissions(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
- r"""Tests the specified permissions against the IAM access control
+ r"""Tests the specified IAM permissions against the IAM access control
policy for a function.
- If the function does not exist, this will
- return an empty set of permissions, not a NOT_FOUND error.
+ If the function does not exist, this will return an empty set
+ of permissions, not a NOT_FOUND error.
Args:
request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`):
The request object. Request message for
`TestIamPermissions` method.
- retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
- should be retried.
+ retry (google.api_core.retry_async.AsyncRetry): Designation of what errors,
+ if any, should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
- ~iam_policy_pb2.PolicyTestIamPermissionsResponse:
+ ~.iam_policy_pb2.TestIamPermissionsResponse:
Response message for ``TestIamPermissions`` method.
"""
# Create or coerce a protobuf request object.
@@ -1554,11 +1632,9 @@ async def test_iam_permissions(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.test_iam_permissions,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[
+ self._client._transport.test_iam_permissions
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1591,5 +1667,8 @@ async def __aexit__(self, exc_type, exc, tb):
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
__all__ = ("PublisherAsyncClient",)
diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py
index 5a4b5a6ff..7467b7540 100644
--- a/google/pubsub_v1/services/publisher/client.py
+++ b/google/pubsub_v1/services/publisher/client.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
# limitations under the License.
#
from collections import OrderedDict
+from http import HTTPStatus
+import json
+import logging as std_logging
import functools
import os
import re
@@ -44,12 +47,22 @@
from google.auth.transport.grpc import SslCredentials # type: ignore
from google.auth.exceptions import MutualTLSChannelError # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
try:
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
OptionalRetry = Union[retries.Retry, object, None] # type: ignore
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
from google.protobuf import duration_pb2 # type: ignore
@@ -155,6 +168,34 @@ def _get_default_mtls_endpoint(api_endpoint):
_DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}"
_DEFAULT_UNIVERSE = "googleapis.com"
+ @staticmethod
+ def _use_client_cert_effective():
+ """Returns whether client certificate should be used for mTLS if the
+ google-auth version supports should_use_client_cert automatic mTLS enablement.
+
+ Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
+
+ Returns:
+ bool: whether client certificate should be used for mTLS
+ Raises:
+ ValueError: (If using a version of google-auth without should_use_client_cert and
+ GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
+ """
+ # check if google-auth version supports should_use_client_cert for automatic mTLS enablement
+ if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
+ return mtls.should_use_client_cert()
+ else: # pragma: NO COVER
+ # if unsupported, fallback to reading from env var
+ use_client_cert_str = os.getenv(
+ "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
+ ).lower()
+ if use_client_cert_str not in ("true", "false"):
+ raise ValueError(
+ "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
+ " either `true` or `false`"
+ )
+ return use_client_cert_str == "true"
+
@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -202,6 +243,30 @@ def transport(self) -> PublisherTransport:
"""
return self._transport
+ @staticmethod
+ def crypto_key_path(
+ project: str,
+ location: str,
+ key_ring: str,
+ crypto_key: str,
+ ) -> str:
+ """Returns a fully-qualified crypto_key string."""
+ return "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}".format(
+ project=project,
+ location=location,
+ key_ring=key_ring,
+ crypto_key=crypto_key,
+ )
+
+ @staticmethod
+ def parse_crypto_key_path(path: str) -> Dict[str, str]:
+ """Parses a crypto_key path into its component segments."""
+ m = re.match(
+ r"^projects/(?P.+?)/locations/(?P.+?)/keyRings/(?P.+?)/cryptoKeys/(?P.+?)$",
+ path,
+ )
+ return m.groupdict() if m else {}
+
@staticmethod
def schema_path(
project: str,
@@ -219,6 +284,23 @@ def parse_schema_path(path: str) -> Dict[str, str]:
m = re.match(r"^projects/(?P.+?)/schemas/(?P.+?)$", path)
return m.groupdict() if m else {}
+ @staticmethod
+ def snapshot_path(
+ project: str,
+ snapshot: str,
+ ) -> str:
+ """Returns a fully-qualified snapshot string."""
+ return "projects/{project}/snapshots/{snapshot}".format(
+ project=project,
+ snapshot=snapshot,
+ )
+
+ @staticmethod
+ def parse_snapshot_path(path: str) -> Dict[str, str]:
+ """Parses a snapshot path into its component segments."""
+ m = re.match(r"^projects/(?P.+?)/snapshots/(?P.+?)$", path)
+ return m.groupdict() if m else {}
+
@staticmethod
def subscription_path(
project: str,
@@ -373,12 +455,8 @@ def get_mtls_endpoint_and_cert_source(
)
if client_options is None:
client_options = client_options_lib.ClientOptions()
- use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
+ use_client_cert = PublisherClient._use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
- if use_client_cert not in ("true", "false"):
- raise ValueError(
- "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError(
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
@@ -386,7 +464,7 @@ def get_mtls_endpoint_and_cert_source(
# Figure out the client cert source to use.
client_cert_source = None
- if use_client_cert == "true":
+ if use_client_cert:
if client_options.client_cert_source:
client_cert_source = client_options.client_cert_source
elif mtls.has_default_client_cert_source():
@@ -418,20 +496,14 @@ def _read_environment_variables():
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
is not any of ["auto", "never", "always"].
"""
- use_client_cert = os.getenv(
- "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
- ).lower()
+ use_client_cert = PublisherClient._use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
- if use_client_cert not in ("true", "false"):
- raise ValueError(
- "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError(
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
+ return use_client_cert, use_mtls_endpoint, universe_domain_env
@staticmethod
def _get_client_cert_source(provided_cert_source, use_cert_flag):
@@ -511,52 +583,45 @@ def _get_universe_domain(
raise ValueError("Universe Domain cannot be an empty string.")
return universe_domain
- @staticmethod
- def _compare_universes(
- client_universe: str, credentials: ga_credentials.Credentials
- ) -> bool:
- """Returns True iff the universe domains used by the client and credentials match.
-
- Args:
- client_universe (str): The universe domain configured via the client options.
- credentials (ga_credentials.Credentials): The credentials being used in the client.
+ def _validate_universe_domain(self):
+ """Validates client's and credentials' universe domains are consistent.
Returns:
- bool: True iff client_universe matches the universe in credentials.
+ bool: True iff the configured universe domain is valid.
Raises:
- ValueError: when client_universe does not match the universe in credentials.
+ ValueError: If the configured universe domain is not valid.
"""
- default_universe = PublisherClient._DEFAULT_UNIVERSE
- credentials_universe = getattr(credentials, "universe_domain", default_universe)
-
- if client_universe != credentials_universe:
- raise ValueError(
- "The configured universe domain "
- f"({client_universe}) does not match the universe domain "
- f"found in the credentials ({credentials_universe}). "
- "If you haven't configured the universe domain explicitly, "
- f"`{default_universe}` is the default."
- )
+ # NOTE (b/349488459): universe validation is disabled until further notice.
return True
- def _validate_universe_domain(self):
- """Validates client's and credentials' universe domains are consistent.
-
- Returns:
- bool: True iff the configured universe domain is valid.
+ def _add_cred_info_for_auth_errors(
+ self, error: core_exceptions.GoogleAPICallError
+ ) -> None:
+ """Adds credential info string to error details for 401/403/404 errors.
- Raises:
- ValueError: If the configured universe domain is not valid.
+ Args:
+ error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info.
"""
- self._is_universe_domain_valid = (
- self._is_universe_domain_valid
- or PublisherClient._compare_universes(
- self.universe_domain, self.transport._credentials
- )
- )
- return self._is_universe_domain_valid
+ if error.code not in [
+ HTTPStatus.UNAUTHORIZED,
+ HTTPStatus.FORBIDDEN,
+ HTTPStatus.NOT_FOUND,
+ ]:
+ return
+
+ cred = self._transport._credentials
+
+ # get_cred_info is only available in google-auth>=2.35.0
+ if not hasattr(cred, "get_cred_info"):
+ return
+
+ # ignore the type check since pypy test fails when get_cred_info
+ # is not available
+ cred_info = cred.get_cred_info() # type: ignore
+ if cred_info and hasattr(error._details, "append"):
+ error._details.append(json.dumps(cred_info))
@property
def api_endpoint(self):
@@ -662,6 +727,10 @@ def __init__(
# Initialize the universe domain validation.
self._is_universe_domain_valid = False
+ if CLIENT_LOGGING_SUPPORTED: # pragma: NO COVER
+ # Setup logging.
+ client_logging.initialize_logging()
+
api_key_value = getattr(self._client_options, "api_key", None)
if api_key_value and credentials:
raise ValueError(
@@ -715,7 +784,7 @@ def __init__(
emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST")
if emulator_host:
- if issubclass(transport_init, type(self)._transport_registry["grpc"]):
+ if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore
channel = grpc.insecure_channel(target=emulator_host)
else:
channel = grpc.aio.insecure_channel(target=emulator_host)
@@ -733,6 +802,29 @@ def __init__(
api_audience=self._client_options.api_audience,
)
+ if "async" not in str(self._transport):
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.pubsub_v1.PublisherClient`.",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "universeDomain": getattr(
+ self._transport._credentials, "universe_domain", ""
+ ),
+ "credentialsType": f"{type(self._transport._credentials).__module__}.{type(self._transport._credentials).__qualname__}",
+ "credentialsInfo": getattr(
+ self.transport._credentials, "get_cred_info", lambda: None
+ )(),
+ }
+ if hasattr(self._transport, "_credentials")
+ else {
+ "serviceName": "google.pubsub.v1.Publisher",
+ "credentialsType": None,
+ },
+ )
+
def create_topic(
self,
request: Optional[Union[pubsub.Topic, dict]] = None,
@@ -740,7 +832,7 @@ def create_topic(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Creates the given topic with the given name. See the [resource
name rules]
@@ -776,14 +868,14 @@ def sample_create_topic():
request (Union[google.pubsub_v1.types.Topic, dict]):
The request object. A topic resource.
name (str):
- Required. The name of the topic. It must have the format
- ``"projects/{project}/topics/{topic}"``. ``{topic}``
- must start with a letter, and contain only letters
- (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``),
- underscores (``_``), periods (``.``), tildes (``~``),
- plus (``+``) or percent signs (``%``). It must be
- between 3 and 255 characters in length, and it must not
- start with ``"goog"``.
+ Required. Identifier. The name of the topic. It must
+ have the format ``"projects/{project}/topics/{topic}"``.
+ ``{topic}`` must start with a letter, and contain only
+ letters (``[A-Za-z]``), numbers (``[0-9]``), dashes
+ (``-``), underscores (``_``), periods (``.``), tildes
+ (``~``), plus (``+``) or percent signs (``%``). It must
+ be between 3 and 255 characters in length, and it must
+ not start with ``"goog"``.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -792,8 +884,10 @@ def sample_create_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Topic:
@@ -802,7 +896,10 @@ def sample_create_topic():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -850,7 +947,7 @@ def update_topic(
update_mask: Optional[field_mask_pb2.FieldMask] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Updates an existing topic by updating the fields
specified in the update mask. Note that certain
@@ -909,8 +1006,10 @@ def sample_update_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Topic:
@@ -919,7 +1018,10 @@ def sample_update_topic():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic, update_mask])
+ flattened_params = [topic, update_mask]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -971,7 +1073,7 @@ def publish(
messages: Optional[MutableSequence[pubsub.PubsubMessage]] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.PublishResponse:
r"""Adds one or more messages to the topic. Returns ``NOT_FOUND`` if
the topic does not exist.
@@ -1022,8 +1124,10 @@ def sample_publish():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.PublishResponse:
@@ -1032,7 +1136,10 @@ def sample_publish():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic, messages])
+ flattened_params = [topic, messages]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1081,7 +1188,7 @@ def get_topic(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Gets the configuration of a topic.
@@ -1125,8 +1232,10 @@ def sample_get_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Topic:
@@ -1135,7 +1244,10 @@ def sample_get_topic():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1182,7 +1294,7 @@ def list_topics(
project: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListTopicsPager:
r"""Lists matching topics.
@@ -1227,8 +1339,10 @@ def sample_list_topics():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.publisher.pagers.ListTopicsPager:
@@ -1241,7 +1355,10 @@ def sample_list_topics():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([project])
+ flattened_params = [project]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1299,7 +1416,7 @@ def list_topic_subscriptions(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListTopicSubscriptionsPager:
r"""Lists the names of the attached subscriptions on this
topic.
@@ -1346,8 +1463,10 @@ def sample_list_topic_subscriptions():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsPager:
@@ -1360,7 +1479,10 @@ def sample_list_topic_subscriptions():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1418,7 +1540,7 @@ def list_topic_snapshots(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListTopicSnapshotsPager:
r"""Lists the names of the snapshots on this topic. Snapshots are
used in
@@ -1469,8 +1591,10 @@ def sample_list_topic_snapshots():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsPager:
@@ -1483,7 +1607,10 @@ def sample_list_topic_snapshots():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1541,7 +1668,7 @@ def delete_topic(
topic: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Deletes the topic with the given name. Returns ``NOT_FOUND`` if
the topic does not exist. After a topic is deleted, a new topic
@@ -1587,13 +1714,18 @@ def sample_delete_topic():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([topic])
+ flattened_params = [topic]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1636,7 +1768,7 @@ def detach_subscription(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.DetachSubscriptionResponse:
r"""Detaches a subscription from this topic. All messages retained
in the subscription are dropped. Subsequent ``Pull`` and
@@ -1678,8 +1810,10 @@ def sample_detach_subscription():
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.DetachSubscriptionResponse:
@@ -1738,7 +1872,7 @@ def set_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Sets the IAM access control policy on the specified function.
@@ -1752,8 +1886,10 @@ def set_iam_policy(
should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -1830,11 +1966,7 @@ def set_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.set_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.set_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1845,16 +1977,20 @@ def set_iam_policy(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
def get_iam_policy(
self,
@@ -1862,7 +1998,7 @@ def get_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Gets the IAM access control policy for a function.
@@ -1877,8 +2013,10 @@ def get_iam_policy(
any, should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -1955,11 +2093,7 @@ def get_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.get_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.get_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1970,16 +2104,20 @@ def get_iam_policy(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
def test_iam_permissions(
self,
@@ -1987,7 +2125,7 @@ def test_iam_permissions(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: TimeoutType = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
r"""Tests the specified IAM permissions against the IAM access control
policy for a function.
@@ -2003,8 +2141,10 @@ def test_iam_permissions(
if any, should be retried.
timeout (TimeoutType):
The timeout for this request.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.iam_policy_pb2.TestIamPermissionsResponse:
Response message for ``TestIamPermissions`` method.
@@ -2018,11 +2158,7 @@ def test_iam_permissions(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.test_iam_permissions,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2033,21 +2169,27 @@ def test_iam_permissions(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
__all__ = ("PublisherClient",)
diff --git a/google/pubsub_v1/services/publisher/pagers.py b/google/pubsub_v1/services/publisher/pagers.py
index de3490c39..162d9da79 100644
--- a/google/pubsub_v1/services/publisher/pagers.py
+++ b/google/pubsub_v1/services/publisher/pagers.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@ def __init__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -80,8 +80,10 @@ def __init__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListTopicsRequest(request)
@@ -140,7 +142,7 @@ def __init__(
*,
retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -154,8 +156,10 @@ def __init__(
retry (google.api_core.retry.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListTopicsRequest(request)
@@ -218,7 +222,7 @@ def __init__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -232,8 +236,10 @@ def __init__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListTopicSubscriptionsRequest(request)
@@ -292,7 +298,7 @@ def __init__(
*,
retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -306,8 +312,10 @@ def __init__(
retry (google.api_core.retry.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListTopicSubscriptionsRequest(request)
@@ -370,7 +378,7 @@ def __init__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -384,8 +392,10 @@ def __init__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListTopicSnapshotsRequest(request)
@@ -444,7 +454,7 @@ def __init__(
*,
retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -458,8 +468,10 @@ def __init__(
retry (google.api_core.retry.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListTopicSnapshotsRequest(request)
diff --git a/google/pubsub_v1/services/publisher/transports/README.rst b/google/pubsub_v1/services/publisher/transports/README.rst
new file mode 100644
index 000000000..489748f4d
--- /dev/null
+++ b/google/pubsub_v1/services/publisher/transports/README.rst
@@ -0,0 +1,9 @@
+
+transport inheritance structure
+_______________________________
+
+`PublisherTransport` is the ABC for all transports.
+- public child `PublisherGrpcTransport` for sync gRPC transport (defined in `grpc.py`).
+- public child `PublisherGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`).
+- private child `_BasePublisherRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`).
+- public child `PublisherRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`).
diff --git a/google/pubsub_v1/services/publisher/transports/__init__.py b/google/pubsub_v1/services/publisher/transports/__init__.py
index 393b9a55f..75bfa7de0 100644
--- a/google/pubsub_v1/services/publisher/transports/__init__.py
+++ b/google/pubsub_v1/services/publisher/transports/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py
index 800ba82ce..b9d6a6279 100644
--- a/google/pubsub_v1/services/publisher/transports/base.py
+++ b/google/pubsub_v1/services/publisher/transports/base.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
from google.api_core import retry as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
@@ -35,6 +36,9 @@
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
class PublisherTransport(abc.ABC):
"""Abstract transport class for Publisher."""
@@ -69,9 +73,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.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is mutually exclusive with credentials.
+ This argument is mutually exclusive with credentials. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A list of scopes.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
@@ -275,6 +280,21 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
+ self.get_iam_policy: gapic_v1.method.wrap_method(
+ self.get_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.set_iam_policy: gapic_v1.method.wrap_method(
+ self.set_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.test_iam_permissions: gapic_v1.method.wrap_method(
+ self.test_iam_permissions,
+ default_timeout=None,
+ client_info=client_info,
+ ),
}
def close(self):
diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py
index b6e07b21e..e192152d8 100644
--- a/google/pubsub_v1/services/publisher/transports/grpc.py
+++ b/google/pubsub_v1/services/publisher/transports/grpc.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import json
+import logging as std_logging
+import pickle
import warnings
from typing import Callable, Dict, Optional, Sequence, Tuple, Union
@@ -21,8 +24,11 @@
import google.auth # type: ignore
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
+from google.protobuf.json_format import MessageToJson
+import google.protobuf.message
import grpc # type: ignore
+import proto # type: ignore
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
@@ -30,6 +36,80 @@
from google.pubsub_v1.types import pubsub
from .base import PublisherTransport, DEFAULT_CLIENT_INFO
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
+
+class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER
+ def intercept_unary_unary(self, continuation, client_call_details, request):
+ logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ )
+ if logging_enabled: # pragma: NO COVER
+ request_metadata = client_call_details.metadata
+ if isinstance(request, proto.Message):
+ request_payload = type(request).to_json(request)
+ elif isinstance(request, google.protobuf.message.Message):
+ request_payload = MessageToJson(request)
+ else:
+ request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
+
+ request_metadata = {
+ key: value.decode("utf-8") if isinstance(value, bytes) else value
+ for key, value in request_metadata
+ }
+ grpc_request = {
+ "payload": request_payload,
+ "requestMethod": "grpc",
+ "metadata": dict(request_metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for {client_call_details.method}",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": str(client_call_details.method),
+ "request": grpc_request,
+ "metadata": grpc_request["metadata"],
+ },
+ )
+ response = continuation(client_call_details, request)
+ if logging_enabled: # pragma: NO COVER
+ response_metadata = response.trailing_metadata()
+ # Convert gRPC metadata `` to list of tuples
+ metadata = (
+ dict([(k, str(v)) for k, v in response_metadata])
+ if response_metadata
+ else None
+ )
+ result = response.result()
+ if isinstance(result, proto.Message):
+ response_payload = type(result).to_json(result)
+ elif isinstance(result, google.protobuf.message.Message):
+ response_payload = MessageToJson(result)
+ else:
+ response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
+ grpc_response = {
+ "payload": response_payload,
+ "metadata": metadata,
+ "status": "OK",
+ }
+ _LOGGER.debug(
+ f"Received response for {client_call_details.method}.",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": client_call_details.method,
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class PublisherGrpcTransport(PublisherTransport):
"""gRPC backend transport for Publisher.
@@ -75,9 +155,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
This argument is ignored if a ``channel`` instance is provided.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
This argument is ignored if a ``channel`` instance is provided.
+ This argument will be removed in the next major version of this library.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
ignored if a ``channel`` instance is provided.
channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]):
@@ -186,7 +267,12 @@ def __init__(
],
)
- # Wrap messages. This must be done after self._grpc_channel exists
+ self._interceptor = _LoggingClientInterceptor()
+ self._logged_channel = grpc.intercept_channel(
+ self._grpc_channel, self._interceptor
+ )
+
+ # Wrap messages. This must be done after self._logged_channel exists
self._prep_wrapped_messages(client_info)
@classmethod
@@ -207,9 +293,10 @@ def create_channel(
credentials identify this application to the service. If
none are specified, the client will attempt to ascertain
the credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is mutually exclusive with credentials.
+ This argument is mutually exclusive with credentials. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -260,7 +347,7 @@ def create_topic(self) -> Callable[[pubsub.Topic], pubsub.Topic]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_topic" not in self._stubs:
- self._stubs["create_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["create_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/CreateTopic",
request_serializer=pubsub.Topic.serialize,
response_deserializer=pubsub.Topic.deserialize,
@@ -286,7 +373,7 @@ def update_topic(self) -> Callable[[pubsub.UpdateTopicRequest], pubsub.Topic]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_topic" not in self._stubs:
- self._stubs["update_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["update_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/UpdateTopic",
request_serializer=pubsub.UpdateTopicRequest.serialize,
response_deserializer=pubsub.Topic.deserialize,
@@ -311,7 +398,7 @@ def publish(self) -> Callable[[pubsub.PublishRequest], pubsub.PublishResponse]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "publish" not in self._stubs:
- self._stubs["publish"] = self.grpc_channel.unary_unary(
+ self._stubs["publish"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/Publish",
request_serializer=pubsub.PublishRequest.serialize,
response_deserializer=pubsub.PublishResponse.deserialize,
@@ -335,7 +422,7 @@ def get_topic(self) -> Callable[[pubsub.GetTopicRequest], pubsub.Topic]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_topic" not in self._stubs:
- self._stubs["get_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["get_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/GetTopic",
request_serializer=pubsub.GetTopicRequest.serialize,
response_deserializer=pubsub.Topic.deserialize,
@@ -361,7 +448,7 @@ def list_topics(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_topics" not in self._stubs:
- self._stubs["list_topics"] = self.grpc_channel.unary_unary(
+ self._stubs["list_topics"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/ListTopics",
request_serializer=pubsub.ListTopicsRequest.serialize,
response_deserializer=pubsub.ListTopicsResponse.deserialize,
@@ -390,7 +477,7 @@ def list_topic_subscriptions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_topic_subscriptions" not in self._stubs:
- self._stubs["list_topic_subscriptions"] = self.grpc_channel.unary_unary(
+ self._stubs["list_topic_subscriptions"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/ListTopicSubscriptions",
request_serializer=pubsub.ListTopicSubscriptionsRequest.serialize,
response_deserializer=pubsub.ListTopicSubscriptionsResponse.deserialize,
@@ -423,7 +510,7 @@ def list_topic_snapshots(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_topic_snapshots" not in self._stubs:
- self._stubs["list_topic_snapshots"] = self.grpc_channel.unary_unary(
+ self._stubs["list_topic_snapshots"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/ListTopicSnapshots",
request_serializer=pubsub.ListTopicSnapshotsRequest.serialize,
response_deserializer=pubsub.ListTopicSnapshotsResponse.deserialize,
@@ -452,7 +539,7 @@ def delete_topic(self) -> Callable[[pubsub.DeleteTopicRequest], empty_pb2.Empty]
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_topic" not in self._stubs:
- self._stubs["delete_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/DeleteTopic",
request_serializer=pubsub.DeleteTopicRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -484,13 +571,16 @@ def detach_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "detach_subscription" not in self._stubs:
- self._stubs["detach_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["detach_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/DetachSubscription",
request_serializer=pubsub.DetachSubscriptionRequest.serialize,
response_deserializer=pubsub.DetachSubscriptionResponse.deserialize,
)
return self._stubs["detach_subscription"]
+ def close(self):
+ self._logged_channel.close()
+
@property
def set_iam_policy(
self,
@@ -509,7 +599,7 @@ def set_iam_policy(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "set_iam_policy" not in self._stubs:
- self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
+ self._stubs["set_iam_policy"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/SetIamPolicy",
request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
@@ -535,7 +625,7 @@ def get_iam_policy(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_iam_policy" not in self._stubs:
- self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
+ self._stubs["get_iam_policy"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/GetIamPolicy",
request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
@@ -564,16 +654,13 @@ def test_iam_permissions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "test_iam_permissions" not in self._stubs:
- self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
+ self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/TestIamPermissions",
request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
)
return self._stubs["test_iam_permissions"]
- def close(self):
- self.grpc_channel.close()
-
@property
def kind(self) -> str:
return "grpc"
diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py
index 3d98d6b51..14b9fdd06 100644
--- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py
+++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import inspect
+import json
+import pickle
+import logging as std_logging
import warnings
from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union
@@ -22,8 +26,11 @@
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
+from google.protobuf.json_format import MessageToJson
+import google.protobuf.message
import grpc # type: ignore
+import proto # type: ignore
from grpc.experimental import aio # type: ignore
from google.iam.v1 import iam_policy_pb2 # type: ignore
@@ -33,6 +40,82 @@
from .base import PublisherTransport, DEFAULT_CLIENT_INFO
from .grpc import PublisherGrpcTransport
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
+
+class _LoggingClientAIOInterceptor(
+ grpc.aio.UnaryUnaryClientInterceptor
+): # pragma: NO COVER
+ async def intercept_unary_unary(self, continuation, client_call_details, request):
+ logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ )
+ if logging_enabled: # pragma: NO COVER
+ request_metadata = client_call_details.metadata
+ if isinstance(request, proto.Message):
+ request_payload = type(request).to_json(request)
+ elif isinstance(request, google.protobuf.message.Message):
+ request_payload = MessageToJson(request)
+ else:
+ request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
+
+ request_metadata = {
+ key: value.decode("utf-8") if isinstance(value, bytes) else value
+ for key, value in request_metadata
+ }
+ grpc_request = {
+ "payload": request_payload,
+ "requestMethod": "grpc",
+ "metadata": dict(request_metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for {client_call_details.method}",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": str(client_call_details.method),
+ "request": grpc_request,
+ "metadata": grpc_request["metadata"],
+ },
+ )
+ response = await continuation(client_call_details, request)
+ if logging_enabled: # pragma: NO COVER
+ response_metadata = await response.trailing_metadata()
+ # Convert gRPC metadata `` to list of tuples
+ metadata = (
+ dict([(k, str(v)) for k, v in response_metadata])
+ if response_metadata
+ else None
+ )
+ result = await response
+ if isinstance(result, proto.Message):
+ response_payload = type(result).to_json(result)
+ elif isinstance(result, google.protobuf.message.Message):
+ response_payload = MessageToJson(result)
+ else:
+ response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
+ grpc_response = {
+ "payload": response_payload,
+ "metadata": metadata,
+ "status": "OK",
+ }
+ _LOGGER.debug(
+ f"Received response to rpc {client_call_details.method}.",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": str(client_call_details.method),
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class PublisherGrpcAsyncIOTransport(PublisherTransport):
"""gRPC AsyncIO backend transport for Publisher.
@@ -69,8 +152,9 @@ def create_channel(
credentials identify this application to the service. If
none are specified, the client will attempt to ascertain
the credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
- be loaded with :func:`google.auth.load_credentials_from_file`.
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
+ be loaded with :func:`google.auth.load_credentials_from_file`. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -121,9 +205,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
This argument is ignored if a ``channel`` instance is provided.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
This argument is ignored if a ``channel`` instance is provided.
+ This argument will be removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -232,7 +317,13 @@ def __init__(
],
)
- # Wrap messages. This must be done after self._grpc_channel exists
+ self._interceptor = _LoggingClientAIOInterceptor()
+ self._grpc_channel._unary_unary_interceptors.append(self._interceptor)
+ self._logged_channel = self._grpc_channel
+ self._wrap_with_kind = (
+ "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters
+ )
+ # Wrap messages. This must be done after self._logged_channel exists
self._prep_wrapped_messages(client_info)
@property
@@ -264,7 +355,7 @@ def create_topic(self) -> Callable[[pubsub.Topic], Awaitable[pubsub.Topic]]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_topic" not in self._stubs:
- self._stubs["create_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["create_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/CreateTopic",
request_serializer=pubsub.Topic.serialize,
response_deserializer=pubsub.Topic.deserialize,
@@ -292,7 +383,7 @@ def update_topic(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_topic" not in self._stubs:
- self._stubs["update_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["update_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/UpdateTopic",
request_serializer=pubsub.UpdateTopicRequest.serialize,
response_deserializer=pubsub.Topic.deserialize,
@@ -319,7 +410,7 @@ def publish(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "publish" not in self._stubs:
- self._stubs["publish"] = self.grpc_channel.unary_unary(
+ self._stubs["publish"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/Publish",
request_serializer=pubsub.PublishRequest.serialize,
response_deserializer=pubsub.PublishResponse.deserialize,
@@ -343,7 +434,7 @@ def get_topic(self) -> Callable[[pubsub.GetTopicRequest], Awaitable[pubsub.Topic
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_topic" not in self._stubs:
- self._stubs["get_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["get_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/GetTopic",
request_serializer=pubsub.GetTopicRequest.serialize,
response_deserializer=pubsub.Topic.deserialize,
@@ -369,7 +460,7 @@ def list_topics(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_topics" not in self._stubs:
- self._stubs["list_topics"] = self.grpc_channel.unary_unary(
+ self._stubs["list_topics"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/ListTopics",
request_serializer=pubsub.ListTopicsRequest.serialize,
response_deserializer=pubsub.ListTopicsResponse.deserialize,
@@ -399,7 +490,7 @@ def list_topic_subscriptions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_topic_subscriptions" not in self._stubs:
- self._stubs["list_topic_subscriptions"] = self.grpc_channel.unary_unary(
+ self._stubs["list_topic_subscriptions"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/ListTopicSubscriptions",
request_serializer=pubsub.ListTopicSubscriptionsRequest.serialize,
response_deserializer=pubsub.ListTopicSubscriptionsResponse.deserialize,
@@ -432,7 +523,7 @@ def list_topic_snapshots(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_topic_snapshots" not in self._stubs:
- self._stubs["list_topic_snapshots"] = self.grpc_channel.unary_unary(
+ self._stubs["list_topic_snapshots"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/ListTopicSnapshots",
request_serializer=pubsub.ListTopicSnapshotsRequest.serialize,
response_deserializer=pubsub.ListTopicSnapshotsResponse.deserialize,
@@ -463,7 +554,7 @@ def delete_topic(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_topic" not in self._stubs:
- self._stubs["delete_topic"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_topic"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/DeleteTopic",
request_serializer=pubsub.DeleteTopicRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -495,97 +586,17 @@ def detach_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "detach_subscription" not in self._stubs:
- self._stubs["detach_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["detach_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Publisher/DetachSubscription",
request_serializer=pubsub.DetachSubscriptionRequest.serialize,
response_deserializer=pubsub.DetachSubscriptionResponse.deserialize,
)
return self._stubs["detach_subscription"]
- @property
- def set_iam_policy(
- self,
- ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]:
- r"""Return a callable for the set iam policy method over gRPC.
- Sets the IAM access control policy on the specified
- function. Replaces any existing policy.
- Returns:
- Callable[[~.SetIamPolicyRequest],
- Awaitable[~.Policy]]:
- 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 "set_iam_policy" not in self._stubs:
- self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/SetIamPolicy",
- request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
- response_deserializer=policy_pb2.Policy.FromString,
- )
- return self._stubs["set_iam_policy"]
-
- @property
- def get_iam_policy(
- self,
- ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]:
- r"""Return a callable for the get iam policy method over gRPC.
- Gets the IAM access control policy for a function.
- Returns an empty policy if the function exists and does
- not have a policy set.
- Returns:
- Callable[[~.GetIamPolicyRequest],
- Awaitable[~.Policy]]:
- 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 "get_iam_policy" not in self._stubs:
- self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/GetIamPolicy",
- request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
- response_deserializer=policy_pb2.Policy.FromString,
- )
- return self._stubs["get_iam_policy"]
-
- @property
- def test_iam_permissions(
- self,
- ) -> Callable[
- [iam_policy_pb2.TestIamPermissionsRequest],
- Awaitable[iam_policy_pb2.TestIamPermissionsResponse],
- ]:
- r"""Return a callable for the test iam permissions method over gRPC.
- Tests the specified permissions against the IAM access control
- policy for a function. If the function does not exist, this will
- return an empty set of permissions, not a NOT_FOUND error.
- Returns:
- Callable[[~.TestIamPermissionsRequest],
- Awaitable[~.TestIamPermissionsResponse]]:
- 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 "test_iam_permissions" not in self._stubs:
- self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/TestIamPermissions",
- request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
- response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
- )
- return self._stubs["test_iam_permissions"]
-
def _prep_wrapped_messages(self, client_info):
"""Precompute the wrapped methods, overriding the base class method to use async wrappers."""
self._wrapped_methods = {
- self.create_topic: gapic_v1.method_async.wrap_method(
+ self.create_topic: self._wrap_method(
self.create_topic,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -599,7 +610,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.update_topic: gapic_v1.method_async.wrap_method(
+ self.update_topic: self._wrap_method(
self.update_topic,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -613,7 +624,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.publish: gapic_v1.method_async.wrap_method(
+ self.publish: self._wrap_method(
self.publish,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -633,7 +644,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.get_topic: gapic_v1.method_async.wrap_method(
+ self.get_topic: self._wrap_method(
self.get_topic,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -649,7 +660,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.list_topics: gapic_v1.method_async.wrap_method(
+ self.list_topics: self._wrap_method(
self.list_topics,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -665,7 +676,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.list_topic_subscriptions: gapic_v1.method_async.wrap_method(
+ self.list_topic_subscriptions: self._wrap_method(
self.list_topic_subscriptions,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -681,7 +692,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.list_topic_snapshots: gapic_v1.method_async.wrap_method(
+ self.list_topic_snapshots: self._wrap_method(
self.list_topic_snapshots,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -697,7 +708,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.delete_topic: gapic_v1.method_async.wrap_method(
+ self.delete_topic: self._wrap_method(
self.delete_topic,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -711,7 +722,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.detach_subscription: gapic_v1.method_async.wrap_method(
+ self.detach_subscription: self._wrap_method(
self.detach_subscription,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -725,10 +736,114 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
+ self.get_iam_policy: self._wrap_method(
+ self.get_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.set_iam_policy: self._wrap_method(
+ self.set_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.test_iam_permissions: self._wrap_method(
+ self.test_iam_permissions,
+ default_timeout=None,
+ client_info=client_info,
+ ),
}
+ def _wrap_method(self, func, *args, **kwargs):
+ if self._wrap_with_kind: # pragma: NO COVER
+ kwargs["kind"] = self.kind
+ return gapic_v1.method_async.wrap_method(func, *args, **kwargs)
+
def close(self):
- return self.grpc_channel.close()
+ return self._logged_channel.close()
+
+ @property
+ def kind(self) -> str:
+ return "grpc_asyncio"
+
+ @property
+ def set_iam_policy(
+ self,
+ ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]:
+ r"""Return a callable for the set iam policy method over gRPC.
+ Sets the IAM access control policy on the specified
+ function. Replaces any existing policy.
+ Returns:
+ Callable[[~.SetIamPolicyRequest],
+ ~.Policy]:
+ 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 "set_iam_policy" not in self._stubs:
+ self._stubs["set_iam_policy"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/SetIamPolicy",
+ request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
+ response_deserializer=policy_pb2.Policy.FromString,
+ )
+ return self._stubs["set_iam_policy"]
+
+ @property
+ def get_iam_policy(
+ self,
+ ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]:
+ r"""Return a callable for the get iam policy method over gRPC.
+ Gets the IAM access control policy for a function.
+ Returns an empty policy if the function exists and does
+ not have a policy set.
+ Returns:
+ Callable[[~.GetIamPolicyRequest],
+ ~.Policy]:
+ 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 "get_iam_policy" not in self._stubs:
+ self._stubs["get_iam_policy"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/GetIamPolicy",
+ request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
+ response_deserializer=policy_pb2.Policy.FromString,
+ )
+ return self._stubs["get_iam_policy"]
+
+ @property
+ def test_iam_permissions(
+ self,
+ ) -> Callable[
+ [iam_policy_pb2.TestIamPermissionsRequest],
+ iam_policy_pb2.TestIamPermissionsResponse,
+ ]:
+ r"""Return a callable for the test iam permissions method over gRPC.
+ Tests the specified permissions against the IAM access control
+ policy for a function. If the function does not exist, this will
+ return an empty set of permissions, not a NOT_FOUND error.
+ Returns:
+ Callable[[~.TestIamPermissionsRequest],
+ ~.TestIamPermissionsResponse]:
+ 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 "test_iam_permissions" not in self._stubs:
+ self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/TestIamPermissions",
+ request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
+ response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
+ )
+ return self._stubs["test_iam_permissions"]
__all__ = ("PublisherGrpcAsyncIOTransport",)
diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py
index 4d70c3e64..aeb07184c 100644
--- a/google/pubsub_v1/services/publisher/transports/rest.py
+++ b/google/pubsub_v1/services/publisher/transports/rest.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,48 +13,58 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging
+import json # type: ignore
from google.auth.transport.requests import AuthorizedSession # type: ignore
-import json # type: ignore
-import grpc # type: ignore
-from google.auth.transport.grpc import SslCredentials # type: ignore
from google.auth import credentials as ga_credentials # type: ignore
from google.api_core import exceptions as core_exceptions
from google.api_core import retry as retries
from google.api_core import rest_helpers
from google.api_core import rest_streaming
-from google.api_core import path_template
from google.api_core import gapic_v1
+import google.protobuf
from google.protobuf import json_format
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
+
from requests import __version__ as requests_version
import dataclasses
-import re
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
import warnings
+
+from google.protobuf import empty_pb2 # type: ignore
+from google.pubsub_v1.types import pubsub
+
+
+from .rest_base import _BasePublisherRestTransport
+from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO
+
try:
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
OptionalRetry = Union[retries.Retry, object, None] # type: ignore
+try:
+ from google.api_core import client_logging # type: ignore
-from google.iam.v1 import iam_policy_pb2 # type: ignore
-from google.iam.v1 import policy_pb2 # type: ignore
-from google.protobuf import empty_pb2 # type: ignore
-from google.pubsub_v1.types import pubsub
-
-from .base import PublisherTransport, DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+_LOGGER = logging.getLogger(__name__)
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version,
grpc_version=None,
- rest_version=requests_version,
+ rest_version=f"requests@{requests_version}",
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
class PublisherRestInterceptor:
"""Interceptor for Publisher.
@@ -146,8 +156,8 @@ def post_update_topic(self, response):
"""
def pre_create_topic(
- self, request: pubsub.Topic, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, str]]]:
+ self, request: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]]
+ ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for create_topic
Override in a subclass to manipulate the request or metadata
@@ -158,15 +168,38 @@ def pre_create_topic(
def post_create_topic(self, response: pubsub.Topic) -> pubsub.Topic:
"""Post-rpc interceptor for create_topic
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_create_topic_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_create_topic` interceptor runs
+ before the `post_create_topic_with_metadata` interceptor.
"""
return response
+ def post_create_topic_with_metadata(
+ self, response: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]]
+ ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for create_topic
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_create_topic_with_metadata`
+ interceptor in new development instead of the `post_create_topic` interceptor.
+ When both interceptors are used, this `post_create_topic_with_metadata` interceptor runs after the
+ `post_create_topic` interceptor. The (possibly modified) response returned by
+ `post_create_topic` will be passed to
+ `post_create_topic_with_metadata`.
+ """
+ return response, metadata
+
def pre_delete_topic(
- self, request: pubsub.DeleteTopicRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.DeleteTopicRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.DeleteTopicRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.DeleteTopicRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for delete_topic
Override in a subclass to manipulate the request or metadata
@@ -177,8 +210,10 @@ def pre_delete_topic(
def pre_detach_subscription(
self,
request: pubsub.DetachSubscriptionRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.DetachSubscriptionRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.DetachSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for detach_subscription
Override in a subclass to manipulate the request or metadata
@@ -191,15 +226,42 @@ def post_detach_subscription(
) -> pubsub.DetachSubscriptionResponse:
"""Post-rpc interceptor for detach_subscription
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_detach_subscription_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_detach_subscription` interceptor runs
+ before the `post_detach_subscription_with_metadata` interceptor.
"""
return response
+ def post_detach_subscription_with_metadata(
+ self,
+ response: pubsub.DetachSubscriptionResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.DetachSubscriptionResponse, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Post-rpc interceptor for detach_subscription
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_detach_subscription_with_metadata`
+ interceptor in new development instead of the `post_detach_subscription` interceptor.
+ When both interceptors are used, this `post_detach_subscription_with_metadata` interceptor runs after the
+ `post_detach_subscription` interceptor. The (possibly modified) response returned by
+ `post_detach_subscription` will be passed to
+ `post_detach_subscription_with_metadata`.
+ """
+ return response, metadata
+
def pre_get_topic(
- self, request: pubsub.GetTopicRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.GetTopicRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.GetTopicRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.GetTopicRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for get_topic
Override in a subclass to manipulate the request or metadata
@@ -210,15 +272,38 @@ def pre_get_topic(
def post_get_topic(self, response: pubsub.Topic) -> pubsub.Topic:
"""Post-rpc interceptor for get_topic
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_get_topic_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_get_topic` interceptor runs
+ before the `post_get_topic_with_metadata` interceptor.
"""
return response
+ def post_get_topic_with_metadata(
+ self, response: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]]
+ ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for get_topic
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_get_topic_with_metadata`
+ interceptor in new development instead of the `post_get_topic` interceptor.
+ When both interceptors are used, this `post_get_topic_with_metadata` interceptor runs after the
+ `post_get_topic` interceptor. The (possibly modified) response returned by
+ `post_get_topic` will be passed to
+ `post_get_topic_with_metadata`.
+ """
+ return response, metadata
+
def pre_list_topics(
- self, request: pubsub.ListTopicsRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.ListTopicsRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.ListTopicsRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.ListTopicsRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for list_topics
Override in a subclass to manipulate the request or metadata
@@ -231,17 +316,42 @@ def post_list_topics(
) -> pubsub.ListTopicsResponse:
"""Post-rpc interceptor for list_topics
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_list_topics_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_list_topics` interceptor runs
+ before the `post_list_topics_with_metadata` interceptor.
"""
return response
+ def post_list_topics_with_metadata(
+ self,
+ response: pubsub.ListTopicsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.ListTopicsResponse, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for list_topics
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_list_topics_with_metadata`
+ interceptor in new development instead of the `post_list_topics` interceptor.
+ When both interceptors are used, this `post_list_topics_with_metadata` interceptor runs after the
+ `post_list_topics` interceptor. The (possibly modified) response returned by
+ `post_list_topics` will be passed to
+ `post_list_topics_with_metadata`.
+ """
+ return response, metadata
+
def pre_list_topic_snapshots(
self,
request: pubsub.ListTopicSnapshotsRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.ListTopicSnapshotsRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.ListTopicSnapshotsRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for list_topic_snapshots
Override in a subclass to manipulate the request or metadata
@@ -254,17 +364,44 @@ def post_list_topic_snapshots(
) -> pubsub.ListTopicSnapshotsResponse:
"""Post-rpc interceptor for list_topic_snapshots
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_list_topic_snapshots_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_list_topic_snapshots` interceptor runs
+ before the `post_list_topic_snapshots_with_metadata` interceptor.
"""
return response
+ def post_list_topic_snapshots_with_metadata(
+ self,
+ response: pubsub.ListTopicSnapshotsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.ListTopicSnapshotsResponse, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Post-rpc interceptor for list_topic_snapshots
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_list_topic_snapshots_with_metadata`
+ interceptor in new development instead of the `post_list_topic_snapshots` interceptor.
+ When both interceptors are used, this `post_list_topic_snapshots_with_metadata` interceptor runs after the
+ `post_list_topic_snapshots` interceptor. The (possibly modified) response returned by
+ `post_list_topic_snapshots` will be passed to
+ `post_list_topic_snapshots_with_metadata`.
+ """
+ return response, metadata
+
def pre_list_topic_subscriptions(
self,
request: pubsub.ListTopicSubscriptionsRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.ListTopicSubscriptionsRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.ListTopicSubscriptionsRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for list_topic_subscriptions
Override in a subclass to manipulate the request or metadata
@@ -277,15 +414,42 @@ def post_list_topic_subscriptions(
) -> pubsub.ListTopicSubscriptionsResponse:
"""Post-rpc interceptor for list_topic_subscriptions
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_list_topic_subscriptions_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_list_topic_subscriptions` interceptor runs
+ before the `post_list_topic_subscriptions_with_metadata` interceptor.
"""
return response
+ def post_list_topic_subscriptions_with_metadata(
+ self,
+ response: pubsub.ListTopicSubscriptionsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.ListTopicSubscriptionsResponse, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Post-rpc interceptor for list_topic_subscriptions
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_list_topic_subscriptions_with_metadata`
+ interceptor in new development instead of the `post_list_topic_subscriptions` interceptor.
+ When both interceptors are used, this `post_list_topic_subscriptions_with_metadata` interceptor runs after the
+ `post_list_topic_subscriptions` interceptor. The (possibly modified) response returned by
+ `post_list_topic_subscriptions` will be passed to
+ `post_list_topic_subscriptions_with_metadata`.
+ """
+ return response, metadata
+
def pre_publish(
- self, request: pubsub.PublishRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.PublishRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.PublishRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.PublishRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for publish
Override in a subclass to manipulate the request or metadata
@@ -296,15 +460,40 @@ def pre_publish(
def post_publish(self, response: pubsub.PublishResponse) -> pubsub.PublishResponse:
"""Post-rpc interceptor for publish
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_publish_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_publish` interceptor runs
+ before the `post_publish_with_metadata` interceptor.
"""
return response
+ def post_publish_with_metadata(
+ self,
+ response: pubsub.PublishResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.PublishResponse, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for publish
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_publish_with_metadata`
+ interceptor in new development instead of the `post_publish` interceptor.
+ When both interceptors are used, this `post_publish_with_metadata` interceptor runs after the
+ `post_publish` interceptor. The (possibly modified) response returned by
+ `post_publish` will be passed to
+ `post_publish_with_metadata`.
+ """
+ return response, metadata
+
def pre_update_topic(
- self, request: pubsub.UpdateTopicRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.UpdateTopicRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.UpdateTopicRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.UpdateTopicRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for update_topic
Override in a subclass to manipulate the request or metadata
@@ -315,17 +504,40 @@ def pre_update_topic(
def post_update_topic(self, response: pubsub.Topic) -> pubsub.Topic:
"""Post-rpc interceptor for update_topic
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_update_topic_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Publisher server but before
- it is returned to user code.
+ it is returned to user code. This `post_update_topic` interceptor runs
+ before the `post_update_topic_with_metadata` interceptor.
"""
return response
+ def post_update_topic_with_metadata(
+ self, response: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]]
+ ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for update_topic
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Publisher server but before it is returned to user code.
+
+ We recommend only using this `post_update_topic_with_metadata`
+ interceptor in new development instead of the `post_update_topic` interceptor.
+ When both interceptors are used, this `post_update_topic_with_metadata` interceptor runs after the
+ `post_update_topic` interceptor. The (possibly modified) response returned by
+ `post_update_topic` will be passed to
+ `post_update_topic_with_metadata`.
+ """
+ return response, metadata
+
def pre_get_iam_policy(
self,
request: iam_policy_pb2.GetIamPolicyRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for get_iam_policy
Override in a subclass to manipulate the request or metadata
@@ -345,8 +557,10 @@ def post_get_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy:
def pre_set_iam_policy(
self,
request: iam_policy_pb2.SetIamPolicyRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for set_iam_policy
Override in a subclass to manipulate the request or metadata
@@ -366,8 +580,11 @@ def post_set_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy:
def pre_test_iam_permissions(
self,
request: iam_policy_pb2.TestIamPermissionsRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.TestIamPermissionsRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.TestIamPermissionsRequest,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
"""Pre-rpc interceptor for test_iam_permissions
Override in a subclass to manipulate the request or metadata
@@ -394,8 +611,8 @@ class PublisherRestStub:
_interceptor: PublisherRestInterceptor
-class PublisherRestTransport(PublisherTransport):
- """REST backend transport for Publisher.
+class PublisherRestTransport(_BasePublisherRestTransport):
+ """REST backend synchronous transport for Publisher.
The service that an application uses to manipulate topics,
and to send messages to a topic.
@@ -405,7 +622,6 @@ class PublisherRestTransport(PublisherTransport):
and call it.
It sends JSON representations of protocol buffers over HTTP/1.1
-
"""
def __init__(
@@ -434,9 +650,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if ``channel`` is provided. This argument will be
+ removed in the next major version of this library.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
ignored if ``channel`` is provided.
client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
@@ -459,21 +676,12 @@ def __init__(
# TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
# TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
# credentials object
- maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host)
- if maybe_url_match is None:
- raise ValueError(
- f"Unexpected hostname structure: {host}"
- ) # pragma: NO COVER
-
- url_match_items = maybe_url_match.groupdict()
-
- host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
-
super().__init__(
host=host,
credentials=credentials,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ url_scheme=url_scheme,
api_audience=api_audience,
)
self._session = AuthorizedSession(
@@ -484,19 +692,32 @@ def __init__(
self._interceptor = interceptor or PublisherRestInterceptor()
self._prep_wrapped_messages(client_info)
- class _CreateTopic(PublisherRestStub):
+ class _CreateTopic(_BasePublisherRestTransport._BaseCreateTopic, PublisherRestStub):
def __hash__(self):
- return hash("CreateTopic")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.CreateTopic")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -504,7 +725,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Call the create topic method over HTTP.
@@ -514,53 +735,74 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Topic:
A topic resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "put",
- "uri": "/v1/{name=projects/*/topics/*}",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_create_topic(request, metadata)
- pb_request = pubsub.Topic.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BasePublisherRestTransport._BaseCreateTopic._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_create_topic(request, metadata)
+ transcoded_request = (
+ _BasePublisherRestTransport._BaseCreateTopic._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BasePublisherRestTransport._BaseCreateTopic._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BasePublisherRestTransport._BaseCreateTopic._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.CreateTopic",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "CreateTopic",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = PublisherRestTransport._CreateTopic._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -573,22 +815,60 @@ def __call__(
pb_resp = pubsub.Topic.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_create_topic(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_create_topic_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Topic.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.create_topic",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "CreateTopic",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _DeleteTopic(PublisherRestStub):
+ class _DeleteTopic(_BasePublisherRestTransport._BaseDeleteTopic, PublisherRestStub):
def __hash__(self):
- return hash("DeleteTopic")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.DeleteTopic")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -596,7 +876,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
):
r"""Call the delete topic method over HTTP.
@@ -606,42 +886,65 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "delete",
- "uri": "/v1/{topic=projects/*/topics/*}",
- },
- ]
- request, metadata = self._interceptor.pre_delete_topic(request, metadata)
- pb_request = pubsub.DeleteTopicRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BasePublisherRestTransport._BaseDeleteTopic._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_delete_topic(request, metadata)
+ transcoded_request = (
+ _BasePublisherRestTransport._BaseDeleteTopic._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BasePublisherRestTransport._BaseDeleteTopic._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.DeleteTopic",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "DeleteTopic",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = PublisherRestTransport._DeleteTopic._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -649,19 +952,33 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
- class _DetachSubscription(PublisherRestStub):
+ class _DetachSubscription(
+ _BasePublisherRestTransport._BaseDetachSubscription, PublisherRestStub
+ ):
def __hash__(self):
- return hash("DetachSubscription")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.DetachSubscription")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -669,7 +986,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.DetachSubscriptionResponse:
r"""Call the detach subscription method over HTTP.
@@ -680,8 +997,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.DetachSubscriptionResponse:
@@ -690,40 +1009,57 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}:detach",
- },
- ]
+ http_options = (
+ _BasePublisherRestTransport._BaseDetachSubscription._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_detach_subscription(
request, metadata
)
- pb_request = pubsub.DetachSubscriptionRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BasePublisherRestTransport._BaseDetachSubscription._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BasePublisherRestTransport._BaseDetachSubscription._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.DetachSubscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "DetachSubscription",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = PublisherRestTransport._DetachSubscription._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -736,22 +1072,62 @@ def __call__(
pb_resp = pubsub.DetachSubscriptionResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_detach_subscription(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_detach_subscription_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.DetachSubscriptionResponse.to_json(
+ response
+ )
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.detach_subscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "DetachSubscription",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _GetTopic(PublisherRestStub):
+ class _GetTopic(_BasePublisherRestTransport._BaseGetTopic, PublisherRestStub):
def __hash__(self):
- return hash("GetTopic")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.GetTopic")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -759,7 +1135,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Call the get topic method over HTTP.
@@ -769,46 +1145,67 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Topic:
A topic resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{topic=projects/*/topics/*}",
- },
- ]
- request, metadata = self._interceptor.pre_get_topic(request, metadata)
- pb_request = pubsub.GetTopicRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = _BasePublisherRestTransport._BaseGetTopic._get_http_options()
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_get_topic(request, metadata)
+ transcoded_request = (
+ _BasePublisherRestTransport._BaseGetTopic._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BasePublisherRestTransport._BaseGetTopic._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.GetTopic",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "GetTopic",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = PublisherRestTransport._GetTopic._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -821,22 +1218,60 @@ def __call__(
pb_resp = pubsub.Topic.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_get_topic(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_get_topic_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Topic.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.get_topic",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "GetTopic",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ListTopics(PublisherRestStub):
+ class _ListTopics(_BasePublisherRestTransport._BaseListTopics, PublisherRestStub):
def __hash__(self):
- return hash("ListTopics")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.ListTopics")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -844,7 +1279,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.ListTopicsResponse:
r"""Call the list topics method over HTTP.
@@ -854,46 +1289,69 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.ListTopicsResponse:
Response for the ``ListTopics`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{project=projects/*}/topics",
- },
- ]
- request, metadata = self._interceptor.pre_list_topics(request, metadata)
- pb_request = pubsub.ListTopicsRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BasePublisherRestTransport._BaseListTopics._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_list_topics(request, metadata)
+ transcoded_request = (
+ _BasePublisherRestTransport._BaseListTopics._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BasePublisherRestTransport._BaseListTopics._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.ListTopics",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "ListTopics",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = PublisherRestTransport._ListTopics._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -906,22 +1364,62 @@ def __call__(
pb_resp = pubsub.ListTopicsResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_list_topics(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_topics_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.ListTopicsResponse.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.list_topics",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "ListTopics",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ListTopicSnapshots(PublisherRestStub):
+ class _ListTopicSnapshots(
+ _BasePublisherRestTransport._BaseListTopicSnapshots, PublisherRestStub
+ ):
def __hash__(self):
- return hash("ListTopicSnapshots")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.ListTopicSnapshots")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -929,7 +1427,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.ListTopicSnapshotsResponse:
r"""Call the list topic snapshots method over HTTP.
@@ -939,48 +1437,67 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.ListTopicSnapshotsResponse:
Response for the ``ListTopicSnapshots`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{topic=projects/*/topics/*}/snapshots",
- },
- ]
+ http_options = (
+ _BasePublisherRestTransport._BaseListTopicSnapshots._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_list_topic_snapshots(
request, metadata
)
- pb_request = pubsub.ListTopicSnapshotsRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BasePublisherRestTransport._BaseListTopicSnapshots._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BasePublisherRestTransport._BaseListTopicSnapshots._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.ListTopicSnapshots",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "ListTopicSnapshots",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = PublisherRestTransport._ListTopicSnapshots._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -993,22 +1510,64 @@ def __call__(
pb_resp = pubsub.ListTopicSnapshotsResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_list_topic_snapshots(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_topic_snapshots_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.ListTopicSnapshotsResponse.to_json(
+ response
+ )
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.list_topic_snapshots",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "ListTopicSnapshots",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ListTopicSubscriptions(PublisherRestStub):
+ class _ListTopicSubscriptions(
+ _BasePublisherRestTransport._BaseListTopicSubscriptions, PublisherRestStub
+ ):
def __hash__(self):
- return hash("ListTopicSubscriptions")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.ListTopicSubscriptions")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -1016,7 +1575,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.ListTopicSubscriptionsResponse:
r"""Call the list topic subscriptions method over HTTP.
@@ -1026,48 +1585,67 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.ListTopicSubscriptionsResponse:
Response for the ``ListTopicSubscriptions`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{topic=projects/*/topics/*}/subscriptions",
- },
- ]
+ http_options = (
+ _BasePublisherRestTransport._BaseListTopicSubscriptions._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_list_topic_subscriptions(
request, metadata
)
- pb_request = pubsub.ListTopicSubscriptionsRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BasePublisherRestTransport._BaseListTopicSubscriptions._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BasePublisherRestTransport._BaseListTopicSubscriptions._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.ListTopicSubscriptions",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "ListTopicSubscriptions",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = PublisherRestTransport._ListTopicSubscriptions._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1080,22 +1658,63 @@ def __call__(
pb_resp = pubsub.ListTopicSubscriptionsResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_list_topic_subscriptions(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_topic_subscriptions_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.ListTopicSubscriptionsResponse.to_json(
+ response
+ )
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.list_topic_subscriptions",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "ListTopicSubscriptions",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _Publish(PublisherRestStub):
+ class _Publish(_BasePublisherRestTransport._BasePublish, PublisherRestStub):
def __hash__(self):
- return hash("Publish")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.Publish")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1103,7 +1722,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.PublishResponse:
r"""Call the publish method over HTTP.
@@ -1113,53 +1732,72 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.PublishResponse:
Response for the ``Publish`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{topic=projects/*/topics/*}:publish",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_publish(request, metadata)
- pb_request = pubsub.PublishRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = _BasePublisherRestTransport._BasePublish._get_http_options()
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_publish(request, metadata)
+ transcoded_request = (
+ _BasePublisherRestTransport._BasePublish._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BasePublisherRestTransport._BasePublish._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BasePublisherRestTransport._BasePublish._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.Publish",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "Publish",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = PublisherRestTransport._Publish._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1172,22 +1810,61 @@ def __call__(
pb_resp = pubsub.PublishResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_publish(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_publish_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.PublishResponse.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.publish",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "Publish",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _UpdateTopic(PublisherRestStub):
+ class _UpdateTopic(_BasePublisherRestTransport._BaseUpdateTopic, PublisherRestStub):
def __hash__(self):
- return hash("UpdateTopic")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("PublisherRestTransport.UpdateTopic")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1195,7 +1872,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Topic:
r"""Call the update topic method over HTTP.
@@ -1205,53 +1882,74 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Topic:
A topic resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "patch",
- "uri": "/v1/{topic.name=projects/*/topics/*}",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_update_topic(request, metadata)
- pb_request = pubsub.UpdateTopicRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BasePublisherRestTransport._BaseUpdateTopic._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_update_topic(request, metadata)
+ transcoded_request = (
+ _BasePublisherRestTransport._BaseUpdateTopic._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BasePublisherRestTransport._BaseUpdateTopic._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BasePublisherRestTransport._BaseUpdateTopic._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.UpdateTopic",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "UpdateTopic",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = PublisherRestTransport._UpdateTopic._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1264,7 +1962,33 @@ def __call__(
pb_resp = pubsub.Topic.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_update_topic(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_update_topic_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Topic.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherClient.update_topic",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "UpdateTopic",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
@property
@@ -1339,14 +2063,41 @@ def update_topic(self) -> Callable[[pubsub.UpdateTopicRequest], pubsub.Topic]:
def get_iam_policy(self):
return self._GetIamPolicy(self._session, self._host, self._interceptor) # type: ignore
- class _GetIamPolicy(PublisherRestStub):
+ class _GetIamPolicy(
+ _BasePublisherRestTransport._BaseGetIamPolicy, PublisherRestStub
+ ):
+ def __hash__(self):
+ return hash("PublisherRestTransport.GetIamPolicy")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.GetIamPolicyRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Call the get iam policy method over HTTP.
@@ -1356,51 +2107,68 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
policy_pb2.Policy: Response from GetIamPolicy method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy",
- },
- ]
+ http_options = (
+ _BasePublisherRestTransport._BaseGetIamPolicy._get_http_options()
+ )
request, metadata = self._interceptor.pre_get_iam_policy(request, metadata)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = (
+ _BasePublisherRestTransport._BaseGetIamPolicy._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = (
+ _BasePublisherRestTransport._BaseGetIamPolicy._get_query_params_json(
+ transcoded_request
+ )
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.GetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "GetIamPolicy",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
+ # Send the request
+ response = PublisherRestTransport._GetIamPolicy._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1408,23 +2176,73 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = policy_pb2.Policy()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_get_iam_policy(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherAsyncClient.GetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "GetIamPolicy",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
def set_iam_policy(self):
return self._SetIamPolicy(self._session, self._host, self._interceptor) # type: ignore
- class _SetIamPolicy(PublisherRestStub):
+ class _SetIamPolicy(
+ _BasePublisherRestTransport._BaseSetIamPolicy, PublisherRestStub
+ ):
+ def __hash__(self):
+ return hash("PublisherRestTransport.SetIamPolicy")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.SetIamPolicyRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Call the set iam policy method over HTTP.
@@ -1434,57 +2252,73 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
policy_pb2.Policy: Response from SetIamPolicy method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy",
- "body": "*",
- },
- ]
+ http_options = (
+ _BasePublisherRestTransport._BaseSetIamPolicy._get_http_options()
+ )
request, metadata = self._interceptor.pre_set_iam_policy(request, metadata)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ transcoded_request = (
+ _BasePublisherRestTransport._BaseSetIamPolicy._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json.dumps(transcoded_request["body"])
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ body = _BasePublisherRestTransport._BaseSetIamPolicy._get_request_body_json(
+ transcoded_request
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = (
+ _BasePublisherRestTransport._BaseSetIamPolicy._get_query_params_json(
+ transcoded_request
+ )
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.SetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "SetIamPolicy",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
- data=body,
+ # Send the request
+ response = PublisherRestTransport._SetIamPolicy._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1492,23 +2326,73 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = policy_pb2.Policy()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_set_iam_policy(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherAsyncClient.SetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "SetIamPolicy",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
def test_iam_permissions(self):
return self._TestIamPermissions(self._session, self._host, self._interceptor) # type: ignore
- class _TestIamPermissions(PublisherRestStub):
+ class _TestIamPermissions(
+ _BasePublisherRestTransport._BaseTestIamPermissions, PublisherRestStub
+ ):
+ def __hash__(self):
+ return hash("PublisherRestTransport.TestIamPermissions")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.TestIamPermissionsRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
r"""Call the test iam permissions method over HTTP.
@@ -1518,59 +2402,71 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions",
- "body": "*",
- },
- ]
+ http_options = (
+ _BasePublisherRestTransport._BaseTestIamPermissions._get_http_options()
+ )
request, metadata = self._interceptor.pre_test_iam_permissions(
request, metadata
)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ transcoded_request = _BasePublisherRestTransport._BaseTestIamPermissions._get_transcoded_request(
+ http_options, request
+ )
- body = json.dumps(transcoded_request["body"])
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ body = _BasePublisherRestTransport._BaseTestIamPermissions._get_request_body_json(
+ transcoded_request
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = _BasePublisherRestTransport._BaseTestIamPermissions._get_query_params_json(
+ transcoded_request
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.PublisherClient.TestIamPermissions",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "TestIamPermissions",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
- data=body,
+ # Send the request
+ response = PublisherRestTransport._TestIamPermissions._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1578,9 +2474,31 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = iam_policy_pb2.TestIamPermissionsResponse()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_test_iam_permissions(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.PublisherAsyncClient.TestIamPermissions",
+ extra={
+ "serviceName": "google.pubsub.v1.Publisher",
+ "rpcName": "TestIamPermissions",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
diff --git a/google/pubsub_v1/services/publisher/transports/rest_base.py b/google/pubsub_v1/services/publisher/transports/rest_base.py
new file mode 100644
index 000000000..14308a300
--- /dev/null
+++ b/google/pubsub_v1/services/publisher/transports/rest_base.py
@@ -0,0 +1,678 @@
+# -*- coding: utf-8 -*-
+# Copyright 2025 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 json # type: ignore
+from google.api_core import path_template
+from google.api_core import gapic_v1
+
+from google.protobuf import json_format
+from google.iam.v1 import iam_policy_pb2 # type: ignore
+from google.iam.v1 import policy_pb2 # type: ignore
+from .base import PublisherTransport, DEFAULT_CLIENT_INFO
+
+import re
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+
+
+from google.protobuf import empty_pb2 # type: ignore
+from google.pubsub_v1.types import pubsub
+
+
+class _BasePublisherRestTransport(PublisherTransport):
+ """Base REST backend transport for Publisher.
+
+ Note: This class is not meant to be used directly. Use its sync and
+ async sub-classes instead.
+
+ This class defines the same methods as the primary client, so the
+ primary client can load the underlying transport implementation
+ and call it.
+
+ It sends JSON representations of protocol buffers over HTTP/1.1
+ """
+
+ def __init__(
+ self,
+ *,
+ host: str = "pubsub.googleapis.com",
+ credentials: Optional[Any] = None,
+ client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
+ always_use_jwt_access: Optional[bool] = False,
+ url_scheme: str = "https",
+ api_audience: Optional[str] = None,
+ ) -> None:
+ """Instantiate the transport.
+ Args:
+ host (Optional[str]):
+ The hostname to connect to (default: 'pubsub.googleapis.com').
+ credentials (Optional[Any]): The
+ authorization credentials to attach to requests. These
+ credentials identify the application to the service; if none
+ are specified, the client will attempt to ascertain the
+ credentials from the environment.
+ client_info (google.api_core.gapic_v1.client_info.ClientInfo):
+ The client info used to send a user-agent string along with
+ API requests. If ``None``, then default info will be used.
+ Generally, you only need to set this if you are developing
+ your own client library.
+ always_use_jwt_access (Optional[bool]): Whether self signed JWT should
+ be used for service account credentials.
+ url_scheme: the protocol scheme for the API endpoint. Normally
+ "https", but for testing or local servers,
+ "http" can be specified.
+ """
+ # Run the base constructor
+ maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host)
+ if maybe_url_match is None:
+ raise ValueError(
+ f"Unexpected hostname structure: {host}"
+ ) # pragma: NO COVER
+
+ url_match_items = maybe_url_match.groupdict()
+
+ host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
+
+ super().__init__(
+ host=host,
+ credentials=credentials,
+ client_info=client_info,
+ always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
+ )
+
+ class _BaseCreateTopic:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "put",
+ "uri": "/v1/{name=projects/*/topics/*}",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.Topic.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseCreateTopic._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseDeleteTopic:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "delete",
+ "uri": "/v1/{topic=projects/*/topics/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.DeleteTopicRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseDeleteTopic._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseDetachSubscription:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}:detach",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.DetachSubscriptionRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseDetachSubscription._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseGetTopic:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{topic=projects/*/topics/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.GetTopicRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseGetTopic._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListTopics:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{project=projects/*}/topics",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.ListTopicsRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseListTopics._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListTopicSnapshots:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{topic=projects/*/topics/*}/snapshots",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.ListTopicSnapshotsRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseListTopicSnapshots._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListTopicSubscriptions:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{topic=projects/*/topics/*}/subscriptions",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.ListTopicSubscriptionsRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseListTopicSubscriptions._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BasePublish:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{topic=projects/*/topics/*}:publish",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.PublishRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BasePublish._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseUpdateTopic:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "patch",
+ "uri": "/v1/{topic.name=projects/*/topics/*}",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.UpdateTopicRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BasePublisherRestTransport._BaseUpdateTopic._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseGetIamPolicy:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+ class _BaseSetIamPolicy:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ body = json.dumps(transcoded_request["body"])
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+ class _BaseTestIamPermissions:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ body = json.dumps(transcoded_request["body"])
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+
+__all__ = ("_BasePublisherRestTransport",)
diff --git a/google/pubsub_v1/services/schema_service/__init__.py b/google/pubsub_v1/services/schema_service/__init__.py
index 570d29e7c..0908014e8 100644
--- a/google/pubsub_v1/services/schema_service/__init__.py
+++ b/google/pubsub_v1/services/schema_service/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py
index 9c8eecaec..b2d139fa0 100644
--- a/google/pubsub_v1/services/schema_service/async_client.py
+++ b/google/pubsub_v1/services/schema_service/async_client.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging as std_logging
from collections import OrderedDict
import re
from typing import (
@@ -36,6 +37,7 @@
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
try:
@@ -53,6 +55,15 @@
from .transports.grpc_asyncio import SchemaServiceGrpcAsyncIOTransport
from .client import SchemaServiceClient
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
class SchemaServiceAsyncClient:
"""Service for doing schema-related operations."""
@@ -258,6 +269,28 @@ def __init__(
client_info=client_info,
)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.pubsub_v1.SchemaServiceAsyncClient`.",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "universeDomain": getattr(
+ self._client._transport._credentials, "universe_domain", ""
+ ),
+ "credentialsType": f"{type(self._client._transport._credentials).__module__}.{type(self._client._transport._credentials).__qualname__}",
+ "credentialsInfo": getattr(
+ self.transport._credentials, "get_cred_info", lambda: None
+ )(),
+ }
+ if hasattr(self._client._transport, "_credentials")
+ else {
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "credentialsType": None,
+ },
+ )
+
async def create_schema(
self,
request: Optional[Union[gp_schema.CreateSchemaRequest, dict]] = None,
@@ -267,7 +300,7 @@ async def create_schema(
schema_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.Schema:
r"""Creates a schema.
@@ -335,8 +368,10 @@ async def sample_create_schema():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -345,7 +380,10 @@ async def sample_create_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([parent, schema, schema_id])
+ flattened_params = [parent, schema, schema_id]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -399,7 +437,7 @@ async def get_schema(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Gets a schema.
@@ -442,8 +480,10 @@ async def sample_get_schema():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -452,7 +492,10 @@ async def sample_get_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -502,7 +545,7 @@ async def list_schemas(
parent: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSchemasAsyncPager:
r"""Lists schemas in a project.
@@ -546,8 +589,10 @@ async def sample_list_schemas():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.schema_service.pagers.ListSchemasAsyncPager:
@@ -560,7 +605,10 @@ async def sample_list_schemas():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([parent])
+ flattened_params = [parent]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -621,7 +669,7 @@ async def list_schema_revisions(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSchemaRevisionsAsyncPager:
r"""Lists all schema revisions for the named schema.
@@ -665,8 +713,10 @@ async def sample_list_schema_revisions():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsAsyncPager:
@@ -679,7 +729,10 @@ async def sample_list_schema_revisions():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -741,7 +794,7 @@ async def commit_schema(
schema: Optional[gp_schema.Schema] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.Schema:
r"""Commits a new schema revision to an existing schema.
@@ -795,8 +848,10 @@ async def sample_commit_schema():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -805,7 +860,10 @@ async def sample_commit_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, schema])
+ flattened_params = [name, schema]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -858,7 +916,7 @@ async def rollback_schema(
revision_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Creates a new schema revision that is a copy of the provided
revision_id.
@@ -913,8 +971,10 @@ async def sample_rollback_schema():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -923,7 +983,10 @@ async def sample_rollback_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, revision_id])
+ flattened_params = [name, revision_id]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -976,7 +1039,7 @@ async def delete_schema_revision(
revision_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Deletes a specific schema revision.
@@ -1029,8 +1092,10 @@ async def sample_delete_schema_revision():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -1039,7 +1104,10 @@ async def sample_delete_schema_revision():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, revision_id])
+ flattened_params = [name, revision_id]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1091,7 +1159,7 @@ async def delete_schema(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Deletes a schema.
@@ -1131,13 +1199,18 @@ async def sample_delete_schema():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1185,7 +1258,7 @@ async def validate_schema(
schema: Optional[gp_schema.Schema] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.ValidateSchemaResponse:
r"""Validates a schema.
@@ -1239,8 +1312,10 @@ async def sample_validate_schema():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.ValidateSchemaResponse:
@@ -1251,7 +1326,10 @@ async def sample_validate_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([parent, schema])
+ flattened_params = [parent, schema]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1302,7 +1380,7 @@ async def validate_message(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.ValidateMessageResponse:
r"""Validates a message against a schema.
@@ -1339,8 +1417,10 @@ async def sample_validate_message():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.ValidateMessageResponse:
@@ -1386,21 +1466,23 @@ async def set_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Sets the IAM access control policy on the specified function.
Replaces any existing policy.
Args:
- request (:class:`~.policy_pb2.SetIamPolicyRequest`):
+ request (:class:`~.iam_policy_pb2.SetIamPolicyRequest`):
The request object. Request message for `SetIamPolicy`
method.
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -1420,6 +1502,7 @@ async def set_iam_policy(
**JSON Example**
::
+
{
"bindings": [
{
@@ -1476,11 +1559,7 @@ async def set_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.set_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[self._client._transport.set_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1508,22 +1587,24 @@ async def get_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Gets the IAM access control policy for a function.
- Returns an empty policy if the function exists and does
- not have a policy set.
+ Returns an empty policy if the function exists and does not have a
+ policy set.
Args:
request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`):
The request object. Request message for `GetIamPolicy`
method.
- retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
- should be retried.
+ retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -1600,11 +1681,7 @@ async def get_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.get_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[self._client._transport.get_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1632,25 +1709,27 @@ async def test_iam_permissions(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
- r"""Tests the specified permissions against the IAM access control
+ r"""Tests the specified IAM permissions against the IAM access control
policy for a function.
- If the function does not exist, this will
- return an empty set of permissions, not a NOT_FOUND error.
+ If the function does not exist, this will return an empty set
+ of permissions, not a NOT_FOUND error.
Args:
request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`):
The request object. Request message for
`TestIamPermissions` method.
- retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
- should be retried.
+ retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
- ~iam_policy_pb2.PolicyTestIamPermissionsResponse:
+ ~.iam_policy_pb2.TestIamPermissionsResponse:
Response message for ``TestIamPermissions`` method.
"""
# Create or coerce a protobuf request object.
@@ -1662,11 +1741,9 @@ async def test_iam_permissions(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.test_iam_permissions,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[
+ self._client._transport.test_iam_permissions
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1699,5 +1776,8 @@ async def __aexit__(self, exc_type, exc, tb):
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
__all__ = ("SchemaServiceAsyncClient",)
diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py
index dff44944d..300f23998 100644
--- a/google/pubsub_v1/services/schema_service/client.py
+++ b/google/pubsub_v1/services/schema_service/client.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
# limitations under the License.
#
from collections import OrderedDict
+from http import HTTPStatus
+import json
+import logging as std_logging
import functools
import os
import re
@@ -43,12 +46,22 @@
from google.auth.transport.grpc import SslCredentials # type: ignore
from google.auth.exceptions import MutualTLSChannelError # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
try:
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
OptionalRetry = Union[retries.Retry, object, None] # type: ignore
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
from google.protobuf import timestamp_pb2 # type: ignore
@@ -140,6 +153,34 @@ def _get_default_mtls_endpoint(api_endpoint):
_DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}"
_DEFAULT_UNIVERSE = "googleapis.com"
+ @staticmethod
+ def _use_client_cert_effective():
+ """Returns whether client certificate should be used for mTLS if the
+ google-auth version supports should_use_client_cert automatic mTLS enablement.
+
+ Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
+
+ Returns:
+ bool: whether client certificate should be used for mTLS
+ Raises:
+ ValueError: (If using a version of google-auth without should_use_client_cert and
+ GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
+ """
+ # check if google-auth version supports should_use_client_cert for automatic mTLS enablement
+ if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
+ return mtls.should_use_client_cert()
+ else: # pragma: NO COVER
+ # if unsupported, fallback to reading from env var
+ use_client_cert_str = os.getenv(
+ "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
+ ).lower()
+ if use_client_cert_str not in ("true", "false"):
+ raise ValueError(
+ "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
+ " either `true` or `false`"
+ )
+ return use_client_cert_str == "true"
+
@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -322,12 +363,8 @@ def get_mtls_endpoint_and_cert_source(
)
if client_options is None:
client_options = client_options_lib.ClientOptions()
- use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
+ use_client_cert = SchemaServiceClient._use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
- if use_client_cert not in ("true", "false"):
- raise ValueError(
- "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError(
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
@@ -335,7 +372,7 @@ def get_mtls_endpoint_and_cert_source(
# Figure out the client cert source to use.
client_cert_source = None
- if use_client_cert == "true":
+ if use_client_cert:
if client_options.client_cert_source:
client_cert_source = client_options.client_cert_source
elif mtls.has_default_client_cert_source():
@@ -367,20 +404,14 @@ def _read_environment_variables():
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
is not any of ["auto", "never", "always"].
"""
- use_client_cert = os.getenv(
- "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
- ).lower()
+ use_client_cert = SchemaServiceClient._use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
- if use_client_cert not in ("true", "false"):
- raise ValueError(
- "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError(
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
+ return use_client_cert, use_mtls_endpoint, universe_domain_env
@staticmethod
def _get_client_cert_source(provided_cert_source, use_cert_flag):
@@ -460,52 +491,45 @@ def _get_universe_domain(
raise ValueError("Universe Domain cannot be an empty string.")
return universe_domain
- @staticmethod
- def _compare_universes(
- client_universe: str, credentials: ga_credentials.Credentials
- ) -> bool:
- """Returns True iff the universe domains used by the client and credentials match.
-
- Args:
- client_universe (str): The universe domain configured via the client options.
- credentials (ga_credentials.Credentials): The credentials being used in the client.
+ def _validate_universe_domain(self):
+ """Validates client's and credentials' universe domains are consistent.
Returns:
- bool: True iff client_universe matches the universe in credentials.
+ bool: True iff the configured universe domain is valid.
Raises:
- ValueError: when client_universe does not match the universe in credentials.
+ ValueError: If the configured universe domain is not valid.
"""
- default_universe = SchemaServiceClient._DEFAULT_UNIVERSE
- credentials_universe = getattr(credentials, "universe_domain", default_universe)
-
- if client_universe != credentials_universe:
- raise ValueError(
- "The configured universe domain "
- f"({client_universe}) does not match the universe domain "
- f"found in the credentials ({credentials_universe}). "
- "If you haven't configured the universe domain explicitly, "
- f"`{default_universe}` is the default."
- )
+ # NOTE (b/349488459): universe validation is disabled until further notice.
return True
- def _validate_universe_domain(self):
- """Validates client's and credentials' universe domains are consistent.
-
- Returns:
- bool: True iff the configured universe domain is valid.
+ def _add_cred_info_for_auth_errors(
+ self, error: core_exceptions.GoogleAPICallError
+ ) -> None:
+ """Adds credential info string to error details for 401/403/404 errors.
- Raises:
- ValueError: If the configured universe domain is not valid.
+ Args:
+ error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info.
"""
- self._is_universe_domain_valid = (
- self._is_universe_domain_valid
- or SchemaServiceClient._compare_universes(
- self.universe_domain, self.transport._credentials
- )
- )
- return self._is_universe_domain_valid
+ if error.code not in [
+ HTTPStatus.UNAUTHORIZED,
+ HTTPStatus.FORBIDDEN,
+ HTTPStatus.NOT_FOUND,
+ ]:
+ return
+
+ cred = self._transport._credentials
+
+ # get_cred_info is only available in google-auth>=2.35.0
+ if not hasattr(cred, "get_cred_info"):
+ return
+
+ # ignore the type check since pypy test fails when get_cred_info
+ # is not available
+ cred_info = cred.get_cred_info() # type: ignore
+ if cred_info and hasattr(error._details, "append"):
+ error._details.append(json.dumps(cred_info))
@property
def api_endpoint(self):
@@ -611,6 +635,10 @@ def __init__(
# Initialize the universe domain validation.
self._is_universe_domain_valid = False
+ if CLIENT_LOGGING_SUPPORTED: # pragma: NO COVER
+ # Setup logging.
+ client_logging.initialize_logging()
+
api_key_value = getattr(self._client_options, "api_key", None)
if api_key_value and credentials:
raise ValueError(
@@ -667,7 +695,7 @@ def __init__(
emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST")
if emulator_host:
- if issubclass(transport_init, type(self)._transport_registry["grpc"]):
+ if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore
channel = grpc.insecure_channel(target=emulator_host)
else:
channel = grpc.aio.insecure_channel(target=emulator_host)
@@ -685,6 +713,29 @@ def __init__(
api_audience=self._client_options.api_audience,
)
+ if "async" not in str(self._transport):
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.pubsub_v1.SchemaServiceClient`.",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "universeDomain": getattr(
+ self._transport._credentials, "universe_domain", ""
+ ),
+ "credentialsType": f"{type(self._transport._credentials).__module__}.{type(self._transport._credentials).__qualname__}",
+ "credentialsInfo": getattr(
+ self.transport._credentials, "get_cred_info", lambda: None
+ )(),
+ }
+ if hasattr(self._transport, "_credentials")
+ else {
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "credentialsType": None,
+ },
+ )
+
def create_schema(
self,
request: Optional[Union[gp_schema.CreateSchemaRequest, dict]] = None,
@@ -694,7 +745,7 @@ def create_schema(
schema_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.Schema:
r"""Creates a schema.
@@ -762,8 +813,10 @@ def sample_create_schema():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -772,7 +825,10 @@ def sample_create_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([parent, schema, schema_id])
+ flattened_params = [parent, schema, schema_id]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -823,7 +879,7 @@ def get_schema(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Gets a schema.
@@ -866,8 +922,10 @@ def sample_get_schema():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -876,7 +934,10 @@ def sample_get_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -923,7 +984,7 @@ def list_schemas(
parent: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSchemasPager:
r"""Lists schemas in a project.
@@ -967,8 +1028,10 @@ def sample_list_schemas():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.schema_service.pagers.ListSchemasPager:
@@ -981,7 +1044,10 @@ def sample_list_schemas():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([parent])
+ flattened_params = [parent]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1039,7 +1105,7 @@ def list_schema_revisions(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSchemaRevisionsPager:
r"""Lists all schema revisions for the named schema.
@@ -1083,8 +1149,10 @@ def sample_list_schema_revisions():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsPager:
@@ -1097,7 +1165,10 @@ def sample_list_schema_revisions():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1156,7 +1227,7 @@ def commit_schema(
schema: Optional[gp_schema.Schema] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.Schema:
r"""Commits a new schema revision to an existing schema.
@@ -1210,8 +1281,10 @@ def sample_commit_schema():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -1220,7 +1293,10 @@ def sample_commit_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, schema])
+ flattened_params = [name, schema]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1270,7 +1346,7 @@ def rollback_schema(
revision_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Creates a new schema revision that is a copy of the provided
revision_id.
@@ -1325,8 +1401,10 @@ def sample_rollback_schema():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -1335,7 +1413,10 @@ def sample_rollback_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, revision_id])
+ flattened_params = [name, revision_id]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1385,7 +1466,7 @@ def delete_schema_revision(
revision_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Deletes a specific schema revision.
@@ -1438,8 +1519,10 @@ def sample_delete_schema_revision():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Schema:
@@ -1448,7 +1531,10 @@ def sample_delete_schema_revision():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, revision_id])
+ flattened_params = [name, revision_id]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1497,7 +1583,7 @@ def delete_schema(
name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Deletes a schema.
@@ -1537,13 +1623,18 @@ def sample_delete_schema():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name])
+ flattened_params = [name]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1588,7 +1679,7 @@ def validate_schema(
schema: Optional[gp_schema.Schema] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.ValidateSchemaResponse:
r"""Validates a schema.
@@ -1642,8 +1733,10 @@ def sample_validate_schema():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.ValidateSchemaResponse:
@@ -1654,7 +1747,10 @@ def sample_validate_schema():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([parent, schema])
+ flattened_params = [parent, schema]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1702,7 +1798,7 @@ def validate_message(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.ValidateMessageResponse:
r"""Validates a message against a schema.
@@ -1739,8 +1835,10 @@ def sample_validate_message():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.ValidateMessageResponse:
@@ -1797,7 +1895,7 @@ def set_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Sets the IAM access control policy on the specified function.
@@ -1810,8 +1908,10 @@ def set_iam_policy(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -1888,11 +1988,7 @@ def set_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.set_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.set_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -1903,16 +1999,20 @@ def set_iam_policy(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
def get_iam_policy(
self,
@@ -1920,7 +2020,7 @@ def get_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Gets the IAM access control policy for a function.
@@ -1934,8 +2034,10 @@ def get_iam_policy(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -2012,11 +2114,7 @@ def get_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.get_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.get_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2027,16 +2125,20 @@ def get_iam_policy(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
def test_iam_permissions(
self,
@@ -2044,7 +2146,7 @@ def test_iam_permissions(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
r"""Tests the specified IAM permissions against the IAM access control
policy for a function.
@@ -2059,8 +2161,10 @@ def test_iam_permissions(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.iam_policy_pb2.TestIamPermissionsResponse:
Response message for ``TestIamPermissions`` method.
@@ -2074,11 +2178,7 @@ def test_iam_permissions(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.test_iam_permissions,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2089,21 +2189,27 @@ def test_iam_permissions(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
__all__ = ("SchemaServiceClient",)
diff --git a/google/pubsub_v1/services/schema_service/pagers.py b/google/pubsub_v1/services/schema_service/pagers.py
index fa42a6b8c..02beaee40 100644
--- a/google/pubsub_v1/services/schema_service/pagers.py
+++ b/google/pubsub_v1/services/schema_service/pagers.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@ def __init__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -80,8 +80,10 @@ def __init__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = schema.ListSchemasRequest(request)
@@ -140,7 +142,7 @@ def __init__(
*,
retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -154,8 +156,10 @@ def __init__(
retry (google.api_core.retry.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = schema.ListSchemasRequest(request)
@@ -218,7 +222,7 @@ def __init__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -232,8 +236,10 @@ def __init__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = schema.ListSchemaRevisionsRequest(request)
@@ -292,7 +298,7 @@ def __init__(
*,
retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -306,8 +312,10 @@ def __init__(
retry (google.api_core.retry.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = schema.ListSchemaRevisionsRequest(request)
diff --git a/google/pubsub_v1/services/schema_service/transports/README.rst b/google/pubsub_v1/services/schema_service/transports/README.rst
new file mode 100644
index 000000000..a0a06949e
--- /dev/null
+++ b/google/pubsub_v1/services/schema_service/transports/README.rst
@@ -0,0 +1,9 @@
+
+transport inheritance structure
+_______________________________
+
+`SchemaServiceTransport` is the ABC for all transports.
+- public child `SchemaServiceGrpcTransport` for sync gRPC transport (defined in `grpc.py`).
+- public child `SchemaServiceGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`).
+- private child `_BaseSchemaServiceRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`).
+- public child `SchemaServiceRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`).
diff --git a/google/pubsub_v1/services/schema_service/transports/__init__.py b/google/pubsub_v1/services/schema_service/transports/__init__.py
index 73976e7fb..78c2fa21d 100644
--- a/google/pubsub_v1/services/schema_service/transports/__init__.py
+++ b/google/pubsub_v1/services/schema_service/transports/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py
index 5c7f35aa8..bfe254e0a 100644
--- a/google/pubsub_v1/services/schema_service/transports/base.py
+++ b/google/pubsub_v1/services/schema_service/transports/base.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
from google.api_core import retry as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
@@ -36,6 +37,9 @@
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
class SchemaServiceTransport(abc.ABC):
"""Abstract transport class for SchemaService."""
@@ -70,9 +74,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.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is mutually exclusive with credentials.
+ This argument is mutually exclusive with credentials. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A list of scopes.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
@@ -276,6 +281,21 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
+ self.get_iam_policy: gapic_v1.method.wrap_method(
+ self.get_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.set_iam_policy: gapic_v1.method.wrap_method(
+ self.set_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.test_iam_permissions: gapic_v1.method.wrap_method(
+ self.test_iam_permissions,
+ default_timeout=None,
+ client_info=client_info,
+ ),
}
def close(self):
diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py
index 421879d19..5bcfd8b9b 100644
--- a/google/pubsub_v1/services/schema_service/transports/grpc.py
+++ b/google/pubsub_v1/services/schema_service/transports/grpc.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import json
+import logging as std_logging
+import pickle
import warnings
from typing import Callable, Dict, Optional, Sequence, Tuple, Union
@@ -21,8 +24,11 @@
import google.auth # type: ignore
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
+from google.protobuf.json_format import MessageToJson
+import google.protobuf.message
import grpc # type: ignore
+import proto # type: ignore
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
@@ -31,6 +37,80 @@
from google.pubsub_v1.types import schema as gp_schema
from .base import SchemaServiceTransport, DEFAULT_CLIENT_INFO
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
+
+class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER
+ def intercept_unary_unary(self, continuation, client_call_details, request):
+ logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ )
+ if logging_enabled: # pragma: NO COVER
+ request_metadata = client_call_details.metadata
+ if isinstance(request, proto.Message):
+ request_payload = type(request).to_json(request)
+ elif isinstance(request, google.protobuf.message.Message):
+ request_payload = MessageToJson(request)
+ else:
+ request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
+
+ request_metadata = {
+ key: value.decode("utf-8") if isinstance(value, bytes) else value
+ for key, value in request_metadata
+ }
+ grpc_request = {
+ "payload": request_payload,
+ "requestMethod": "grpc",
+ "metadata": dict(request_metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for {client_call_details.method}",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": str(client_call_details.method),
+ "request": grpc_request,
+ "metadata": grpc_request["metadata"],
+ },
+ )
+ response = continuation(client_call_details, request)
+ if logging_enabled: # pragma: NO COVER
+ response_metadata = response.trailing_metadata()
+ # Convert gRPC metadata `` to list of tuples
+ metadata = (
+ dict([(k, str(v)) for k, v in response_metadata])
+ if response_metadata
+ else None
+ )
+ result = response.result()
+ if isinstance(result, proto.Message):
+ response_payload = type(result).to_json(result)
+ elif isinstance(result, google.protobuf.message.Message):
+ response_payload = MessageToJson(result)
+ else:
+ response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
+ grpc_response = {
+ "payload": response_payload,
+ "metadata": metadata,
+ "status": "OK",
+ }
+ _LOGGER.debug(
+ f"Received response for {client_call_details.method}.",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": client_call_details.method,
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class SchemaServiceGrpcTransport(SchemaServiceTransport):
"""gRPC backend transport for SchemaService.
@@ -75,9 +155,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
This argument is ignored if a ``channel`` instance is provided.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
This argument is ignored if a ``channel`` instance is provided.
+ This argument will be removed in the next major version of this library.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
ignored if a ``channel`` instance is provided.
channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]):
@@ -186,7 +267,12 @@ def __init__(
],
)
- # Wrap messages. This must be done after self._grpc_channel exists
+ self._interceptor = _LoggingClientInterceptor()
+ self._logged_channel = grpc.intercept_channel(
+ self._grpc_channel, self._interceptor
+ )
+
+ # Wrap messages. This must be done after self._logged_channel exists
self._prep_wrapped_messages(client_info)
@classmethod
@@ -207,9 +293,10 @@ def create_channel(
credentials identify this application to the service. If
none are specified, the client will attempt to ascertain
the credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is mutually exclusive with credentials.
+ This argument is mutually exclusive with credentials. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -260,7 +347,7 @@ def create_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_schema" not in self._stubs:
- self._stubs["create_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["create_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/CreateSchema",
request_serializer=gp_schema.CreateSchemaRequest.serialize,
response_deserializer=gp_schema.Schema.deserialize,
@@ -284,7 +371,7 @@ def get_schema(self) -> Callable[[schema.GetSchemaRequest], schema.Schema]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_schema" not in self._stubs:
- self._stubs["get_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["get_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/GetSchema",
request_serializer=schema.GetSchemaRequest.serialize,
response_deserializer=schema.Schema.deserialize,
@@ -310,7 +397,7 @@ def list_schemas(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_schemas" not in self._stubs:
- self._stubs["list_schemas"] = self.grpc_channel.unary_unary(
+ self._stubs["list_schemas"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ListSchemas",
request_serializer=schema.ListSchemasRequest.serialize,
response_deserializer=schema.ListSchemasResponse.deserialize,
@@ -338,7 +425,7 @@ def list_schema_revisions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_schema_revisions" not in self._stubs:
- self._stubs["list_schema_revisions"] = self.grpc_channel.unary_unary(
+ self._stubs["list_schema_revisions"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ListSchemaRevisions",
request_serializer=schema.ListSchemaRevisionsRequest.serialize,
response_deserializer=schema.ListSchemaRevisionsResponse.deserialize,
@@ -364,7 +451,7 @@ def commit_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "commit_schema" not in self._stubs:
- self._stubs["commit_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["commit_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/CommitSchema",
request_serializer=gp_schema.CommitSchemaRequest.serialize,
response_deserializer=gp_schema.Schema.deserialize,
@@ -391,7 +478,7 @@ def rollback_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "rollback_schema" not in self._stubs:
- self._stubs["rollback_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["rollback_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/RollbackSchema",
request_serializer=schema.RollbackSchemaRequest.serialize,
response_deserializer=schema.Schema.deserialize,
@@ -417,7 +504,7 @@ def delete_schema_revision(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_schema_revision" not in self._stubs:
- self._stubs["delete_schema_revision"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_schema_revision"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/DeleteSchemaRevision",
request_serializer=schema.DeleteSchemaRevisionRequest.serialize,
response_deserializer=schema.Schema.deserialize,
@@ -441,7 +528,7 @@ def delete_schema(self) -> Callable[[schema.DeleteSchemaRequest], empty_pb2.Empt
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_schema" not in self._stubs:
- self._stubs["delete_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/DeleteSchema",
request_serializer=schema.DeleteSchemaRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -467,7 +554,7 @@ def validate_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "validate_schema" not in self._stubs:
- self._stubs["validate_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["validate_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ValidateSchema",
request_serializer=gp_schema.ValidateSchemaRequest.serialize,
response_deserializer=gp_schema.ValidateSchemaResponse.deserialize,
@@ -493,13 +580,16 @@ def validate_message(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "validate_message" not in self._stubs:
- self._stubs["validate_message"] = self.grpc_channel.unary_unary(
+ self._stubs["validate_message"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ValidateMessage",
request_serializer=schema.ValidateMessageRequest.serialize,
response_deserializer=schema.ValidateMessageResponse.deserialize,
)
return self._stubs["validate_message"]
+ def close(self):
+ self._logged_channel.close()
+
@property
def set_iam_policy(
self,
@@ -518,7 +608,7 @@ def set_iam_policy(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "set_iam_policy" not in self._stubs:
- self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
+ self._stubs["set_iam_policy"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/SetIamPolicy",
request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
@@ -544,7 +634,7 @@ def get_iam_policy(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_iam_policy" not in self._stubs:
- self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
+ self._stubs["get_iam_policy"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/GetIamPolicy",
request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
@@ -573,16 +663,13 @@ def test_iam_permissions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "test_iam_permissions" not in self._stubs:
- self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
+ self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/TestIamPermissions",
request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
)
return self._stubs["test_iam_permissions"]
- def close(self):
- self.grpc_channel.close()
-
@property
def kind(self) -> str:
return "grpc"
diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py
index 71c362436..ac2980ded 100644
--- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py
+++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import inspect
+import json
+import pickle
+import logging as std_logging
import warnings
from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union
@@ -22,8 +26,11 @@
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
+from google.protobuf.json_format import MessageToJson
+import google.protobuf.message
import grpc # type: ignore
+import proto # type: ignore
from grpc.experimental import aio # type: ignore
from google.iam.v1 import iam_policy_pb2 # type: ignore
@@ -34,6 +41,82 @@
from .base import SchemaServiceTransport, DEFAULT_CLIENT_INFO
from .grpc import SchemaServiceGrpcTransport
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
+
+class _LoggingClientAIOInterceptor(
+ grpc.aio.UnaryUnaryClientInterceptor
+): # pragma: NO COVER
+ async def intercept_unary_unary(self, continuation, client_call_details, request):
+ logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ )
+ if logging_enabled: # pragma: NO COVER
+ request_metadata = client_call_details.metadata
+ if isinstance(request, proto.Message):
+ request_payload = type(request).to_json(request)
+ elif isinstance(request, google.protobuf.message.Message):
+ request_payload = MessageToJson(request)
+ else:
+ request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
+
+ request_metadata = {
+ key: value.decode("utf-8") if isinstance(value, bytes) else value
+ for key, value in request_metadata
+ }
+ grpc_request = {
+ "payload": request_payload,
+ "requestMethod": "grpc",
+ "metadata": dict(request_metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for {client_call_details.method}",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": str(client_call_details.method),
+ "request": grpc_request,
+ "metadata": grpc_request["metadata"],
+ },
+ )
+ response = await continuation(client_call_details, request)
+ if logging_enabled: # pragma: NO COVER
+ response_metadata = await response.trailing_metadata()
+ # Convert gRPC metadata `` to list of tuples
+ metadata = (
+ dict([(k, str(v)) for k, v in response_metadata])
+ if response_metadata
+ else None
+ )
+ result = await response
+ if isinstance(result, proto.Message):
+ response_payload = type(result).to_json(result)
+ elif isinstance(result, google.protobuf.message.Message):
+ response_payload = MessageToJson(result)
+ else:
+ response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
+ grpc_response = {
+ "payload": response_payload,
+ "metadata": metadata,
+ "status": "OK",
+ }
+ _LOGGER.debug(
+ f"Received response to rpc {client_call_details.method}.",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": str(client_call_details.method),
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class SchemaServiceGrpcAsyncIOTransport(SchemaServiceTransport):
"""gRPC AsyncIO backend transport for SchemaService.
@@ -69,8 +152,9 @@ def create_channel(
credentials identify this application to the service. If
none are specified, the client will attempt to ascertain
the credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
- be loaded with :func:`google.auth.load_credentials_from_file`.
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
+ be loaded with :func:`google.auth.load_credentials_from_file`. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -121,9 +205,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
This argument is ignored if a ``channel`` instance is provided.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
This argument is ignored if a ``channel`` instance is provided.
+ This argument will be removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -232,7 +317,13 @@ def __init__(
],
)
- # Wrap messages. This must be done after self._grpc_channel exists
+ self._interceptor = _LoggingClientAIOInterceptor()
+ self._grpc_channel._unary_unary_interceptors.append(self._interceptor)
+ self._logged_channel = self._grpc_channel
+ self._wrap_with_kind = (
+ "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters
+ )
+ # Wrap messages. This must be done after self._logged_channel exists
self._prep_wrapped_messages(client_info)
@property
@@ -264,7 +355,7 @@ def create_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_schema" not in self._stubs:
- self._stubs["create_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["create_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/CreateSchema",
request_serializer=gp_schema.CreateSchemaRequest.serialize,
response_deserializer=gp_schema.Schema.deserialize,
@@ -290,7 +381,7 @@ def get_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_schema" not in self._stubs:
- self._stubs["get_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["get_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/GetSchema",
request_serializer=schema.GetSchemaRequest.serialize,
response_deserializer=schema.Schema.deserialize,
@@ -316,7 +407,7 @@ def list_schemas(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_schemas" not in self._stubs:
- self._stubs["list_schemas"] = self.grpc_channel.unary_unary(
+ self._stubs["list_schemas"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ListSchemas",
request_serializer=schema.ListSchemasRequest.serialize,
response_deserializer=schema.ListSchemasResponse.deserialize,
@@ -345,7 +436,7 @@ def list_schema_revisions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_schema_revisions" not in self._stubs:
- self._stubs["list_schema_revisions"] = self.grpc_channel.unary_unary(
+ self._stubs["list_schema_revisions"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ListSchemaRevisions",
request_serializer=schema.ListSchemaRevisionsRequest.serialize,
response_deserializer=schema.ListSchemaRevisionsResponse.deserialize,
@@ -371,7 +462,7 @@ def commit_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "commit_schema" not in self._stubs:
- self._stubs["commit_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["commit_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/CommitSchema",
request_serializer=gp_schema.CommitSchemaRequest.serialize,
response_deserializer=gp_schema.Schema.deserialize,
@@ -398,7 +489,7 @@ def rollback_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "rollback_schema" not in self._stubs:
- self._stubs["rollback_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["rollback_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/RollbackSchema",
request_serializer=schema.RollbackSchemaRequest.serialize,
response_deserializer=schema.Schema.deserialize,
@@ -424,7 +515,7 @@ def delete_schema_revision(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_schema_revision" not in self._stubs:
- self._stubs["delete_schema_revision"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_schema_revision"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/DeleteSchemaRevision",
request_serializer=schema.DeleteSchemaRevisionRequest.serialize,
response_deserializer=schema.Schema.deserialize,
@@ -450,7 +541,7 @@ def delete_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_schema" not in self._stubs:
- self._stubs["delete_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/DeleteSchema",
request_serializer=schema.DeleteSchemaRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -478,7 +569,7 @@ def validate_schema(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "validate_schema" not in self._stubs:
- self._stubs["validate_schema"] = self.grpc_channel.unary_unary(
+ self._stubs["validate_schema"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ValidateSchema",
request_serializer=gp_schema.ValidateSchemaRequest.serialize,
response_deserializer=gp_schema.ValidateSchemaResponse.deserialize,
@@ -506,97 +597,17 @@ def validate_message(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "validate_message" not in self._stubs:
- self._stubs["validate_message"] = self.grpc_channel.unary_unary(
+ self._stubs["validate_message"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.SchemaService/ValidateMessage",
request_serializer=schema.ValidateMessageRequest.serialize,
response_deserializer=schema.ValidateMessageResponse.deserialize,
)
return self._stubs["validate_message"]
- @property
- def set_iam_policy(
- self,
- ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]:
- r"""Return a callable for the set iam policy method over gRPC.
- Sets the IAM access control policy on the specified
- function. Replaces any existing policy.
- Returns:
- Callable[[~.SetIamPolicyRequest],
- Awaitable[~.Policy]]:
- 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 "set_iam_policy" not in self._stubs:
- self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/SetIamPolicy",
- request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
- response_deserializer=policy_pb2.Policy.FromString,
- )
- return self._stubs["set_iam_policy"]
-
- @property
- def get_iam_policy(
- self,
- ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]:
- r"""Return a callable for the get iam policy method over gRPC.
- Gets the IAM access control policy for a function.
- Returns an empty policy if the function exists and does
- not have a policy set.
- Returns:
- Callable[[~.GetIamPolicyRequest],
- Awaitable[~.Policy]]:
- 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 "get_iam_policy" not in self._stubs:
- self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/GetIamPolicy",
- request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
- response_deserializer=policy_pb2.Policy.FromString,
- )
- return self._stubs["get_iam_policy"]
-
- @property
- def test_iam_permissions(
- self,
- ) -> Callable[
- [iam_policy_pb2.TestIamPermissionsRequest],
- Awaitable[iam_policy_pb2.TestIamPermissionsResponse],
- ]:
- r"""Return a callable for the test iam permissions method over gRPC.
- Tests the specified permissions against the IAM access control
- policy for a function. If the function does not exist, this will
- return an empty set of permissions, not a NOT_FOUND error.
- Returns:
- Callable[[~.TestIamPermissionsRequest],
- Awaitable[~.TestIamPermissionsResponse]]:
- 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 "test_iam_permissions" not in self._stubs:
- self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/TestIamPermissions",
- request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
- response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
- )
- return self._stubs["test_iam_permissions"]
-
def _prep_wrapped_messages(self, client_info):
"""Precompute the wrapped methods, overriding the base class method to use async wrappers."""
self._wrapped_methods = {
- self.create_schema: gapic_v1.method_async.wrap_method(
+ self.create_schema: self._wrap_method(
self.create_schema,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -610,7 +621,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.get_schema: gapic_v1.method_async.wrap_method(
+ self.get_schema: self._wrap_method(
self.get_schema,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -624,7 +635,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.list_schemas: gapic_v1.method_async.wrap_method(
+ self.list_schemas: self._wrap_method(
self.list_schemas,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -638,7 +649,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.list_schema_revisions: gapic_v1.method_async.wrap_method(
+ self.list_schema_revisions: self._wrap_method(
self.list_schema_revisions,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -652,7 +663,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.commit_schema: gapic_v1.method_async.wrap_method(
+ self.commit_schema: self._wrap_method(
self.commit_schema,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -666,7 +677,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.rollback_schema: gapic_v1.method_async.wrap_method(
+ self.rollback_schema: self._wrap_method(
self.rollback_schema,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -680,7 +691,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.delete_schema_revision: gapic_v1.method_async.wrap_method(
+ self.delete_schema_revision: self._wrap_method(
self.delete_schema_revision,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -694,7 +705,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.delete_schema: gapic_v1.method_async.wrap_method(
+ self.delete_schema: self._wrap_method(
self.delete_schema,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -708,7 +719,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.validate_schema: gapic_v1.method_async.wrap_method(
+ self.validate_schema: self._wrap_method(
self.validate_schema,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -722,7 +733,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.validate_message: gapic_v1.method_async.wrap_method(
+ self.validate_message: self._wrap_method(
self.validate_message,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -736,10 +747,114 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
+ self.get_iam_policy: self._wrap_method(
+ self.get_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.set_iam_policy: self._wrap_method(
+ self.set_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.test_iam_permissions: self._wrap_method(
+ self.test_iam_permissions,
+ default_timeout=None,
+ client_info=client_info,
+ ),
}
+ def _wrap_method(self, func, *args, **kwargs):
+ if self._wrap_with_kind: # pragma: NO COVER
+ kwargs["kind"] = self.kind
+ return gapic_v1.method_async.wrap_method(func, *args, **kwargs)
+
def close(self):
- return self.grpc_channel.close()
+ return self._logged_channel.close()
+
+ @property
+ def kind(self) -> str:
+ return "grpc_asyncio"
+
+ @property
+ def set_iam_policy(
+ self,
+ ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]:
+ r"""Return a callable for the set iam policy method over gRPC.
+ Sets the IAM access control policy on the specified
+ function. Replaces any existing policy.
+ Returns:
+ Callable[[~.SetIamPolicyRequest],
+ ~.Policy]:
+ 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 "set_iam_policy" not in self._stubs:
+ self._stubs["set_iam_policy"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/SetIamPolicy",
+ request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
+ response_deserializer=policy_pb2.Policy.FromString,
+ )
+ return self._stubs["set_iam_policy"]
+
+ @property
+ def get_iam_policy(
+ self,
+ ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]:
+ r"""Return a callable for the get iam policy method over gRPC.
+ Gets the IAM access control policy for a function.
+ Returns an empty policy if the function exists and does
+ not have a policy set.
+ Returns:
+ Callable[[~.GetIamPolicyRequest],
+ ~.Policy]:
+ 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 "get_iam_policy" not in self._stubs:
+ self._stubs["get_iam_policy"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/GetIamPolicy",
+ request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
+ response_deserializer=policy_pb2.Policy.FromString,
+ )
+ return self._stubs["get_iam_policy"]
+
+ @property
+ def test_iam_permissions(
+ self,
+ ) -> Callable[
+ [iam_policy_pb2.TestIamPermissionsRequest],
+ iam_policy_pb2.TestIamPermissionsResponse,
+ ]:
+ r"""Return a callable for the test iam permissions method over gRPC.
+ Tests the specified permissions against the IAM access control
+ policy for a function. If the function does not exist, this will
+ return an empty set of permissions, not a NOT_FOUND error.
+ Returns:
+ Callable[[~.TestIamPermissionsRequest],
+ ~.TestIamPermissionsResponse]:
+ 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 "test_iam_permissions" not in self._stubs:
+ self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/TestIamPermissions",
+ request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
+ response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
+ )
+ return self._stubs["test_iam_permissions"]
__all__ = ("SchemaServiceGrpcAsyncIOTransport",)
diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py
index 953e58052..a0d42c2dd 100644
--- a/google/pubsub_v1/services/schema_service/transports/rest.py
+++ b/google/pubsub_v1/services/schema_service/transports/rest.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,52 +13,59 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging
+import json # type: ignore
from google.auth.transport.requests import AuthorizedSession # type: ignore
-import json # type: ignore
-import grpc # type: ignore
-from google.auth.transport.grpc import SslCredentials # type: ignore
from google.auth import credentials as ga_credentials # type: ignore
from google.api_core import exceptions as core_exceptions
from google.api_core import retry as retries
from google.api_core import rest_helpers
from google.api_core import rest_streaming
-from google.api_core import path_template
from google.api_core import gapic_v1
+import google.protobuf
from google.protobuf import json_format
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
+
from requests import __version__ as requests_version
import dataclasses
-import re
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
import warnings
+
+from google.protobuf import empty_pb2 # type: ignore
+from google.pubsub_v1.types import schema
+from google.pubsub_v1.types import schema as gp_schema
+
+
+from .rest_base import _BaseSchemaServiceRestTransport
+from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO
+
try:
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
OptionalRetry = Union[retries.Retry, object, None] # type: ignore
+try:
+ from google.api_core import client_logging # type: ignore
-from google.iam.v1 import iam_policy_pb2 # type: ignore
-from google.iam.v1 import policy_pb2 # type: ignore
-from google.protobuf import empty_pb2 # type: ignore
-from google.pubsub_v1.types import schema
-from google.pubsub_v1.types import schema as gp_schema
-
-from .base import (
- SchemaServiceTransport,
- DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO,
-)
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+_LOGGER = logging.getLogger(__name__)
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version,
grpc_version=None,
- rest_version=requests_version,
+ rest_version=f"requests@{requests_version}",
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
class SchemaServiceRestInterceptor:
"""Interceptor for SchemaService.
@@ -160,8 +167,8 @@ def post_validate_schema(self, response):
def pre_commit_schema(
self,
request: gp_schema.CommitSchemaRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[gp_schema.CommitSchemaRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[gp_schema.CommitSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for commit_schema
Override in a subclass to manipulate the request or metadata
@@ -172,17 +179,40 @@ def pre_commit_schema(
def post_commit_schema(self, response: gp_schema.Schema) -> gp_schema.Schema:
"""Post-rpc interceptor for commit_schema
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_commit_schema_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_commit_schema` interceptor runs
+ before the `post_commit_schema_with_metadata` interceptor.
"""
return response
+ def post_commit_schema_with_metadata(
+ self,
+ response: gp_schema.Schema,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[gp_schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for commit_schema
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_commit_schema_with_metadata`
+ interceptor in new development instead of the `post_commit_schema` interceptor.
+ When both interceptors are used, this `post_commit_schema_with_metadata` interceptor runs after the
+ `post_commit_schema` interceptor. The (possibly modified) response returned by
+ `post_commit_schema` will be passed to
+ `post_commit_schema_with_metadata`.
+ """
+ return response, metadata
+
def pre_create_schema(
self,
request: gp_schema.CreateSchemaRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[gp_schema.CreateSchemaRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[gp_schema.CreateSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for create_schema
Override in a subclass to manipulate the request or metadata
@@ -193,15 +223,40 @@ def pre_create_schema(
def post_create_schema(self, response: gp_schema.Schema) -> gp_schema.Schema:
"""Post-rpc interceptor for create_schema
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_create_schema_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_create_schema` interceptor runs
+ before the `post_create_schema_with_metadata` interceptor.
"""
return response
+ def post_create_schema_with_metadata(
+ self,
+ response: gp_schema.Schema,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[gp_schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for create_schema
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_create_schema_with_metadata`
+ interceptor in new development instead of the `post_create_schema` interceptor.
+ When both interceptors are used, this `post_create_schema_with_metadata` interceptor runs after the
+ `post_create_schema` interceptor. The (possibly modified) response returned by
+ `post_create_schema` will be passed to
+ `post_create_schema_with_metadata`.
+ """
+ return response, metadata
+
def pre_delete_schema(
- self, request: schema.DeleteSchemaRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[schema.DeleteSchemaRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: schema.DeleteSchemaRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[schema.DeleteSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for delete_schema
Override in a subclass to manipulate the request or metadata
@@ -212,8 +267,10 @@ def pre_delete_schema(
def pre_delete_schema_revision(
self,
request: schema.DeleteSchemaRevisionRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[schema.DeleteSchemaRevisionRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ schema.DeleteSchemaRevisionRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for delete_schema_revision
Override in a subclass to manipulate the request or metadata
@@ -224,15 +281,38 @@ def pre_delete_schema_revision(
def post_delete_schema_revision(self, response: schema.Schema) -> schema.Schema:
"""Post-rpc interceptor for delete_schema_revision
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_delete_schema_revision_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_delete_schema_revision` interceptor runs
+ before the `post_delete_schema_revision_with_metadata` interceptor.
"""
return response
+ def post_delete_schema_revision_with_metadata(
+ self, response: schema.Schema, metadata: Sequence[Tuple[str, Union[str, bytes]]]
+ ) -> Tuple[schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for delete_schema_revision
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_delete_schema_revision_with_metadata`
+ interceptor in new development instead of the `post_delete_schema_revision` interceptor.
+ When both interceptors are used, this `post_delete_schema_revision_with_metadata` interceptor runs after the
+ `post_delete_schema_revision` interceptor. The (possibly modified) response returned by
+ `post_delete_schema_revision` will be passed to
+ `post_delete_schema_revision_with_metadata`.
+ """
+ return response, metadata
+
def pre_get_schema(
- self, request: schema.GetSchemaRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[schema.GetSchemaRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: schema.GetSchemaRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[schema.GetSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for get_schema
Override in a subclass to manipulate the request or metadata
@@ -243,17 +323,40 @@ def pre_get_schema(
def post_get_schema(self, response: schema.Schema) -> schema.Schema:
"""Post-rpc interceptor for get_schema
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_get_schema_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_get_schema` interceptor runs
+ before the `post_get_schema_with_metadata` interceptor.
"""
return response
+ def post_get_schema_with_metadata(
+ self, response: schema.Schema, metadata: Sequence[Tuple[str, Union[str, bytes]]]
+ ) -> Tuple[schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for get_schema
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_get_schema_with_metadata`
+ interceptor in new development instead of the `post_get_schema` interceptor.
+ When both interceptors are used, this `post_get_schema_with_metadata` interceptor runs after the
+ `post_get_schema` interceptor. The (possibly modified) response returned by
+ `post_get_schema` will be passed to
+ `post_get_schema_with_metadata`.
+ """
+ return response, metadata
+
def pre_list_schema_revisions(
self,
request: schema.ListSchemaRevisionsRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[schema.ListSchemaRevisionsRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ schema.ListSchemaRevisionsRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for list_schema_revisions
Override in a subclass to manipulate the request or metadata
@@ -266,15 +369,42 @@ def post_list_schema_revisions(
) -> schema.ListSchemaRevisionsResponse:
"""Post-rpc interceptor for list_schema_revisions
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_list_schema_revisions_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_list_schema_revisions` interceptor runs
+ before the `post_list_schema_revisions_with_metadata` interceptor.
"""
return response
+ def post_list_schema_revisions_with_metadata(
+ self,
+ response: schema.ListSchemaRevisionsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ schema.ListSchemaRevisionsResponse, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Post-rpc interceptor for list_schema_revisions
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_list_schema_revisions_with_metadata`
+ interceptor in new development instead of the `post_list_schema_revisions` interceptor.
+ When both interceptors are used, this `post_list_schema_revisions_with_metadata` interceptor runs after the
+ `post_list_schema_revisions` interceptor. The (possibly modified) response returned by
+ `post_list_schema_revisions` will be passed to
+ `post_list_schema_revisions_with_metadata`.
+ """
+ return response, metadata
+
def pre_list_schemas(
- self, request: schema.ListSchemasRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[schema.ListSchemasRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: schema.ListSchemasRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[schema.ListSchemasRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for list_schemas
Override in a subclass to manipulate the request or metadata
@@ -287,15 +417,40 @@ def post_list_schemas(
) -> schema.ListSchemasResponse:
"""Post-rpc interceptor for list_schemas
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_list_schemas_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_list_schemas` interceptor runs
+ before the `post_list_schemas_with_metadata` interceptor.
"""
return response
+ def post_list_schemas_with_metadata(
+ self,
+ response: schema.ListSchemasResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[schema.ListSchemasResponse, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for list_schemas
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_list_schemas_with_metadata`
+ interceptor in new development instead of the `post_list_schemas` interceptor.
+ When both interceptors are used, this `post_list_schemas_with_metadata` interceptor runs after the
+ `post_list_schemas` interceptor. The (possibly modified) response returned by
+ `post_list_schemas` will be passed to
+ `post_list_schemas_with_metadata`.
+ """
+ return response, metadata
+
def pre_rollback_schema(
- self, request: schema.RollbackSchemaRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[schema.RollbackSchemaRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: schema.RollbackSchemaRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[schema.RollbackSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for rollback_schema
Override in a subclass to manipulate the request or metadata
@@ -306,17 +461,38 @@ def pre_rollback_schema(
def post_rollback_schema(self, response: schema.Schema) -> schema.Schema:
"""Post-rpc interceptor for rollback_schema
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_rollback_schema_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_rollback_schema` interceptor runs
+ before the `post_rollback_schema_with_metadata` interceptor.
"""
return response
+ def post_rollback_schema_with_metadata(
+ self, response: schema.Schema, metadata: Sequence[Tuple[str, Union[str, bytes]]]
+ ) -> Tuple[schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for rollback_schema
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_rollback_schema_with_metadata`
+ interceptor in new development instead of the `post_rollback_schema` interceptor.
+ When both interceptors are used, this `post_rollback_schema_with_metadata` interceptor runs after the
+ `post_rollback_schema` interceptor. The (possibly modified) response returned by
+ `post_rollback_schema` will be passed to
+ `post_rollback_schema_with_metadata`.
+ """
+ return response, metadata
+
def pre_validate_message(
self,
request: schema.ValidateMessageRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[schema.ValidateMessageRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[schema.ValidateMessageRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for validate_message
Override in a subclass to manipulate the request or metadata
@@ -329,17 +505,42 @@ def post_validate_message(
) -> schema.ValidateMessageResponse:
"""Post-rpc interceptor for validate_message
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_validate_message_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_validate_message` interceptor runs
+ before the `post_validate_message_with_metadata` interceptor.
"""
return response
+ def post_validate_message_with_metadata(
+ self,
+ response: schema.ValidateMessageResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[schema.ValidateMessageResponse, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for validate_message
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_validate_message_with_metadata`
+ interceptor in new development instead of the `post_validate_message` interceptor.
+ When both interceptors are used, this `post_validate_message_with_metadata` interceptor runs after the
+ `post_validate_message` interceptor. The (possibly modified) response returned by
+ `post_validate_message` will be passed to
+ `post_validate_message_with_metadata`.
+ """
+ return response, metadata
+
def pre_validate_schema(
self,
request: gp_schema.ValidateSchemaRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[gp_schema.ValidateSchemaRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ gp_schema.ValidateSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for validate_schema
Override in a subclass to manipulate the request or metadata
@@ -352,17 +553,44 @@ def post_validate_schema(
) -> gp_schema.ValidateSchemaResponse:
"""Post-rpc interceptor for validate_schema
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_validate_schema_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the SchemaService server but before
- it is returned to user code.
+ it is returned to user code. This `post_validate_schema` interceptor runs
+ before the `post_validate_schema_with_metadata` interceptor.
"""
return response
+ def post_validate_schema_with_metadata(
+ self,
+ response: gp_schema.ValidateSchemaResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ gp_schema.ValidateSchemaResponse, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Post-rpc interceptor for validate_schema
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the SchemaService server but before it is returned to user code.
+
+ We recommend only using this `post_validate_schema_with_metadata`
+ interceptor in new development instead of the `post_validate_schema` interceptor.
+ When both interceptors are used, this `post_validate_schema_with_metadata` interceptor runs after the
+ `post_validate_schema` interceptor. The (possibly modified) response returned by
+ `post_validate_schema` will be passed to
+ `post_validate_schema_with_metadata`.
+ """
+ return response, metadata
+
def pre_get_iam_policy(
self,
request: iam_policy_pb2.GetIamPolicyRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for get_iam_policy
Override in a subclass to manipulate the request or metadata
@@ -382,8 +610,10 @@ def post_get_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy:
def pre_set_iam_policy(
self,
request: iam_policy_pb2.SetIamPolicyRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for set_iam_policy
Override in a subclass to manipulate the request or metadata
@@ -403,8 +633,11 @@ def post_set_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy:
def pre_test_iam_permissions(
self,
request: iam_policy_pb2.TestIamPermissionsRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.TestIamPermissionsRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.TestIamPermissionsRequest,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
"""Pre-rpc interceptor for test_iam_permissions
Override in a subclass to manipulate the request or metadata
@@ -431,8 +664,8 @@ class SchemaServiceRestStub:
_interceptor: SchemaServiceRestInterceptor
-class SchemaServiceRestTransport(SchemaServiceTransport):
- """REST backend transport for SchemaService.
+class SchemaServiceRestTransport(_BaseSchemaServiceRestTransport):
+ """REST backend synchronous transport for SchemaService.
Service for doing schema-related operations.
@@ -441,7 +674,6 @@ class SchemaServiceRestTransport(SchemaServiceTransport):
and call it.
It sends JSON representations of protocol buffers over HTTP/1.1
-
"""
def __init__(
@@ -470,9 +702,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if ``channel`` is provided. This argument will be
+ removed in the next major version of this library.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
ignored if ``channel`` is provided.
client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
@@ -495,21 +728,12 @@ def __init__(
# TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
# TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
# credentials object
- maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host)
- if maybe_url_match is None:
- raise ValueError(
- f"Unexpected hostname structure: {host}"
- ) # pragma: NO COVER
-
- url_match_items = maybe_url_match.groupdict()
-
- host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
-
super().__init__(
host=host,
credentials=credentials,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ url_scheme=url_scheme,
api_audience=api_audience,
)
self._session = AuthorizedSession(
@@ -520,19 +744,34 @@ def __init__(
self._interceptor = interceptor or SchemaServiceRestInterceptor()
self._prep_wrapped_messages(client_info)
- class _CommitSchema(SchemaServiceRestStub):
+ class _CommitSchema(
+ _BaseSchemaServiceRestTransport._BaseCommitSchema, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("CommitSchema")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.CommitSchema")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -540,7 +779,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.Schema:
r"""Call the commit schema method over HTTP.
@@ -550,53 +789,70 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.gp_schema.Schema:
A schema resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{name=projects/*/schemas/*}:commit",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_commit_schema(request, metadata)
- pb_request = gp_schema.CommitSchemaRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseCommitSchema._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_commit_schema(request, metadata)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseCommitSchema._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSchemaServiceRestTransport._BaseCommitSchema._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseCommitSchema._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.CommitSchema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "CommitSchema",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SchemaServiceRestTransport._CommitSchema._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -609,22 +865,63 @@ def __call__(
pb_resp = gp_schema.Schema.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_commit_schema(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_commit_schema_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = gp_schema.Schema.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.commit_schema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "CommitSchema",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _CreateSchema(SchemaServiceRestStub):
+ class _CreateSchema(
+ _BaseSchemaServiceRestTransport._BaseCreateSchema, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("CreateSchema")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.CreateSchema")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -632,7 +929,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.Schema:
r"""Call the create schema method over HTTP.
@@ -642,53 +939,70 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.gp_schema.Schema:
A schema resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{parent=projects/*}/schemas",
- "body": "schema",
- },
- ]
- request, metadata = self._interceptor.pre_create_schema(request, metadata)
- pb_request = gp_schema.CreateSchemaRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseCreateSchema._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_create_schema(request, metadata)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseCreateSchema._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSchemaServiceRestTransport._BaseCreateSchema._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseCreateSchema._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.CreateSchema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "CreateSchema",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SchemaServiceRestTransport._CreateSchema._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -701,22 +1015,62 @@ def __call__(
pb_resp = gp_schema.Schema.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_create_schema(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_create_schema_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = gp_schema.Schema.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.create_schema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "CreateSchema",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _DeleteSchema(SchemaServiceRestStub):
+ class _DeleteSchema(
+ _BaseSchemaServiceRestTransport._BaseDeleteSchema, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("DeleteSchema")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.DeleteSchema")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -724,7 +1078,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
):
r"""Call the delete schema method over HTTP.
@@ -734,42 +1088,61 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "delete",
- "uri": "/v1/{name=projects/*/schemas/*}",
- },
- ]
- request, metadata = self._interceptor.pre_delete_schema(request, metadata)
- pb_request = schema.DeleteSchemaRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_delete_schema(request, metadata)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.DeleteSchema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "DeleteSchema",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SchemaServiceRestTransport._DeleteSchema._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -777,19 +1150,33 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
- class _DeleteSchemaRevision(SchemaServiceRestStub):
+ class _DeleteSchemaRevision(
+ _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("DeleteSchemaRevision")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.DeleteSchemaRevision")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -797,7 +1184,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Call the delete schema revision method over HTTP.
@@ -807,48 +1194,67 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.schema.Schema:
A schema resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "delete",
- "uri": "/v1/{name=projects/*/schemas/*}:deleteRevision",
- },
- ]
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_delete_schema_revision(
request, metadata
)
- pb_request = schema.DeleteSchemaRevisionRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.DeleteSchemaRevision",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "DeleteSchemaRevision",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SchemaServiceRestTransport._DeleteSchemaRevision._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -861,22 +1267,62 @@ def __call__(
pb_resp = schema.Schema.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_delete_schema_revision(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_delete_schema_revision_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = schema.Schema.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.delete_schema_revision",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "DeleteSchemaRevision",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _GetSchema(SchemaServiceRestStub):
+ class _GetSchema(
+ _BaseSchemaServiceRestTransport._BaseGetSchema, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("GetSchema")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.GetSchema")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -884,7 +1330,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Call the get schema method over HTTP.
@@ -894,46 +1340,69 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.schema.Schema:
A schema resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{name=projects/*/schemas/*}",
- },
- ]
- request, metadata = self._interceptor.pre_get_schema(request, metadata)
- pb_request = schema.GetSchemaRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseGetSchema._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_get_schema(request, metadata)
+ transcoded_request = (
+ _BaseSchemaServiceRestTransport._BaseGetSchema._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSchemaServiceRestTransport._BaseGetSchema._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.GetSchema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "GetSchema",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SchemaServiceRestTransport._GetSchema._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -946,22 +1415,62 @@ def __call__(
pb_resp = schema.Schema.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_get_schema(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_get_schema_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = schema.Schema.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.get_schema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "GetSchema",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ListSchemaRevisions(SchemaServiceRestStub):
+ class _ListSchemaRevisions(
+ _BaseSchemaServiceRestTransport._BaseListSchemaRevisions, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("ListSchemaRevisions")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.ListSchemaRevisions")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -969,7 +1478,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.ListSchemaRevisionsResponse:
r"""Call the list schema revisions method over HTTP.
@@ -979,48 +1488,67 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.schema.ListSchemaRevisionsResponse:
Response for the ``ListSchemaRevisions`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{name=projects/*/schemas/*}:listRevisions",
- },
- ]
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_list_schema_revisions(
request, metadata
)
- pb_request = schema.ListSchemaRevisionsRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.ListSchemaRevisions",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ListSchemaRevisions",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SchemaServiceRestTransport._ListSchemaRevisions._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1033,22 +1561,64 @@ def __call__(
pb_resp = schema.ListSchemaRevisionsResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_list_schema_revisions(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_schema_revisions_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = schema.ListSchemaRevisionsResponse.to_json(
+ response
+ )
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.list_schema_revisions",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ListSchemaRevisions",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ListSchemas(SchemaServiceRestStub):
+ class _ListSchemas(
+ _BaseSchemaServiceRestTransport._BaseListSchemas, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("ListSchemas")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.ListSchemas")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -1056,7 +1626,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.ListSchemasResponse:
r"""Call the list schemas method over HTTP.
@@ -1066,46 +1636,67 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.schema.ListSchemasResponse:
Response for the ``ListSchemas`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{parent=projects/*}/schemas",
- },
- ]
- request, metadata = self._interceptor.pre_list_schemas(request, metadata)
- pb_request = schema.ListSchemasRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseListSchemas._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_list_schemas(request, metadata)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseListSchemas._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSchemaServiceRestTransport._BaseListSchemas._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.ListSchemas",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ListSchemas",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SchemaServiceRestTransport._ListSchemas._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1118,22 +1709,63 @@ def __call__(
pb_resp = schema.ListSchemasResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_list_schemas(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_schemas_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = schema.ListSchemasResponse.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.list_schemas",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ListSchemas",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _RollbackSchema(SchemaServiceRestStub):
+ class _RollbackSchema(
+ _BaseSchemaServiceRestTransport._BaseRollbackSchema, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("RollbackSchema")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.RollbackSchema")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1141,7 +1773,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.Schema:
r"""Call the rollback schema method over HTTP.
@@ -1151,53 +1783,70 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.schema.Schema:
A schema resource.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{name=projects/*/schemas/*}:rollback",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_rollback_schema(request, metadata)
- pb_request = schema.RollbackSchemaRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_rollback_schema(request, metadata)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.RollbackSchema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "RollbackSchema",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SchemaServiceRestTransport._RollbackSchema._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1210,22 +1859,63 @@ def __call__(
pb_resp = schema.Schema.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_rollback_schema(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_rollback_schema_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = schema.Schema.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.rollback_schema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "RollbackSchema",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ValidateMessage(SchemaServiceRestStub):
+ class _ValidateMessage(
+ _BaseSchemaServiceRestTransport._BaseValidateMessage, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("ValidateMessage")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.ValidateMessage")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1233,7 +1923,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> schema.ValidateMessageResponse:
r"""Call the validate message method over HTTP.
@@ -1243,8 +1933,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.schema.ValidateMessageResponse:
@@ -1253,47 +1945,62 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{parent=projects/*}/schemas:validateMessage",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseValidateMessage._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_validate_message(
request, metadata
)
- pb_request = schema.ValidateMessageRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- # Jsonify the request body
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseValidateMessage._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSchemaServiceRestTransport._BaseValidateMessage._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseValidateMessage._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.ValidateMessage",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ValidateMessage",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SchemaServiceRestTransport._ValidateMessage._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1306,22 +2013,63 @@ def __call__(
pb_resp = schema.ValidateMessageResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_validate_message(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_validate_message_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = schema.ValidateMessageResponse.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.validate_message",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ValidateMessage",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ValidateSchema(SchemaServiceRestStub):
+ class _ValidateSchema(
+ _BaseSchemaServiceRestTransport._BaseValidateSchema, SchemaServiceRestStub
+ ):
def __hash__(self):
- return hash("ValidateSchema")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SchemaServiceRestTransport.ValidateSchema")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1329,7 +2077,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> gp_schema.ValidateSchemaResponse:
r"""Call the validate schema method over HTTP.
@@ -1339,8 +2087,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.gp_schema.ValidateSchemaResponse:
@@ -1349,45 +2099,60 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{parent=projects/*}/schemas:validate",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_validate_schema(request, metadata)
- pb_request = gp_schema.ValidateSchemaRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseValidateSchema._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_validate_schema(request, metadata)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseValidateSchema._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSchemaServiceRestTransport._BaseValidateSchema._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSchemaServiceRestTransport._BaseValidateSchema._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.ValidateSchema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ValidateSchema",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SchemaServiceRestTransport._ValidateSchema._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1400,7 +2165,35 @@ def __call__(
pb_resp = gp_schema.ValidateSchemaResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_validate_schema(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_validate_schema_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = gp_schema.ValidateSchemaResponse.to_json(
+ response
+ )
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceClient.validate_schema",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "ValidateSchema",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
@property
@@ -1485,14 +2278,41 @@ def validate_schema(
def get_iam_policy(self):
return self._GetIamPolicy(self._session, self._host, self._interceptor) # type: ignore
- class _GetIamPolicy(SchemaServiceRestStub):
+ class _GetIamPolicy(
+ _BaseSchemaServiceRestTransport._BaseGetIamPolicy, SchemaServiceRestStub
+ ):
+ def __hash__(self):
+ return hash("SchemaServiceRestTransport.GetIamPolicy")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.GetIamPolicyRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Call the get iam policy method over HTTP.
@@ -1502,51 +2322,64 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
policy_pb2.Policy: Response from GetIamPolicy method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy",
- },
- ]
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_http_options()
+ )
request, metadata = self._interceptor.pre_get_iam_policy(request, metadata)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_query_params_json(
+ transcoded_request
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.GetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "GetIamPolicy",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
+ # Send the request
+ response = SchemaServiceRestTransport._GetIamPolicy._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1554,23 +2387,73 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = policy_pb2.Policy()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_get_iam_policy(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceAsyncClient.GetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "GetIamPolicy",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
def set_iam_policy(self):
return self._SetIamPolicy(self._session, self._host, self._interceptor) # type: ignore
- class _SetIamPolicy(SchemaServiceRestStub):
+ class _SetIamPolicy(
+ _BaseSchemaServiceRestTransport._BaseSetIamPolicy, SchemaServiceRestStub
+ ):
+ def __hash__(self):
+ return hash("SchemaServiceRestTransport.SetIamPolicy")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.SetIamPolicyRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Call the set iam policy method over HTTP.
@@ -1580,57 +2463,69 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
policy_pb2.Policy: Response from SetIamPolicy method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_http_options()
+ )
request, metadata = self._interceptor.pre_set_iam_policy(request, metadata)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_transcoded_request(
+ http_options, request
+ )
- body = json.dumps(transcoded_request["body"])
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ body = _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_request_body_json(
+ transcoded_request
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_query_params_json(
+ transcoded_request
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.SetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "SetIamPolicy",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
- data=body,
+ # Send the request
+ response = SchemaServiceRestTransport._SetIamPolicy._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1638,23 +2533,73 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = policy_pb2.Policy()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_set_iam_policy(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceAsyncClient.SetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "SetIamPolicy",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
def test_iam_permissions(self):
return self._TestIamPermissions(self._session, self._host, self._interceptor) # type: ignore
- class _TestIamPermissions(SchemaServiceRestStub):
+ class _TestIamPermissions(
+ _BaseSchemaServiceRestTransport._BaseTestIamPermissions, SchemaServiceRestStub
+ ):
+ def __hash__(self):
+ return hash("SchemaServiceRestTransport.TestIamPermissions")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.TestIamPermissionsRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
r"""Call the test iam permissions method over HTTP.
@@ -1664,59 +2609,71 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_http_options()
+ )
request, metadata = self._interceptor.pre_test_iam_permissions(
request, metadata
)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ transcoded_request = _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_transcoded_request(
+ http_options, request
+ )
- body = json.dumps(transcoded_request["body"])
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ body = _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_request_body_json(
+ transcoded_request
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_query_params_json(
+ transcoded_request
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SchemaServiceClient.TestIamPermissions",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "TestIamPermissions",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
- data=body,
+ # Send the request
+ response = SchemaServiceRestTransport._TestIamPermissions._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1724,9 +2681,31 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = iam_policy_pb2.TestIamPermissionsResponse()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_test_iam_permissions(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SchemaServiceAsyncClient.TestIamPermissions",
+ extra={
+ "serviceName": "google.pubsub.v1.SchemaService",
+ "rpcName": "TestIamPermissions",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
diff --git a/google/pubsub_v1/services/schema_service/transports/rest_base.py b/google/pubsub_v1/services/schema_service/transports/rest_base.py
new file mode 100644
index 000000000..0ce5285bd
--- /dev/null
+++ b/google/pubsub_v1/services/schema_service/transports/rest_base.py
@@ -0,0 +1,746 @@
+# -*- coding: utf-8 -*-
+# Copyright 2025 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 json # type: ignore
+from google.api_core import path_template
+from google.api_core import gapic_v1
+
+from google.protobuf import json_format
+from google.iam.v1 import iam_policy_pb2 # type: ignore
+from google.iam.v1 import policy_pb2 # type: ignore
+from .base import SchemaServiceTransport, DEFAULT_CLIENT_INFO
+
+import re
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+
+
+from google.protobuf import empty_pb2 # type: ignore
+from google.pubsub_v1.types import schema
+from google.pubsub_v1.types import schema as gp_schema
+
+
+class _BaseSchemaServiceRestTransport(SchemaServiceTransport):
+ """Base REST backend transport for SchemaService.
+
+ Note: This class is not meant to be used directly. Use its sync and
+ async sub-classes instead.
+
+ This class defines the same methods as the primary client, so the
+ primary client can load the underlying transport implementation
+ and call it.
+
+ It sends JSON representations of protocol buffers over HTTP/1.1
+ """
+
+ def __init__(
+ self,
+ *,
+ host: str = "pubsub.googleapis.com",
+ credentials: Optional[Any] = None,
+ client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
+ always_use_jwt_access: Optional[bool] = False,
+ url_scheme: str = "https",
+ api_audience: Optional[str] = None,
+ ) -> None:
+ """Instantiate the transport.
+ Args:
+ host (Optional[str]):
+ The hostname to connect to (default: 'pubsub.googleapis.com').
+ credentials (Optional[Any]): The
+ authorization credentials to attach to requests. These
+ credentials identify the application to the service; if none
+ are specified, the client will attempt to ascertain the
+ credentials from the environment.
+ client_info (google.api_core.gapic_v1.client_info.ClientInfo):
+ The client info used to send a user-agent string along with
+ API requests. If ``None``, then default info will be used.
+ Generally, you only need to set this if you are developing
+ your own client library.
+ always_use_jwt_access (Optional[bool]): Whether self signed JWT should
+ be used for service account credentials.
+ url_scheme: the protocol scheme for the API endpoint. Normally
+ "https", but for testing or local servers,
+ "http" can be specified.
+ """
+ # Run the base constructor
+ maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host)
+ if maybe_url_match is None:
+ raise ValueError(
+ f"Unexpected hostname structure: {host}"
+ ) # pragma: NO COVER
+
+ url_match_items = maybe_url_match.groupdict()
+
+ host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
+
+ super().__init__(
+ host=host,
+ credentials=credentials,
+ client_info=client_info,
+ always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
+ )
+
+ class _BaseCommitSchema:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{name=projects/*/schemas/*}:commit",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = gp_schema.CommitSchemaRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseCommitSchema._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseCreateSchema:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{parent=projects/*}/schemas",
+ "body": "schema",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = gp_schema.CreateSchemaRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseCreateSchema._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseDeleteSchema:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "delete",
+ "uri": "/v1/{name=projects/*/schemas/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = schema.DeleteSchemaRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseDeleteSchemaRevision:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "delete",
+ "uri": "/v1/{name=projects/*/schemas/*}:deleteRevision",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = schema.DeleteSchemaRevisionRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseGetSchema:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{name=projects/*/schemas/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = schema.GetSchemaRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseGetSchema._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListSchemaRevisions:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{name=projects/*/schemas/*}:listRevisions",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = schema.ListSchemaRevisionsRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListSchemas:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{parent=projects/*}/schemas",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = schema.ListSchemasRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseListSchemas._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseRollbackSchema:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{name=projects/*/schemas/*}:rollback",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = schema.RollbackSchemaRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseValidateMessage:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{parent=projects/*}/schemas:validateMessage",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = schema.ValidateMessageRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseValidateMessage._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseValidateSchema:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{parent=projects/*}/schemas:validate",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = gp_schema.ValidateSchemaRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSchemaServiceRestTransport._BaseValidateSchema._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseGetIamPolicy:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+ class _BaseSetIamPolicy:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ body = json.dumps(transcoded_request["body"])
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+ class _BaseTestIamPermissions:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ body = json.dumps(transcoded_request["body"])
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+
+__all__ = ("_BaseSchemaServiceRestTransport",)
diff --git a/google/pubsub_v1/services/subscriber/__init__.py b/google/pubsub_v1/services/subscriber/__init__.py
index e6994bdf6..0e651adb7 100644
--- a/google/pubsub_v1/services/subscriber/__init__.py
+++ b/google/pubsub_v1/services/subscriber/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py
index dcd4f0bbb..a7f1cc3f5 100644
--- a/google/pubsub_v1/services/subscriber/async_client.py
+++ b/google/pubsub_v1/services/subscriber/async_client.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging as std_logging
from collections import OrderedDict
import re
from typing import (
@@ -40,6 +41,7 @@
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
try:
@@ -58,6 +60,15 @@
from .transports.grpc_asyncio import SubscriberGrpcAsyncIOTransport
from .client import SubscriberClient
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
class SubscriberAsyncClient:
"""The service that an application uses to manipulate subscriptions and
@@ -75,6 +86,8 @@ class SubscriberAsyncClient:
_DEFAULT_ENDPOINT_TEMPLATE = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE
_DEFAULT_UNIVERSE = SubscriberClient._DEFAULT_UNIVERSE
+ listing_path = staticmethod(SubscriberClient.listing_path)
+ parse_listing_path = staticmethod(SubscriberClient.parse_listing_path)
snapshot_path = staticmethod(SubscriberClient.snapshot_path)
parse_snapshot_path = staticmethod(SubscriberClient.parse_snapshot_path)
subscription_path = staticmethod(SubscriberClient.subscription_path)
@@ -265,6 +278,28 @@ def __init__(
client_info=client_info,
)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.pubsub_v1.SubscriberAsyncClient`.",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "universeDomain": getattr(
+ self._client._transport._credentials, "universe_domain", ""
+ ),
+ "credentialsType": f"{type(self._client._transport._credentials).__module__}.{type(self._client._transport._credentials).__qualname__}",
+ "credentialsInfo": getattr(
+ self.transport._credentials, "get_cred_info", lambda: None
+ )(),
+ }
+ if hasattr(self._client._transport, "_credentials")
+ else {
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "credentialsType": None,
+ },
+ )
+
async def create_subscription(
self,
request: Optional[Union[pubsub.Subscription, dict]] = None,
@@ -275,7 +310,7 @@ async def create_subscription(
ack_deadline_seconds: Optional[int] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Creates a subscription to a given topic. See the [resource name
rules]
@@ -325,8 +360,8 @@ async def sample_create_subscription():
then the subscriber will pull and ack messages using API
methods. At most one of these fields may be set.
name (:class:`str`):
- Required. The name of the subscription. It must have the
- format
+ Required. Identifier. The name of the subscription. It
+ must have the format
``"projects/{project}/subscriptions/{subscription}"``.
``{subscription}`` must start with a letter, and contain
only letters (``[A-Za-z]``), numbers (``[0-9]``), dashes
@@ -388,8 +423,10 @@ async def sample_create_subscription():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Subscription:
@@ -402,7 +439,10 @@ async def sample_create_subscription():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, topic, push_config, ack_deadline_seconds])
+ flattened_params = [name, topic, push_config, ack_deadline_seconds]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -458,7 +498,7 @@ async def get_subscription(
subscription: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Gets the configuration details of a subscription.
@@ -502,8 +542,10 @@ async def sample_get_subscription():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Subscription:
@@ -516,7 +558,10 @@ async def sample_get_subscription():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription])
+ flattened_params = [subscription]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -569,7 +614,7 @@ async def update_subscription(
update_mask: Optional[field_mask_pb2.FieldMask] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Updates an existing subscription by updating the
fields specified in the update mask. Note that certain
@@ -628,8 +673,10 @@ async def sample_update_subscription():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Subscription:
@@ -642,7 +689,10 @@ async def sample_update_subscription():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, update_mask])
+ flattened_params = [subscription, update_mask]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -696,7 +746,7 @@ async def list_subscriptions(
project: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSubscriptionsAsyncPager:
r"""Lists matching subscriptions.
@@ -740,8 +790,10 @@ async def sample_list_subscriptions():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsAsyncPager:
@@ -754,7 +806,10 @@ async def sample_list_subscriptions():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([project])
+ flattened_params = [project]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -815,7 +870,7 @@ async def delete_subscription(
subscription: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Deletes an existing subscription. All messages retained in the
subscription are immediately dropped. Calls to ``Pull`` after
@@ -861,13 +916,18 @@ async def sample_delete_subscription():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription])
+ flattened_params = [subscription]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -918,7 +978,7 @@ async def modify_ack_deadline(
ack_deadline_seconds: Optional[int] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Modifies the ack deadline for a specific message. This method is
useful to indicate that more time is needed to process a message
@@ -987,13 +1047,18 @@ async def sample_modify_ack_deadline():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, ack_ids, ack_deadline_seconds])
+ flattened_params = [subscription, ack_ids, ack_deadline_seconds]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1047,7 +1112,7 @@ async def acknowledge(
ack_ids: Optional[MutableSequence[str]] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Acknowledges the messages associated with the ``ack_ids`` in the
``AcknowledgeRequest``. The Pub/Sub system can remove the
@@ -1104,13 +1169,18 @@ async def sample_acknowledge():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, ack_ids])
+ flattened_params = [subscription, ack_ids]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1163,7 +1233,7 @@ async def pull(
max_messages: Optional[int] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.PullResponse:
r"""Pulls messages from the server.
@@ -1232,8 +1302,10 @@ async def sample_pull():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.PullResponse:
@@ -1242,7 +1314,10 @@ async def sample_pull():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, return_immediately, max_messages])
+ flattened_params = [subscription, return_immediately, max_messages]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1301,10 +1376,10 @@ def streaming_pull(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> Awaitable[AsyncIterable[pubsub.StreamingPullResponse]]:
r"""Establishes a stream with the server, which sends messages down
- to the client. The client streams acknowledgements and ack
+ to the client. The client streams acknowledgments and ack
deadline modifications back to the server. The server will close
the stream and return the status on any error. The server may
close the stream with status ``UNAVAILABLE`` to reassign
@@ -1354,13 +1429,15 @@ def request_generator():
requests (AsyncIterator[`google.pubsub_v1.types.StreamingPullRequest`]):
The request object AsyncIterator. Request for the ``StreamingPull`` streaming RPC method.
This request is used to establish the initial stream as
- well as to stream acknowledgements and ack deadline
+ well as to stream acknowledgments and ack deadline
modifications from the client to the server.
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
AsyncIterable[google.pubsub_v1.types.StreamingPullResponse]:
@@ -1397,7 +1474,7 @@ async def modify_push_config(
push_config: Optional[pubsub.PushConfig] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Modifies the ``PushConfig`` for a specified subscription.
@@ -1456,13 +1533,18 @@ async def sample_modify_push_config():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, push_config])
+ flattened_params = [subscription, push_config]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1513,7 +1595,7 @@ async def get_snapshot(
snapshot: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Gets the configuration details of a snapshot. Snapshots are used
in
@@ -1561,8 +1643,10 @@ async def sample_get_snapshot():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Snapshot:
@@ -1577,7 +1661,10 @@ async def sample_get_snapshot():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([snapshot])
+ flattened_params = [snapshot]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1627,7 +1714,7 @@ async def list_snapshots(
project: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSnapshotsAsyncPager:
r"""Lists the existing snapshots. Snapshots are used in
`Seek `__
@@ -1675,8 +1762,10 @@ async def sample_list_snapshots():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.subscriber.pagers.ListSnapshotsAsyncPager:
@@ -1689,7 +1778,10 @@ async def sample_list_snapshots():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([project])
+ flattened_params = [project]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1751,7 +1843,7 @@ async def create_snapshot(
subscription: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Creates a snapshot from the requested subscription. Snapshots
are used in
@@ -1834,8 +1926,10 @@ async def sample_create_snapshot():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Snapshot:
@@ -1850,7 +1944,10 @@ async def sample_create_snapshot():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, subscription])
+ flattened_params = [name, subscription]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1903,7 +2000,7 @@ async def update_snapshot(
update_mask: Optional[field_mask_pb2.FieldMask] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Updates an existing snapshot by updating the fields specified in
the update mask. Snapshots are used in
@@ -1959,8 +2056,10 @@ async def sample_update_snapshot():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Snapshot:
@@ -1975,7 +2074,10 @@ async def sample_update_snapshot():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([snapshot, update_mask])
+ flattened_params = [snapshot, update_mask]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -2029,7 +2131,7 @@ async def delete_snapshot(
snapshot: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Removes an existing snapshot. Snapshots are used in [Seek]
(https://cloud.google.com/pubsub/docs/replay-overview)
@@ -2078,13 +2180,18 @@ async def sample_delete_snapshot():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([snapshot])
+ flattened_params = [snapshot]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -2130,7 +2237,7 @@ async def seek(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.SeekResponse:
r"""Seeks an existing subscription to a point in time or to a given
snapshot, whichever is provided in the request. Snapshots are
@@ -2174,8 +2281,10 @@ async def sample_seek():
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.SeekResponse:
@@ -2219,21 +2328,23 @@ async def set_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Sets the IAM access control policy on the specified function.
Replaces any existing policy.
Args:
- request (:class:`~.policy_pb2.SetIamPolicyRequest`):
+ request (:class:`~.iam_policy_pb2.SetIamPolicyRequest`):
The request object. Request message for `SetIamPolicy`
method.
retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -2253,6 +2364,7 @@ async def set_iam_policy(
**JSON Example**
::
+
{
"bindings": [
{
@@ -2309,11 +2421,7 @@ async def set_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.set_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[self._client._transport.set_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2341,22 +2449,24 @@ async def get_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Gets the IAM access control policy for a function.
- Returns an empty policy if the function exists and does
- not have a policy set.
+ Returns an empty policy if the function exists and does not have a
+ policy set.
Args:
request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`):
The request object. Request message for `GetIamPolicy`
method.
- retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
- should be retried.
+ retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -2433,11 +2543,7 @@ async def get_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.get_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[self._client._transport.get_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2465,25 +2571,27 @@ async def test_iam_permissions(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
- r"""Tests the specified permissions against the IAM access control
+ r"""Tests the specified IAM permissions against the IAM access control
policy for a function.
- If the function does not exist, this will
- return an empty set of permissions, not a NOT_FOUND error.
+ If the function does not exist, this will return an empty set
+ of permissions, not a NOT_FOUND error.
Args:
request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`):
The request object. Request message for
`TestIamPermissions` method.
- retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
- should be retried.
+ retry (google.api_core.retry_async.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
- ~iam_policy_pb2.PolicyTestIamPermissionsResponse:
+ ~.iam_policy_pb2.TestIamPermissionsResponse:
Response message for ``TestIamPermissions`` method.
"""
# Create or coerce a protobuf request object.
@@ -2495,11 +2603,9 @@ async def test_iam_permissions(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.test_iam_permissions,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self.transport._wrapped_methods[
+ self._client._transport.test_iam_permissions
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2532,5 +2638,8 @@ async def __aexit__(self, exc_type, exc, tb):
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
__all__ = ("SubscriberAsyncClient",)
diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py
index 9bad804c6..23e7ff6d0 100644
--- a/google/pubsub_v1/services/subscriber/client.py
+++ b/google/pubsub_v1/services/subscriber/client.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
# limitations under the License.
#
from collections import OrderedDict
+from http import HTTPStatus
+import json
+import logging as std_logging
import functools
import os
import re
@@ -46,12 +49,22 @@
from google.auth.transport.grpc import SslCredentials # type: ignore
from google.auth.exceptions import MutualTLSChannelError # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
try:
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
OptionalRetry = Union[retries.Retry, object, None] # type: ignore
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
from google.protobuf import duration_pb2 # type: ignore
@@ -159,6 +172,34 @@ def _get_default_mtls_endpoint(api_endpoint):
_DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}"
_DEFAULT_UNIVERSE = "googleapis.com"
+ @staticmethod
+ def _use_client_cert_effective():
+ """Returns whether client certificate should be used for mTLS if the
+ google-auth version supports should_use_client_cert automatic mTLS enablement.
+
+ Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
+
+ Returns:
+ bool: whether client certificate should be used for mTLS
+ Raises:
+ ValueError: (If using a version of google-auth without should_use_client_cert and
+ GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
+ """
+ # check if google-auth version supports should_use_client_cert for automatic mTLS enablement
+ if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
+ return mtls.should_use_client_cert()
+ else: # pragma: NO COVER
+ # if unsupported, fallback to reading from env var
+ use_client_cert_str = os.getenv(
+ "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
+ ).lower()
+ if use_client_cert_str not in ("true", "false"):
+ raise ValueError(
+ "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
+ " either `true` or `false`"
+ )
+ return use_client_cert_str == "true"
+
@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -206,6 +247,30 @@ def transport(self) -> SubscriberTransport:
"""
return self._transport
+ @staticmethod
+ def listing_path(
+ project: str,
+ location: str,
+ data_exchange: str,
+ listing: str,
+ ) -> str:
+ """Returns a fully-qualified listing string."""
+ return "projects/{project}/locations/{location}/dataExchanges/{data_exchange}/listings/{listing}".format(
+ project=project,
+ location=location,
+ data_exchange=data_exchange,
+ listing=listing,
+ )
+
+ @staticmethod
+ def parse_listing_path(path: str) -> Dict[str, str]:
+ """Parses a listing path into its component segments."""
+ m = re.match(
+ r"^projects/(?P.+?)/locations/(?P.+?)/dataExchanges/(?P.+?)/listings/(?P.+?)$",
+ path,
+ )
+ return m.groupdict() if m else {}
+
@staticmethod
def snapshot_path(
project: str,
@@ -377,12 +442,8 @@ def get_mtls_endpoint_and_cert_source(
)
if client_options is None:
client_options = client_options_lib.ClientOptions()
- use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
+ use_client_cert = SubscriberClient._use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
- if use_client_cert not in ("true", "false"):
- raise ValueError(
- "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError(
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
@@ -390,7 +451,7 @@ def get_mtls_endpoint_and_cert_source(
# Figure out the client cert source to use.
client_cert_source = None
- if use_client_cert == "true":
+ if use_client_cert:
if client_options.client_cert_source:
client_cert_source = client_options.client_cert_source
elif mtls.has_default_client_cert_source():
@@ -422,20 +483,14 @@ def _read_environment_variables():
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
is not any of ["auto", "never", "always"].
"""
- use_client_cert = os.getenv(
- "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
- ).lower()
+ use_client_cert = SubscriberClient._use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
- if use_client_cert not in ("true", "false"):
- raise ValueError(
- "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError(
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
+ return use_client_cert, use_mtls_endpoint, universe_domain_env
@staticmethod
def _get_client_cert_source(provided_cert_source, use_cert_flag):
@@ -515,52 +570,45 @@ def _get_universe_domain(
raise ValueError("Universe Domain cannot be an empty string.")
return universe_domain
- @staticmethod
- def _compare_universes(
- client_universe: str, credentials: ga_credentials.Credentials
- ) -> bool:
- """Returns True iff the universe domains used by the client and credentials match.
-
- Args:
- client_universe (str): The universe domain configured via the client options.
- credentials (ga_credentials.Credentials): The credentials being used in the client.
+ def _validate_universe_domain(self):
+ """Validates client's and credentials' universe domains are consistent.
Returns:
- bool: True iff client_universe matches the universe in credentials.
+ bool: True iff the configured universe domain is valid.
Raises:
- ValueError: when client_universe does not match the universe in credentials.
+ ValueError: If the configured universe domain is not valid.
"""
- default_universe = SubscriberClient._DEFAULT_UNIVERSE
- credentials_universe = getattr(credentials, "universe_domain", default_universe)
-
- if client_universe != credentials_universe:
- raise ValueError(
- "The configured universe domain "
- f"({client_universe}) does not match the universe domain "
- f"found in the credentials ({credentials_universe}). "
- "If you haven't configured the universe domain explicitly, "
- f"`{default_universe}` is the default."
- )
+ # NOTE (b/349488459): universe validation is disabled until further notice.
return True
- def _validate_universe_domain(self):
- """Validates client's and credentials' universe domains are consistent.
-
- Returns:
- bool: True iff the configured universe domain is valid.
+ def _add_cred_info_for_auth_errors(
+ self, error: core_exceptions.GoogleAPICallError
+ ) -> None:
+ """Adds credential info string to error details for 401/403/404 errors.
- Raises:
- ValueError: If the configured universe domain is not valid.
+ Args:
+ error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info.
"""
- self._is_universe_domain_valid = (
- self._is_universe_domain_valid
- or SubscriberClient._compare_universes(
- self.universe_domain, self.transport._credentials
- )
- )
- return self._is_universe_domain_valid
+ if error.code not in [
+ HTTPStatus.UNAUTHORIZED,
+ HTTPStatus.FORBIDDEN,
+ HTTPStatus.NOT_FOUND,
+ ]:
+ return
+
+ cred = self._transport._credentials
+
+ # get_cred_info is only available in google-auth>=2.35.0
+ if not hasattr(cred, "get_cred_info"):
+ return
+
+ # ignore the type check since pypy test fails when get_cred_info
+ # is not available
+ cred_info = cred.get_cred_info() # type: ignore
+ if cred_info and hasattr(error._details, "append"):
+ error._details.append(json.dumps(cred_info))
@property
def api_endpoint(self):
@@ -666,6 +714,10 @@ def __init__(
# Initialize the universe domain validation.
self._is_universe_domain_valid = False
+ if CLIENT_LOGGING_SUPPORTED: # pragma: NO COVER
+ # Setup logging.
+ client_logging.initialize_logging()
+
api_key_value = getattr(self._client_options, "api_key", None)
if api_key_value and credentials:
raise ValueError(
@@ -719,7 +771,7 @@ def __init__(
emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST")
if emulator_host:
- if issubclass(transport_init, type(self)._transport_registry["grpc"]):
+ if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore
channel = grpc.insecure_channel(target=emulator_host)
else:
channel = grpc.aio.insecure_channel(target=emulator_host)
@@ -737,6 +789,29 @@ def __init__(
api_audience=self._client_options.api_audience,
)
+ if "async" not in str(self._transport):
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.pubsub_v1.SubscriberClient`.",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "universeDomain": getattr(
+ self._transport._credentials, "universe_domain", ""
+ ),
+ "credentialsType": f"{type(self._transport._credentials).__module__}.{type(self._transport._credentials).__qualname__}",
+ "credentialsInfo": getattr(
+ self.transport._credentials, "get_cred_info", lambda: None
+ )(),
+ }
+ if hasattr(self._transport, "_credentials")
+ else {
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "credentialsType": None,
+ },
+ )
+
def create_subscription(
self,
request: Optional[Union[pubsub.Subscription, dict]] = None,
@@ -747,7 +822,7 @@ def create_subscription(
ack_deadline_seconds: Optional[int] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Creates a subscription to a given topic. See the [resource name
rules]
@@ -797,8 +872,8 @@ def sample_create_subscription():
then the subscriber will pull and ack messages using API
methods. At most one of these fields may be set.
name (str):
- Required. The name of the subscription. It must have the
- format
+ Required. Identifier. The name of the subscription. It
+ must have the format
``"projects/{project}/subscriptions/{subscription}"``.
``{subscription}`` must start with a letter, and contain
only letters (``[A-Za-z]``), numbers (``[0-9]``), dashes
@@ -860,8 +935,10 @@ def sample_create_subscription():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Subscription:
@@ -874,7 +951,10 @@ def sample_create_subscription():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, topic, push_config, ack_deadline_seconds])
+ flattened_params = [name, topic, push_config, ack_deadline_seconds]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -927,7 +1007,7 @@ def get_subscription(
subscription: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Gets the configuration details of a subscription.
@@ -971,8 +1051,10 @@ def sample_get_subscription():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Subscription:
@@ -985,7 +1067,10 @@ def sample_get_subscription():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription])
+ flattened_params = [subscription]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1035,7 +1120,7 @@ def update_subscription(
update_mask: Optional[field_mask_pb2.FieldMask] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Updates an existing subscription by updating the
fields specified in the update mask. Note that certain
@@ -1094,8 +1179,10 @@ def sample_update_subscription():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Subscription:
@@ -1108,7 +1195,10 @@ def sample_update_subscription():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, update_mask])
+ flattened_params = [subscription, update_mask]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1159,7 +1249,7 @@ def list_subscriptions(
project: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSubscriptionsPager:
r"""Lists matching subscriptions.
@@ -1203,8 +1293,10 @@ def sample_list_subscriptions():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsPager:
@@ -1217,7 +1309,10 @@ def sample_list_subscriptions():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([project])
+ flattened_params = [project]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1275,7 +1370,7 @@ def delete_subscription(
subscription: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Deletes an existing subscription. All messages retained in the
subscription are immediately dropped. Calls to ``Pull`` after
@@ -1321,13 +1416,18 @@ def sample_delete_subscription():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription])
+ flattened_params = [subscription]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1375,7 +1475,7 @@ def modify_ack_deadline(
ack_deadline_seconds: Optional[int] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Modifies the ack deadline for a specific message. This method is
useful to indicate that more time is needed to process a message
@@ -1444,13 +1544,18 @@ def sample_modify_ack_deadline():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, ack_ids, ack_deadline_seconds])
+ flattened_params = [subscription, ack_ids, ack_deadline_seconds]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1501,7 +1606,7 @@ def acknowledge(
ack_ids: Optional[MutableSequence[str]] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Acknowledges the messages associated with the ``ack_ids`` in the
``AcknowledgeRequest``. The Pub/Sub system can remove the
@@ -1558,13 +1663,18 @@ def sample_acknowledge():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, ack_ids])
+ flattened_params = [subscription, ack_ids]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1614,7 +1724,7 @@ def pull(
max_messages: Optional[int] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.PullResponse:
r"""Pulls messages from the server.
@@ -1683,8 +1793,10 @@ def sample_pull():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.PullResponse:
@@ -1693,7 +1805,10 @@ def sample_pull():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, return_immediately, max_messages])
+ flattened_params = [subscription, return_immediately, max_messages]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1751,10 +1866,10 @@ def streaming_pull(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> Iterable[pubsub.StreamingPullResponse]:
r"""Establishes a stream with the server, which sends messages down
- to the client. The client streams acknowledgements and ack
+ to the client. The client streams acknowledgments and ack
deadline modifications back to the server. The server will close
the stream and return the status on any error. The server may
close the stream with status ``UNAVAILABLE`` to reassign
@@ -1804,13 +1919,15 @@ def request_generator():
requests (Iterator[google.pubsub_v1.types.StreamingPullRequest]):
The request object iterator. Request for the ``StreamingPull`` streaming RPC method.
This request is used to establish the initial stream as
- well as to stream acknowledgements and ack deadline
+ well as to stream acknowledgments and ack deadline
modifications from the client to the server.
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
Iterable[google.pubsub_v1.types.StreamingPullResponse]:
@@ -1850,7 +1967,7 @@ def modify_push_config(
push_config: Optional[pubsub.PushConfig] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Modifies the ``PushConfig`` for a specified subscription.
@@ -1909,13 +2026,18 @@ def sample_modify_push_config():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([subscription, push_config])
+ flattened_params = [subscription, push_config]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -1963,7 +2085,7 @@ def get_snapshot(
snapshot: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Gets the configuration details of a snapshot. Snapshots are used
in
@@ -2011,8 +2133,10 @@ def sample_get_snapshot():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Snapshot:
@@ -2027,7 +2151,10 @@ def sample_get_snapshot():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([snapshot])
+ flattened_params = [snapshot]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -2074,7 +2201,7 @@ def list_snapshots(
project: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListSnapshotsPager:
r"""Lists the existing snapshots. Snapshots are used in
`Seek `__
@@ -2122,8 +2249,10 @@ def sample_list_snapshots():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.services.subscriber.pagers.ListSnapshotsPager:
@@ -2136,7 +2265,10 @@ def sample_list_snapshots():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([project])
+ flattened_params = [project]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -2195,7 +2327,7 @@ def create_snapshot(
subscription: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Creates a snapshot from the requested subscription. Snapshots
are used in
@@ -2278,8 +2410,10 @@ def sample_create_snapshot():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Snapshot:
@@ -2294,7 +2428,10 @@ def sample_create_snapshot():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([name, subscription])
+ flattened_params = [name, subscription]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -2344,7 +2481,7 @@ def update_snapshot(
update_mask: Optional[field_mask_pb2.FieldMask] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Updates an existing snapshot by updating the fields specified in
the update mask. Snapshots are used in
@@ -2400,8 +2537,10 @@ def sample_update_snapshot():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.Snapshot:
@@ -2416,7 +2555,10 @@ def sample_update_snapshot():
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([snapshot, update_mask])
+ flattened_params = [snapshot, update_mask]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -2467,7 +2609,7 @@ def delete_snapshot(
snapshot: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> None:
r"""Removes an existing snapshot. Snapshots are used in [Seek]
(https://cloud.google.com/pubsub/docs/replay-overview)
@@ -2516,13 +2658,18 @@ def sample_delete_snapshot():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
# Create or coerce a protobuf request object.
# - Quick check: If we got a request object, we should *not* have
# gotten any keyword arguments that map to the request.
- has_flattened_params = any([snapshot])
+ flattened_params = [snapshot]
+ has_flattened_params = (
+ len([param for param in flattened_params if param is not None]) > 0
+ )
if request is not None and has_flattened_params:
raise ValueError(
"If the `request` argument is set, then none of "
@@ -2565,7 +2712,7 @@ def seek(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.SeekResponse:
r"""Seeks an existing subscription to a point in time or to a given
snapshot, whichever is provided in the request. Snapshots are
@@ -2609,8 +2756,10 @@ def sample_seek():
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
google.pubsub_v1.types.SeekResponse:
@@ -2667,7 +2816,7 @@ def set_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Sets the IAM access control policy on the specified function.
@@ -2680,8 +2829,10 @@ def set_iam_policy(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -2758,11 +2909,7 @@ def set_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.set_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.set_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2773,16 +2920,20 @@ def set_iam_policy(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
def get_iam_policy(
self,
@@ -2790,7 +2941,7 @@ def get_iam_policy(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Gets the IAM access control policy for a function.
@@ -2804,8 +2955,10 @@ def get_iam_policy(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.policy_pb2.Policy:
Defines an Identity and Access Management (IAM) policy.
@@ -2882,11 +3035,7 @@ def get_iam_policy(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.get_iam_policy,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.get_iam_policy]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2897,16 +3046,20 @@ def get_iam_policy(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
def test_iam_permissions(
self,
@@ -2914,7 +3067,7 @@ def test_iam_permissions(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
r"""Tests the specified IAM permissions against the IAM access control
policy for a function.
@@ -2929,8 +3082,10 @@ def test_iam_permissions(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.iam_policy_pb2.TestIamPermissionsResponse:
Response message for ``TestIamPermissions`` method.
@@ -2944,11 +3099,7 @@ def test_iam_permissions(
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method.wrap_method(
- self._transport.test_iam_permissions,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -2959,21 +3110,27 @@ def test_iam_permissions(
# Validate the universe domain.
self._validate_universe_domain()
- # Send the request.
- response = rpc(
- request,
- retry=retry,
- timeout=timeout,
- metadata=metadata,
- )
+ try:
+ # Send the request.
+ response = rpc(
+ request,
+ retry=retry,
+ timeout=timeout,
+ metadata=metadata,
+ )
- # Done; return the response.
- return response
+ # Done; return the response.
+ return response
+ except core_exceptions.GoogleAPICallError as e:
+ self._add_cred_info_for_auth_errors(e)
+ raise e
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
__all__ = ("SubscriberClient",)
diff --git a/google/pubsub_v1/services/subscriber/pagers.py b/google/pubsub_v1/services/subscriber/pagers.py
index c09c42027..9f879cfc8 100644
--- a/google/pubsub_v1/services/subscriber/pagers.py
+++ b/google/pubsub_v1/services/subscriber/pagers.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@ def __init__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -80,8 +80,10 @@ def __init__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListSubscriptionsRequest(request)
@@ -140,7 +142,7 @@ def __init__(
*,
retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -154,8 +156,10 @@ def __init__(
retry (google.api_core.retry.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListSubscriptionsRequest(request)
@@ -218,7 +222,7 @@ def __init__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -232,8 +236,10 @@ def __init__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListSnapshotsRequest(request)
@@ -292,7 +298,7 @@ def __init__(
*,
retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
timeout: Union[float, object] = gapic_v1.method.DEFAULT,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -306,8 +312,10 @@ def __init__(
retry (google.api_core.retry.AsyncRetry): 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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
self._method = method
self._request = pubsub.ListSnapshotsRequest(request)
diff --git a/google/pubsub_v1/services/subscriber/transports/README.rst b/google/pubsub_v1/services/subscriber/transports/README.rst
new file mode 100644
index 000000000..2df98ffe6
--- /dev/null
+++ b/google/pubsub_v1/services/subscriber/transports/README.rst
@@ -0,0 +1,9 @@
+
+transport inheritance structure
+_______________________________
+
+`SubscriberTransport` is the ABC for all transports.
+- public child `SubscriberGrpcTransport` for sync gRPC transport (defined in `grpc.py`).
+- public child `SubscriberGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`).
+- private child `_BaseSubscriberRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`).
+- public child `SubscriberRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`).
diff --git a/google/pubsub_v1/services/subscriber/transports/__init__.py b/google/pubsub_v1/services/subscriber/transports/__init__.py
index af60aba8e..73e9fd44f 100644
--- a/google/pubsub_v1/services/subscriber/transports/__init__.py
+++ b/google/pubsub_v1/services/subscriber/transports/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py
index c5fa183b1..a25ff562f 100644
--- a/google/pubsub_v1/services/subscriber/transports/base.py
+++ b/google/pubsub_v1/services/subscriber/transports/base.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
from google.api_core import retry as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore
+import google.protobuf
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
@@ -35,6 +36,9 @@
client_library_version=package_version.__version__
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
class SubscriberTransport(abc.ABC):
"""Abstract transport class for Subscriber."""
@@ -69,9 +73,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.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is mutually exclusive with credentials.
+ This argument is mutually exclusive with credentials. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A list of scopes.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
@@ -378,6 +383,21 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
+ self.get_iam_policy: gapic_v1.method.wrap_method(
+ self.get_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.set_iam_policy: gapic_v1.method.wrap_method(
+ self.set_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.test_iam_permissions: gapic_v1.method.wrap_method(
+ self.test_iam_permissions,
+ default_timeout=None,
+ client_info=client_info,
+ ),
}
def close(self):
diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py
index e706190ce..705163791 100644
--- a/google/pubsub_v1/services/subscriber/transports/grpc.py
+++ b/google/pubsub_v1/services/subscriber/transports/grpc.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import json
+import logging as std_logging
+import pickle
import warnings
from typing import Callable, Dict, Optional, Sequence, Tuple, Union
@@ -21,8 +24,11 @@
import google.auth # type: ignore
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
+from google.protobuf.json_format import MessageToJson
+import google.protobuf.message
import grpc # type: ignore
+import proto # type: ignore
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
@@ -30,6 +36,80 @@
from google.pubsub_v1.types import pubsub
from .base import SubscriberTransport, DEFAULT_CLIENT_INFO
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
+
+class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER
+ def intercept_unary_unary(self, continuation, client_call_details, request):
+ logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ )
+ if logging_enabled: # pragma: NO COVER
+ request_metadata = client_call_details.metadata
+ if isinstance(request, proto.Message):
+ request_payload = type(request).to_json(request)
+ elif isinstance(request, google.protobuf.message.Message):
+ request_payload = MessageToJson(request)
+ else:
+ request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
+
+ request_metadata = {
+ key: value.decode("utf-8") if isinstance(value, bytes) else value
+ for key, value in request_metadata
+ }
+ grpc_request = {
+ "payload": request_payload,
+ "requestMethod": "grpc",
+ "metadata": dict(request_metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for {client_call_details.method}",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": str(client_call_details.method),
+ "request": grpc_request,
+ "metadata": grpc_request["metadata"],
+ },
+ )
+ response = continuation(client_call_details, request)
+ if logging_enabled: # pragma: NO COVER
+ response_metadata = response.trailing_metadata()
+ # Convert gRPC metadata `` to list of tuples
+ metadata = (
+ dict([(k, str(v)) for k, v in response_metadata])
+ if response_metadata
+ else None
+ )
+ result = response.result()
+ if isinstance(result, proto.Message):
+ response_payload = type(result).to_json(result)
+ elif isinstance(result, google.protobuf.message.Message):
+ response_payload = MessageToJson(result)
+ else:
+ response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
+ grpc_response = {
+ "payload": response_payload,
+ "metadata": metadata,
+ "status": "OK",
+ }
+ _LOGGER.debug(
+ f"Received response for {client_call_details.method}.",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": client_call_details.method,
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class SubscriberGrpcTransport(SubscriberTransport):
"""gRPC backend transport for Subscriber.
@@ -77,9 +157,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
This argument is ignored if a ``channel`` instance is provided.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
This argument is ignored if a ``channel`` instance is provided.
+ This argument will be removed in the next major version of this library.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
ignored if a ``channel`` instance is provided.
channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]):
@@ -188,7 +269,12 @@ def __init__(
],
)
- # Wrap messages. This must be done after self._grpc_channel exists
+ self._interceptor = _LoggingClientInterceptor()
+ self._logged_channel = grpc.intercept_channel(
+ self._grpc_channel, self._interceptor
+ )
+
+ # Wrap messages. This must be done after self._logged_channel exists
self._prep_wrapped_messages(client_info)
@classmethod
@@ -209,9 +295,10 @@ def create_channel(
credentials identify this application to the service. If
none are specified, the client will attempt to ascertain
the credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is mutually exclusive with credentials.
+ This argument is mutually exclusive with credentials. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -274,7 +361,7 @@ def create_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_subscription" not in self._stubs:
- self._stubs["create_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["create_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/CreateSubscription",
request_serializer=pubsub.Subscription.serialize,
response_deserializer=pubsub.Subscription.deserialize,
@@ -300,7 +387,7 @@ def get_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_subscription" not in self._stubs:
- self._stubs["get_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["get_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/GetSubscription",
request_serializer=pubsub.GetSubscriptionRequest.serialize,
response_deserializer=pubsub.Subscription.deserialize,
@@ -329,7 +416,7 @@ def update_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_subscription" not in self._stubs:
- self._stubs["update_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["update_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/UpdateSubscription",
request_serializer=pubsub.UpdateSubscriptionRequest.serialize,
response_deserializer=pubsub.Subscription.deserialize,
@@ -355,7 +442,7 @@ def list_subscriptions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_subscriptions" not in self._stubs:
- self._stubs["list_subscriptions"] = self.grpc_channel.unary_unary(
+ self._stubs["list_subscriptions"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ListSubscriptions",
request_serializer=pubsub.ListSubscriptionsRequest.serialize,
response_deserializer=pubsub.ListSubscriptionsResponse.deserialize,
@@ -386,7 +473,7 @@ def delete_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_subscription" not in self._stubs:
- self._stubs["delete_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/DeleteSubscription",
request_serializer=pubsub.DeleteSubscriptionRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -417,7 +504,7 @@ def modify_ack_deadline(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "modify_ack_deadline" not in self._stubs:
- self._stubs["modify_ack_deadline"] = self.grpc_channel.unary_unary(
+ self._stubs["modify_ack_deadline"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ModifyAckDeadline",
request_serializer=pubsub.ModifyAckDeadlineRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -448,7 +535,7 @@ def acknowledge(self) -> Callable[[pubsub.AcknowledgeRequest], empty_pb2.Empty]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "acknowledge" not in self._stubs:
- self._stubs["acknowledge"] = self.grpc_channel.unary_unary(
+ self._stubs["acknowledge"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/Acknowledge",
request_serializer=pubsub.AcknowledgeRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -472,7 +559,7 @@ def pull(self) -> Callable[[pubsub.PullRequest], pubsub.PullResponse]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "pull" not in self._stubs:
- self._stubs["pull"] = self.grpc_channel.unary_unary(
+ self._stubs["pull"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/Pull",
request_serializer=pubsub.PullRequest.serialize,
response_deserializer=pubsub.PullResponse.deserialize,
@@ -486,7 +573,7 @@ def streaming_pull(
r"""Return a callable for the streaming pull method over gRPC.
Establishes a stream with the server, which sends messages down
- to the client. The client streams acknowledgements and ack
+ to the client. The client streams acknowledgments and ack
deadline modifications back to the server. The server will close
the stream and return the status on any error. The server may
close the stream with status ``UNAVAILABLE`` to reassign
@@ -505,7 +592,7 @@ def streaming_pull(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "streaming_pull" not in self._stubs:
- self._stubs["streaming_pull"] = self.grpc_channel.stream_stream(
+ self._stubs["streaming_pull"] = self._logged_channel.stream_stream(
"/google.pubsub.v1.Subscriber/StreamingPull",
request_serializer=pubsub.StreamingPullRequest.serialize,
response_deserializer=pubsub.StreamingPullResponse.deserialize,
@@ -537,7 +624,7 @@ def modify_push_config(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "modify_push_config" not in self._stubs:
- self._stubs["modify_push_config"] = self.grpc_channel.unary_unary(
+ self._stubs["modify_push_config"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ModifyPushConfig",
request_serializer=pubsub.ModifyPushConfigRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -566,7 +653,7 @@ def get_snapshot(self) -> Callable[[pubsub.GetSnapshotRequest], pubsub.Snapshot]
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_snapshot" not in self._stubs:
- self._stubs["get_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["get_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/GetSnapshot",
request_serializer=pubsub.GetSnapshotRequest.serialize,
response_deserializer=pubsub.Snapshot.deserialize,
@@ -596,7 +683,7 @@ def list_snapshots(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_snapshots" not in self._stubs:
- self._stubs["list_snapshots"] = self.grpc_channel.unary_unary(
+ self._stubs["list_snapshots"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ListSnapshots",
request_serializer=pubsub.ListSnapshotsRequest.serialize,
response_deserializer=pubsub.ListSnapshotsResponse.deserialize,
@@ -640,7 +727,7 @@ def create_snapshot(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_snapshot" not in self._stubs:
- self._stubs["create_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["create_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/CreateSnapshot",
request_serializer=pubsub.CreateSnapshotRequest.serialize,
response_deserializer=pubsub.Snapshot.deserialize,
@@ -671,7 +758,7 @@ def update_snapshot(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_snapshot" not in self._stubs:
- self._stubs["update_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["update_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/UpdateSnapshot",
request_serializer=pubsub.UpdateSnapshotRequest.serialize,
response_deserializer=pubsub.Snapshot.deserialize,
@@ -706,7 +793,7 @@ def delete_snapshot(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_snapshot" not in self._stubs:
- self._stubs["delete_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/DeleteSnapshot",
request_serializer=pubsub.DeleteSnapshotRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -738,13 +825,16 @@ def seek(self) -> Callable[[pubsub.SeekRequest], pubsub.SeekResponse]:
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "seek" not in self._stubs:
- self._stubs["seek"] = self.grpc_channel.unary_unary(
+ self._stubs["seek"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/Seek",
request_serializer=pubsub.SeekRequest.serialize,
response_deserializer=pubsub.SeekResponse.deserialize,
)
return self._stubs["seek"]
+ def close(self):
+ self._logged_channel.close()
+
@property
def set_iam_policy(
self,
@@ -763,7 +853,7 @@ def set_iam_policy(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "set_iam_policy" not in self._stubs:
- self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
+ self._stubs["set_iam_policy"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/SetIamPolicy",
request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
@@ -789,7 +879,7 @@ def get_iam_policy(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_iam_policy" not in self._stubs:
- self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
+ self._stubs["get_iam_policy"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/GetIamPolicy",
request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
@@ -818,16 +908,13 @@ def test_iam_permissions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "test_iam_permissions" not in self._stubs:
- self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
+ self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary(
"/google.iam.v1.IAMPolicy/TestIamPermissions",
request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
)
return self._stubs["test_iam_permissions"]
- def close(self):
- self.grpc_channel.close()
-
@property
def kind(self) -> str:
return "grpc"
diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py
index 9dab4a21b..ad53fe76c 100644
--- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py
+++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import inspect
+import json
+import pickle
+import logging as std_logging
import warnings
from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union
@@ -22,8 +26,11 @@
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
+from google.protobuf.json_format import MessageToJson
+import google.protobuf.message
import grpc # type: ignore
+import proto # type: ignore
from grpc.experimental import aio # type: ignore
from google.iam.v1 import iam_policy_pb2 # type: ignore
@@ -33,6 +40,82 @@
from .base import SubscriberTransport, DEFAULT_CLIENT_INFO
from .grpc import SubscriberGrpcTransport
+try:
+ from google.api_core import client_logging # type: ignore
+
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+
+_LOGGER = std_logging.getLogger(__name__)
+
+
+class _LoggingClientAIOInterceptor(
+ grpc.aio.UnaryUnaryClientInterceptor
+): # pragma: NO COVER
+ async def intercept_unary_unary(self, continuation, client_call_details, request):
+ logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ )
+ if logging_enabled: # pragma: NO COVER
+ request_metadata = client_call_details.metadata
+ if isinstance(request, proto.Message):
+ request_payload = type(request).to_json(request)
+ elif isinstance(request, google.protobuf.message.Message):
+ request_payload = MessageToJson(request)
+ else:
+ request_payload = f"{type(request).__name__}: {pickle.dumps(request)}"
+
+ request_metadata = {
+ key: value.decode("utf-8") if isinstance(value, bytes) else value
+ for key, value in request_metadata
+ }
+ grpc_request = {
+ "payload": request_payload,
+ "requestMethod": "grpc",
+ "metadata": dict(request_metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for {client_call_details.method}",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": str(client_call_details.method),
+ "request": grpc_request,
+ "metadata": grpc_request["metadata"],
+ },
+ )
+ response = await continuation(client_call_details, request)
+ if logging_enabled: # pragma: NO COVER
+ response_metadata = await response.trailing_metadata()
+ # Convert gRPC metadata `` to list of tuples
+ metadata = (
+ dict([(k, str(v)) for k, v in response_metadata])
+ if response_metadata
+ else None
+ )
+ result = await response
+ if isinstance(result, proto.Message):
+ response_payload = type(result).to_json(result)
+ elif isinstance(result, google.protobuf.message.Message):
+ response_payload = MessageToJson(result)
+ else:
+ response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
+ grpc_response = {
+ "payload": response_payload,
+ "metadata": metadata,
+ "status": "OK",
+ }
+ _LOGGER.debug(
+ f"Received response to rpc {client_call_details.method}.",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": str(client_call_details.method),
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class SubscriberGrpcAsyncIOTransport(SubscriberTransport):
"""gRPC AsyncIO backend transport for Subscriber.
@@ -71,8 +154,9 @@ def create_channel(
credentials identify this application to the service. If
none are specified, the client will attempt to ascertain
the credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
- be loaded with :func:`google.auth.load_credentials_from_file`.
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
+ be loaded with :func:`google.auth.load_credentials_from_file`. This argument will be
+ removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -123,9 +207,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
This argument is ignored if a ``channel`` instance is provided.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
This argument is ignored if a ``channel`` instance is provided.
+ This argument will be removed in the next major version of this library.
scopes (Optional[Sequence[str]]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
@@ -234,7 +319,13 @@ def __init__(
],
)
- # Wrap messages. This must be done after self._grpc_channel exists
+ self._interceptor = _LoggingClientAIOInterceptor()
+ self._grpc_channel._unary_unary_interceptors.append(self._interceptor)
+ self._logged_channel = self._grpc_channel
+ self._wrap_with_kind = (
+ "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters
+ )
+ # Wrap messages. This must be done after self._logged_channel exists
self._prep_wrapped_messages(client_info)
@property
@@ -278,7 +369,7 @@ def create_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_subscription" not in self._stubs:
- self._stubs["create_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["create_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/CreateSubscription",
request_serializer=pubsub.Subscription.serialize,
response_deserializer=pubsub.Subscription.deserialize,
@@ -304,7 +395,7 @@ def get_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_subscription" not in self._stubs:
- self._stubs["get_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["get_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/GetSubscription",
request_serializer=pubsub.GetSubscriptionRequest.serialize,
response_deserializer=pubsub.Subscription.deserialize,
@@ -333,7 +424,7 @@ def update_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_subscription" not in self._stubs:
- self._stubs["update_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["update_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/UpdateSubscription",
request_serializer=pubsub.UpdateSubscriptionRequest.serialize,
response_deserializer=pubsub.Subscription.deserialize,
@@ -361,7 +452,7 @@ def list_subscriptions(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_subscriptions" not in self._stubs:
- self._stubs["list_subscriptions"] = self.grpc_channel.unary_unary(
+ self._stubs["list_subscriptions"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ListSubscriptions",
request_serializer=pubsub.ListSubscriptionsRequest.serialize,
response_deserializer=pubsub.ListSubscriptionsResponse.deserialize,
@@ -392,7 +483,7 @@ def delete_subscription(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_subscription" not in self._stubs:
- self._stubs["delete_subscription"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_subscription"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/DeleteSubscription",
request_serializer=pubsub.DeleteSubscriptionRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -423,7 +514,7 @@ def modify_ack_deadline(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "modify_ack_deadline" not in self._stubs:
- self._stubs["modify_ack_deadline"] = self.grpc_channel.unary_unary(
+ self._stubs["modify_ack_deadline"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ModifyAckDeadline",
request_serializer=pubsub.ModifyAckDeadlineRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -456,7 +547,7 @@ def acknowledge(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "acknowledge" not in self._stubs:
- self._stubs["acknowledge"] = self.grpc_channel.unary_unary(
+ self._stubs["acknowledge"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/Acknowledge",
request_serializer=pubsub.AcknowledgeRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -480,7 +571,7 @@ def pull(self) -> Callable[[pubsub.PullRequest], Awaitable[pubsub.PullResponse]]
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "pull" not in self._stubs:
- self._stubs["pull"] = self.grpc_channel.unary_unary(
+ self._stubs["pull"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/Pull",
request_serializer=pubsub.PullRequest.serialize,
response_deserializer=pubsub.PullResponse.deserialize,
@@ -496,7 +587,7 @@ def streaming_pull(
r"""Return a callable for the streaming pull method over gRPC.
Establishes a stream with the server, which sends messages down
- to the client. The client streams acknowledgements and ack
+ to the client. The client streams acknowledgments and ack
deadline modifications back to the server. The server will close
the stream and return the status on any error. The server may
close the stream with status ``UNAVAILABLE`` to reassign
@@ -515,7 +606,7 @@ def streaming_pull(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "streaming_pull" not in self._stubs:
- self._stubs["streaming_pull"] = self.grpc_channel.stream_stream(
+ self._stubs["streaming_pull"] = self._logged_channel.stream_stream(
"/google.pubsub.v1.Subscriber/StreamingPull",
request_serializer=pubsub.StreamingPullRequest.serialize,
response_deserializer=pubsub.StreamingPullResponse.deserialize,
@@ -547,7 +638,7 @@ def modify_push_config(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "modify_push_config" not in self._stubs:
- self._stubs["modify_push_config"] = self.grpc_channel.unary_unary(
+ self._stubs["modify_push_config"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ModifyPushConfig",
request_serializer=pubsub.ModifyPushConfigRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -578,7 +669,7 @@ def get_snapshot(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_snapshot" not in self._stubs:
- self._stubs["get_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["get_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/GetSnapshot",
request_serializer=pubsub.GetSnapshotRequest.serialize,
response_deserializer=pubsub.Snapshot.deserialize,
@@ -610,7 +701,7 @@ def list_snapshots(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_snapshots" not in self._stubs:
- self._stubs["list_snapshots"] = self.grpc_channel.unary_unary(
+ self._stubs["list_snapshots"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/ListSnapshots",
request_serializer=pubsub.ListSnapshotsRequest.serialize,
response_deserializer=pubsub.ListSnapshotsResponse.deserialize,
@@ -654,7 +745,7 @@ def create_snapshot(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "create_snapshot" not in self._stubs:
- self._stubs["create_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["create_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/CreateSnapshot",
request_serializer=pubsub.CreateSnapshotRequest.serialize,
response_deserializer=pubsub.Snapshot.deserialize,
@@ -685,7 +776,7 @@ def update_snapshot(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_snapshot" not in self._stubs:
- self._stubs["update_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["update_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/UpdateSnapshot",
request_serializer=pubsub.UpdateSnapshotRequest.serialize,
response_deserializer=pubsub.Snapshot.deserialize,
@@ -720,7 +811,7 @@ def delete_snapshot(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_snapshot" not in self._stubs:
- self._stubs["delete_snapshot"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_snapshot"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/DeleteSnapshot",
request_serializer=pubsub.DeleteSnapshotRequest.serialize,
response_deserializer=empty_pb2.Empty.FromString,
@@ -752,97 +843,17 @@ def seek(self) -> Callable[[pubsub.SeekRequest], Awaitable[pubsub.SeekResponse]]
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "seek" not in self._stubs:
- self._stubs["seek"] = self.grpc_channel.unary_unary(
+ self._stubs["seek"] = self._logged_channel.unary_unary(
"/google.pubsub.v1.Subscriber/Seek",
request_serializer=pubsub.SeekRequest.serialize,
response_deserializer=pubsub.SeekResponse.deserialize,
)
return self._stubs["seek"]
- @property
- def set_iam_policy(
- self,
- ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]:
- r"""Return a callable for the set iam policy method over gRPC.
- Sets the IAM access control policy on the specified
- function. Replaces any existing policy.
- Returns:
- Callable[[~.SetIamPolicyRequest],
- Awaitable[~.Policy]]:
- 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 "set_iam_policy" not in self._stubs:
- self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/SetIamPolicy",
- request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
- response_deserializer=policy_pb2.Policy.FromString,
- )
- return self._stubs["set_iam_policy"]
-
- @property
- def get_iam_policy(
- self,
- ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]:
- r"""Return a callable for the get iam policy method over gRPC.
- Gets the IAM access control policy for a function.
- Returns an empty policy if the function exists and does
- not have a policy set.
- Returns:
- Callable[[~.GetIamPolicyRequest],
- Awaitable[~.Policy]]:
- 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 "get_iam_policy" not in self._stubs:
- self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/GetIamPolicy",
- request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
- response_deserializer=policy_pb2.Policy.FromString,
- )
- return self._stubs["get_iam_policy"]
-
- @property
- def test_iam_permissions(
- self,
- ) -> Callable[
- [iam_policy_pb2.TestIamPermissionsRequest],
- Awaitable[iam_policy_pb2.TestIamPermissionsResponse],
- ]:
- r"""Return a callable for the test iam permissions method over gRPC.
- Tests the specified permissions against the IAM access control
- policy for a function. If the function does not exist, this will
- return an empty set of permissions, not a NOT_FOUND error.
- Returns:
- Callable[[~.TestIamPermissionsRequest],
- Awaitable[~.TestIamPermissionsResponse]]:
- 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 "test_iam_permissions" not in self._stubs:
- self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
- "/google.iam.v1.IAMPolicy/TestIamPermissions",
- request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
- response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
- )
- return self._stubs["test_iam_permissions"]
-
def _prep_wrapped_messages(self, client_info):
"""Precompute the wrapped methods, overriding the base class method to use async wrappers."""
self._wrapped_methods = {
- self.create_subscription: gapic_v1.method_async.wrap_method(
+ self.create_subscription: self._wrap_method(
self.create_subscription,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -858,7 +869,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.get_subscription: gapic_v1.method_async.wrap_method(
+ self.get_subscription: self._wrap_method(
self.get_subscription,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -874,7 +885,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.update_subscription: gapic_v1.method_async.wrap_method(
+ self.update_subscription: self._wrap_method(
self.update_subscription,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -888,7 +899,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.list_subscriptions: gapic_v1.method_async.wrap_method(
+ self.list_subscriptions: self._wrap_method(
self.list_subscriptions,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -904,7 +915,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.delete_subscription: gapic_v1.method_async.wrap_method(
+ self.delete_subscription: self._wrap_method(
self.delete_subscription,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -918,7 +929,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.modify_ack_deadline: gapic_v1.method_async.wrap_method(
+ self.modify_ack_deadline: self._wrap_method(
self.modify_ack_deadline,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -932,7 +943,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.acknowledge: gapic_v1.method_async.wrap_method(
+ self.acknowledge: self._wrap_method(
self.acknowledge,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -946,7 +957,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.pull: gapic_v1.method_async.wrap_method(
+ self.pull: self._wrap_method(
self.pull,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -963,7 +974,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.streaming_pull: gapic_v1.method_async.wrap_method(
+ self.streaming_pull: self._wrap_method(
self.streaming_pull,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -981,7 +992,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=900.0,
client_info=client_info,
),
- self.modify_push_config: gapic_v1.method_async.wrap_method(
+ self.modify_push_config: self._wrap_method(
self.modify_push_config,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -995,7 +1006,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.get_snapshot: gapic_v1.method_async.wrap_method(
+ self.get_snapshot: self._wrap_method(
self.get_snapshot,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -1011,7 +1022,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.list_snapshots: gapic_v1.method_async.wrap_method(
+ self.list_snapshots: self._wrap_method(
self.list_snapshots,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -1027,7 +1038,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.create_snapshot: gapic_v1.method_async.wrap_method(
+ self.create_snapshot: self._wrap_method(
self.create_snapshot,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -1041,7 +1052,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.update_snapshot: gapic_v1.method_async.wrap_method(
+ self.update_snapshot: self._wrap_method(
self.update_snapshot,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -1055,7 +1066,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.delete_snapshot: gapic_v1.method_async.wrap_method(
+ self.delete_snapshot: self._wrap_method(
self.delete_snapshot,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -1069,7 +1080,7 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
- self.seek: gapic_v1.method_async.wrap_method(
+ self.seek: self._wrap_method(
self.seek,
default_retry=retries.AsyncRetry(
initial=0.1,
@@ -1085,10 +1096,114 @@ def _prep_wrapped_messages(self, client_info):
default_timeout=60.0,
client_info=client_info,
),
+ self.get_iam_policy: self._wrap_method(
+ self.get_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.set_iam_policy: self._wrap_method(
+ self.set_iam_policy,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.test_iam_permissions: self._wrap_method(
+ self.test_iam_permissions,
+ default_timeout=None,
+ client_info=client_info,
+ ),
}
+ def _wrap_method(self, func, *args, **kwargs):
+ if self._wrap_with_kind: # pragma: NO COVER
+ kwargs["kind"] = self.kind
+ return gapic_v1.method_async.wrap_method(func, *args, **kwargs)
+
def close(self):
- return self.grpc_channel.close()
+ return self._logged_channel.close()
+
+ @property
+ def kind(self) -> str:
+ return "grpc_asyncio"
+
+ @property
+ def set_iam_policy(
+ self,
+ ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]:
+ r"""Return a callable for the set iam policy method over gRPC.
+ Sets the IAM access control policy on the specified
+ function. Replaces any existing policy.
+ Returns:
+ Callable[[~.SetIamPolicyRequest],
+ ~.Policy]:
+ 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 "set_iam_policy" not in self._stubs:
+ self._stubs["set_iam_policy"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/SetIamPolicy",
+ request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
+ response_deserializer=policy_pb2.Policy.FromString,
+ )
+ return self._stubs["set_iam_policy"]
+
+ @property
+ def get_iam_policy(
+ self,
+ ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]:
+ r"""Return a callable for the get iam policy method over gRPC.
+ Gets the IAM access control policy for a function.
+ Returns an empty policy if the function exists and does
+ not have a policy set.
+ Returns:
+ Callable[[~.GetIamPolicyRequest],
+ ~.Policy]:
+ 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 "get_iam_policy" not in self._stubs:
+ self._stubs["get_iam_policy"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/GetIamPolicy",
+ request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
+ response_deserializer=policy_pb2.Policy.FromString,
+ )
+ return self._stubs["get_iam_policy"]
+
+ @property
+ def test_iam_permissions(
+ self,
+ ) -> Callable[
+ [iam_policy_pb2.TestIamPermissionsRequest],
+ iam_policy_pb2.TestIamPermissionsResponse,
+ ]:
+ r"""Return a callable for the test iam permissions method over gRPC.
+ Tests the specified permissions against the IAM access control
+ policy for a function. If the function does not exist, this will
+ return an empty set of permissions, not a NOT_FOUND error.
+ Returns:
+ Callable[[~.TestIamPermissionsRequest],
+ ~.TestIamPermissionsResponse]:
+ 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 "test_iam_permissions" not in self._stubs:
+ self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary(
+ "/google.iam.v1.IAMPolicy/TestIamPermissions",
+ request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
+ response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
+ )
+ return self._stubs["test_iam_permissions"]
__all__ = ("SubscriberGrpcAsyncIOTransport",)
diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py
index 0b5f2ccc9..50a247cef 100644
--- a/google/pubsub_v1/services/subscriber/transports/rest.py
+++ b/google/pubsub_v1/services/subscriber/transports/rest.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,48 +13,58 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging
+import json # type: ignore
from google.auth.transport.requests import AuthorizedSession # type: ignore
-import json # type: ignore
-import grpc # type: ignore
-from google.auth.transport.grpc import SslCredentials # type: ignore
from google.auth import credentials as ga_credentials # type: ignore
from google.api_core import exceptions as core_exceptions
from google.api_core import retry as retries
from google.api_core import rest_helpers
from google.api_core import rest_streaming
-from google.api_core import path_template
from google.api_core import gapic_v1
+import google.protobuf
from google.protobuf import json_format
from google.iam.v1 import iam_policy_pb2 # type: ignore
from google.iam.v1 import policy_pb2 # type: ignore
+
from requests import __version__ as requests_version
import dataclasses
-import re
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
import warnings
+
+from google.protobuf import empty_pb2 # type: ignore
+from google.pubsub_v1.types import pubsub
+
+
+from .rest_base import _BaseSubscriberRestTransport
+from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO
+
try:
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
OptionalRetry = Union[retries.Retry, object, None] # type: ignore
+try:
+ from google.api_core import client_logging # type: ignore
-from google.iam.v1 import iam_policy_pb2 # type: ignore
-from google.iam.v1 import policy_pb2 # type: ignore
-from google.protobuf import empty_pb2 # type: ignore
-from google.pubsub_v1.types import pubsub
-
-from .base import SubscriberTransport, DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO
+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
+except ImportError: # pragma: NO COVER
+ CLIENT_LOGGING_SUPPORTED = False
+_LOGGER = logging.getLogger(__name__)
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version,
grpc_version=None,
- rest_version=requests_version,
+ rest_version=f"requests@{requests_version}",
)
+if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER
+ DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__
+
class SubscriberRestInterceptor:
"""Interceptor for Subscriber.
@@ -178,8 +188,10 @@ def post_update_subscription(self, response):
"""
def pre_acknowledge(
- self, request: pubsub.AcknowledgeRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.AcknowledgeRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.AcknowledgeRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.AcknowledgeRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for acknowledge
Override in a subclass to manipulate the request or metadata
@@ -188,8 +200,10 @@ def pre_acknowledge(
return request, metadata
def pre_create_snapshot(
- self, request: pubsub.CreateSnapshotRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.CreateSnapshotRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.CreateSnapshotRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.CreateSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for create_snapshot
Override in a subclass to manipulate the request or metadata
@@ -200,15 +214,40 @@ def pre_create_snapshot(
def post_create_snapshot(self, response: pubsub.Snapshot) -> pubsub.Snapshot:
"""Post-rpc interceptor for create_snapshot
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_create_snapshot_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_create_snapshot` interceptor runs
+ before the `post_create_snapshot_with_metadata` interceptor.
"""
return response
+ def post_create_snapshot_with_metadata(
+ self,
+ response: pubsub.Snapshot,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.Snapshot, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for create_snapshot
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_create_snapshot_with_metadata`
+ interceptor in new development instead of the `post_create_snapshot` interceptor.
+ When both interceptors are used, this `post_create_snapshot_with_metadata` interceptor runs after the
+ `post_create_snapshot` interceptor. The (possibly modified) response returned by
+ `post_create_snapshot` will be passed to
+ `post_create_snapshot_with_metadata`.
+ """
+ return response, metadata
+
def pre_create_subscription(
- self, request: pubsub.Subscription, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.Subscription,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for create_subscription
Override in a subclass to manipulate the request or metadata
@@ -221,15 +260,40 @@ def post_create_subscription(
) -> pubsub.Subscription:
"""Post-rpc interceptor for create_subscription
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_create_subscription_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_create_subscription` interceptor runs
+ before the `post_create_subscription_with_metadata` interceptor.
"""
return response
+ def post_create_subscription_with_metadata(
+ self,
+ response: pubsub.Subscription,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for create_subscription
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_create_subscription_with_metadata`
+ interceptor in new development instead of the `post_create_subscription` interceptor.
+ When both interceptors are used, this `post_create_subscription_with_metadata` interceptor runs after the
+ `post_create_subscription` interceptor. The (possibly modified) response returned by
+ `post_create_subscription` will be passed to
+ `post_create_subscription_with_metadata`.
+ """
+ return response, metadata
+
def pre_delete_snapshot(
- self, request: pubsub.DeleteSnapshotRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.DeleteSnapshotRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.DeleteSnapshotRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.DeleteSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for delete_snapshot
Override in a subclass to manipulate the request or metadata
@@ -240,8 +304,10 @@ def pre_delete_snapshot(
def pre_delete_subscription(
self,
request: pubsub.DeleteSubscriptionRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.DeleteSubscriptionRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.DeleteSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for delete_subscription
Override in a subclass to manipulate the request or metadata
@@ -250,8 +316,10 @@ def pre_delete_subscription(
return request, metadata
def pre_get_snapshot(
- self, request: pubsub.GetSnapshotRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.GetSnapshotRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.GetSnapshotRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.GetSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for get_snapshot
Override in a subclass to manipulate the request or metadata
@@ -262,17 +330,40 @@ def pre_get_snapshot(
def post_get_snapshot(self, response: pubsub.Snapshot) -> pubsub.Snapshot:
"""Post-rpc interceptor for get_snapshot
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_get_snapshot_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_get_snapshot` interceptor runs
+ before the `post_get_snapshot_with_metadata` interceptor.
"""
return response
+ def post_get_snapshot_with_metadata(
+ self,
+ response: pubsub.Snapshot,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.Snapshot, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for get_snapshot
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_get_snapshot_with_metadata`
+ interceptor in new development instead of the `post_get_snapshot` interceptor.
+ When both interceptors are used, this `post_get_snapshot_with_metadata` interceptor runs after the
+ `post_get_snapshot` interceptor. The (possibly modified) response returned by
+ `post_get_snapshot` will be passed to
+ `post_get_snapshot_with_metadata`.
+ """
+ return response, metadata
+
def pre_get_subscription(
self,
request: pubsub.GetSubscriptionRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.GetSubscriptionRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.GetSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for get_subscription
Override in a subclass to manipulate the request or metadata
@@ -285,15 +376,40 @@ def post_get_subscription(
) -> pubsub.Subscription:
"""Post-rpc interceptor for get_subscription
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_get_subscription_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_get_subscription` interceptor runs
+ before the `post_get_subscription_with_metadata` interceptor.
"""
return response
+ def post_get_subscription_with_metadata(
+ self,
+ response: pubsub.Subscription,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for get_subscription
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_get_subscription_with_metadata`
+ interceptor in new development instead of the `post_get_subscription` interceptor.
+ When both interceptors are used, this `post_get_subscription_with_metadata` interceptor runs after the
+ `post_get_subscription` interceptor. The (possibly modified) response returned by
+ `post_get_subscription` will be passed to
+ `post_get_subscription_with_metadata`.
+ """
+ return response, metadata
+
def pre_list_snapshots(
- self, request: pubsub.ListSnapshotsRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.ListSnapshotsRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.ListSnapshotsRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.ListSnapshotsRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for list_snapshots
Override in a subclass to manipulate the request or metadata
@@ -306,17 +422,42 @@ def post_list_snapshots(
) -> pubsub.ListSnapshotsResponse:
"""Post-rpc interceptor for list_snapshots
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_list_snapshots_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_list_snapshots` interceptor runs
+ before the `post_list_snapshots_with_metadata` interceptor.
"""
return response
+ def post_list_snapshots_with_metadata(
+ self,
+ response: pubsub.ListSnapshotsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.ListSnapshotsResponse, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for list_snapshots
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_list_snapshots_with_metadata`
+ interceptor in new development instead of the `post_list_snapshots` interceptor.
+ When both interceptors are used, this `post_list_snapshots_with_metadata` interceptor runs after the
+ `post_list_snapshots` interceptor. The (possibly modified) response returned by
+ `post_list_snapshots` will be passed to
+ `post_list_snapshots_with_metadata`.
+ """
+ return response, metadata
+
def pre_list_subscriptions(
self,
request: pubsub.ListSubscriptionsRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.ListSubscriptionsRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.ListSubscriptionsRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for list_subscriptions
Override in a subclass to manipulate the request or metadata
@@ -329,17 +470,44 @@ def post_list_subscriptions(
) -> pubsub.ListSubscriptionsResponse:
"""Post-rpc interceptor for list_subscriptions
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_list_subscriptions_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_list_subscriptions` interceptor runs
+ before the `post_list_subscriptions_with_metadata` interceptor.
"""
return response
+ def post_list_subscriptions_with_metadata(
+ self,
+ response: pubsub.ListSubscriptionsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.ListSubscriptionsResponse, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Post-rpc interceptor for list_subscriptions
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_list_subscriptions_with_metadata`
+ interceptor in new development instead of the `post_list_subscriptions` interceptor.
+ When both interceptors are used, this `post_list_subscriptions_with_metadata` interceptor runs after the
+ `post_list_subscriptions` interceptor. The (possibly modified) response returned by
+ `post_list_subscriptions` will be passed to
+ `post_list_subscriptions_with_metadata`.
+ """
+ return response, metadata
+
def pre_modify_ack_deadline(
self,
request: pubsub.ModifyAckDeadlineRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.ModifyAckDeadlineRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.ModifyAckDeadlineRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for modify_ack_deadline
Override in a subclass to manipulate the request or metadata
@@ -350,8 +518,8 @@ def pre_modify_ack_deadline(
def pre_modify_push_config(
self,
request: pubsub.ModifyPushConfigRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.ModifyPushConfigRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.ModifyPushConfigRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for modify_push_config
Override in a subclass to manipulate the request or metadata
@@ -360,8 +528,10 @@ def pre_modify_push_config(
return request, metadata
def pre_pull(
- self, request: pubsub.PullRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.PullRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.PullRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.PullRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for pull
Override in a subclass to manipulate the request or metadata
@@ -372,15 +542,40 @@ def pre_pull(
def post_pull(self, response: pubsub.PullResponse) -> pubsub.PullResponse:
"""Post-rpc interceptor for pull
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_pull_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_pull` interceptor runs
+ before the `post_pull_with_metadata` interceptor.
"""
return response
+ def post_pull_with_metadata(
+ self,
+ response: pubsub.PullResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.PullResponse, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for pull
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_pull_with_metadata`
+ interceptor in new development instead of the `post_pull` interceptor.
+ When both interceptors are used, this `post_pull_with_metadata` interceptor runs after the
+ `post_pull` interceptor. The (possibly modified) response returned by
+ `post_pull` will be passed to
+ `post_pull_with_metadata`.
+ """
+ return response, metadata
+
def pre_seek(
- self, request: pubsub.SeekRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.SeekRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.SeekRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.SeekRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for seek
Override in a subclass to manipulate the request or metadata
@@ -391,15 +586,40 @@ def pre_seek(
def post_seek(self, response: pubsub.SeekResponse) -> pubsub.SeekResponse:
"""Post-rpc interceptor for seek
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_seek_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_seek` interceptor runs
+ before the `post_seek_with_metadata` interceptor.
"""
return response
+ def post_seek_with_metadata(
+ self,
+ response: pubsub.SeekResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.SeekResponse, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for seek
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_seek_with_metadata`
+ interceptor in new development instead of the `post_seek` interceptor.
+ When both interceptors are used, this `post_seek_with_metadata` interceptor runs after the
+ `post_seek` interceptor. The (possibly modified) response returned by
+ `post_seek` will be passed to
+ `post_seek_with_metadata`.
+ """
+ return response, metadata
+
def pre_update_snapshot(
- self, request: pubsub.UpdateSnapshotRequest, metadata: Sequence[Tuple[str, str]]
- ) -> Tuple[pubsub.UpdateSnapshotRequest, Sequence[Tuple[str, str]]]:
+ self,
+ request: pubsub.UpdateSnapshotRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.UpdateSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]:
"""Pre-rpc interceptor for update_snapshot
Override in a subclass to manipulate the request or metadata
@@ -410,17 +630,42 @@ def pre_update_snapshot(
def post_update_snapshot(self, response: pubsub.Snapshot) -> pubsub.Snapshot:
"""Post-rpc interceptor for update_snapshot
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_update_snapshot_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_update_snapshot` interceptor runs
+ before the `post_update_snapshot_with_metadata` interceptor.
"""
return response
+ def post_update_snapshot_with_metadata(
+ self,
+ response: pubsub.Snapshot,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.Snapshot, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for update_snapshot
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_update_snapshot_with_metadata`
+ interceptor in new development instead of the `post_update_snapshot` interceptor.
+ When both interceptors are used, this `post_update_snapshot_with_metadata` interceptor runs after the
+ `post_update_snapshot` interceptor. The (possibly modified) response returned by
+ `post_update_snapshot` will be passed to
+ `post_update_snapshot_with_metadata`.
+ """
+ return response, metadata
+
def pre_update_subscription(
self,
request: pubsub.UpdateSubscriptionRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[pubsub.UpdateSubscriptionRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ pubsub.UpdateSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for update_subscription
Override in a subclass to manipulate the request or metadata
@@ -433,17 +678,42 @@ def post_update_subscription(
) -> pubsub.Subscription:
"""Post-rpc interceptor for update_subscription
- Override in a subclass to manipulate the response
+ DEPRECATED. Please use the `post_update_subscription_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
after it is returned by the Subscriber server but before
- it is returned to user code.
+ it is returned to user code. This `post_update_subscription` interceptor runs
+ before the `post_update_subscription_with_metadata` interceptor.
"""
return response
+ def post_update_subscription_with_metadata(
+ self,
+ response: pubsub.Subscription,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for update_subscription
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the Subscriber server but before it is returned to user code.
+
+ We recommend only using this `post_update_subscription_with_metadata`
+ interceptor in new development instead of the `post_update_subscription` interceptor.
+ When both interceptors are used, this `post_update_subscription_with_metadata` interceptor runs after the
+ `post_update_subscription` interceptor. The (possibly modified) response returned by
+ `post_update_subscription` will be passed to
+ `post_update_subscription_with_metadata`.
+ """
+ return response, metadata
+
def pre_get_iam_policy(
self,
request: iam_policy_pb2.GetIamPolicyRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for get_iam_policy
Override in a subclass to manipulate the request or metadata
@@ -463,8 +733,10 @@ def post_get_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy:
def pre_set_iam_policy(
self,
request: iam_policy_pb2.SetIamPolicyRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
"""Pre-rpc interceptor for set_iam_policy
Override in a subclass to manipulate the request or metadata
@@ -484,8 +756,11 @@ def post_set_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy:
def pre_test_iam_permissions(
self,
request: iam_policy_pb2.TestIamPermissionsRequest,
- metadata: Sequence[Tuple[str, str]],
- ) -> Tuple[iam_policy_pb2.TestIamPermissionsRequest, Sequence[Tuple[str, str]]]:
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ iam_policy_pb2.TestIamPermissionsRequest,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
"""Pre-rpc interceptor for test_iam_permissions
Override in a subclass to manipulate the request or metadata
@@ -512,8 +787,8 @@ class SubscriberRestStub:
_interceptor: SubscriberRestInterceptor
-class SubscriberRestTransport(SubscriberTransport):
- """REST backend transport for Subscriber.
+class SubscriberRestTransport(_BaseSubscriberRestTransport):
+ """REST backend synchronous transport for Subscriber.
The service that an application uses to manipulate subscriptions and
to consume messages from a subscription via the ``Pull`` method or
@@ -525,7 +800,6 @@ class SubscriberRestTransport(SubscriberTransport):
and call it.
It sends JSON representations of protocol buffers over HTTP/1.1
-
"""
def __init__(
@@ -554,9 +828,10 @@ def __init__(
are specified, the client will attempt to ascertain the
credentials from the environment.
- credentials_file (Optional[str]): A file with credentials that can
+ credentials_file (Optional[str]): Deprecated. A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if ``channel`` is provided. This argument will be
+ removed in the next major version of this library.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
ignored if ``channel`` is provided.
client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
@@ -579,21 +854,12 @@ def __init__(
# TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
# TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
# credentials object
- maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host)
- if maybe_url_match is None:
- raise ValueError(
- f"Unexpected hostname structure: {host}"
- ) # pragma: NO COVER
-
- url_match_items = maybe_url_match.groupdict()
-
- host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
-
super().__init__(
host=host,
credentials=credentials,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ url_scheme=url_scheme,
api_audience=api_audience,
)
self._session = AuthorizedSession(
@@ -604,19 +870,34 @@ def __init__(
self._interceptor = interceptor or SubscriberRestInterceptor()
self._prep_wrapped_messages(client_info)
- class _Acknowledge(SubscriberRestStub):
+ class _Acknowledge(
+ _BaseSubscriberRestTransport._BaseAcknowledge, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("Acknowledge")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.Acknowledge")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -624,7 +905,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
):
r"""Call the acknowledge method over HTTP.
@@ -634,49 +915,70 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}:acknowledge",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_acknowledge(request, metadata)
- pb_request = pubsub.AcknowledgeRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSubscriberRestTransport._BaseAcknowledge._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_acknowledge(request, metadata)
+ transcoded_request = (
+ _BaseSubscriberRestTransport._BaseAcknowledge._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSubscriberRestTransport._BaseAcknowledge._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BaseAcknowledge._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.Acknowledge",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "Acknowledge",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._Acknowledge._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -684,19 +986,34 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
- class _CreateSnapshot(SubscriberRestStub):
+ class _CreateSnapshot(
+ _BaseSubscriberRestTransport._BaseCreateSnapshot, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("CreateSnapshot")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.CreateSnapshot")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -704,7 +1021,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Call the create snapshot method over HTTP.
@@ -714,8 +1031,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Snapshot:
@@ -728,45 +1047,64 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "put",
- "uri": "/v1/{name=projects/*/snapshots/*}",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_create_snapshot(request, metadata)
- pb_request = pubsub.CreateSnapshotRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSubscriberRestTransport._BaseCreateSnapshot._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_create_snapshot(request, metadata)
+ transcoded_request = _BaseSubscriberRestTransport._BaseCreateSnapshot._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = (
+ _BaseSubscriberRestTransport._BaseCreateSnapshot._get_request_body_json(
+ transcoded_request
+ )
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BaseCreateSnapshot._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.CreateSnapshot",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "CreateSnapshot",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._CreateSnapshot._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -779,22 +1117,63 @@ def __call__(
pb_resp = pubsub.Snapshot.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_create_snapshot(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_create_snapshot_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Snapshot.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.create_snapshot",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "CreateSnapshot",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _CreateSubscription(SubscriberRestStub):
+ class _CreateSubscription(
+ _BaseSubscriberRestTransport._BaseCreateSubscription, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("CreateSubscription")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.CreateSubscription")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -802,7 +1181,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Call the create subscription method over HTTP.
@@ -815,8 +1194,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Subscription:
@@ -827,47 +1208,62 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "put",
- "uri": "/v1/{name=projects/*/subscriptions/*}",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseCreateSubscription._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_create_subscription(
request, metadata
)
- pb_request = pubsub.Subscription.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- # Jsonify the request body
+ transcoded_request = _BaseSubscriberRestTransport._BaseCreateSubscription._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSubscriberRestTransport._BaseCreateSubscription._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSubscriberRestTransport._BaseCreateSubscription._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.CreateSubscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "CreateSubscription",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._CreateSubscription._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -880,22 +1276,62 @@ def __call__(
pb_resp = pubsub.Subscription.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_create_subscription(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_create_subscription_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Subscription.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.create_subscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "CreateSubscription",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _DeleteSnapshot(SubscriberRestStub):
+ class _DeleteSnapshot(
+ _BaseSubscriberRestTransport._BaseDeleteSnapshot, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("DeleteSnapshot")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.DeleteSnapshot")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -903,7 +1339,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
):
r"""Call the delete snapshot method over HTTP.
@@ -913,42 +1349,63 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "delete",
- "uri": "/v1/{snapshot=projects/*/snapshots/*}",
- },
- ]
- request, metadata = self._interceptor.pre_delete_snapshot(request, metadata)
- pb_request = pubsub.DeleteSnapshotRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_delete_snapshot(request, metadata)
+ transcoded_request = _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.DeleteSnapshot",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "DeleteSnapshot",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SubscriberRestTransport._DeleteSnapshot._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -956,19 +1413,33 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
- class _DeleteSubscription(SubscriberRestStub):
+ class _DeleteSubscription(
+ _BaseSubscriberRestTransport._BaseDeleteSubscription, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("DeleteSubscription")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.DeleteSubscription")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -976,7 +1447,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
):
r"""Call the delete subscription method over HTTP.
@@ -987,44 +1458,63 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "delete",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseDeleteSubscription._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_delete_subscription(
request, metadata
)
- pb_request = pubsub.DeleteSubscriptionRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BaseSubscriberRestTransport._BaseDeleteSubscription._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSubscriberRestTransport._BaseDeleteSubscription._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.DeleteSubscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "DeleteSubscription",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SubscriberRestTransport._DeleteSubscription._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1032,19 +1522,33 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
- class _GetSnapshot(SubscriberRestStub):
+ class _GetSnapshot(
+ _BaseSubscriberRestTransport._BaseGetSnapshot, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("GetSnapshot")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.GetSnapshot")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -1052,7 +1556,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Call the get snapshot method over HTTP.
@@ -1062,8 +1566,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Snapshot:
@@ -1076,38 +1582,59 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{snapshot=projects/*/snapshots/*}",
- },
- ]
- request, metadata = self._interceptor.pre_get_snapshot(request, metadata)
- pb_request = pubsub.GetSnapshotRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSubscriberRestTransport._BaseGetSnapshot._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_get_snapshot(request, metadata)
+ transcoded_request = (
+ _BaseSubscriberRestTransport._BaseGetSnapshot._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BaseGetSnapshot._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.GetSnapshot",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "GetSnapshot",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SubscriberRestTransport._GetSnapshot._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1120,22 +1647,62 @@ def __call__(
pb_resp = pubsub.Snapshot.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_get_snapshot(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_get_snapshot_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Snapshot.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.get_snapshot",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "GetSnapshot",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _GetSubscription(SubscriberRestStub):
+ class _GetSubscription(
+ _BaseSubscriberRestTransport._BaseGetSubscription, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("GetSubscription")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.GetSubscription")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -1143,7 +1710,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Call the get subscription method over HTTP.
@@ -1154,8 +1721,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Subscription:
@@ -1166,40 +1735,57 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseGetSubscription._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_get_subscription(
request, metadata
)
- pb_request = pubsub.GetSubscriptionRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BaseSubscriberRestTransport._BaseGetSubscription._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSubscriberRestTransport._BaseGetSubscription._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.GetSubscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "GetSubscription",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SubscriberRestTransport._GetSubscription._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1212,22 +1798,62 @@ def __call__(
pb_resp = pubsub.Subscription.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_get_subscription(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_get_subscription_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Subscription.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.get_subscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "GetSubscription",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ListSnapshots(SubscriberRestStub):
+ class _ListSnapshots(
+ _BaseSubscriberRestTransport._BaseListSnapshots, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("ListSnapshots")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.ListSnapshots")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -1235,7 +1861,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.ListSnapshotsResponse:
r"""Call the list snapshots method over HTTP.
@@ -1245,46 +1871,69 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.ListSnapshotsResponse:
Response for the ``ListSnapshots`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{project=projects/*}/snapshots",
- },
- ]
- request, metadata = self._interceptor.pre_list_snapshots(request, metadata)
- pb_request = pubsub.ListSnapshotsRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSubscriberRestTransport._BaseListSnapshots._get_http_options()
+ )
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ request, metadata = self._interceptor.pre_list_snapshots(request, metadata)
+ transcoded_request = (
+ _BaseSubscriberRestTransport._BaseListSnapshots._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BaseListSnapshots._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.ListSnapshots",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "ListSnapshots",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SubscriberRestTransport._ListSnapshots._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1297,22 +1946,62 @@ def __call__(
pb_resp = pubsub.ListSnapshotsResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_list_snapshots(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_snapshots_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.ListSnapshotsResponse.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.list_snapshots",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "ListSnapshots",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ListSubscriptions(SubscriberRestStub):
+ class _ListSubscriptions(
+ _BaseSubscriberRestTransport._BaseListSubscriptions, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("ListSubscriptions")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.ListSubscriptions")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
def __call__(
self,
@@ -1320,7 +2009,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.ListSubscriptionsResponse:
r"""Call the list subscriptions method over HTTP.
@@ -1330,48 +2019,67 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.ListSubscriptionsResponse:
Response for the ``ListSubscriptions`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{project=projects/*}/subscriptions",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseListSubscriptions._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_list_subscriptions(
request, metadata
)
- pb_request = pubsub.ListSubscriptionsRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = _BaseSubscriberRestTransport._BaseListSubscriptions._get_transcoded_request(
+ http_options, request
+ )
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSubscriberRestTransport._BaseListSubscriptions._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.ListSubscriptions",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "ListSubscriptions",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
+ response = SubscriberRestTransport._ListSubscriptions._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1384,22 +2092,65 @@ def __call__(
pb_resp = pubsub.ListSubscriptionsResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_list_subscriptions(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_subscriptions_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.ListSubscriptionsResponse.to_json(
+ response
+ )
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.list_subscriptions",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "ListSubscriptions",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _ModifyAckDeadline(SubscriberRestStub):
+ class _ModifyAckDeadline(
+ _BaseSubscriberRestTransport._BaseModifyAckDeadline, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("ModifyAckDeadline")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.ModifyAckDeadline")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1407,7 +2158,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
):
r"""Call the modify ack deadline method over HTTP.
@@ -1418,51 +2169,68 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyAckDeadline",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_modify_ack_deadline(
request, metadata
)
- pb_request = pubsub.ModifyAckDeadlineRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- # Jsonify the request body
+ transcoded_request = _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.ModifyAckDeadline",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "ModifyAckDeadline",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._ModifyAckDeadline._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1470,19 +2238,34 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
- class _ModifyPushConfig(SubscriberRestStub):
+ class _ModifyPushConfig(
+ _BaseSubscriberRestTransport._BaseModifyPushConfig, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("ModifyPushConfig")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.ModifyPushConfig")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1490,7 +2273,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
):
r"""Call the modify push config method over HTTP.
@@ -1501,51 +2284,68 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyPushConfig",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseModifyPushConfig._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_modify_push_config(
request, metadata
)
- pb_request = pubsub.ModifyPushConfigRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- # Jsonify the request body
+ transcoded_request = _BaseSubscriberRestTransport._BaseModifyPushConfig._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSubscriberRestTransport._BaseModifyPushConfig._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSubscriberRestTransport._BaseModifyPushConfig._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.ModifyPushConfig",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "ModifyPushConfig",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._ModifyPushConfig._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1553,19 +2353,32 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
- class _Pull(SubscriberRestStub):
+ class _Pull(_BaseSubscriberRestTransport._BasePull, SubscriberRestStub):
def __hash__(self):
- return hash("Pull")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.Pull")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1573,7 +2386,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.PullResponse:
r"""Call the pull method over HTTP.
@@ -1583,53 +2396,72 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.PullResponse:
Response for the ``Pull`` method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}:pull",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_pull(request, metadata)
- pb_request = pubsub.PullRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = _BaseSubscriberRestTransport._BasePull._get_http_options()
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_pull(request, metadata)
+ transcoded_request = (
+ _BaseSubscriberRestTransport._BasePull._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSubscriberRestTransport._BasePull._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BasePull._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.Pull",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "Pull",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._Pull._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1642,22 +2474,59 @@ def __call__(
pb_resp = pubsub.PullResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_pull(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_pull_with_metadata(resp, response_metadata)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.PullResponse.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.pull",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "Pull",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _Seek(SubscriberRestStub):
+ class _Seek(_BaseSubscriberRestTransport._BaseSeek, SubscriberRestStub):
def __hash__(self):
- return hash("Seek")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.Seek")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1665,7 +2534,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.SeekResponse:
r"""Call the seek method over HTTP.
@@ -1675,8 +2544,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.SeekResponse:
@@ -1685,45 +2556,62 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{subscription=projects/*/subscriptions/*}:seek",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_seek(request, metadata)
- pb_request = pubsub.SeekRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = _BaseSubscriberRestTransport._BaseSeek._get_http_options()
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_seek(request, metadata)
+ transcoded_request = (
+ _BaseSubscriberRestTransport._BaseSeek._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSubscriberRestTransport._BaseSeek._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BaseSeek._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.Seek",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "Seek",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._Seek._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1736,12 +2624,38 @@ def __call__(
pb_resp = pubsub.SeekResponse.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_seek(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_seek_with_metadata(resp, response_metadata)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.SeekResponse.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.seek",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "Seek",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _StreamingPull(SubscriberRestStub):
+ class _StreamingPull(
+ _BaseSubscriberRestTransport._BaseStreamingPull, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("StreamingPull")
+ return hash("SubscriberRestTransport.StreamingPull")
def __call__(
self,
@@ -1749,25 +2663,40 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> rest_streaming.ResponseIterator:
raise NotImplementedError(
"Method StreamingPull is not available over REST transport"
)
- class _UpdateSnapshot(SubscriberRestStub):
+ class _UpdateSnapshot(
+ _BaseSubscriberRestTransport._BaseUpdateSnapshot, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("UpdateSnapshot")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.UpdateSnapshot")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1775,7 +2704,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Snapshot:
r"""Call the update snapshot method over HTTP.
@@ -1786,8 +2715,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Snapshot:
@@ -1800,45 +2731,64 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "patch",
- "uri": "/v1/{snapshot.name=projects/*/snapshots/*}",
- "body": "*",
- },
- ]
- request, metadata = self._interceptor.pre_update_snapshot(request, metadata)
- pb_request = pubsub.UpdateSnapshotRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
+ http_options = (
+ _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_http_options()
+ )
- # Jsonify the request body
+ request, metadata = self._interceptor.pre_update_snapshot(request, metadata)
+ transcoded_request = _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = (
+ _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_request_body_json(
+ transcoded_request
+ )
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
+ query_params = (
+ _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_query_params_json(
+ transcoded_request
)
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.UpdateSnapshot",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "UpdateSnapshot",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._UpdateSnapshot._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1851,22 +2801,63 @@ def __call__(
pb_resp = pubsub.Snapshot.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_update_snapshot(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_update_snapshot_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Snapshot.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.update_snapshot",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "UpdateSnapshot",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
- class _UpdateSubscription(SubscriberRestStub):
+ class _UpdateSubscription(
+ _BaseSubscriberRestTransport._BaseUpdateSubscription, SubscriberRestStub
+ ):
def __hash__(self):
- return hash("UpdateSubscription")
-
- __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
-
- @classmethod
- def _get_unset_required_fields(cls, message_dict):
- return {
- k: v
- for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
- if k not in message_dict
- }
+ return hash("SubscriberRestTransport.UpdateSubscription")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
def __call__(
self,
@@ -1874,7 +2865,7 @@ def __call__(
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pubsub.Subscription:
r"""Call the update subscription method over HTTP.
@@ -1885,8 +2876,10 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
~.pubsub.Subscription:
@@ -1897,47 +2890,62 @@ def __call__(
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "patch",
- "uri": "/v1/{subscription.name=projects/*/subscriptions/*}",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseUpdateSubscription._get_http_options()
+ )
+
request, metadata = self._interceptor.pre_update_subscription(
request, metadata
)
- pb_request = pubsub.UpdateSubscriptionRequest.pb(request)
- transcoded_request = path_template.transcode(http_options, pb_request)
-
- # Jsonify the request body
+ transcoded_request = _BaseSubscriberRestTransport._BaseUpdateSubscription._get_transcoded_request(
+ http_options, request
+ )
- body = json_format.MessageToJson(
- transcoded_request["body"], use_integers_for_enums=True
+ body = _BaseSubscriberRestTransport._BaseUpdateSubscription._get_request_body_json(
+ transcoded_request
)
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
# Jsonify the query params
- query_params = json.loads(
- json_format.MessageToJson(
- transcoded_request["query_params"],
- use_integers_for_enums=True,
- )
+ query_params = _BaseSubscriberRestTransport._BaseUpdateSubscription._get_query_params_json(
+ transcoded_request
)
- query_params.update(self._get_unset_required_fields(query_params))
- query_params["$alt"] = "json;enum-encoding=int"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = type(request).to_json(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.UpdateSubscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "UpdateSubscription",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
# Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params, strict=True),
- data=body,
+ response = SubscriberRestTransport._UpdateSubscription._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -1950,7 +2958,33 @@ def __call__(
pb_resp = pubsub.Subscription.pb(resp)
json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
resp = self._interceptor.post_update_subscription(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_update_subscription_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = pubsub.Subscription.to_json(response)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberClient.update_subscription",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "UpdateSubscription",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
return resp
@property
@@ -2077,14 +3111,41 @@ def update_subscription(
def get_iam_policy(self):
return self._GetIamPolicy(self._session, self._host, self._interceptor) # type: ignore
- class _GetIamPolicy(SubscriberRestStub):
+ class _GetIamPolicy(
+ _BaseSubscriberRestTransport._BaseGetIamPolicy, SubscriberRestStub
+ ):
+ def __hash__(self):
+ return hash("SubscriberRestTransport.GetIamPolicy")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.GetIamPolicyRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Call the get iam policy method over HTTP.
@@ -2094,51 +3155,68 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
policy_pb2.Policy: Response from GetIamPolicy method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy",
- },
- {
- "method": "get",
- "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseGetIamPolicy._get_http_options()
+ )
request, metadata = self._interceptor.pre_get_iam_policy(request, metadata)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
-
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ transcoded_request = (
+ _BaseSubscriberRestTransport._BaseGetIamPolicy._get_transcoded_request(
+ http_options, request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = (
+ _BaseSubscriberRestTransport._BaseGetIamPolicy._get_query_params_json(
+ transcoded_request
+ )
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.GetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "GetIamPolicy",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
+ # Send the request
+ response = SubscriberRestTransport._GetIamPolicy._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -2146,23 +3224,73 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = policy_pb2.Policy()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_get_iam_policy(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberAsyncClient.GetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "GetIamPolicy",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
def set_iam_policy(self):
return self._SetIamPolicy(self._session, self._host, self._interceptor) # type: ignore
- class _SetIamPolicy(SubscriberRestStub):
+ class _SetIamPolicy(
+ _BaseSubscriberRestTransport._BaseSetIamPolicy, SubscriberRestStub
+ ):
+ def __hash__(self):
+ return hash("SubscriberRestTransport.SetIamPolicy")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.SetIamPolicyRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> policy_pb2.Policy:
r"""Call the set iam policy method over HTTP.
@@ -2172,57 +3300,75 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
policy_pb2.Policy: Response from SetIamPolicy method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseSetIamPolicy._get_http_options()
+ )
request, metadata = self._interceptor.pre_set_iam_policy(request, metadata)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ transcoded_request = (
+ _BaseSubscriberRestTransport._BaseSetIamPolicy._get_transcoded_request(
+ http_options, request
+ )
+ )
- body = json.dumps(transcoded_request["body"])
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ body = (
+ _BaseSubscriberRestTransport._BaseSetIamPolicy._get_request_body_json(
+ transcoded_request
+ )
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = (
+ _BaseSubscriberRestTransport._BaseSetIamPolicy._get_query_params_json(
+ transcoded_request
+ )
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.SetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "SetIamPolicy",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
- data=body,
+ # Send the request
+ response = SubscriberRestTransport._SetIamPolicy._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -2230,23 +3376,73 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = policy_pb2.Policy()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_set_iam_policy(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberAsyncClient.SetIamPolicy",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "SetIamPolicy",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
def test_iam_permissions(self):
return self._TestIamPermissions(self._session, self._host, self._interceptor) # type: ignore
- class _TestIamPermissions(SubscriberRestStub):
+ class _TestIamPermissions(
+ _BaseSubscriberRestTransport._BaseTestIamPermissions, SubscriberRestStub
+ ):
+ def __hash__(self):
+ return hash("SubscriberRestTransport.TestIamPermissions")
+
+ @staticmethod
+ def _get_response(
+ host,
+ metadata,
+ query_params,
+ session,
+ timeout,
+ transcoded_request,
+ body=None,
+ ):
+ uri = transcoded_request["uri"]
+ method = transcoded_request["method"]
+ headers = dict(metadata)
+ headers["Content-Type"] = "application/json"
+ response = getattr(session, method)(
+ "{host}{uri}".format(host=host, uri=uri),
+ timeout=timeout,
+ headers=headers,
+ params=rest_helpers.flatten_query_params(query_params, strict=True),
+ data=body,
+ )
+ return response
+
def __call__(
self,
request: iam_policy_pb2.TestIamPermissionsRequest,
*,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
timeout: Optional[float] = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> iam_policy_pb2.TestIamPermissionsResponse:
r"""Call the test iam permissions method over HTTP.
@@ -2256,59 +3452,71 @@ def __call__(
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.
+ metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be
+ sent along with the request as metadata. Normally, each value must be of type `str`,
+ but for metadata keys ending with the suffix `-bin`, the corresponding values must
+ be of type `bytes`.
Returns:
iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method.
"""
- http_options: List[Dict[str, str]] = [
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions",
- "body": "*",
- },
- {
- "method": "post",
- "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions",
- "body": "*",
- },
- ]
+ http_options = (
+ _BaseSubscriberRestTransport._BaseTestIamPermissions._get_http_options()
+ )
request, metadata = self._interceptor.pre_test_iam_permissions(
request, metadata
)
- request_kwargs = json_format.MessageToDict(request)
- transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ transcoded_request = _BaseSubscriberRestTransport._BaseTestIamPermissions._get_transcoded_request(
+ http_options, request
+ )
- body = json.dumps(transcoded_request["body"])
- uri = transcoded_request["uri"]
- method = transcoded_request["method"]
+ body = _BaseSubscriberRestTransport._BaseTestIamPermissions._get_request_body_json(
+ transcoded_request
+ )
# Jsonify the query params
- query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ query_params = _BaseSubscriberRestTransport._BaseTestIamPermissions._get_query_params_json(
+ transcoded_request
+ )
- # Send the request
- headers = dict(metadata)
- headers["Content-Type"] = "application/json"
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ request_url = "{host}{uri}".format(
+ host=self._host, uri=transcoded_request["uri"]
+ )
+ method = transcoded_request["method"]
+ try:
+ request_payload = json_format.MessageToJson(request)
+ except:
+ request_payload = None
+ http_request = {
+ "payload": request_payload,
+ "requestMethod": method,
+ "requestUrl": request_url,
+ "headers": dict(metadata),
+ }
+ _LOGGER.debug(
+ f"Sending request for google.pubsub_v1.SubscriberClient.TestIamPermissions",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "TestIamPermissions",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
- response = getattr(self._session, method)(
- "{host}{uri}".format(host=self._host, uri=uri),
- timeout=timeout,
- headers=headers,
- params=rest_helpers.flatten_query_params(query_params),
- data=body,
+ # Send the request
+ response = SubscriberRestTransport._TestIamPermissions._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
)
# In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
@@ -2316,9 +3524,31 @@ def __call__(
if response.status_code >= 400:
raise core_exceptions.from_http_response(response)
+ content = response.content.decode("utf-8")
resp = iam_policy_pb2.TestIamPermissionsResponse()
- resp = json_format.Parse(response.content.decode("utf-8"), resp)
+ resp = json_format.Parse(content, resp)
resp = self._interceptor.post_test_iam_permissions(resp)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = json_format.MessageToJson(resp)
+ except:
+ response_payload = None
+ http_response = {
+ "payload": response_payload,
+ "headers": dict(response.headers),
+ "status": response.status_code,
+ }
+ _LOGGER.debug(
+ "Received response for google.pubsub_v1.SubscriberAsyncClient.TestIamPermissions",
+ extra={
+ "serviceName": "google.pubsub.v1.Subscriber",
+ "rpcName": "TestIamPermissions",
+ "httpResponse": http_response,
+ "metadata": http_response["headers"],
+ },
+ )
return resp
@property
diff --git a/google/pubsub_v1/services/subscriber/transports/rest_base.py b/google/pubsub_v1/services/subscriber/transports/rest_base.py
new file mode 100644
index 000000000..f4fb07656
--- /dev/null
+++ b/google/pubsub_v1/services/subscriber/transports/rest_base.py
@@ -0,0 +1,1024 @@
+# -*- coding: utf-8 -*-
+# Copyright 2025 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 json # type: ignore
+from google.api_core import path_template
+from google.api_core import gapic_v1
+
+from google.protobuf import json_format
+from google.iam.v1 import iam_policy_pb2 # type: ignore
+from google.iam.v1 import policy_pb2 # type: ignore
+from .base import SubscriberTransport, DEFAULT_CLIENT_INFO
+
+import re
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+
+
+from google.protobuf import empty_pb2 # type: ignore
+from google.pubsub_v1.types import pubsub
+
+
+class _BaseSubscriberRestTransport(SubscriberTransport):
+ """Base REST backend transport for Subscriber.
+
+ Note: This class is not meant to be used directly. Use its sync and
+ async sub-classes instead.
+
+ This class defines the same methods as the primary client, so the
+ primary client can load the underlying transport implementation
+ and call it.
+
+ It sends JSON representations of protocol buffers over HTTP/1.1
+ """
+
+ def __init__(
+ self,
+ *,
+ host: str = "pubsub.googleapis.com",
+ credentials: Optional[Any] = None,
+ client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
+ always_use_jwt_access: Optional[bool] = False,
+ url_scheme: str = "https",
+ api_audience: Optional[str] = None,
+ ) -> None:
+ """Instantiate the transport.
+ Args:
+ host (Optional[str]):
+ The hostname to connect to (default: 'pubsub.googleapis.com').
+ credentials (Optional[Any]): The
+ authorization credentials to attach to requests. These
+ credentials identify the application to the service; if none
+ are specified, the client will attempt to ascertain the
+ credentials from the environment.
+ client_info (google.api_core.gapic_v1.client_info.ClientInfo):
+ The client info used to send a user-agent string along with
+ API requests. If ``None``, then default info will be used.
+ Generally, you only need to set this if you are developing
+ your own client library.
+ always_use_jwt_access (Optional[bool]): Whether self signed JWT should
+ be used for service account credentials.
+ url_scheme: the protocol scheme for the API endpoint. Normally
+ "https", but for testing or local servers,
+ "http" can be specified.
+ """
+ # Run the base constructor
+ maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host)
+ if maybe_url_match is None:
+ raise ValueError(
+ f"Unexpected hostname structure: {host}"
+ ) # pragma: NO COVER
+
+ url_match_items = maybe_url_match.groupdict()
+
+ host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host
+
+ super().__init__(
+ host=host,
+ credentials=credentials,
+ client_info=client_info,
+ always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
+ )
+
+ class _BaseAcknowledge:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}:acknowledge",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.AcknowledgeRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseAcknowledge._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseCreateSnapshot:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "put",
+ "uri": "/v1/{name=projects/*/snapshots/*}",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.CreateSnapshotRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseCreateSnapshot._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseCreateSubscription:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "put",
+ "uri": "/v1/{name=projects/*/subscriptions/*}",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.Subscription.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseCreateSubscription._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseDeleteSnapshot:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "delete",
+ "uri": "/v1/{snapshot=projects/*/snapshots/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.DeleteSnapshotRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseDeleteSubscription:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "delete",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.DeleteSubscriptionRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseDeleteSubscription._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseGetSnapshot:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{snapshot=projects/*/snapshots/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.GetSnapshotRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseGetSnapshot._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseGetSubscription:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.GetSubscriptionRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseGetSubscription._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListSnapshots:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{project=projects/*}/snapshots",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.ListSnapshotsRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseListSnapshots._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListSubscriptions:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{project=projects/*}/subscriptions",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.ListSubscriptionsRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseListSubscriptions._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseModifyAckDeadline:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyAckDeadline",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.ModifyAckDeadlineRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseModifyPushConfig:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyPushConfig",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.ModifyPushConfigRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseModifyPushConfig._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BasePull:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}:pull",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.PullRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BasePull._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseSeek:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{subscription=projects/*/subscriptions/*}:seek",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.SeekRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseSeek._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseStreamingPull:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ class _BaseUpdateSnapshot:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "patch",
+ "uri": "/v1/{snapshot.name=projects/*/snapshots/*}",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.UpdateSnapshotRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseUpdateSubscription:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {}
+
+ @classmethod
+ def _get_unset_required_fields(cls, message_dict):
+ return {
+ k: v
+ for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items()
+ if k not in message_dict
+ }
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "patch",
+ "uri": "/v1/{subscription.name=projects/*/subscriptions/*}",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = pubsub.UpdateSubscriptionRequest.pb(request)
+ transcoded_request = path_template.transcode(http_options, pb_request)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ # Jsonify the request body
+
+ body = json_format.MessageToJson(
+ transcoded_request["body"], use_integers_for_enums=True
+ )
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(
+ json_format.MessageToJson(
+ transcoded_request["query_params"],
+ use_integers_for_enums=True,
+ )
+ )
+ query_params.update(
+ _BaseSubscriberRestTransport._BaseUpdateSubscription._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseGetIamPolicy:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy",
+ },
+ {
+ "method": "get",
+ "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+ class _BaseSetIamPolicy:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ body = json.dumps(transcoded_request["body"])
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+ class _BaseTestIamPermissions:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ @staticmethod
+ def _get_http_options():
+ http_options: List[Dict[str, str]] = [
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions",
+ "body": "*",
+ },
+ {
+ "method": "post",
+ "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions",
+ "body": "*",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ request_kwargs = json_format.MessageToDict(request)
+ transcoded_request = path_template.transcode(http_options, **request_kwargs)
+ return transcoded_request
+
+ @staticmethod
+ def _get_request_body_json(transcoded_request):
+ body = json.dumps(transcoded_request["body"])
+ return body
+
+ @staticmethod
+ def _get_query_params_json(transcoded_request):
+ query_params = json.loads(json.dumps(transcoded_request["query_params"]))
+ return query_params
+
+
+__all__ = ("_BaseSubscriberRestTransport",)
diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py
index 62568bf66..593abc464 100644
--- a/google/pubsub_v1/types/__init__.py
+++ b/google/pubsub_v1/types/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
from .pubsub import (
AcknowledgeRequest,
+ AIInference,
BigQueryConfig,
CloudStorageConfig,
CreateSnapshotRequest,
@@ -31,6 +32,8 @@
GetSubscriptionRequest,
GetTopicRequest,
IngestionDataSourceSettings,
+ IngestionFailureEvent,
+ JavaScriptUDF,
ListSnapshotsRequest,
ListSnapshotsResponse,
ListSubscriptionsRequest,
@@ -42,8 +45,10 @@
ListTopicSubscriptionsRequest,
ListTopicSubscriptionsResponse,
MessageStoragePolicy,
+ MessageTransform,
ModifyAckDeadlineRequest,
ModifyPushConfigRequest,
+ PlatformLogsSettings,
PublishRequest,
PublishResponse,
PubsubMessage,
@@ -95,6 +100,7 @@
__all__ = (
"TimeoutType",
"AcknowledgeRequest",
+ "AIInference",
"BigQueryConfig",
"CloudStorageConfig",
"CreateSnapshotRequest",
@@ -109,6 +115,8 @@
"GetSubscriptionRequest",
"GetTopicRequest",
"IngestionDataSourceSettings",
+ "IngestionFailureEvent",
+ "JavaScriptUDF",
"ListSnapshotsRequest",
"ListSnapshotsResponse",
"ListSubscriptionsRequest",
@@ -120,8 +128,10 @@
"ListTopicSubscriptionsRequest",
"ListTopicSubscriptionsResponse",
"MessageStoragePolicy",
+ "MessageTransform",
"ModifyAckDeadlineRequest",
"ModifyPushConfigRequest",
+ "PlatformLogsSettings",
"PublishRequest",
"PublishResponse",
"PubsubMessage",
diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py
index 5f8266014..1a5663c29 100644
--- a/google/pubsub_v1/types/pubsub.py
+++ b/google/pubsub_v1/types/pubsub.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
from google.protobuf import duration_pb2 # type: ignore
from google.protobuf import field_mask_pb2 # type: ignore
+from google.protobuf import struct_pb2 # type: ignore
from google.protobuf import timestamp_pb2 # type: ignore
from google.pubsub_v1.types import schema as gp_schema
@@ -31,6 +32,11 @@
"MessageStoragePolicy",
"SchemaSettings",
"IngestionDataSourceSettings",
+ "PlatformLogsSettings",
+ "IngestionFailureEvent",
+ "JavaScriptUDF",
+ "AIInference",
+ "MessageTransform",
"Topic",
"PubsubMessage",
"GetTopicRequest",
@@ -160,6 +166,11 @@ class SchemaSettings(proto.Message):
class IngestionDataSourceSettings(proto.Message):
r"""Settings for an ingestion data source on a topic.
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
.. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
Attributes:
@@ -167,6 +178,25 @@ class IngestionDataSourceSettings(proto.Message):
Optional. Amazon Kinesis Data Streams.
This field is a member of `oneof`_ ``source``.
+ cloud_storage (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage):
+ Optional. Cloud Storage.
+
+ This field is a member of `oneof`_ ``source``.
+ azure_event_hubs (google.pubsub_v1.types.IngestionDataSourceSettings.AzureEventHubs):
+ Optional. Azure Event Hubs.
+
+ This field is a member of `oneof`_ ``source``.
+ aws_msk (google.pubsub_v1.types.IngestionDataSourceSettings.AwsMsk):
+ Optional. Amazon MSK.
+
+ This field is a member of `oneof`_ ``source``.
+ confluent_cloud (google.pubsub_v1.types.IngestionDataSourceSettings.ConfluentCloud):
+ Optional. Confluent Cloud.
+
+ This field is a member of `oneof`_ ``source``.
+ platform_logs_settings (google.pubsub_v1.types.PlatformLogsSettings):
+ Optional. Platform Logs settings. If unset,
+ no Platform Logs will be generated.
"""
class AwsKinesis(proto.Message):
@@ -213,13 +243,13 @@ class State(proto.Enum):
Permission denied encountered while consuming data from
Kinesis. This can happen if:
- - The provided ``aws_role_arn`` does not exist or does not
- have the appropriate permissions attached.
- - The provided ``aws_role_arn`` is not set up properly for
- Identity Federation using ``gcp_service_account``.
- - The Pub/Sub SA is not granted the
- ``iam.serviceAccounts.getOpenIdToken`` permission on
- ``gcp_service_account``.
+ - The provided ``aws_role_arn`` does not exist or does not
+ have the appropriate permissions attached.
+ - The provided ``aws_role_arn`` is not set up properly for
+ Identity Federation using ``gcp_service_account``.
+ - The Pub/Sub SA is not granted the
+ ``iam.serviceAccounts.getOpenIdToken`` permission on
+ ``gcp_service_account``.
PUBLISH_PERMISSION_DENIED (3):
Permission denied encountered while publishing to the topic.
This can happen if the Pub/Sub SA has not been granted the
@@ -259,12 +289,1179 @@ class State(proto.Enum):
number=5,
)
+ class CloudStorage(proto.Message):
+ r"""Ingestion settings for Cloud Storage.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ state (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.State):
+ Output only. An output-only field that
+ indicates the state of the Cloud Storage
+ ingestion source.
+ bucket (str):
+ Optional. Cloud Storage bucket. The bucket name must be
+ without any prefix like "gs://". See the [bucket naming
+ requirements]
+ (https://cloud.google.com/storage/docs/buckets#naming).
+ text_format (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.TextFormat):
+ Optional. Data from Cloud Storage will be
+ interpreted as text.
+
+ This field is a member of `oneof`_ ``input_format``.
+ avro_format (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.AvroFormat):
+ Optional. Data from Cloud Storage will be
+ interpreted in Avro format.
+
+ This field is a member of `oneof`_ ``input_format``.
+ pubsub_avro_format (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat):
+ Optional. It will be assumed data from Cloud Storage was
+ written via `Cloud Storage
+ subscriptions `__.
+
+ This field is a member of `oneof`_ ``input_format``.
+ minimum_object_create_time (google.protobuf.timestamp_pb2.Timestamp):
+ Optional. Only objects with a larger or equal
+ creation timestamp will be ingested.
+ match_glob (str):
+ Optional. Glob pattern used to match objects that will be
+ ingested. If unset, all objects will be ingested. See the
+ `supported
+ patterns `__.
+ """
+
+ class State(proto.Enum):
+ r"""Possible states for ingestion from Cloud Storage.
+
+ Values:
+ STATE_UNSPECIFIED (0):
+ Default value. This value is unused.
+ ACTIVE (1):
+ Ingestion is active.
+ CLOUD_STORAGE_PERMISSION_DENIED (2):
+ Permission denied encountered while calling the Cloud
+ Storage API. This can happen if the Pub/Sub SA has not been
+ granted the `appropriate
+ permissions `__:
+
+ - storage.objects.list: to list the objects in a bucket.
+ - storage.objects.get: to read the objects in a bucket.
+ - storage.buckets.get: to verify the bucket exists.
+ PUBLISH_PERMISSION_DENIED (3):
+ Permission denied encountered while publishing to the topic.
+ This can happen if the Pub/Sub SA has not been granted the
+ `appropriate publish
+ permissions `__
+ BUCKET_NOT_FOUND (4):
+ The provided Cloud Storage bucket doesn't
+ exist.
+ TOO_MANY_OBJECTS (5):
+ The Cloud Storage bucket has too many
+ objects, ingestion will be paused.
+ """
+ STATE_UNSPECIFIED = 0
+ ACTIVE = 1
+ CLOUD_STORAGE_PERMISSION_DENIED = 2
+ PUBLISH_PERMISSION_DENIED = 3
+ BUCKET_NOT_FOUND = 4
+ TOO_MANY_OBJECTS = 5
+
+ class TextFormat(proto.Message):
+ r"""Configuration for reading Cloud Storage data in text format. Each
+ line of text as specified by the delimiter will be set to the
+ ``data`` field of a Pub/Sub message.
+
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ delimiter (str):
+ Optional. When unset, '\n' is used.
+
+ This field is a member of `oneof`_ ``_delimiter``.
+ """
+
+ delimiter: str = proto.Field(
+ proto.STRING,
+ number=1,
+ optional=True,
+ )
+
+ class AvroFormat(proto.Message):
+ r"""Configuration for reading Cloud Storage data in Avro binary format.
+ The bytes of each object will be set to the ``data`` field of a
+ Pub/Sub message.
+
+ """
+
+ class PubSubAvroFormat(proto.Message):
+ r"""Configuration for reading Cloud Storage data written via `Cloud
+ Storage
+ subscriptions `__.
+ The data and attributes fields of the originally exported Pub/Sub
+ message will be restored when publishing.
+
+ """
+
+ state: "IngestionDataSourceSettings.CloudStorage.State" = proto.Field(
+ proto.ENUM,
+ number=1,
+ enum="IngestionDataSourceSettings.CloudStorage.State",
+ )
+ bucket: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ text_format: "IngestionDataSourceSettings.CloudStorage.TextFormat" = (
+ proto.Field(
+ proto.MESSAGE,
+ number=3,
+ oneof="input_format",
+ message="IngestionDataSourceSettings.CloudStorage.TextFormat",
+ )
+ )
+ avro_format: "IngestionDataSourceSettings.CloudStorage.AvroFormat" = (
+ proto.Field(
+ proto.MESSAGE,
+ number=4,
+ oneof="input_format",
+ message="IngestionDataSourceSettings.CloudStorage.AvroFormat",
+ )
+ )
+ pubsub_avro_format: "IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat" = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="input_format",
+ message="IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat",
+ )
+ minimum_object_create_time: timestamp_pb2.Timestamp = proto.Field(
+ proto.MESSAGE,
+ number=6,
+ message=timestamp_pb2.Timestamp,
+ )
+ match_glob: str = proto.Field(
+ proto.STRING,
+ number=9,
+ )
+
+ class AzureEventHubs(proto.Message):
+ r"""Ingestion settings for Azure Event Hubs.
+
+ Attributes:
+ state (google.pubsub_v1.types.IngestionDataSourceSettings.AzureEventHubs.State):
+ Output only. An output-only field that
+ indicates the state of the Event Hubs ingestion
+ source.
+ resource_group (str):
+ Optional. Name of the resource group within
+ the azure subscription.
+ namespace (str):
+ Optional. The name of the Event Hubs
+ namespace.
+ event_hub (str):
+ Optional. The name of the Event Hub.
+ client_id (str):
+ Optional. The client id of the Azure
+ application that is being used to authenticate
+ Pub/Sub.
+ tenant_id (str):
+ Optional. The tenant id of the Azure
+ application that is being used to authenticate
+ Pub/Sub.
+ subscription_id (str):
+ Optional. The Azure subscription id.
+ gcp_service_account (str):
+ Optional. The GCP service account to be used
+ for Federated Identity authentication.
+ """
+
+ class State(proto.Enum):
+ r"""Possible states for managed ingestion from Event Hubs.
+
+ Values:
+ STATE_UNSPECIFIED (0):
+ Default value. This value is unused.
+ ACTIVE (1):
+ Ingestion is active.
+ EVENT_HUBS_PERMISSION_DENIED (2):
+ Permission denied encountered while consuming data from
+ Event Hubs. This can happen when ``client_id``, or
+ ``tenant_id`` are invalid. Or the right permissions haven't
+ been granted.
+ PUBLISH_PERMISSION_DENIED (3):
+ Permission denied encountered while
+ publishing to the topic.
+ NAMESPACE_NOT_FOUND (4):
+ The provided Event Hubs namespace couldn't be
+ found.
+ EVENT_HUB_NOT_FOUND (5):
+ The provided Event Hub couldn't be found.
+ SUBSCRIPTION_NOT_FOUND (6):
+ The provided Event Hubs subscription couldn't
+ be found.
+ RESOURCE_GROUP_NOT_FOUND (7):
+ The provided Event Hubs resource group
+ couldn't be found.
+ """
+ STATE_UNSPECIFIED = 0
+ ACTIVE = 1
+ EVENT_HUBS_PERMISSION_DENIED = 2
+ PUBLISH_PERMISSION_DENIED = 3
+ NAMESPACE_NOT_FOUND = 4
+ EVENT_HUB_NOT_FOUND = 5
+ SUBSCRIPTION_NOT_FOUND = 6
+ RESOURCE_GROUP_NOT_FOUND = 7
+
+ state: "IngestionDataSourceSettings.AzureEventHubs.State" = proto.Field(
+ proto.ENUM,
+ number=1,
+ enum="IngestionDataSourceSettings.AzureEventHubs.State",
+ )
+ resource_group: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ namespace: str = proto.Field(
+ proto.STRING,
+ number=3,
+ )
+ event_hub: str = proto.Field(
+ proto.STRING,
+ number=4,
+ )
+ client_id: str = proto.Field(
+ proto.STRING,
+ number=5,
+ )
+ tenant_id: str = proto.Field(
+ proto.STRING,
+ number=6,
+ )
+ subscription_id: str = proto.Field(
+ proto.STRING,
+ number=7,
+ )
+ gcp_service_account: str = proto.Field(
+ proto.STRING,
+ number=8,
+ )
+
+ class AwsMsk(proto.Message):
+ r"""Ingestion settings for Amazon MSK.
+
+ Attributes:
+ state (google.pubsub_v1.types.IngestionDataSourceSettings.AwsMsk.State):
+ Output only. An output-only field that
+ indicates the state of the Amazon MSK ingestion
+ source.
+ cluster_arn (str):
+ Required. The Amazon Resource Name (ARN) that
+ uniquely identifies the cluster.
+ topic (str):
+ Required. The name of the topic in the Amazon
+ MSK cluster that Pub/Sub will import from.
+ aws_role_arn (str):
+ Required. AWS role ARN to be used for
+ Federated Identity authentication with Amazon
+ MSK. Check the Pub/Sub docs for how to set up
+ this role and the required permissions that need
+ to be attached to it.
+ gcp_service_account (str):
+ Required. The GCP service account to be used for Federated
+ Identity authentication with Amazon MSK (via a
+ ``AssumeRoleWithWebIdentity`` call for the provided role).
+ The ``aws_role_arn`` must be set up with
+ ``accounts.google.com:sub`` equals to this service account
+ number.
+ """
+
+ class State(proto.Enum):
+ r"""Possible states for managed ingestion from Amazon MSK.
+
+ Values:
+ STATE_UNSPECIFIED (0):
+ Default value. This value is unused.
+ ACTIVE (1):
+ Ingestion is active.
+ MSK_PERMISSION_DENIED (2):
+ Permission denied encountered while consuming
+ data from Amazon MSK.
+ PUBLISH_PERMISSION_DENIED (3):
+ Permission denied encountered while
+ publishing to the topic.
+ CLUSTER_NOT_FOUND (4):
+ The provided MSK cluster wasn't found.
+ TOPIC_NOT_FOUND (5):
+ The provided topic wasn't found.
+ """
+ STATE_UNSPECIFIED = 0
+ ACTIVE = 1
+ MSK_PERMISSION_DENIED = 2
+ PUBLISH_PERMISSION_DENIED = 3
+ CLUSTER_NOT_FOUND = 4
+ TOPIC_NOT_FOUND = 5
+
+ state: "IngestionDataSourceSettings.AwsMsk.State" = proto.Field(
+ proto.ENUM,
+ number=1,
+ enum="IngestionDataSourceSettings.AwsMsk.State",
+ )
+ cluster_arn: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ topic: str = proto.Field(
+ proto.STRING,
+ number=3,
+ )
+ aws_role_arn: str = proto.Field(
+ proto.STRING,
+ number=4,
+ )
+ gcp_service_account: str = proto.Field(
+ proto.STRING,
+ number=5,
+ )
+
+ class ConfluentCloud(proto.Message):
+ r"""Ingestion settings for Confluent Cloud.
+
+ Attributes:
+ state (google.pubsub_v1.types.IngestionDataSourceSettings.ConfluentCloud.State):
+ Output only. An output-only field that
+ indicates the state of the Confluent Cloud
+ ingestion source.
+ bootstrap_server (str):
+ Required. The address of the bootstrap
+ server. The format is url:port.
+ cluster_id (str):
+ Required. The id of the cluster.
+ topic (str):
+ Required. The name of the topic in the
+ Confluent Cloud cluster that Pub/Sub will import
+ from.
+ identity_pool_id (str):
+ Required. The id of the identity pool to be
+ used for Federated Identity authentication with
+ Confluent Cloud. See
+ https://docs.confluent.io/cloud/current/security/authenticate/workload-identities/identity-providers/oauth/identity-pools.html#add-oauth-identity-pools.
+ gcp_service_account (str):
+ Required. The GCP service account to be used for Federated
+ Identity authentication with ``identity_pool_id``.
+ """
+
+ class State(proto.Enum):
+ r"""Possible states for managed ingestion from Confluent Cloud.
+
+ Values:
+ STATE_UNSPECIFIED (0):
+ Default value. This value is unused.
+ ACTIVE (1):
+ Ingestion is active.
+ CONFLUENT_CLOUD_PERMISSION_DENIED (2):
+ Permission denied encountered while consuming
+ data from Confluent Cloud.
+ PUBLISH_PERMISSION_DENIED (3):
+ Permission denied encountered while
+ publishing to the topic.
+ UNREACHABLE_BOOTSTRAP_SERVER (4):
+ The provided bootstrap server address is
+ unreachable.
+ CLUSTER_NOT_FOUND (5):
+ The provided cluster wasn't found.
+ TOPIC_NOT_FOUND (6):
+ The provided topic wasn't found.
+ """
+ STATE_UNSPECIFIED = 0
+ ACTIVE = 1
+ CONFLUENT_CLOUD_PERMISSION_DENIED = 2
+ PUBLISH_PERMISSION_DENIED = 3
+ UNREACHABLE_BOOTSTRAP_SERVER = 4
+ CLUSTER_NOT_FOUND = 5
+ TOPIC_NOT_FOUND = 6
+
+ state: "IngestionDataSourceSettings.ConfluentCloud.State" = proto.Field(
+ proto.ENUM,
+ number=1,
+ enum="IngestionDataSourceSettings.ConfluentCloud.State",
+ )
+ bootstrap_server: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ cluster_id: str = proto.Field(
+ proto.STRING,
+ number=3,
+ )
+ topic: str = proto.Field(
+ proto.STRING,
+ number=4,
+ )
+ identity_pool_id: str = proto.Field(
+ proto.STRING,
+ number=5,
+ )
+ gcp_service_account: str = proto.Field(
+ proto.STRING,
+ number=6,
+ )
+
aws_kinesis: AwsKinesis = proto.Field(
proto.MESSAGE,
number=1,
oneof="source",
message=AwsKinesis,
)
+ cloud_storage: CloudStorage = proto.Field(
+ proto.MESSAGE,
+ number=2,
+ oneof="source",
+ message=CloudStorage,
+ )
+ azure_event_hubs: AzureEventHubs = proto.Field(
+ proto.MESSAGE,
+ number=3,
+ oneof="source",
+ message=AzureEventHubs,
+ )
+ aws_msk: AwsMsk = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="source",
+ message=AwsMsk,
+ )
+ confluent_cloud: ConfluentCloud = proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="source",
+ message=ConfluentCloud,
+ )
+ platform_logs_settings: "PlatformLogsSettings" = proto.Field(
+ proto.MESSAGE,
+ number=4,
+ message="PlatformLogsSettings",
+ )
+
+
+class PlatformLogsSettings(proto.Message):
+ r"""Settings for Platform Logs produced by Pub/Sub.
+
+ Attributes:
+ severity (google.pubsub_v1.types.PlatformLogsSettings.Severity):
+ Optional. The minimum severity level of
+ Platform Logs that will be written.
+ """
+
+ class Severity(proto.Enum):
+ r"""Severity levels of Platform Logs.
+
+ Values:
+ SEVERITY_UNSPECIFIED (0):
+ Default value. Logs level is unspecified.
+ Logs will be disabled.
+ DISABLED (1):
+ Logs will be disabled.
+ DEBUG (2):
+ Debug logs and higher-severity logs will be
+ written.
+ INFO (3):
+ Info logs and higher-severity logs will be
+ written.
+ WARNING (4):
+ Warning logs and higher-severity logs will be
+ written.
+ ERROR (5):
+ Only error logs will be written.
+ """
+ SEVERITY_UNSPECIFIED = 0
+ DISABLED = 1
+ DEBUG = 2
+ INFO = 3
+ WARNING = 4
+ ERROR = 5
+
+ severity: Severity = proto.Field(
+ proto.ENUM,
+ number=1,
+ enum=Severity,
+ )
+
+
+class IngestionFailureEvent(proto.Message):
+ r"""Payload of the Platform Log entry sent when a failure is
+ encountered while ingesting.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ topic (str):
+ Required. Name of the import topic. Format is:
+ projects/{project_name}/topics/{topic_name}.
+ error_message (str):
+ Required. Error details explaining why
+ ingestion to Pub/Sub has failed.
+ cloud_storage_failure (google.pubsub_v1.types.IngestionFailureEvent.CloudStorageFailure):
+ Optional. Failure when ingesting from Cloud
+ Storage.
+
+ This field is a member of `oneof`_ ``failure``.
+ aws_msk_failure (google.pubsub_v1.types.IngestionFailureEvent.AwsMskFailureReason):
+ Optional. Failure when ingesting from Amazon
+ MSK.
+
+ This field is a member of `oneof`_ ``failure``.
+ azure_event_hubs_failure (google.pubsub_v1.types.IngestionFailureEvent.AzureEventHubsFailureReason):
+ Optional. Failure when ingesting from Azure
+ Event Hubs.
+
+ This field is a member of `oneof`_ ``failure``.
+ confluent_cloud_failure (google.pubsub_v1.types.IngestionFailureEvent.ConfluentCloudFailureReason):
+ Optional. Failure when ingesting from
+ Confluent Cloud.
+
+ This field is a member of `oneof`_ ``failure``.
+ aws_kinesis_failure (google.pubsub_v1.types.IngestionFailureEvent.AwsKinesisFailureReason):
+ Optional. Failure when ingesting from AWS
+ Kinesis.
+
+ This field is a member of `oneof`_ ``failure``.
+ """
+
+ class ApiViolationReason(proto.Message):
+ r"""Specifies the reason why some data may have been left out of the
+ desired Pub/Sub message due to the API message limits
+ (https://cloud.google.com/pubsub/quotas#resource_limits). For
+ example, when the number of attributes is larger than 100, the
+ number of attributes is truncated to 100 to respect the limit on the
+ attribute count. Other attribute limits are treated similarly. When
+ the size of the desired message would've been larger than 10MB, the
+ message won't be published at all, and ingestion of the subsequent
+ messages will proceed as normal.
+
+ """
+
+ class AvroFailureReason(proto.Message):
+ r"""Set when an Avro file is unsupported or its format is not
+ valid. When this occurs, one or more Avro objects won't be
+ ingested.
+
+ """
+
+ class SchemaViolationReason(proto.Message):
+ r"""Set when a Pub/Sub message fails to get published due to a
+ schema validation violation.
+
+ """
+
+ class MessageTransformationFailureReason(proto.Message):
+ r"""Set when a Pub/Sub message fails to get published due to a
+ message transformation error.
+
+ """
+
+ class CloudStorageFailure(proto.Message):
+ r"""Failure when ingesting from a Cloud Storage source.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ bucket (str):
+ Optional. Name of the Cloud Storage bucket
+ used for ingestion.
+ object_name (str):
+ Optional. Name of the Cloud Storage object
+ which contained the section that couldn't be
+ ingested.
+ object_generation (int):
+ Optional. Generation of the Cloud Storage
+ object which contained the section that couldn't
+ be ingested.
+ avro_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.AvroFailureReason):
+ Optional. Failure encountered when parsing an
+ Avro file.
+
+ This field is a member of `oneof`_ ``reason``.
+ api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason):
+ Optional. The Pub/Sub API limits prevented
+ the desired message from being published.
+
+ This field is a member of `oneof`_ ``reason``.
+ schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason):
+ Optional. The Pub/Sub message failed schema
+ validation.
+
+ This field is a member of `oneof`_ ``reason``.
+ message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason):
+ Optional. Failure encountered when applying a
+ message transformation to the Pub/Sub message.
+
+ This field is a member of `oneof`_ ``reason``.
+ """
+
+ bucket: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ object_name: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ object_generation: int = proto.Field(
+ proto.INT64,
+ number=3,
+ )
+ avro_failure_reason: "IngestionFailureEvent.AvroFailureReason" = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="reason",
+ message="IngestionFailureEvent.AvroFailureReason",
+ )
+ api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="reason",
+ message="IngestionFailureEvent.ApiViolationReason",
+ )
+ schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = (
+ proto.Field(
+ proto.MESSAGE,
+ number=7,
+ oneof="reason",
+ message="IngestionFailureEvent.SchemaViolationReason",
+ )
+ )
+ message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field(
+ proto.MESSAGE,
+ number=8,
+ oneof="reason",
+ message="IngestionFailureEvent.MessageTransformationFailureReason",
+ )
+
+ class AwsMskFailureReason(proto.Message):
+ r"""Failure when ingesting from an Amazon MSK source.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ cluster_arn (str):
+ Optional. The ARN of the cluster of the topic
+ being ingested from.
+ kafka_topic (str):
+ Optional. The name of the Kafka topic being
+ ingested from.
+ partition_id (int):
+ Optional. The partition ID of the message
+ that failed to be ingested.
+ offset (int):
+ Optional. The offset within the partition of
+ the message that failed to be ingested.
+ api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason):
+ Optional. The Pub/Sub API limits prevented
+ the desired message from being published.
+
+ This field is a member of `oneof`_ ``reason``.
+ schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason):
+ Optional. The Pub/Sub message failed schema
+ validation.
+
+ This field is a member of `oneof`_ ``reason``.
+ message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason):
+ Optional. Failure encountered when applying a
+ message transformation to the Pub/Sub message.
+
+ This field is a member of `oneof`_ ``reason``.
+ """
+
+ cluster_arn: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ kafka_topic: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ partition_id: int = proto.Field(
+ proto.INT64,
+ number=3,
+ )
+ offset: int = proto.Field(
+ proto.INT64,
+ number=4,
+ )
+ api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="reason",
+ message="IngestionFailureEvent.ApiViolationReason",
+ )
+ schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = (
+ proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="reason",
+ message="IngestionFailureEvent.SchemaViolationReason",
+ )
+ )
+ message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field(
+ proto.MESSAGE,
+ number=7,
+ oneof="reason",
+ message="IngestionFailureEvent.MessageTransformationFailureReason",
+ )
+
+ class AzureEventHubsFailureReason(proto.Message):
+ r"""Failure when ingesting from an Azure Event Hubs source.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ namespace (str):
+ Optional. The namespace containing the event
+ hub being ingested from.
+ event_hub (str):
+ Optional. The name of the event hub being
+ ingested from.
+ partition_id (int):
+ Optional. The partition ID of the message
+ that failed to be ingested.
+ offset (int):
+ Optional. The offset within the partition of
+ the message that failed to be ingested.
+ api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason):
+ Optional. The Pub/Sub API limits prevented
+ the desired message from being published.
+
+ This field is a member of `oneof`_ ``reason``.
+ schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason):
+ Optional. The Pub/Sub message failed schema
+ validation.
+
+ This field is a member of `oneof`_ ``reason``.
+ message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason):
+ Optional. Failure encountered when applying a
+ message transformation to the Pub/Sub message.
+
+ This field is a member of `oneof`_ ``reason``.
+ """
+
+ namespace: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ event_hub: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ partition_id: int = proto.Field(
+ proto.INT64,
+ number=3,
+ )
+ offset: int = proto.Field(
+ proto.INT64,
+ number=4,
+ )
+ api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="reason",
+ message="IngestionFailureEvent.ApiViolationReason",
+ )
+ schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = (
+ proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="reason",
+ message="IngestionFailureEvent.SchemaViolationReason",
+ )
+ )
+ message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field(
+ proto.MESSAGE,
+ number=7,
+ oneof="reason",
+ message="IngestionFailureEvent.MessageTransformationFailureReason",
+ )
+
+ class ConfluentCloudFailureReason(proto.Message):
+ r"""Failure when ingesting from a Confluent Cloud source.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ cluster_id (str):
+ Optional. The cluster ID containing the topic
+ being ingested from.
+ kafka_topic (str):
+ Optional. The name of the Kafka topic being
+ ingested from.
+ partition_id (int):
+ Optional. The partition ID of the message
+ that failed to be ingested.
+ offset (int):
+ Optional. The offset within the partition of
+ the message that failed to be ingested.
+ api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason):
+ Optional. The Pub/Sub API limits prevented
+ the desired message from being published.
+
+ This field is a member of `oneof`_ ``reason``.
+ schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason):
+ Optional. The Pub/Sub message failed schema
+ validation.
+
+ This field is a member of `oneof`_ ``reason``.
+ message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason):
+ Optional. Failure encountered when applying a
+ message transformation to the Pub/Sub message.
+
+ This field is a member of `oneof`_ ``reason``.
+ """
+
+ cluster_id: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ kafka_topic: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ partition_id: int = proto.Field(
+ proto.INT64,
+ number=3,
+ )
+ offset: int = proto.Field(
+ proto.INT64,
+ number=4,
+ )
+ api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="reason",
+ message="IngestionFailureEvent.ApiViolationReason",
+ )
+ schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = (
+ proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="reason",
+ message="IngestionFailureEvent.SchemaViolationReason",
+ )
+ )
+ message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field(
+ proto.MESSAGE,
+ number=7,
+ oneof="reason",
+ message="IngestionFailureEvent.MessageTransformationFailureReason",
+ )
+
+ class AwsKinesisFailureReason(proto.Message):
+ r"""Failure when ingesting from an AWS Kinesis source.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ stream_arn (str):
+ Optional. The stream ARN of the Kinesis
+ stream being ingested from.
+ partition_key (str):
+ Optional. The partition key of the message
+ that failed to be ingested.
+ sequence_number (str):
+ Optional. The sequence number of the message
+ that failed to be ingested.
+ schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason):
+ Optional. The Pub/Sub message failed schema
+ validation.
+
+ This field is a member of `oneof`_ ``reason``.
+ message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason):
+ Optional. Failure encountered when applying a
+ message transformation to the Pub/Sub message.
+
+ This field is a member of `oneof`_ ``reason``.
+ api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason):
+ Optional. The message failed to be published
+ due to an API violation. This is only set when
+ the size of the data field of the Kinesis record
+ is zero.
+
+ This field is a member of `oneof`_ ``reason``.
+ """
+
+ stream_arn: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ partition_key: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ sequence_number: str = proto.Field(
+ proto.STRING,
+ number=3,
+ )
+ schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = (
+ proto.Field(
+ proto.MESSAGE,
+ number=4,
+ oneof="reason",
+ message="IngestionFailureEvent.SchemaViolationReason",
+ )
+ )
+ message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="reason",
+ message="IngestionFailureEvent.MessageTransformationFailureReason",
+ )
+ api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="reason",
+ message="IngestionFailureEvent.ApiViolationReason",
+ )
+
+ topic: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ error_message: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+ cloud_storage_failure: CloudStorageFailure = proto.Field(
+ proto.MESSAGE,
+ number=3,
+ oneof="failure",
+ message=CloudStorageFailure,
+ )
+ aws_msk_failure: AwsMskFailureReason = proto.Field(
+ proto.MESSAGE,
+ number=4,
+ oneof="failure",
+ message=AwsMskFailureReason,
+ )
+ azure_event_hubs_failure: AzureEventHubsFailureReason = proto.Field(
+ proto.MESSAGE,
+ number=5,
+ oneof="failure",
+ message=AzureEventHubsFailureReason,
+ )
+ confluent_cloud_failure: ConfluentCloudFailureReason = proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="failure",
+ message=ConfluentCloudFailureReason,
+ )
+ aws_kinesis_failure: AwsKinesisFailureReason = proto.Field(
+ proto.MESSAGE,
+ number=7,
+ oneof="failure",
+ message=AwsKinesisFailureReason,
+ )
+
+
+class JavaScriptUDF(proto.Message):
+ r"""User-defined JavaScript function that can transform or filter
+ a Pub/Sub message.
+
+ Attributes:
+ function_name (str):
+ Required. Name of the JavasScript function
+ that should applied to Pub/Sub messages.
+ code (str):
+ Required. JavaScript code that contains a function
+ ``function_name`` with the below signature:
+
+ ::
+
+ /**
+ * Transforms a Pub/Sub message.
+
+ * @return {(Object)>|null)} - To
+ * filter a message, return `null`. To transform a message return a map
+ * with the following keys:
+ * - (required) 'data' : {string}
+ * - (optional) 'attributes' : {Object}
+ * Returning empty `attributes` will remove all attributes from the
+ * message.
+ *
+ * @param {(Object)>} Pub/Sub
+ * message. Keys:
+ * - (required) 'data' : {string}
+ * - (required) 'attributes' : {Object}
+ *
+ * @param {Object} metadata - Pub/Sub message metadata.
+ * Keys:
+ * - (optional) 'message_id' : {string}
+ * - (optional) 'publish_time': {string} YYYY-MM-DDTHH:MM:SSZ format
+ * - (optional) 'ordering_key': {string}
+ */
+
+ function (message, metadata) {
+ }
+ """
+
+ function_name: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ code: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+
+
+class AIInference(proto.Message):
+ r"""Configuration for making inference requests against Vertex AI
+ models.
+
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ endpoint (str):
+ Required. An endpoint to a Vertex AI model of the form
+ ``projects/{project}/locations/{location}/endpoints/{endpoint}``
+ or
+ ``projects/{project}/locations/{location}/publishers/{publisher}/models/{model}``.
+ Vertex AI API requests will be sent to this endpoint.
+ unstructured_inference (google.pubsub_v1.types.AIInference.UnstructuredInference):
+ Optional. Requests and responses can be any
+ arbitrary JSON object.
+
+ This field is a member of `oneof`_ ``inference_mode``.
+ service_account_email (str):
+ Optional. The service account to use to make prediction
+ requests against endpoints. The resource creator or updater
+ that specifies this field must have
+ ``iam.serviceAccounts.actAs`` permission on the service
+ account. If not specified, the Pub/Sub `service
+ agent <{$universe.dns_names.final_documentation_domain}/iam/docs/service-agents>`__,
+ service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com,
+ is used.
+ """
+
+ class UnstructuredInference(proto.Message):
+ r"""Configuration for making inferences using arbitrary JSON
+ payloads.
+
+ Attributes:
+ parameters (google.protobuf.struct_pb2.Struct):
+ Optional. A parameters object to be included
+ in each inference request. The parameters object
+ is combined with the data field of the Pub/Sub
+ message to form the inference request.
+ """
+
+ parameters: struct_pb2.Struct = proto.Field(
+ proto.MESSAGE,
+ number=1,
+ message=struct_pb2.Struct,
+ )
+
+ endpoint: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ unstructured_inference: UnstructuredInference = proto.Field(
+ proto.MESSAGE,
+ number=2,
+ oneof="inference_mode",
+ message=UnstructuredInference,
+ )
+ service_account_email: str = proto.Field(
+ proto.STRING,
+ number=3,
+ )
+
+
+class MessageTransform(proto.Message):
+ r"""All supported message transforms types.
+
+ This message has `oneof`_ fields (mutually exclusive fields).
+ For each oneof, at most one member field can be set at the same time.
+ Setting any member of the oneof automatically clears all other
+ members.
+
+ .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields
+
+ Attributes:
+ javascript_udf (google.pubsub_v1.types.JavaScriptUDF):
+ Optional. JavaScript User Defined Function. If multiple
+ JavaScriptUDF's are specified on a resource, each must have
+ a unique ``function_name``.
+
+ This field is a member of `oneof`_ ``transform``.
+ ai_inference (google.pubsub_v1.types.AIInference):
+ Optional. AI Inference. Specifies the Vertex
+ AI endpoint that inference requests built from
+ the Pub/Sub message data and provided parameters
+ will be sent to.
+
+ This field is a member of `oneof`_ ``transform``.
+ enabled (bool):
+ Optional. This field is deprecated, use the ``disabled``
+ field to disable transforms.
+ disabled (bool):
+ Optional. If true, the transform is disabled and will not be
+ applied to messages. Defaults to ``false``.
+ """
+
+ javascript_udf: "JavaScriptUDF" = proto.Field(
+ proto.MESSAGE,
+ number=2,
+ oneof="transform",
+ message="JavaScriptUDF",
+ )
+ ai_inference: "AIInference" = proto.Field(
+ proto.MESSAGE,
+ number=6,
+ oneof="transform",
+ message="AIInference",
+ )
+ enabled: bool = proto.Field(
+ proto.BOOL,
+ number=3,
+ )
+ disabled: bool = proto.Field(
+ proto.BOOL,
+ number=4,
+ )
class Topic(proto.Message):
@@ -272,10 +1469,10 @@ class Topic(proto.Message):
Attributes:
name (str):
- Required. The name of the topic. It must have the format
- ``"projects/{project}/topics/{topic}"``. ``{topic}`` must
- start with a letter, and contain only letters
- (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``),
+ Required. Identifier. The name of the topic. It must have
+ the format ``"projects/{project}/topics/{topic}"``.
+ ``{topic}`` must start with a letter, and contain only
+ letters (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``),
underscores (``_``), periods (``.``), tildes (``~``), plus
(``+``) or percent signs (``%``). It must be between 3 and
255 characters in length, and it must not start with
@@ -319,6 +1516,21 @@ class Topic(proto.Message):
ingestion_data_source_settings (google.pubsub_v1.types.IngestionDataSourceSettings):
Optional. Settings for ingestion from a data
source into this topic.
+ message_transforms (MutableSequence[google.pubsub_v1.types.MessageTransform]):
+ Optional. Transforms to be applied to
+ messages published to the topic. Transforms are
+ applied in the order specified.
+ tags (MutableMapping[str, str]):
+ Optional. Input only. Immutable. Tag
+ keys/values directly bound to this resource. For
+ example:
+
+ "123/environment": "production",
+ "123/costCenter": "marketing"
+ See
+ https://docs.cloud.google.com/pubsub/docs/tags
+ for more information on using tags with Pub/Sub
+ resources.
"""
class State(proto.Enum):
@@ -382,6 +1594,16 @@ class State(proto.Enum):
number=10,
message="IngestionDataSourceSettings",
)
+ message_transforms: MutableSequence["MessageTransform"] = proto.RepeatedField(
+ proto.MESSAGE,
+ number=13,
+ message="MessageTransform",
+ )
+ tags: MutableMapping[str, str] = proto.MapField(
+ proto.STRING,
+ proto.STRING,
+ number=14,
+ )
class PubsubMessage(proto.Message):
@@ -755,8 +1977,8 @@ class Subscription(proto.Message):
Attributes:
name (str):
- Required. The name of the subscription. It must have the
- format
+ Required. Identifier. The name of the subscription. It must
+ have the format
``"projects/{project}/subscriptions/{subscription}"``.
``{subscription}`` must start with a letter, and contain
only letters (``[A-Za-z]``), numbers (``[0-9]``), dashes
@@ -821,7 +2043,7 @@ class Subscription(proto.Message):
published. If ``retain_acked_messages`` is true, then this
also configures the retention of acknowledged messages, and
thus configures how far back in time a ``Seek`` can be done.
- Defaults to 7 days. Cannot be more than 7 days or less than
+ Defaults to 7 days. Cannot be more than 31 days or less than
10 minutes.
labels (MutableMapping[str, str]):
Optional. See `Creating and managing
@@ -868,8 +2090,8 @@ class Subscription(proto.Message):
This generally implies that messages will be
retried as soon as possible for healthy
subscribers. RetryPolicy will be triggered on
- NACKs or acknowledgement deadline exceeded
- events for a given message.
+ NACKs or acknowledgment deadline exceeded events
+ for a given message.
detached (bool):
Optional. Indicates whether the subscription is detached
from its topic. Detached subscriptions don't receive
@@ -882,11 +2104,11 @@ class Subscription(proto.Message):
for the delivery of a message with a given value of
``message_id`` on this subscription:
- - The message sent to a subscriber is guaranteed not to be
- resent before the message's acknowledgement deadline
- expires.
- - An acknowledged message will not be resent to a
- subscriber.
+ - The message sent to a subscriber is guaranteed not to be
+ resent before the message's acknowledgment deadline
+ expires.
+ - An acknowledged message will not be resent to a
+ subscriber.
Note that subscribers may still receive multiple copies of a
message when ``enable_exactly_once_delivery`` is true if the
@@ -906,6 +2128,26 @@ class Subscription(proto.Message):
Output only. An output-only field indicating
whether or not the subscription can receive
messages.
+ analytics_hub_subscription_info (google.pubsub_v1.types.Subscription.AnalyticsHubSubscriptionInfo):
+ Output only. Information about the associated
+ Analytics Hub subscription. Only set if the
+ subscription is created by Analytics Hub.
+ message_transforms (MutableSequence[google.pubsub_v1.types.MessageTransform]):
+ Optional. Transforms to be applied to
+ messages before they are delivered to
+ subscribers. Transforms are applied in the order
+ specified.
+ tags (MutableMapping[str, str]):
+ Optional. Input only. Immutable. Tag
+ keys/values directly bound to this resource. For
+ example:
+
+ "123/environment": "production",
+ "123/costCenter": "marketing"
+ See
+ https://docs.cloud.google.com/pubsub/docs/tags
+ for more information on using tags with Pub/Sub
+ resources.
"""
class State(proto.Enum):
@@ -927,6 +2169,31 @@ class State(proto.Enum):
ACTIVE = 1
RESOURCE_ERROR = 2
+ class AnalyticsHubSubscriptionInfo(proto.Message):
+ r"""Information about an associated `Analytics Hub
+ subscription `__.
+
+ Attributes:
+ listing (str):
+ Optional. The name of the associated Analytics Hub listing
+ resource. Pattern:
+ "projects/{project}/locations/{location}/dataExchanges/{data_exchange}/listings/{listing}".
+ subscription (str):
+ Optional. The name of the associated
+ Analytics Hub subscription resource. Pattern:
+
+ "projects/{project}/locations/{location}/subscriptions/{subscription}".
+ """
+
+ listing: str = proto.Field(
+ proto.STRING,
+ number=1,
+ )
+ subscription: str = proto.Field(
+ proto.STRING,
+ number=2,
+ )
+
name: str = proto.Field(
proto.STRING,
number=1,
@@ -1009,6 +2276,21 @@ class State(proto.Enum):
number=19,
enum=State,
)
+ analytics_hub_subscription_info: AnalyticsHubSubscriptionInfo = proto.Field(
+ proto.MESSAGE,
+ number=23,
+ message=AnalyticsHubSubscriptionInfo,
+ )
+ message_transforms: MutableSequence["MessageTransform"] = proto.RepeatedField(
+ proto.MESSAGE,
+ number=25,
+ message="MessageTransform",
+ )
+ tags: MutableMapping[str, str] = proto.MapField(
+ proto.STRING,
+ proto.STRING,
+ number=26,
+ )
class RetryPolicy(proto.Message):
@@ -1017,7 +2299,7 @@ class RetryPolicy(proto.Message):
Retry delay will be exponential based on provided minimum and
maximum backoffs. https://en.wikipedia.org/wiki/Exponential_backoff.
- RetryPolicy will be triggered on NACKs or acknowledgement deadline
+ RetryPolicy will be triggered on NACKs or acknowledgment deadline
exceeded events for a given message.
Retry Policy is implemented on a best effort basis. At times, the
@@ -1077,7 +2359,7 @@ class DeadLetterPolicy(proto.Message):
message. The value must be between 5 and 100.
The number of delivery attempts is defined as 1 + (the sum
- of number of NACKs and number of times the acknowledgement
+ of number of NACKs and number of times the acknowledgment
deadline has been exceeded for the message).
A NACK is any call to ModifyAckDeadline with a 0 deadline.
@@ -1156,10 +2438,10 @@ class PushConfig(proto.Message):
The only supported values for the ``x-goog-version``
attribute are:
- - ``v1beta1``: uses the push format defined in the v1beta1
- Pub/Sub API.
- - ``v1`` or ``v1beta2``: uses the push format defined in
- the v1 Pub/Sub API.
+ - ``v1beta1``: uses the push format defined in the v1beta1
+ Pub/Sub API.
+ - ``v1`` or ``v1beta2``: uses the push format defined in the
+ v1 Pub/Sub API.
For example: ``attributes { "x-goog-version": "v1" }``
oidc_token (google.pubsub_v1.types.PushConfig.OidcToken):
@@ -1325,12 +2607,11 @@ class State(proto.Enum):
Cannot write to the BigQuery table because of permission
denied errors. This can happen if
- - Pub/Sub SA has not been granted the `appropriate BigQuery
- IAM
- permissions `__
- - bigquery.googleapis.com API is not enabled for the
- project
- (`instructions `__)
+ - Pub/Sub SA has not been granted the `appropriate BigQuery
+ IAM
+ permissions `__
+ - bigquery.googleapis.com API is not enabled for the project
+ (`instructions `__)
NOT_FOUND (3):
Cannot write to the BigQuery table because it
does not exist.
@@ -1341,6 +2622,10 @@ class State(proto.Enum):
Cannot write to the destination because enforce_in_transit
is set to true and the destination locations are not in the
allowed regions.
+ VERTEX_AI_LOCATION_RESTRICTION (6):
+ Cannot write to the BigQuery table because the table is not
+ in the same location as where Vertex AI models used in
+ ``message_transform``\ s are deployed.
"""
STATE_UNSPECIFIED = 0
ACTIVE = 1
@@ -1348,6 +2633,7 @@ class State(proto.Enum):
NOT_FOUND = 3
SCHEMA_MISMATCH = 4
IN_TRANSIT_LOCATION_RESTRICTION = 5
+ VERTEX_AI_LOCATION_RESTRICTION = 6
table: str = proto.Field(
proto.STRING,
@@ -1426,7 +2712,7 @@ class CloudStorageConfig(proto.Message):
elapse before a new Cloud Storage file is
created. Min 1 minute, max 10 minutes, default 5
minutes. May not exceed the subscription's
- acknowledgement deadline.
+ acknowledgment deadline.
max_bytes (int):
Optional. The maximum bytes that can be written to a Cloud
Storage file before a new file is created. Min 1 KB, max 10
@@ -1474,6 +2760,10 @@ class State(proto.Enum):
Cannot write to the Cloud Storage bucket due
to an incompatibility between the topic schema
and subscription settings.
+ VERTEX_AI_LOCATION_RESTRICTION (6):
+ Cannot write to the Cloud Storage bucket because the bucket
+ is not in the same location as where Vertex AI models used
+ in ``message_transform``\ s are deployed.
"""
STATE_UNSPECIFIED = 0
ACTIVE = 1
@@ -1481,6 +2771,7 @@ class State(proto.Enum):
NOT_FOUND = 3
IN_TRANSIT_LOCATION_RESTRICTION = 4
SCHEMA_MISMATCH = 5
+ VERTEX_AI_LOCATION_RESTRICTION = 6
class TextConfig(proto.Message):
r"""Configuration for writing message data in text format.
@@ -1878,7 +3169,7 @@ class AcknowledgeRequest(proto.Message):
class StreamingPullRequest(proto.Message):
r"""Request for the ``StreamingPull`` streaming RPC method. This request
is used to establish the initial stream as well as to stream
- acknowledgements and ack deadline modifications from the client to
+ acknowledgments and ack deadline modifications from the client to
the server.
Attributes:
@@ -1889,12 +3180,12 @@ class StreamingPullRequest(proto.Message):
client to server. Format is
``projects/{project}/subscriptions/{sub}``.
ack_ids (MutableSequence[str]):
- Optional. List of acknowledgement IDs for acknowledging
+ Optional. List of acknowledgment IDs for acknowledging
previously received messages (received on this stream or a
different stream). If an ack ID has expired, the
corresponding message may be redelivered later.
Acknowledging a message more than once will not result in an
- error. If the acknowledgement ID is malformed, the stream
+ error. If the acknowledgment ID is malformed, the stream
will be aborted with status ``INVALID_ARGUMENT``.
modify_deadline_seconds (MutableSequence[int]):
Optional. The list of new ack deadlines for the IDs listed
@@ -1912,7 +3203,7 @@ class StreamingPullRequest(proto.Message):
request. If the value is < 0 (an error), the stream will be
aborted with status ``INVALID_ARGUMENT``.
modify_deadline_ack_ids (MutableSequence[str]):
- Optional. List of acknowledgement IDs whose deadline will be
+ Optional. List of acknowledgment IDs whose deadline will be
modified based on the corresponding element in
``modify_deadline_seconds``. This field can be used to
indicate that more time is needed to process a message by
@@ -1958,6 +3249,11 @@ class StreamingPullRequest(proto.Message):
only be set on the initial StreamingPullRequest. If it is
set on a subsequent request, the stream will be aborted with
status ``INVALID_ARGUMENT``.
+ protocol_version (int):
+ Optional. The protocol version used by the client. This
+ property can only be set on the initial
+ StreamingPullRequest. If it is set on a subsequent request,
+ the stream will be aborted with status ``INVALID_ARGUMENT``.
"""
subscription: str = proto.Field(
@@ -1992,6 +3288,10 @@ class StreamingPullRequest(proto.Message):
proto.INT64,
number=8,
)
+ protocol_version: int = proto.Field(
+ proto.INT64,
+ number=10,
+ )
class StreamingPullResponse(proto.Message):
@@ -2000,36 +3300,37 @@ class StreamingPullResponse(proto.Message):
Attributes:
received_messages (MutableSequence[google.pubsub_v1.types.ReceivedMessage]):
- Optional. Received Pub/Sub messages. This
- will not be empty.
+ Optional. Received Pub/Sub messages.
acknowledge_confirmation (google.pubsub_v1.types.StreamingPullResponse.AcknowledgeConfirmation):
Optional. This field will only be set if
- ``enable_exactly_once_delivery`` is set to ``true``.
+ ``enable_exactly_once_delivery`` is set to ``true`` and is
+ not guaranteed to be populated.
modify_ack_deadline_confirmation (google.pubsub_v1.types.StreamingPullResponse.ModifyAckDeadlineConfirmation):
Optional. This field will only be set if
- ``enable_exactly_once_delivery`` is set to ``true``.
+ ``enable_exactly_once_delivery`` is set to ``true`` and is
+ not guaranteed to be populated.
subscription_properties (google.pubsub_v1.types.StreamingPullResponse.SubscriptionProperties):
Optional. Properties associated with this
subscription.
"""
class AcknowledgeConfirmation(proto.Message):
- r"""Acknowledgement IDs sent in one or more previous requests to
+ r"""Acknowledgment IDs sent in one or more previous requests to
acknowledge a previously received message.
Attributes:
ack_ids (MutableSequence[str]):
Optional. Successfully processed
- acknowledgement IDs.
+ acknowledgment IDs.
invalid_ack_ids (MutableSequence[str]):
- Optional. List of acknowledgement IDs that
- were malformed or whose acknowledgement deadline
+ Optional. List of acknowledgment IDs that
+ were malformed or whose acknowledgment deadline
has expired.
unordered_ack_ids (MutableSequence[str]):
- Optional. List of acknowledgement IDs that
+ Optional. List of acknowledgment IDs that
were out of order.
temporary_failed_ack_ids (MutableSequence[str]):
- Optional. List of acknowledgement IDs that
+ Optional. List of acknowledgment IDs that
failed processing with temporary issues.
"""
@@ -2051,19 +3352,19 @@ class AcknowledgeConfirmation(proto.Message):
)
class ModifyAckDeadlineConfirmation(proto.Message):
- r"""Acknowledgement IDs sent in one or more previous requests to
+ r"""Acknowledgment IDs sent in one or more previous requests to
modify the deadline for a specific message.
Attributes:
ack_ids (MutableSequence[str]):
Optional. Successfully processed
- acknowledgement IDs.
+ acknowledgment IDs.
invalid_ack_ids (MutableSequence[str]):
- Optional. List of acknowledgement IDs that
- were malformed or whose acknowledgement deadline
+ Optional. List of acknowledgment IDs that
+ were malformed or whose acknowledgment deadline
has expired.
temporary_failed_ack_ids (MutableSequence[str]):
- Optional. List of acknowledgement IDs that
+ Optional. List of acknowledgment IDs that
failed processing with temporary issues.
"""
@@ -2149,6 +3450,17 @@ class CreateSnapshotRequest(proto.Message):
labels (MutableMapping[str, str]):
Optional. See `Creating and managing
labels `__.
+ tags (MutableMapping[str, str]):
+ Optional. Input only. Immutable. Tag
+ keys/values directly bound to this resource. For
+ example:
+
+ "123/environment": "production",
+ "123/costCenter": "marketing"
+ See
+ https://docs.cloud.google.com/pubsub/docs/tags
+ for more information on using tags with Pub/Sub
+ resources.
"""
name: str = proto.Field(
@@ -2164,6 +3476,11 @@ class CreateSnapshotRequest(proto.Message):
proto.STRING,
number=3,
)
+ tags: MutableMapping[str, str] = proto.MapField(
+ proto.STRING,
+ proto.STRING,
+ number=4,
+ )
class UpdateSnapshotRequest(proto.Message):
diff --git a/google/pubsub_v1/types/schema.py b/google/pubsub_v1/types/schema.py
index 9353e4817..e1f376ed9 100644
--- a/google/pubsub_v1/types/schema.py
+++ b/google/pubsub_v1/types/schema.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/librarian.py b/librarian.py
new file mode 100644
index 000000000..ecaa36d0e
--- /dev/null
+++ b/librarian.py
@@ -0,0 +1,360 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+from pathlib import Path
+import re
+import shutil
+import textwrap
+
+import synthtool as s
+import synthtool.gcp as gcp
+from synthtool.languages import python
+
+# ----------------------------------------------------------------------------
+# Copy the generated client from the owl-bot staging directory
+# ----------------------------------------------------------------------------
+
+clean_up_generated_samples = True
+
+# Load the default version defined in .repo-metadata.json.
+default_version = json.load(open(".repo-metadata.json", "rt")).get(
+ "default_version"
+)
+
+for library in s.get_staging_dirs(default_version):
+ if clean_up_generated_samples:
+ shutil.rmtree("samples/generated_samples", ignore_errors=True)
+ clean_up_generated_samples = False
+
+ # DEFAULT SCOPES and SERVICE_ADDRESS are being used. so let's force them in.
+ s.replace(
+ library / f"google/pubsub_{library.name}/services/*er/*client.py",
+ r"""DEFAULT_ENDPOINT = \"pubsub\.googleapis\.com\"""",
+ """
+ # The scopes needed to make gRPC calls to all of the methods defined in
+ # this service
+ _DEFAULT_SCOPES = (
+ 'https://www.googleapis.com/auth/cloud-platform',
+ 'https://www.googleapis.com/auth/pubsub',
+ )
+
+ SERVICE_ADDRESS = "pubsub.googleapis.com:443"
+ \"""The default address of the service.\"""
+
+ \g<0>""",
+ )
+
+ # Modify GRPC options in transports.
+ count = s.replace(
+ [
+ library / f"google/pubsub_{library.name}/services/*/transports/grpc*",
+ library / f"tests/unit/gapic/pubsub_{library.name}/*",
+ ],
+ "options=\[.*?\]",
+ """options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ("grpc.max_metadata_size", 4 * 1024 * 1024),
+ ("grpc.keepalive_time_ms", 30000),
+ ]""",
+ flags=re.MULTILINE | re.DOTALL,
+ )
+
+ if count < 15:
+ raise Exception("Expected replacements for gRPC channel options not made.")
+
+ # If the emulator is used, force an insecure gRPC channel to avoid SSL errors.
+ clients_to_patch = [
+ library / f"google/pubsub_{library.name}/services/publisher/client.py",
+ library / f"google/pubsub_{library.name}/services/subscriber/client.py",
+ library / f"google/pubsub_{library.name}/services/schema_service/client.py",
+ ]
+ err_msg = (
+ "Expected replacements for gRPC channel to use with the emulator not made."
+ )
+
+ count = s.replace(clients_to_patch, r"import os", "import functools\n\g<0>")
+
+ if count < len(clients_to_patch):
+ raise Exception(err_msg)
+
+ count = s.replace(
+ clients_to_patch,
+ f"from \.transports\.base",
+ "\nimport grpc\n\g<0>",
+ )
+
+ if count < len(clients_to_patch):
+ raise Exception(err_msg)
+
+ # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator
+ # code below to test files.
+ count = s.replace(
+ clients_to_patch,
+ r"# initialize with the provided callable or the passed in class",
+ """\g<0>
+
+ emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST")
+ if emulator_host:
+ if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore
+ channel = grpc.insecure_channel(target=emulator_host)
+ else:
+ channel = grpc.aio.insecure_channel(target=emulator_host)
+ transport_init = functools.partial(transport_init, channel=channel)
+
+ """,
+ )
+
+ if count < len(clients_to_patch):
+ raise Exception(err_msg)
+
+ # Monkey patch the streaming_pull() GAPIC method to disable pre-fetching stream
+ # results.
+ s.replace(
+ library / f"google/pubsub_{library.name}/services/subscriber/client.py",
+ (
+ r"# Wrap the RPC method.*\n"
+ r"\s+# and friendly error.*\n"
+ r"\s+rpc = self\._transport\._wrapped_methods\[self\._transport\.streaming_pull\]"
+ ),
+ """
+ # Wrappers in api-core should not automatically pre-fetch the first
+ # stream result, as this breaks the stream when re-opening it.
+ # https://github.com/googleapis/python-pubsub/issues/93#issuecomment-630762257
+ self._transport.streaming_pull._prefetch_first_result_ = False
+
+ \g<0>""",
+ )
+
+ # Emit deprecation warning if return_immediately flag is set with synchronous pull.
+ s.replace(
+ library / f"google/pubsub_{library.name}/services/subscriber/*client.py",
+ r"from google.pubsub_v1 import gapic_version as package_version",
+ "import warnings\n\g<0>",
+ )
+
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/services/subscriber/*client.py",
+ r"""
+ ([^\n\S]+(?:async\ )?def\ pull\(.*?->\ pubsub\.PullResponse:.*?)
+ ((?P[^\n\S]+)\#\ Wrap\ the\ RPC\ method)
+ """,
+ textwrap.dedent(
+ """
+ \g<1>
+ \gif request.return_immediately:
+ \g warnings.warn(
+ \g "The return_immediately flag is deprecated and should be set to False.",
+ \g category=DeprecationWarning,
+ \g )
+
+ \g<2>"""
+ ),
+ flags=re.MULTILINE | re.DOTALL | re.VERBOSE,
+ )
+
+ if count != 2:
+ raise Exception("Too many or too few replacements in pull() methods.")
+
+ # Silence deprecation warnings in pull() method flattened parameter tests.
+ s.replace(
+ library / f"tests/unit/gapic/pubsub_{library.name}/test_subscriber.py",
+ "import os",
+ "\g<0>\nimport warnings",
+ )
+
+ count = s.replace(
+ library / f"tests/unit/gapic/pubsub_{library.name}/test_subscriber.py",
+ textwrap.dedent(
+ r"""
+ ([^\n\S]+# Call the method with a truthy value for each flattened field,
+ [^\n\S]+# using the keyword arguments to the method\.)
+ \s+(client\.pull\(.*?\))"""
+ ),
+ """\n\g<1>
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", category=DeprecationWarning)
+ \g<2>""",
+ flags=re.MULTILINE | re.DOTALL,
+ )
+
+ if count < 1:
+ raise Exception("Catch warnings replacement failed.")
+
+ count = s.replace(
+ library / f"tests/unit/gapic/pubsub_{library.name}/test_subscriber.py",
+ textwrap.dedent(
+ r"""
+ ([^\n\S]+# Call the method with a truthy value for each flattened field,
+ [^\n\S]+# using the keyword arguments to the method\.)
+ \s+response = (await client\.pull\(.*?\))"""
+ ),
+ """\n\g<1>
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", category=DeprecationWarning)
+ \g<2>""",
+ flags=re.MULTILINE | re.DOTALL,
+ )
+
+ if count < 1:
+ raise Exception("Catch warnings replacement failed.")
+
+ # Make sure that client library version is present in user agent header.
+ count = s.replace(
+ [
+ library
+ / f"google/pubsub_{library.name}/services/publisher/async_client.py",
+ library / f"google/pubsub_{library.name}/services/publisher/client.py",
+ library
+ / f"google/pubsub_{library.name}/services/publisher/transports/base.py",
+ library
+ / f"google/pubsub_{library.name}/services/schema_service/async_client.py",
+ library / f"google/pubsub_{library.name}/services/schema_service/client.py",
+ library
+ / f"google/pubsub_{library.name}/services/schema_service/transports/base.py",
+ library
+ / f"google/pubsub_{library.name}/services/subscriber/async_client.py",
+ library / f"google/pubsub_{library.name}/services/subscriber/client.py",
+ library
+ / f"google/pubsub_{library.name}/services/subscriber/transports/base.py",
+ ],
+ r"""gapic_version=package_version.__version__""",
+ "client_library_version=package_version.__version__",
+ )
+
+ if count < 1:
+ raise Exception("client_library_version replacement failed.")
+
+ # Allow timeout to be an instance of google.api_core.timeout.*
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/types/__init__.py",
+ r"from \.pubsub import \(",
+ "from typing import Union\n\n\g<0>",
+ )
+
+ if count < 1:
+ raise Exception("Catch timeout replacement 1 failed.")
+
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/types/__init__.py",
+ r"__all__ = \(\n",
+ textwrap.dedent(
+ '''\
+ TimeoutType = Union[
+ int,
+ float,
+ "google.api_core.timeout.ConstantTimeout",
+ "google.api_core.timeout.ExponentialTimeout",
+ ]
+ """The type of the timeout parameter of publisher client methods."""
+
+ \g<0> "TimeoutType",'''
+ ),
+ )
+
+ if count < 1:
+ raise Exception("Catch timeout replacement 2 failed.")
+
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/services/publisher/*client.py",
+ r"from google.api_core import retry as retries.*\n",
+ "\g<0>from google.api_core import timeout as timeouts # type: ignore\n",
+ )
+
+ if count < 1:
+ raise Exception("Catch timeout replacement 3 failed.")
+
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/services/publisher/*client.py",
+ f"from google\.pubsub_{library.name}\.types import pubsub",
+ f"\g<0>\nfrom google.pubsub_{library.name}.types import TimeoutType",
+ )
+
+ if count < 1:
+ raise Exception("Catch timeout replacement 4 failed.")
+
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/services/publisher/*client.py",
+ r"(\s+)timeout: Union\[float, object\] = gapic_v1.method.DEFAULT.*\n",
+ f"\g<1>timeout: TimeoutType = gapic_{library.name}.method.DEFAULT,",
+ )
+
+ if count < 1:
+ raise Exception("Catch timeout replacement 5 failed.")
+
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/services/publisher/*client.py",
+ r"([^\S\r\n]+)timeout \(float\): (.*)\n",
+ ("\g<1>timeout (TimeoutType):\n" "\g<1> \g<2>\n"),
+ )
+
+ if count < 1:
+ raise Exception("Catch timeout replacement 6 failed.")
+
+ # Override the default max retry deadline for publisher methods.
+ count = s.replace(
+ library / f"google/pubsub_{library.name}/services/publisher/transports/base.py",
+ r"deadline=60\.0",
+ "deadline=600.0",
+ )
+ if count < 9:
+ raise Exception(
+ "Default retry deadline not overriden for all publisher methods."
+ )
+
+ # The namespace package declaration in google/cloud/__init__.py should be excluded
+ # from coverage.
+ count = s.replace(
+ library / ".coveragerc",
+ "google/pubsub/__init__.py",
+ """google/cloud/__init__.py
+ google/pubsub/__init__.py""",
+ )
+
+ if count < 1:
+ raise Exception(".coveragerc replacement failed.")
+
+ s.move(
+ [library],
+ excludes=[
+ "noxfile.py",
+ "README.rst",
+ "docs/**/*",
+ "setup.py",
+ ],
+ )
+s.remove_staging_dirs()
+
+# ----------------------------------------------------------------------------
+# Add templated files
+# ----------------------------------------------------------------------------
+
+templated_files = gcp.CommonTemplates().py_library(
+ microgenerator=True,
+ samples=True,
+ cov_level=99,
+ versions=gcp.common.detect_versions(path="./google", default_first=True),
+ unit_test_python_versions=["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"],
+ unit_test_dependencies=["flaky"],
+ system_test_python_versions=["3.12"],
+ system_test_external_dependencies=["psutil", "flaky"],
+)
+s.move(templated_files, excludes=[".coveragerc", ".github/**", "README.rst", "docs/**", ".kokoro/**"])
+
+python.py_samples(skip_readmes=True)
+
+# run format session for all directories which have a noxfile
+for noxfile in Path(".").glob("**/noxfile.py"):
+ s.shell.run(["nox", "-s", "blacken"], cwd=noxfile.parent, hide_output=False)
diff --git a/mypy.ini b/mypy.ini
index 574c5aed3..a3cb5c292 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,3 +1,3 @@
[mypy]
-python_version = 3.7
+python_version = 3.14
namespace_packages = True
diff --git a/noxfile.py b/noxfile.py
index c6d0c11a3..d7fa4a7e8 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -14,6 +14,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# DO NOT EDIT THIS FILE OUTSIDE OF `.librarian/generator-input`
+# The source of truth for this file is `.librarian/generator-input`
+
+
# Generated by synthtool. DO NOT EDIT!
from __future__ import absolute_import
@@ -30,13 +34,20 @@
FLAKE8_VERSION = "flake8==6.1.0"
BLACK_VERSION = "black[jupyter]==23.7.0"
ISORT_VERSION = "isort==5.11.0"
-LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"]
+LINT_PATHS = ["google", "tests", "noxfile.py", "setup.py"]
MYPY_VERSION = "mypy==1.10.0"
-DEFAULT_PYTHON_VERSION = "3.8"
+DEFAULT_PYTHON_VERSION = "3.14"
-UNIT_TEST_PYTHON_VERSIONS: List[str] = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
+UNIT_TEST_PYTHON_VERSIONS: List[str] = [
+ "3.9",
+ "3.10",
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+]
UNIT_TEST_STANDARD_DEPENDENCIES = [
"mock",
"asyncmock",
@@ -46,7 +57,9 @@
]
UNIT_TEST_EXTERNAL_DEPENDENCIES: List[str] = []
UNIT_TEST_LOCAL_DEPENDENCIES: List[str] = []
-UNIT_TEST_DEPENDENCIES: List[str] = []
+UNIT_TEST_DEPENDENCIES: List[str] = [
+ "flaky",
+]
UNIT_TEST_EXTRAS: List[str] = []
UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {}
@@ -67,7 +80,6 @@
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
-# 'docfx' is excluded since it only needs to run in 'docs-presubmit'
nox.options.sessions = [
"unit",
"system",
@@ -79,6 +91,7 @@
# https://github.com/googleapis/python-pubsub/pull/552#issuecomment-1016256936
# "mypy_samples", # TODO: uncomment when the check passes
"docs",
+ "docfx",
"format",
]
@@ -95,8 +108,7 @@ def mypy(session):
# Version 2.1.1 of google-api-core version is the first type-checked release.
# Version 2.2.0 of google-cloud-core version is the first type-checked release.
session.install(
- "google-api-core[grpc]>=2.1.1",
- "google-cloud-core>=2.2.0",
+ "google-api-core[grpc]>=2.1.1", "google-cloud-core>=2.2.0", "types-requests"
)
# Just install the type info directly, since "mypy --install-types" might
@@ -108,7 +120,8 @@ def mypy(session):
# TODO: Only check the hand-written layer, the generated code does not pass
# mypy checks yet.
# https://github.com/googleapis/gapic-generator-python/issues/1092
- session.run("mypy", "-p", "google.cloud")
+ # TODO: Re-enable mypy checks once we merge, since incremental checks are failing due to protobuf upgrade
+ # session.run("mypy", "-p", "google.cloud", "--exclude", "google/pubsub_v1/")
@nox.session(python=DEFAULT_PYTHON_VERSION)
@@ -122,7 +135,9 @@ def mypy_samples(session):
# Just install the type info directly, since "mypy --install-types" might
# require an additional pass.
- session.install("types-mock", "types-protobuf", "types-setuptools")
+ session.install(
+ "types-mock", "types-protobuf", "types-setuptools", "types-requests"
+ )
session.run(
"mypy",
@@ -182,7 +197,7 @@ def format(session):
@nox.session(python=DEFAULT_PYTHON_VERSION)
def lint_setup_py(session):
"""Verify that setup.py is valid (including RST check)."""
- session.install("docutils", "pygments")
+ session.install("setuptools", "docutils", "pygments")
session.run("python", "setup.py", "check", "--restructuredtext", "--strict")
@@ -222,7 +237,12 @@ def install_unittest_dependencies(session, *constraints):
def unit(session, protobuf_implementation):
# Install all test dependencies, then install this package in-place.
- if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12"):
+ if protobuf_implementation == "cpp" and session.python in (
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+ ):
session.skip("cpp implementation is not supported in python 3.11+")
constraints_path = str(
@@ -313,15 +333,15 @@ def system(session):
if system_test_exists:
session.run(
"py.test",
- "--quiet",
+ "--verbose",
f"--junitxml=system_{session.python}_sponge_log.xml",
system_test_path,
*session.posargs,
)
- if system_test_folder_exists:
+ if os.path.exists(system_test_folder_path):
session.run(
"py.test",
- "--quiet",
+ "--verbose",
f"--junitxml=system_{session.python}_sponge_log.xml",
system_test_folder_path,
*session.posargs,
@@ -336,11 +356,12 @@ def cover(session):
test runs (not system test runs), and then erases coverage data.
"""
session.install("coverage", "pytest-cov")
- session.run("coverage", "report", "--show-missing", "--fail-under=100")
+ session.run("coverage", "report", "--show-missing", "--fail-under=99")
session.run("coverage", "erase")
+# py > 3.10 not supported yet
@nox.session(python="3.10")
def docs(session):
"""Build the docs for this library."""
@@ -376,6 +397,7 @@ def docs(session):
)
+# py > 3.10 not supported yet
@nox.session(python="3.10")
def docfx(session):
"""Build the docfx yaml files for this library."""
@@ -422,7 +444,7 @@ def docfx(session):
)
-@nox.session(python="3.12")
+@nox.session(python="3.14")
@nox.parametrize(
"protobuf_implementation",
["python", "upb", "cpp"],
@@ -430,7 +452,12 @@ def docfx(session):
def prerelease_deps(session, protobuf_implementation):
"""Run all tests with prerelease versions of dependencies installed."""
- if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12"):
+ if protobuf_implementation == "cpp" and session.python in (
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+ ):
session.skip("cpp implementation is not supported in python 3.11+")
# Install all dependencies
diff --git a/pytest.ini b/pytest.ini
index 34f393316..41cad40d6 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -6,8 +6,6 @@ filterwarnings =
ignore:.*custom tp_new.*in Python 3.14:DeprecationWarning
# Remove once https://github.com/grpc/grpc/issues/35086 is fixed
ignore:There is no current event loop:DeprecationWarning:grpc.aio._channel
- # Remove after support for Python 3.7 is dropped
- ignore:After January 1, 2024, new releases of this library will drop support for Python 3.7:DeprecationWarning
# Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1938 is fixed
ignore:The return_immediately flag is deprecated and should be set to False.:DeprecationWarning
# Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1939 is fixed
@@ -18,5 +16,13 @@ filterwarnings =
# Remove once the minimum supported version of googleapis-common-protos is 1.62.0
ignore:.*pkg_resources.declare_namespace:DeprecationWarning
ignore:.*pkg_resources is deprecated as an API:DeprecationWarning
- # Remove once https://github.com/googleapis/python-pubsub/issues/1206 is fixed.
- ignore:.*connections\(\) is deprecated and will be removed; use net_connections\(\) instead:DeprecationWarning
\ No newline at end of file
+ # Remove once https://github.com/googleapis/gapic-generator-python/issues/2303 is fixed
+ ignore:The python-bigquery library will stop supporting Python 3.7:PendingDeprecationWarning
+ # Remove once we move off credential files https://github.com/googleapis/google-auth-library-python/pull/1812
+ # Note that these are used in tests only
+ ignore:Your config file at [/home/kbuilder/.docker/config.json] contains these credential helper entries:DeprecationWarning
+ ignore:The `credentials_file` argument is deprecated because of a potential security risk:DeprecationWarning
+ ignore:You are using a Python version.*which Google will stop supporting in new releases of google\.api_core.*:FutureWarning
+ ignore:You are using a non-supported Python version \(([\d\.]+)\)\. Google will not post any further updates to google\.api_core.*:FutureWarning
+ # These google library EOL warnings for Python versions don't matter for the purposes of a test.
+ ignore::FutureWarning:google.*:
diff --git a/release-please-config.json b/release-please-config.json
deleted file mode 100644
index 909352415..000000000
--- a/release-please-config.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
- "packages": {
- ".": {
- "release-type": "python",
- "extra-files": [
- "google/pubsub/gapic_version.py",
- "google/pubsub_v1/gapic_version.py",
- {
- "type": "json",
- "path": "samples/generated_samples/snippet_metadata_google.pubsub.v1.json",
- "jsonpath": "$.clientLibrary.version"
- }
- ]
- }
- },
- "release-type": "python",
- "plugins": [
- {
- "type": "sentence-case"
- }
- ],
- "initial-version": "0.1.0"
-}
-
\ No newline at end of file
diff --git a/renovate.json b/renovate.json
index 39b2a0ec9..c7875c469 100644
--- a/renovate.json
+++ b/renovate.json
@@ -5,7 +5,7 @@
":preserveSemverRanges",
":disableDependencyDashboard"
],
- "ignorePaths": [".pre-commit-config.yaml", ".kokoro/requirements.txt", "setup.py"],
+ "ignorePaths": [".pre-commit-config.yaml", ".kokoro/requirements.txt", "setup.py", ".github/workflows/unittest.yml"],
"pip_requirements": {
"fileMatch": ["requirements-test.txt", "samples/[\\S/]*constraints.txt", "samples/[\\S/]*constraints-test.txt"]
}
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py
index 5694b24ef..e1bf1f2c1 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py
index 7de319c67..941fea1d4 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py
index 3d2b74803..2fad1b099 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py
index e760eddbd..27b58c27a 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py
index 86508954b..22fb9e7e6 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py
index af6b45837..058c10e73 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py
index a8bea68c2..a8de7a307 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py
index 051b98093..d2846a750 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py
index 4eed18b38..e8a3e2e8d 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py
index 1e2266757..3a51a39b8 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py
index 959f1e824..cbc81e48f 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py
index c648b8d1a..dee0821cd 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py
index dc4a36ef8..0fc18583a 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py
index 8d71be398..2d2a987ee 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py
index 5c6b63a2d..536b7f099 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py
index 9af236d44..e89f90320 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py
index d9451b624..a814eab54 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py
index 9b7326c2c..46c967e4e 100644
--- a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py
index b93ec0b5d..e24d459c8 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py
index c7caa1cbb..d3be03abe 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py
index 9cdd164f5..7eaf44f44 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py
index ce8619977..da7cf76c9 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py
index 1bbebfff5..6fffc7395 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py
index a35c3af1b..fa37387cd 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py
index 118fe7340..4d1ac5e19 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py
index 77dfe887b..64640ba16 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py
index 240df5a2a..feb39e86e 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py
index 2d45faebf..cf387dbcf 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py
index 7ea512001..9c2f61ad4 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py
index 6aebac042..08b49520c 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py
index 963618fa9..7d88f3194 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py
index d7b9e6464..776abc3d4 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py
index b50a08b18..66628743c 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py
index 4d83c8cac..2a5d2687d 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py
index 3dcd25490..127b90fec 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py
index 4f509d6b3..08e3b9142 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py
index 5deb08a44..5cdc6072d 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py
index cc7826792..af9792f1e 100644
--- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py
index 7fc2967d5..37ea78fa1 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py
index 85b492df0..80cc79a64 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py
index 3f7c691da..f1084952b 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py
index 7738b03da..207b31599 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py
index 306218036..64a7f134d 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py
index cbb05f62a..7efb7a912 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py
index 5bea031a7..b92fab270 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py
index 01342901b..dd7533eaf 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py
index 08a15f4ed..12c85f95e 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py
index 1cde73a72..c9285d87e 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py
index 40960acc7..fd22fe023 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py
index 3ee2e9c62..a027bcddf 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py
index 47c739998..12eabdec4 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py
index 22832bc89..13b7ea626 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py
index bcc3be15d..0d3698773 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py
index 3477e32d3..4568bef48 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py
index 7b00831a3..b7811265a 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py
index 8e1a96487..5bdc68dd5 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py
index 8e9372292..4492740cd 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py
index 1d32afec2..d198d4bab 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py
index 1d88c9590..155db77c6 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py
index 43ac23e4f..bca872f9d 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py
index 3e6f26a0b..d351f26cf 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py
index 950f18cc1..e11007592 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py
index 9038bfd11..b5eab9a46 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py
index b57af8fb0..8a0063f66 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py
index 332c12895..b2ecd899b 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py
index 47926a80a..2de009269 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py
index 833885af5..7aa873ec1 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py
index 1c8f8530d..7cb4af13e 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py
index 5c00a74dc..ed6a5512b 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py
index 5cc3de6ec..a592001ec 100644
--- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py
+++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json
index d66015ac4..2a423fd86 100644
--- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json
+++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json
@@ -8,7 +8,7 @@
],
"language": "PYTHON",
"name": "google-cloud-pubsub",
- "version": "0.1.0"
+ "version": "2.35.0"
},
"snippets": [
{
@@ -47,7 +47,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Topic",
@@ -127,7 +127,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Topic",
@@ -208,7 +208,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_topic"
@@ -285,7 +285,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_topic"
@@ -359,7 +359,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.DetachSubscriptionResponse",
@@ -435,7 +435,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.DetachSubscriptionResponse",
@@ -516,7 +516,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Topic",
@@ -596,7 +596,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Topic",
@@ -677,7 +677,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsAsyncPager",
@@ -757,7 +757,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsPager",
@@ -838,7 +838,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsAsyncPager",
@@ -918,7 +918,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsPager",
@@ -999,7 +999,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicsAsyncPager",
@@ -1079,7 +1079,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicsPager",
@@ -1164,7 +1164,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.PublishResponse",
@@ -1248,7 +1248,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.PublishResponse",
@@ -1333,7 +1333,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Topic",
@@ -1417,7 +1417,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Topic",
@@ -1502,7 +1502,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -1586,7 +1586,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -1675,7 +1675,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -1763,7 +1763,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -1848,7 +1848,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -1932,7 +1932,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -2013,7 +2013,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_schema"
@@ -2090,7 +2090,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_schema"
@@ -2168,7 +2168,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -2248,7 +2248,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -2329,7 +2329,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsAsyncPager",
@@ -2409,7 +2409,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsPager",
@@ -2490,7 +2490,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemasAsyncPager",
@@ -2570,7 +2570,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemasPager",
@@ -2655,7 +2655,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -2739,7 +2739,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Schema",
@@ -2816,7 +2816,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.ValidateMessageResponse",
@@ -2892,7 +2892,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.ValidateMessageResponse",
@@ -2977,7 +2977,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.ValidateSchemaResponse",
@@ -3061,7 +3061,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.ValidateSchemaResponse",
@@ -3146,7 +3146,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "acknowledge"
@@ -3227,7 +3227,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "acknowledge"
@@ -3309,7 +3309,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Snapshot",
@@ -3393,7 +3393,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Snapshot",
@@ -3486,7 +3486,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Subscription",
@@ -3578,7 +3578,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Subscription",
@@ -3659,7 +3659,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_snapshot"
@@ -3736,7 +3736,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_snapshot"
@@ -3814,7 +3814,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_subscription"
@@ -3891,7 +3891,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "delete_subscription"
@@ -3969,7 +3969,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Snapshot",
@@ -4049,7 +4049,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Snapshot",
@@ -4130,7 +4130,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Subscription",
@@ -4210,7 +4210,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Subscription",
@@ -4291,7 +4291,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.subscriber.pagers.ListSnapshotsAsyncPager",
@@ -4371,7 +4371,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.subscriber.pagers.ListSnapshotsPager",
@@ -4452,7 +4452,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsAsyncPager",
@@ -4532,7 +4532,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsPager",
@@ -4621,7 +4621,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "modify_ack_deadline"
@@ -4706,7 +4706,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "modify_ack_deadline"
@@ -4788,7 +4788,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "modify_push_config"
@@ -4869,7 +4869,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"shortName": "modify_push_config"
@@ -4955,7 +4955,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.PullResponse",
@@ -5043,7 +5043,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.PullResponse",
@@ -5120,7 +5120,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.SeekResponse",
@@ -5196,7 +5196,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.SeekResponse",
@@ -5273,7 +5273,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "Iterable[google.pubsub_v1.types.StreamingPullResponse]",
@@ -5349,7 +5349,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "Iterable[google.pubsub_v1.types.StreamingPullResponse]",
@@ -5434,7 +5434,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Snapshot",
@@ -5518,7 +5518,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Snapshot",
@@ -5603,7 +5603,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Subscription",
@@ -5687,7 +5687,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.pubsub_v1.types.Subscription",
diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py
index 3b7135946..50f2fce56 100644
--- a/samples/snippets/noxfile.py
+++ b/samples/snippets/noxfile.py
@@ -87,9 +87,8 @@ def get_pytest_env_vars() -> Dict[str, str]:
return ret
-# DO NOT EDIT - automatically generated.
# All versions used to test samples.
-ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
+ALL_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
# Any default versions that should be ignored.
IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"]
diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py
index 73afc8c97..13a70bee6 100644
--- a/samples/snippets/publisher.py
+++ b/samples/snippets/publisher.py
@@ -103,6 +103,262 @@ def create_topic_with_kinesis_ingestion(
# [END pubsub_create_topic_with_kinesis_ingestion]
+def create_topic_with_cloud_storage_ingestion(
+ project_id: str,
+ topic_id: str,
+ bucket: str,
+ input_format: str,
+ text_delimiter: str,
+ match_glob: str,
+ minimum_object_create_time: str,
+) -> None:
+ """Create a new Pub/Sub topic with Cloud Storage Ingestion Settings."""
+ # [START pubsub_create_topic_with_cloud_storage_ingestion]
+ from google.cloud import pubsub_v1
+ from google.protobuf import timestamp_pb2
+ from google.pubsub_v1.types import Topic
+ from google.pubsub_v1.types import IngestionDataSourceSettings
+
+ # TODO(developer)
+ # project_id = "your-project-id"
+ # topic_id = "your-topic-id"
+ # bucket = "your-bucket"
+ # input_format = "text" (can be one of "text", "avro", "pubsub_avro")
+ # text_delimiter = "\n"
+ # match_glob = "**.txt"
+ # minimum_object_create_time = "YYYY-MM-DDThh:mm:ssZ"
+
+ publisher = pubsub_v1.PublisherClient()
+ topic_path = publisher.topic_path(project_id, topic_id)
+
+ cloud_storage_settings = IngestionDataSourceSettings.CloudStorage(
+ bucket=bucket,
+ )
+ if input_format == "text":
+ cloud_storage_settings.text_format = (
+ IngestionDataSourceSettings.CloudStorage.TextFormat(
+ delimiter=text_delimiter
+ )
+ )
+ elif input_format == "avro":
+ cloud_storage_settings.avro_format = (
+ IngestionDataSourceSettings.CloudStorage.AvroFormat()
+ )
+ elif input_format == "pubsub_avro":
+ cloud_storage_settings.pubsub_avro_format = (
+ IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat()
+ )
+ else:
+ print(
+ "Invalid input_format: "
+ + input_format
+ + "; must be in ('text', 'avro', 'pubsub_avro')"
+ )
+ return
+
+ if match_glob:
+ cloud_storage_settings.match_glob = match_glob
+
+ if minimum_object_create_time:
+ try:
+ minimum_object_create_time_timestamp = timestamp_pb2.Timestamp()
+ minimum_object_create_time_timestamp.FromJsonString(
+ minimum_object_create_time
+ )
+ cloud_storage_settings.minimum_object_create_time = (
+ minimum_object_create_time_timestamp
+ )
+ except ValueError:
+ print("Invalid minimum_object_create_time: " + minimum_object_create_time)
+ return
+
+ request = Topic(
+ name=topic_path,
+ ingestion_data_source_settings=IngestionDataSourceSettings(
+ cloud_storage=cloud_storage_settings,
+ ),
+ )
+
+ topic = publisher.create_topic(request=request)
+
+ print(f"Created topic: {topic.name} with Cloud Storage Ingestion Settings")
+ # [END pubsub_create_topic_with_cloud_storage_ingestion]
+
+
+def create_topic_with_aws_msk_ingestion(
+ project_id: str,
+ topic_id: str,
+ cluster_arn: str,
+ msk_topic: str,
+ aws_role_arn: str,
+ gcp_service_account: str,
+) -> None:
+ """Create a new Pub/Sub topic with AWS MSK Ingestion Settings."""
+ # [START pubsub_create_topic_with_aws_msk_ingestion]
+ from google.cloud import pubsub_v1
+ from google.pubsub_v1.types import Topic
+ from google.pubsub_v1.types import IngestionDataSourceSettings
+
+ # TODO(developer)
+ # project_id = "your-project-id"
+ # topic_id = "your-topic-id"
+ # cluster_arn = "your-cluster-arn"
+ # msk_topic = "your-msk-topic"
+ # aws_role_arn = "your-aws-role-arn"
+ # gcp_service_account = "your-gcp-service-account"
+
+ publisher = pubsub_v1.PublisherClient()
+ topic_path = publisher.topic_path(project_id, topic_id)
+
+ request = Topic(
+ name=topic_path,
+ ingestion_data_source_settings=IngestionDataSourceSettings(
+ aws_msk=IngestionDataSourceSettings.AwsMsk(
+ cluster_arn=cluster_arn,
+ topic=msk_topic,
+ aws_role_arn=aws_role_arn,
+ gcp_service_account=gcp_service_account,
+ )
+ ),
+ )
+
+ topic = publisher.create_topic(request=request)
+
+ print(f"Created topic: {topic.name} with AWS MSK Ingestion Settings")
+ # [END pubsub_create_topic_with_aws_msk_ingestion]
+
+
+def create_topic_with_azure_event_hubs_ingestion(
+ project_id: str,
+ topic_id: str,
+ resource_group: str,
+ namespace: str,
+ event_hub: str,
+ client_id: str,
+ tenant_id: str,
+ subscription_id: str,
+ gcp_service_account: str,
+) -> None:
+ """Create a new Pub/Sub topic with Azure Event Hubs Ingestion Settings."""
+ # [START pubsub_create_topic_with_azure_event_hubs_ingestion]
+ from google.cloud import pubsub_v1
+ from google.pubsub_v1.types import Topic
+ from google.pubsub_v1.types import IngestionDataSourceSettings
+
+ # TODO(developer)
+ # project_id = "your-project-id"
+ # topic_id = "your-topic-id"
+ # resource_group = "your-resource-group"
+ # namespace = "your-namespace"
+ # event_hub = "your-event-hub"
+ # client_id = "your-client-id"
+ # tenant_id = "your-tenant-id"
+ # subscription_id = "your-subscription-id"
+ # gcp_service_account = "your-gcp-service-account"
+
+ publisher = pubsub_v1.PublisherClient()
+ topic_path = publisher.topic_path(project_id, topic_id)
+
+ request = Topic(
+ name=topic_path,
+ ingestion_data_source_settings=IngestionDataSourceSettings(
+ azure_event_hubs=IngestionDataSourceSettings.AzureEventHubs(
+ resource_group=resource_group,
+ namespace=namespace,
+ event_hub=event_hub,
+ client_id=client_id,
+ tenant_id=tenant_id,
+ subscription_id=subscription_id,
+ gcp_service_account=gcp_service_account,
+ )
+ ),
+ )
+
+ topic = publisher.create_topic(request=request)
+
+ print(f"Created topic: {topic.name} with Azure Event Hubs Ingestion Settings")
+ # [END pubsub_create_topic_with_azure_event_hubs_ingestion]
+
+
+def create_topic_with_confluent_cloud_ingestion(
+ project_id: str,
+ topic_id: str,
+ bootstrap_server: str,
+ cluster_id: str,
+ confluent_topic: str,
+ identity_pool_id: str,
+ gcp_service_account: str,
+) -> None:
+ """Create a new Pub/Sub topic with Confluent Cloud Ingestion Settings."""
+ # [START pubsub_create_topic_with_confluent_cloud_ingestion]
+ from google.cloud import pubsub_v1
+ from google.pubsub_v1.types import Topic
+ from google.pubsub_v1.types import IngestionDataSourceSettings
+
+ # TODO(developer)
+ # project_id = "your-project-id"
+ # topic_id = "your-topic-id"
+ # bootstrap_server = "your-bootstrap-server"
+ # cluster_id = "your-cluster-id"
+ # confluent_topic = "your-confluent-topic"
+ # identity_pool_id = "your-identity-pool-id"
+ # gcp_service_account = "your-gcp-service-account"
+
+ publisher = pubsub_v1.PublisherClient()
+ topic_path = publisher.topic_path(project_id, topic_id)
+
+ request = Topic(
+ name=topic_path,
+ ingestion_data_source_settings=IngestionDataSourceSettings(
+ confluent_cloud=IngestionDataSourceSettings.ConfluentCloud(
+ bootstrap_server=bootstrap_server,
+ cluster_id=cluster_id,
+ topic=confluent_topic,
+ identity_pool_id=identity_pool_id,
+ gcp_service_account=gcp_service_account,
+ )
+ ),
+ )
+
+ topic = publisher.create_topic(request=request)
+
+ print(f"Created topic: {topic.name} with Confluent Cloud Ingestion Settings")
+ # [END pubsub_create_topic_with_confluent_cloud_ingestion]
+
+
+def create_topic_with_smt(
+ project_id: str,
+ topic_id: str,
+) -> None:
+ """Create a new Pub/Sub topic with a UDF SMT."""
+ # [START pubsub_create_topic_with_smt]
+ from google.cloud import pubsub_v1
+ from google.pubsub_v1.types import JavaScriptUDF, MessageTransform, Topic
+
+ # TODO(developer)
+ # project_id = "your-project-id"
+ # topic_id = "your-topic-id"
+
+ code = """function redactSSN(message, metadata) {
+ const data = JSON.parse(message.data);
+ delete data['ssn'];
+ message.data = JSON.stringify(data);
+ return message;
+ }"""
+ udf = JavaScriptUDF(code=code, function_name="redactSSN")
+ transforms = [MessageTransform(javascript_udf=udf)]
+
+ publisher = pubsub_v1.PublisherClient()
+ topic_path = publisher.topic_path(project_id, topic_id)
+
+ request = Topic(name=topic_path, message_transforms=transforms)
+
+ topic = publisher.create_topic(request=request)
+
+ print(f"Created topic: {topic.name} with SMT")
+ # [END pubsub_create_topic_with_smt]
+
+
def update_topic_type(
project_id: str,
topic_id: str,
@@ -170,6 +426,83 @@ def delete_topic(project_id: str, topic_id: str) -> None:
# [END pubsub_delete_topic]
+def pubsub_publish_otel_tracing(
+ topic_project_id: str, trace_project_id: str, topic_id: str
+) -> None:
+ """
+ Publish to `topic_id` in `topic_project_id` with OpenTelemetry enabled.
+ Export the OpenTelemetry traces to Google Cloud Trace in project
+ `trace_project_id`
+
+ Args:
+ topic_project_id: project ID of the topic to publish to.
+ trace_project_id: project ID to export Cloud Trace to.
+ topic_id: topic ID to publish to.
+
+ Returns:
+ None
+ """
+ # [START pubsub_publish_otel_tracing]
+
+ from opentelemetry import trace
+ from opentelemetry.sdk.trace import TracerProvider
+ from opentelemetry.sdk.trace.export import (
+ BatchSpanProcessor,
+ )
+ from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+ from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBased
+
+ from google.cloud.pubsub_v1 import PublisherClient
+ from google.cloud.pubsub_v1.types import PublisherOptions
+
+ # TODO(developer)
+ # topic_project_id = "your-topic-project-id"
+ # trace_project_id = "your-trace-project-id"
+ # topic_id = "your-topic-id"
+
+ # In this sample, we use a Google Cloud Trace to export the OpenTelemetry
+ # traces: https://cloud.google.com/trace/docs/setup/python-ot
+ # Choose and configure the exporter for your set up accordingly.
+
+ sampler = ParentBased(root=TraceIdRatioBased(1))
+ trace.set_tracer_provider(TracerProvider(sampler=sampler))
+
+ # Export to Google Trace.
+ cloud_trace_exporter = CloudTraceSpanExporter(
+ project_id=trace_project_id,
+ )
+ trace.get_tracer_provider().add_span_processor(
+ BatchSpanProcessor(cloud_trace_exporter)
+ )
+
+ # Set the `enable_open_telemetry_tracing` option to True when creating
+ # the publisher client. This in itself is necessary and sufficient for
+ # the library to export OpenTelemetry traces. However, where the traces
+ # must be exported to needs to be configured based on your OpenTelemetry
+ # set up. Refer: https://opentelemetry.io/docs/languages/python/exporters/
+ publisher = PublisherClient(
+ publisher_options=PublisherOptions(
+ enable_open_telemetry_tracing=True,
+ ),
+ )
+
+ # The `topic_path` method creates a fully qualified identifier
+ # in the form `projects/{project_id}/topics/{topic_id}`
+ topic_path = publisher.topic_path(topic_project_id, topic_id)
+ # Publish messages.
+ for n in range(1, 10):
+ data_str = f"Message number {n}"
+ # Data must be a bytestring
+ data = data_str.encode("utf-8")
+ # When you publish a message, the client returns a future.
+ future = publisher.publish(topic_path, data)
+ print(future.result())
+
+ print(f"Published messages to {topic_path}.")
+
+ # [END pubsub_publish_otel_tracing]
+
+
def publish_messages(project_id: str, topic_id: str) -> None:
"""Publishes multiple messages to a Pub/Sub topic."""
# [START pubsub_quickstart_publisher]
@@ -509,7 +842,7 @@ def detach_subscription(project_id: str, subscription_id: str) -> None:
# [END pubsub_detach_subscription]
-if __name__ == "__main__":
+if __name__ == "__main__": # noqa: C901
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -522,6 +855,13 @@ def detach_subscription(project_id: str, subscription_id: str) -> None:
create_parser = subparsers.add_parser("create", help=create_topic.__doc__)
create_parser.add_argument("topic_id")
+ pubsub_publish_otel_tracing_parser = subparsers.add_parser(
+ "pubsub-publish-otel-tracing", help=pubsub_publish_otel_tracing.__doc__
+ )
+ pubsub_publish_otel_tracing_parser.add_argument("topic_project_id")
+ pubsub_publish_otel_tracing_parser.add_argument("trace_project_id")
+ pubsub_publish_otel_tracing_parser.add_argument("topic_id")
+
create_topic_with_kinesis_ingestion_parser = subparsers.add_parser(
"create_kinesis_ingestion", help=create_topic_with_kinesis_ingestion.__doc__
)
@@ -531,6 +871,61 @@ def detach_subscription(project_id: str, subscription_id: str) -> None:
create_topic_with_kinesis_ingestion_parser.add_argument("aws_role_arn")
create_topic_with_kinesis_ingestion_parser.add_argument("gcp_service_account")
+ create_topic_with_cloud_storage_ingestion_parser = subparsers.add_parser(
+ "create_cloud_storage_ingestion",
+ help=create_topic_with_cloud_storage_ingestion.__doc__,
+ )
+ create_topic_with_cloud_storage_ingestion_parser.add_argument("topic_id")
+ create_topic_with_cloud_storage_ingestion_parser.add_argument("bucket")
+ create_topic_with_cloud_storage_ingestion_parser.add_argument("input_format")
+ create_topic_with_cloud_storage_ingestion_parser.add_argument("text_delimiter")
+ create_topic_with_cloud_storage_ingestion_parser.add_argument("match_glob")
+ create_topic_with_cloud_storage_ingestion_parser.add_argument(
+ "minimum_object_create_time"
+ )
+
+ create_topic_with_aws_msk_ingestion_parser = subparsers.add_parser(
+ "create_aws_msk_ingestion", help=create_topic_with_aws_msk_ingestion.__doc__
+ )
+ create_topic_with_aws_msk_ingestion_parser.add_argument("topic_id")
+ create_topic_with_aws_msk_ingestion_parser.add_argument("cluster_arn")
+ create_topic_with_aws_msk_ingestion_parser.add_argument("msk_topic")
+ create_topic_with_aws_msk_ingestion_parser.add_argument("aws_role_arn")
+ create_topic_with_aws_msk_ingestion_parser.add_argument("gcp_service_account")
+
+ create_topic_with_azure_event_hubs_ingestion_parser = subparsers.add_parser(
+ "create_azure_event_hubs_ingestion",
+ help=create_topic_with_azure_event_hubs_ingestion.__doc__,
+ )
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument("topic_id")
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument("resource_group")
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument("namespace")
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument("event_hub")
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument("client_id")
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument("tenant_id")
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument("subscription_id")
+ create_topic_with_azure_event_hubs_ingestion_parser.add_argument(
+ "gcp_service_account"
+ )
+
+ create_topic_with_confluent_cloud_ingestion_parser = subparsers.add_parser(
+ "create_confluent_cloud_ingestion",
+ help=create_topic_with_confluent_cloud_ingestion.__doc__,
+ )
+ create_topic_with_confluent_cloud_ingestion_parser.add_argument("topic_id")
+ create_topic_with_confluent_cloud_ingestion_parser.add_argument("bootstrap_server")
+ create_topic_with_confluent_cloud_ingestion_parser.add_argument("cluster_id")
+ create_topic_with_confluent_cloud_ingestion_parser.add_argument("confluent_topic")
+ create_topic_with_confluent_cloud_ingestion_parser.add_argument("identity_pool_id")
+ create_topic_with_confluent_cloud_ingestion_parser.add_argument(
+ "gcp_service_account"
+ )
+
+ create_parser = subparsers.add_parser(
+ "create_smt", help=create_topic_with_smt.__doc__
+ )
+ create_parser.add_argument("topic_id")
+
update_topic_type_parser = subparsers.add_parser(
"update_kinesis_ingestion", help=update_topic_type.__doc__
)
@@ -609,6 +1004,49 @@ def detach_subscription(project_id: str, subscription_id: str) -> None:
args.aws_role_arn,
args.gcp_service_account,
)
+ elif args.command == "create_cloud_storage_ingestion":
+ create_topic_with_cloud_storage_ingestion(
+ args.project_id,
+ args.topic_id,
+ args.bucket,
+ args.input_format,
+ args.text_delimiter,
+ args.match_glob,
+ args.minimum_object_create_time,
+ )
+ elif args.command == "create_aws_msk_ingestion":
+ create_topic_with_aws_msk_ingestion(
+ args.project_id,
+ args.topic_id,
+ args.cluster_arn,
+ args.msk_topic,
+ args.aws_role_arn,
+ args.gcp_service_account,
+ )
+ elif args.command == "create_azure_event_hubs_ingestion":
+ create_topic_with_azure_event_hubs_ingestion(
+ args.project_id,
+ args.topic_id,
+ args.resource_group,
+ args.namespace,
+ args.event_hub,
+ args.client_id,
+ args.tenant_id,
+ args.subscription_id,
+ args.gcp_service_account,
+ )
+ elif args.command == "create_confluent_cloud_ingestion":
+ create_topic_with_confluent_cloud_ingestion(
+ args.project_id,
+ args.topic_id,
+ args.bootstrap_server,
+ args.cluster_id,
+ args.confluent_topic,
+ args.identity_pool_id,
+ args.gcp_service_account,
+ )
+ elif args.command == "create_smt":
+ create_topic_with_smt(args.project_id, f"{args.topic_id}-smt")
elif args.command == "update_kinesis_ingestion":
update_topic_type(
args.project_id,
@@ -638,3 +1076,7 @@ def detach_subscription(project_id: str, subscription_id: str) -> None:
resume_publish_with_ordering_keys(args.project_id, args.topic_id)
elif args.command == "detach-subscription":
detach_subscription(args.project_id, args.subscription_id)
+ elif args.command == "pubsub-publish-otel-tracing":
+ pubsub_publish_otel_tracing(
+ args.topic_project_id, args.trace_project_id, args.topic_id
+ )
diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py
index adb015e8a..43c6b2848 100644
--- a/samples/snippets/publisher_test.py
+++ b/samples/snippets/publisher_test.py
@@ -162,6 +162,178 @@ def test_create_topic_with_kinesis_ingestion(
publisher_client.delete_topic(request={"topic": topic_path})
+def test_create_topic_with_cloud_storage_ingestion(
+ publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str]
+) -> None:
+ # The scope of `topic_path` is limited to this function.
+ topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID)
+
+ bucket = "pubsub-cloud-storage-bucket"
+ input_format = "text"
+ text_delimiter = ","
+ match_glob = "**.txt"
+ minimum_object_create_time = "1970-01-01T00:00:01Z"
+
+ try:
+ publisher_client.delete_topic(request={"topic": topic_path})
+ except NotFound:
+ pass
+
+ publisher.create_topic_with_cloud_storage_ingestion(
+ PROJECT_ID,
+ TOPIC_ID,
+ bucket,
+ input_format,
+ text_delimiter,
+ match_glob,
+ minimum_object_create_time,
+ )
+
+ out, _ = capsys.readouterr()
+ assert f"Created topic: {topic_path} with Cloud Storage Ingestion Settings" in out
+
+ # Clean up resource created for the test.
+ publisher_client.delete_topic(request={"topic": topic_path})
+
+
+def test_create_topic_with_aws_msk_ingestion(
+ publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str]
+) -> None:
+ # The scope of `topic_path` is limited to this function.
+ topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID)
+
+ # Outside of automated CI tests, these values must be of actual AWS resources for the test to pass.
+ cluster_arn = (
+ "arn:aws:kafka:us-east-1:111111111111:cluster/fake-cluster-name/11111111-1111-1"
+ )
+ msk_topic = "fake-msk-topic-name"
+ aws_role_arn = "arn:aws:iam::111111111111:role/fake-role-name"
+ gcp_service_account = (
+ "fake-service-account@fake-gcp-project.iam.gserviceaccount.com"
+ )
+
+ try:
+ publisher_client.delete_topic(request={"topic": topic_path})
+ except NotFound:
+ pass
+
+ publisher.create_topic_with_aws_msk_ingestion(
+ PROJECT_ID,
+ TOPIC_ID,
+ cluster_arn,
+ msk_topic,
+ aws_role_arn,
+ gcp_service_account,
+ )
+
+ out, _ = capsys.readouterr()
+ assert f"Created topic: {topic_path} with AWS MSK Ingestion Settings" in out
+
+ # Clean up resource created for the test.
+ publisher_client.delete_topic(request={"topic": topic_path})
+
+
+def test_create_topic_with_azure_event_hubs_ingestion(
+ publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str]
+) -> None:
+ # The scope of `topic_path` is limited to this function.
+ topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID)
+
+ # Outside of automated CI tests, these values must be of actual Azure resources for the test to pass.
+ resource_group = "fake-resource-group"
+ namespace = "fake-namespace"
+ event_hub = "fake-event-hub"
+ client_id = "fake-client-id"
+ tenant_id = "fake-tenant-id"
+ subcription_id = "fake-subscription-id"
+ gcp_service_account = (
+ "fake-service-account@fake-gcp-project.iam.gserviceaccount.com"
+ )
+
+ try:
+ publisher_client.delete_topic(request={"topic": topic_path})
+ except NotFound:
+ pass
+
+ publisher.create_topic_with_azure_event_hubs_ingestion(
+ PROJECT_ID,
+ TOPIC_ID,
+ resource_group,
+ namespace,
+ event_hub,
+ client_id,
+ tenant_id,
+ subcription_id,
+ gcp_service_account,
+ )
+
+ out, _ = capsys.readouterr()
+ assert (
+ f"Created topic: {topic_path} with Azure Event Hubs Ingestion Settings" in out
+ )
+
+ # Clean up resource created for the test.
+ publisher_client.delete_topic(request={"topic": topic_path})
+
+
+def test_create_topic_with_confluent_cloud_ingestion(
+ publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str]
+) -> None:
+ # The scope of `topic_path` is limited to this function.
+ topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID)
+
+ # Outside of automated CI tests, these values must be of actual Confluent resources for the test to pass.
+ bootstrap_server = "fake-bootstrap-server-id.us-south1.gcp.confluent.cloud:9092"
+ cluster_id = "fake-cluster-id"
+ confluent_topic = "fake-confluent-topic-name"
+ identity_pool_id = "fake-identity-pool-id"
+ gcp_service_account = (
+ "fake-service-account@fake-gcp-project.iam.gserviceaccount.com"
+ )
+
+ try:
+ publisher_client.delete_topic(request={"topic": topic_path})
+ except NotFound:
+ pass
+
+ publisher.create_topic_with_confluent_cloud_ingestion(
+ PROJECT_ID,
+ TOPIC_ID,
+ bootstrap_server,
+ cluster_id,
+ confluent_topic,
+ identity_pool_id,
+ gcp_service_account,
+ )
+
+ out, _ = capsys.readouterr()
+ assert f"Created topic: {topic_path} with Confluent Cloud Ingestion Settings" in out
+
+ # Clean up resource created for the test.
+ publisher_client.delete_topic(request={"topic": topic_path})
+
+
+def test_create_with_smt(
+ publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str]
+) -> None:
+ smt_topic_name = f"{TOPIC_ID}-smt"
+ # The scope of `topic_path` is limited to this function.
+ topic_path = publisher_client.topic_path(PROJECT_ID, smt_topic_name)
+
+ try:
+ publisher_client.delete_topic(request={"topic": topic_path})
+ except NotFound:
+ pass
+
+ publisher.create_topic_with_smt(PROJECT_ID, smt_topic_name)
+
+ out, _ = capsys.readouterr()
+ assert f"Created topic: {topic_path} with SMT" in out
+
+ # Clean up resource created for the test.
+ publisher_client.delete_topic(request={"topic": topic_path})
+
+
def test_update_topic_type(
publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str]
) -> None:
diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt
index 43e018272..1aeb6d04c 100644
--- a/samples/snippets/requirements-test.txt
+++ b/samples/snippets/requirements-test.txt
@@ -1,7 +1,7 @@
backoff==2.2.1
-pytest===7.4.4; python_version == '3.7'
-pytest==8.2.2; python_version >= '3.8'
-mock==5.1.0
+pytest==8.4.2; python_version <= '3.9'
+pytest==9.0.2; python_version > '3.9'
+mock==5.2.0
flaky==3.8.1
-google-cloud-bigquery==3.25.0
-google-cloud-storage==2.17.0
+google-cloud-bigquery==3.40.0
+google-cloud-storage==3.9.0
diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 0f8d2f938..25029a9f9 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -1,5 +1,7 @@
-google-cloud-pubsub==2.22.0
-avro==1.11.3
-protobuf===4.24.4; python_version == '3.7'
-protobuf==5.27.2; python_version >= '3.8'
-avro==1.11.3
+google-cloud-pubsub==2.34.0
+avro==1.12.1
+protobuf==6.33.5
+avro==1.12.1
+opentelemetry-api==1.39.1
+opentelemetry-sdk==1.39.1
+opentelemetry-exporter-gcp-trace==1.11.0
diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py
index 79cd0ebf1..94d083ae1 100644
--- a/samples/snippets/subscriber.py
+++ b/samples/snippets/subscriber.py
@@ -68,6 +68,94 @@ def list_subscriptions_in_project(project_id: str) -> None:
# [END pubsub_list_subscriptions]
+def pubsub_subscribe_otel_tracing(
+ subscription_project_id: str,
+ cloud_trace_project_id: str,
+ subscription_id: str,
+ timeout: Optional[float] = None,
+) -> None:
+ """
+ Subscribe to `subscription_id` in `subscription_project_id` with OpenTelemetry enabled.
+ Export the OpenTelemetry traces to Google Cloud Trace in project
+ `trace_project_id`
+ Args:
+ subscription_project_id: project ID of the subscription.
+ cloud_trace_project_id: project ID to export Cloud Trace to.
+ subscription_id: subscription ID to subscribe from.
+ timeout: time until which to subscribe to.
+ Returns:
+ None
+ """
+ # [START pubsub_subscribe_otel_tracing]
+ from opentelemetry import trace
+ from opentelemetry.sdk.trace import TracerProvider
+ from opentelemetry.sdk.trace.export import (
+ BatchSpanProcessor,
+ )
+ from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+ from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBased
+
+ from google.cloud import pubsub_v1
+ from google.cloud.pubsub_v1 import SubscriberClient
+ from google.cloud.pubsub_v1.types import SubscriberOptions
+
+ # TODO(developer)
+ # subscription_project_id = "your-subscription-project-id"
+ # subscription_id = "your-subscription-id"
+ # cloud_trace_project_id = "your-cloud-trace-project-id"
+ # timeout = 300.0
+
+ # In this sample, we use a Google Cloud Trace to export the OpenTelemetry
+ # traces: https://cloud.google.com/trace/docs/setup/python-ot
+ # Choose and configure the exporter for your set up accordingly.
+
+ sampler = ParentBased(root=TraceIdRatioBased(1))
+ trace.set_tracer_provider(TracerProvider(sampler=sampler))
+
+ # Export to Google Trace
+ cloud_trace_exporter = CloudTraceSpanExporter(
+ project_id=cloud_trace_project_id,
+ )
+ trace.get_tracer_provider().add_span_processor(
+ BatchSpanProcessor(cloud_trace_exporter)
+ )
+ # Set the `enable_open_telemetry_tracing` option to True when creating
+ # the subscriber client. This in itself is necessary and sufficient for
+ # the library to export OpenTelemetry traces. However, where the traces
+ # must be exported to needs to be configured based on your OpenTelemetry
+ # set up. Refer: https://opentelemetry.io/docs/languages/python/exporters/
+ subscriber = SubscriberClient(
+ subscriber_options=SubscriberOptions(enable_open_telemetry_tracing=True)
+ )
+
+ # The `subscription_path` method creates a fully qualified identifier
+ # in the form `projects/{project_id}/subscriptions/{subscription_id}`
+ subscription_path = subscriber.subscription_path(
+ subscription_project_id, subscription_id
+ )
+
+ # Define callback to be called when a message is received.
+ def callback(message: pubsub_v1.subscriber.message.Message) -> None:
+ # Ack message after processing it.
+ print(message.data)
+ message.ack()
+
+ # Wrap subscriber in a 'with' block to automatically call close() when done.
+ with subscriber:
+ try:
+ # Optimistically subscribe to messages on the subscription.
+ streaming_pull_future = subscriber.subscribe(
+ subscription_path, callback=callback
+ )
+ streaming_pull_future.result(timeout=timeout)
+ except TimeoutError:
+ print("Successfully subscribed until the timeout passed.")
+ streaming_pull_future.cancel() # Trigger the shutdown.
+ streaming_pull_future.result() # Block until the shutdown is complete.
+
+ # [END pubsub_subscribe_otel_tracing]
+
+
def create_subscription(project_id: str, topic_id: str, subscription_id: str) -> None:
"""Create a new pull subscription on the given topic."""
# [START pubsub_create_pull_subscription]
@@ -469,7 +557,7 @@ def create_cloudstorage_subscription(
# Min 1 minutes, max 10 minutes
max_duration=max_duration,
# Min 1 KB, max 10 GiB
- max_bytes=2000,
+ max_bytes=10000000,
)
# Wrap the subscriber in a 'with' block to automatically call close() to
@@ -490,6 +578,45 @@ def create_cloudstorage_subscription(
# [END pubsub_create_cloud_storage_subscription]
+def create_subscription_with_smt(
+ project_id: str, topic_id: str, subscription_id: str
+) -> None:
+ """Create a subscription with a UDF SMT."""
+ # [START pubsub_create_subscription_with_smt]
+ from google.cloud import pubsub_v1
+ from google.pubsub_v1.types import JavaScriptUDF, MessageTransform
+
+ # TODO(developer): Choose an existing topic.
+ # project_id = "your-project-id"
+ # topic_id = "your-topic-id"
+ # subscription_id = "your-subscription-id"
+
+ publisher = pubsub_v1.PublisherClient()
+ subscriber = pubsub_v1.SubscriberClient()
+ topic_path = publisher.topic_path(project_id, topic_id)
+ subscription_path = subscriber.subscription_path(project_id, subscription_id)
+
+ code = """function redactSSN(message, metadata) {
+ const data = JSON.parse(message.data);
+ delete data['ssn'];
+ message.data = JSON.stringify(data);
+ return message;
+ }"""
+ udf = JavaScriptUDF(code=code, function_name="redactSSN")
+ transforms = [MessageTransform(javascript_udf=udf)]
+
+ with subscriber:
+ subscription = subscriber.create_subscription(
+ request={
+ "name": subscription_path,
+ "topic": topic_path,
+ "message_transforms": transforms,
+ }
+ )
+ print(f"Created subscription with SMT: {subscription}")
+ # [END pubsub_create_subscription_with_smt]
+
+
def delete_subscription(project_id: str, subscription_id: str) -> None:
"""Deletes an existing Pub/Sub topic."""
# [START pubsub_delete_subscription]
@@ -1147,6 +1274,14 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None:
"list-in-project", help=list_subscriptions_in_project.__doc__
)
+ otel_subscribe_parse = subparsers.add_parser(
+ "otel-subscribe", help=pubsub_subscribe_otel_tracing.__doc__
+ )
+ otel_subscribe_parse.add_argument("subscription_project_id")
+ otel_subscribe_parse.add_argument("cloud_trace_project_id")
+ otel_subscribe_parse.add_argument("subscription_id")
+ otel_subscribe_parse.add_argument("timeout", default=None, type=float, nargs="?")
+
create_parser = subparsers.add_parser("create", help=create_subscription.__doc__)
create_parser.add_argument("topic_id")
create_parser.add_argument("subscription_id")
@@ -1214,6 +1349,12 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None:
create_cloudstorage_subscription_parser.add_argument("subscription_id")
create_cloudstorage_subscription_parser.add_argument("bucket")
+ create_subscription_with_smt_parser = subparsers.add_parser(
+ "create-with-smt", help=create_subscription_with_smt.__doc__
+ )
+ create_subscription_with_smt_parser.add_argument("topic_id")
+ create_subscription_with_smt_parser.add_argument("subscription_id")
+
delete_parser = subparsers.add_parser("delete", help=delete_subscription.__doc__)
delete_parser.add_argument("subscription_id")
@@ -1375,6 +1516,10 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None:
create_cloudstorage_subscription(
args.project_id, args.topic_id, args.subscription_id, args.bucket
)
+ elif args.command == "create-with-smt":
+ create_subscription_with_smt(
+ args.project_id, f"{args.topic_id}-smt", f"{args.subscription_id}-smt"
+ )
elif args.command == "delete":
delete_subscription(args.project_id, args.subscription_id)
@@ -1428,3 +1573,10 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None:
receive_messages_with_concurrency_control(
args.project_id, args.subscription_id, args.timeout
)
+ elif args.command == "otel-subscribe":
+ pubsub_subscribe_otel_tracing(
+ args.subscription_project_id,
+ args.cloud_trace_project_id,
+ args.subscription_id,
+ args.timeout,
+ )
diff --git a/samples/snippets/subscriber_test.py b/samples/snippets/subscriber_test.py
index 86f7a94ce..d1f1db94c 100644
--- a/samples/snippets/subscriber_test.py
+++ b/samples/snippets/subscriber_test.py
@@ -579,6 +579,37 @@ def test_create_push_subscription(
subscriber_client.delete_subscription(request={"subscription": subscription_path})
+def test_create_subscription_with_smt(
+ subscriber_client: pubsub_v1.SubscriberClient,
+ topic: str,
+ capsys: CaptureFixture[str],
+) -> None:
+ subscription_for_create_name = (
+ f"subscription-test-subscription-for-create-with-smt-{PY_VERSION}-{UUID}-smt"
+ )
+
+ subscription_path = subscriber_client.subscription_path(
+ PROJECT_ID, subscription_for_create_name
+ )
+
+ try:
+ subscriber_client.delete_subscription(
+ request={"subscription": subscription_path}
+ )
+ except NotFound:
+ pass
+
+ subscriber.create_subscription_with_smt(
+ PROJECT_ID, TOPIC, subscription_for_create_name
+ )
+
+ out, _ = capsys.readouterr()
+ assert f"{subscription_for_create_name}" in out
+
+ # Clean up.
+ subscriber_client.delete_subscription(request={"subscription": subscription_path})
+
+
def test_update_push_subscription(
subscriber_client: pubsub_v1.SubscriberClient,
topic: str,
diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py
deleted file mode 100644
index 35262ea8d..000000000
--- a/scripts/fixup_pubsub_v1_keywords.py
+++ /dev/null
@@ -1,213 +0,0 @@
-#! /usr/bin/env python3
-# -*- coding: utf-8 -*-
-# Copyright 2024 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 argparse
-import os
-import libcst as cst
-import pathlib
-import sys
-from typing import (Any, Callable, Dict, List, Sequence, Tuple)
-
-
-def partition(
- predicate: Callable[[Any], bool],
- iterator: Sequence[Any]
-) -> Tuple[List[Any], List[Any]]:
- """A stable, out-of-place partition."""
- results = ([], [])
-
- for i in iterator:
- results[int(predicate(i))].append(i)
-
- # Returns trueList, falseList
- return results[1], results[0]
-
-
-class pubsubCallTransformer(cst.CSTTransformer):
- CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata')
- METHOD_TO_PARAMS: Dict[str, Tuple[str]] = {
- 'acknowledge': ('subscription', 'ack_ids', ),
- 'commit_schema': ('name', 'schema', ),
- 'create_schema': ('parent', 'schema', 'schema_id', ),
- 'create_snapshot': ('name', 'subscription', 'labels', ),
- 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', ),
- 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', ),
- 'delete_schema': ('name', ),
- 'delete_schema_revision': ('name', 'revision_id', ),
- 'delete_snapshot': ('snapshot', ),
- 'delete_subscription': ('subscription', ),
- 'delete_topic': ('topic', ),
- 'detach_subscription': ('subscription', ),
- 'get_schema': ('name', 'view', ),
- 'get_snapshot': ('snapshot', ),
- 'get_subscription': ('subscription', ),
- 'get_topic': ('topic', ),
- 'list_schema_revisions': ('name', 'view', 'page_size', 'page_token', ),
- 'list_schemas': ('parent', 'view', 'page_size', 'page_token', ),
- 'list_snapshots': ('project', 'page_size', 'page_token', ),
- 'list_subscriptions': ('project', 'page_size', 'page_token', ),
- 'list_topics': ('project', 'page_size', 'page_token', ),
- 'list_topic_snapshots': ('topic', 'page_size', 'page_token', ),
- 'list_topic_subscriptions': ('topic', 'page_size', 'page_token', ),
- 'modify_ack_deadline': ('subscription', 'ack_ids', 'ack_deadline_seconds', ),
- 'modify_push_config': ('subscription', 'push_config', ),
- 'publish': ('topic', 'messages', ),
- 'pull': ('subscription', 'max_messages', 'return_immediately', ),
- 'rollback_schema': ('name', 'revision_id', ),
- 'seek': ('subscription', 'time', 'snapshot', ),
- 'streaming_pull': ('subscription', 'stream_ack_deadline_seconds', 'ack_ids', 'modify_deadline_seconds', 'modify_deadline_ack_ids', 'client_id', 'max_outstanding_messages', 'max_outstanding_bytes', ),
- 'update_snapshot': ('snapshot', 'update_mask', ),
- 'update_subscription': ('subscription', 'update_mask', ),
- 'update_topic': ('topic', 'update_mask', ),
- 'validate_message': ('parent', 'name', 'schema', 'message', 'encoding', ),
- 'validate_schema': ('parent', 'schema', ),
- 'get_iam_policy': ('resource', 'options', ),
- 'set_iam_policy': ('resource', 'policy', ),
- 'test_iam_permissions': ('resource', 'permissions', ),
- }
-
- def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode:
- try:
- key = original.func.attr.value
- kword_params = self.METHOD_TO_PARAMS[key]
- except (AttributeError, KeyError):
- # Either not a method from the API or too convoluted to be sure.
- return updated
-
- # If the existing code is valid, keyword args come after positional args.
- # Therefore, all positional args must map to the first parameters.
- args, kwargs = partition(lambda a: not bool(a.keyword), updated.args)
- if any(k.keyword.value == "request" for k in kwargs):
- # We've already fixed this file, don't fix it again.
- return updated
-
- kwargs, ctrl_kwargs = partition(
- lambda a: a.keyword.value not in self.CTRL_PARAMS,
- kwargs
- )
-
- args, ctrl_args = args[:len(kword_params)], args[len(kword_params):]
- ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl))
- for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS))
-
- request_arg = cst.Arg(
- value=cst.Dict([
- cst.DictElement(
- cst.SimpleString("'{}'".format(name)),
-cst.Element(value=arg.value)
- )
- # Note: the args + kwargs looks silly, but keep in mind that
- # the control parameters had to be stripped out, and that
- # those could have been passed positionally or by keyword.
- for name, arg in zip(kword_params, args + kwargs)]),
- keyword=cst.Name("request")
- )
-
- return updated.with_changes(
- args=[request_arg] + ctrl_kwargs
- )
-
-
-def fix_files(
- in_dir: pathlib.Path,
- out_dir: pathlib.Path,
- *,
- transformer=pubsubCallTransformer(),
-):
- """Duplicate the input dir to the output dir, fixing file method calls.
-
- Preconditions:
- * in_dir is a real directory
- * out_dir is a real, empty directory
- """
- pyfile_gen = (
- pathlib.Path(os.path.join(root, f))
- for root, _, files in os.walk(in_dir)
- for f in files if os.path.splitext(f)[1] == ".py"
- )
-
- for fpath in pyfile_gen:
- with open(fpath, 'r') as f:
- src = f.read()
-
- # Parse the code and insert method call fixes.
- tree = cst.parse_module(src)
- updated = tree.visit(transformer)
-
- # Create the path and directory structure for the new file.
- updated_path = out_dir.joinpath(fpath.relative_to(in_dir))
- updated_path.parent.mkdir(parents=True, exist_ok=True)
-
- # Generate the updated source file at the corresponding path.
- with open(updated_path, 'w') as f:
- f.write(updated.code)
-
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(
- description="""Fix up source that uses the pubsub client library.
-
-The existing sources are NOT overwritten but are copied to output_dir with changes made.
-
-Note: This tool operates at a best-effort level at converting positional
- parameters in client method calls to keyword based parameters.
- Cases where it WILL FAIL include
- A) * or ** expansion in a method call.
- B) Calls via function or method alias (includes free function calls)
- C) Indirect or dispatched calls (e.g. the method is looked up dynamically)
-
- These all constitute false negatives. The tool will also detect false
- positives when an API method shares a name with another method.
-""")
- parser.add_argument(
- '-d',
- '--input-directory',
- required=True,
- dest='input_dir',
- help='the input directory to walk for python files to fix up',
- )
- parser.add_argument(
- '-o',
- '--output-directory',
- required=True,
- dest='output_dir',
- help='the directory to output files fixed via un-flattening',
- )
- args = parser.parse_args()
- input_dir = pathlib.Path(args.input_dir)
- output_dir = pathlib.Path(args.output_dir)
- if not input_dir.is_dir():
- print(
- f"input directory '{input_dir}' does not exist or is not a directory",
- file=sys.stderr,
- )
- sys.exit(-1)
-
- if not output_dir.is_dir():
- print(
- f"output directory '{output_dir}' does not exist or is not a directory",
- file=sys.stderr,
- )
- sys.exit(-1)
-
- if os.listdir(output_dir):
- print(
- f"output directory '{output_dir}' is not empty",
- file=sys.stderr,
- )
- sys.exit(-1)
-
- fix_files(input_dir, output_dir)
diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst
index 6f069c6c8..f21db80c4 100644
--- a/scripts/readme-gen/templates/install_deps.tmpl.rst
+++ b/scripts/readme-gen/templates/install_deps.tmpl.rst
@@ -12,7 +12,7 @@ Install Dependencies
.. _Python Development Environment Setup Guide:
https://cloud.google.com/python/setup
-#. Create a virtualenv. Samples are compatible with Python 3.7+.
+#. Create a virtualenv. Samples are compatible with Python 3.9+.
.. code-block:: bash
diff --git a/setup.py b/setup.py
index dbb66cf7c..211a3306f 100644
--- a/setup.py
+++ b/setup.py
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+
+# DO NOT EDIT THIS FILE OUTSIDE OF `.librarian/generator-input`
+# The source of truth for this file is `.librarian/generator-input`
+
import io
import os
@@ -36,15 +40,19 @@
release_status = "Development Status :: 5 - Production/Stable"
dependencies = [
- "grpcio >= 1.51.3, < 2.0dev", # https://github.com/googleapis/python-pubsub/issues/609
+ "grpcio >= 1.51.3, < 2.0.0; python_version < '3.14'", # https://github.com/googleapis/python-pubsub/issues/609
+ "grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
# google-api-core >= 1.34.0 is allowed in order to support google-api-core 1.x
- "google-auth >= 2.14.1, <3.0.0dev",
- "google-api-core[grpc] >= 1.34.0, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*",
- "proto-plus >= 1.22.0, <2.0.0dev",
- "proto-plus >= 1.22.2, <2.0.0dev; python_version>='3.11'",
- "protobuf>=3.20.2,<6.0.0dev,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5",
- "grpc-google-iam-v1 >= 0.12.4, < 1.0.0dev",
+ "google-auth >= 2.14.1, <3.0.0",
+ "google-api-core[grpc] >= 1.34.0, <3.0.0,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*",
+ "proto-plus >= 1.22.0, <2.0.0",
+ "proto-plus >= 1.22.2, <2.0.0; python_version>='3.11'",
+ "proto-plus >= 1.25.0, < 2.0.0; python_version >= '3.13'",
+ "protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5",
+ "grpc-google-iam-v1 >= 0.12.4, < 1.0.0",
"grpcio-status >= 1.33.2",
+ "opentelemetry-api >= 1.27.0",
+ "opentelemetry-sdk >= 1.27.0",
]
extras = {"libcst": "libcst >= 0.3.10"}
url = "https://github.com/googleapis/python-pubsub"
@@ -76,12 +84,12 @@
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Operating System :: OS Independent",
"Topic :: Internet",
],
@@ -89,8 +97,7 @@
packages=packages,
install_requires=dependencies,
extras_require=extras,
- python_requires=">=3.7",
- scripts=["scripts/fixup_pubsub_v1_keywords.py"],
+ python_requires=">=3.9",
include_package_data=True,
zip_safe=False,
)
diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt
index ad3f0fa58..ef1c92fff 100644
--- a/testing/constraints-3.10.txt
+++ b/testing/constraints-3.10.txt
@@ -2,6 +2,8 @@
# This constraints file is required for unit tests.
# List all library dependencies and extras in this file.
google-api-core
+google-auth
+grpcio
proto-plus
protobuf
grpc-google-iam-v1
diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt
index ad3f0fa58..ef1c92fff 100644
--- a/testing/constraints-3.11.txt
+++ b/testing/constraints-3.11.txt
@@ -2,6 +2,8 @@
# This constraints file is required for unit tests.
# List all library dependencies and extras in this file.
google-api-core
+google-auth
+grpcio
proto-plus
protobuf
grpc-google-iam-v1
diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt
index ad3f0fa58..ef1c92fff 100644
--- a/testing/constraints-3.12.txt
+++ b/testing/constraints-3.12.txt
@@ -2,6 +2,8 @@
# This constraints file is required for unit tests.
# List all library dependencies and extras in this file.
google-api-core
+google-auth
+grpcio
proto-plus
protobuf
grpc-google-iam-v1
diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt
new file mode 100644
index 000000000..2ae5a677e
--- /dev/null
+++ b/testing/constraints-3.13.txt
@@ -0,0 +1,13 @@
+# We use the constraints file for the latest Python version
+# (currently this file) to check that the latest
+# major versions of dependencies are supported in setup.py.
+# List all library dependencies and extras in this file.
+# Require the latest major version be installed for each dependency.
+# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0",
+# Then this file should have google-cloud-foo>=1
+google-api-core>=2
+google-auth>=2
+grpcio>=1
+proto-plus>=1
+protobuf>=6
+grpc-google-iam-v1>=0
diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt
new file mode 100644
index 000000000..2ae5a677e
--- /dev/null
+++ b/testing/constraints-3.14.txt
@@ -0,0 +1,13 @@
+# We use the constraints file for the latest Python version
+# (currently this file) to check that the latest
+# major versions of dependencies are supported in setup.py.
+# List all library dependencies and extras in this file.
+# Require the latest major version be installed for each dependency.
+# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0",
+# Then this file should have google-cloud-foo>=1
+google-api-core>=2
+google-auth>=2
+grpcio>=1
+proto-plus>=1
+protobuf>=6
+grpc-google-iam-v1>=0
diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt
deleted file mode 100644
index 08db5de87..000000000
--- a/testing/constraints-3.7.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-# This constraints file is used to check that lower bounds
-# are correct in setup.py
-# List all library dependencies and extras in this file.
-# Pin the version to the lower bound.
-# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0dev",
-# Then this file should have google-cloud-foo==1.14.0
-google-api-core==1.34.0
-google-auth==2.14.1
-proto-plus==1.22.0
-protobuf==3.20.2
-grpc-google-iam-v1==0.12.4
-grpcio==1.51.3
-grpcio-status==1.33.2
diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt
deleted file mode 100644
index 30520e2d0..000000000
--- a/testing/constraints-3.8.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- coding: utf-8 -*-
-# This constraints file is required for unit tests.
-# List all library dependencies and extras in this file.
-google-api-core==1.34.0
-proto-plus
-protobuf
-grpc-google-iam-v1
diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt
index ad3f0fa58..ef1c92fff 100644
--- a/testing/constraints-3.9.txt
+++ b/testing/constraints-3.9.txt
@@ -2,6 +2,8 @@
# This constraints file is required for unit tests.
# List all library dependencies and extras in this file.
google-api-core
+google-auth
+grpcio
proto-plus
protobuf
grpc-google-iam-v1
diff --git a/tests/__init__.py b/tests/__init__.py
index 8f6cf0682..cbf94b283 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/tests/system.py b/tests/system.py
index 6bf8ef10f..9db2a5e12 100644
--- a/tests/system.py
+++ b/tests/system.py
@@ -20,16 +20,11 @@
import operator as op
import os
import psutil
-import sys
import threading
import time
from typing import Any, Callable, cast, TypeVar
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
from flaky import flaky
import pytest
@@ -46,7 +41,7 @@
from test_utils.system import unique_resource_id
C = TypeVar("C", bound=Callable[..., Any])
-typed_flaky = cast(Callable[[C], C], flaky(max_runs=3, min_passes=1))
+typed_flaky = cast(Callable[[C], C], flaky(max_runs=5, min_passes=1))
@pytest.fixture(scope="module")
@@ -468,7 +463,7 @@ def test_subscriber_not_leaking_open_sockets(
publisher.create_topic(name=topic_path)
current_process = psutil.Process()
- conn_count_start = len(current_process.connections())
+ conn_count_start = len(current_process.net_connections())
# Publish a few messages, then synchronously pull them and check that
# no sockets are leaked.
@@ -487,7 +482,7 @@ def test_subscriber_not_leaking_open_sockets(
response = subscriber.pull(subscription=subscription_path, max_messages=3)
assert len(response.received_messages) == 3
- conn_count_end = len(current_process.connections())
+ conn_count_end = len(current_process.net_connections())
# To avoid flakiness, use <= in the assertion, since on rare occasions additional
# sockets are closed, causing the == assertion to fail.
@@ -616,6 +611,7 @@ def test_streaming_pull_ack_deadline(
finally:
subscription_future.cancel()
+ @typed_flaky
def test_streaming_pull_max_messages(
self, publisher, topic_path_base, subscription_path_base, cleanup
):
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
index 8f6cf0682..cbf94b283 100644
--- a/tests/unit/__init__.py
+++ b/tests/unit/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/tests/unit/gapic/__init__.py b/tests/unit/gapic/__init__.py
index 8f6cf0682..cbf94b283 100644
--- a/tests/unit/gapic/__init__.py
+++ b/tests/unit/gapic/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/tests/unit/gapic/pubsub_v1/__init__.py b/tests/unit/gapic/pubsub_v1/__init__.py
index 8f6cf0682..cbf94b283 100644
--- a/tests/unit/gapic/pubsub_v1/__init__.py
+++ b/tests/unit/gapic/pubsub_v1/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py
index 5af39dbf8..0edfbd382 100644
--- a/tests/unit/gapic/pubsub_v1/test_publisher.py
+++ b/tests/unit/gapic/pubsub_v1/test_publisher.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,16 +15,12 @@
#
import os
-# try/except added for compatibility with python < 3.8
-try:
- from unittest import mock
- from unittest.mock import AsyncMock # pragma: NO COVER
-except ImportError: # pragma: NO COVER
- import mock
+
+import mock
import grpc
from grpc.experimental import aio
-from collections.abc import Iterable
+from collections.abc import Iterable, AsyncIterable
from google.protobuf import json_format
import json
import math
@@ -37,6 +33,13 @@
from requests.sessions import Session
from google.protobuf import json_format
+try:
+ from google.auth.aio import credentials as ga_credentials_async
+
+ HAS_GOOGLE_AUTH_AIO = True
+except ImportError: # pragma: NO COVER
+ HAS_GOOGLE_AUTH_AIO = False
+
from google.api_core import client_options
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
@@ -52,6 +55,7 @@
from google.oauth2 import service_account
from google.protobuf import duration_pb2 # type: ignore
from google.protobuf import field_mask_pb2 # type: ignore
+from google.protobuf import struct_pb2 # type: ignore
from google.protobuf import timestamp_pb2 # type: ignore
from google.pubsub_v1.services.publisher import PublisherAsyncClient
from google.pubsub_v1.services.publisher import PublisherClient
@@ -62,10 +66,32 @@
import google.auth
+CRED_INFO_JSON = {
+ "credential_source": "/path/to/file",
+ "credential_type": "service account credentials",
+ "principal": "service-account@example.com",
+}
+CRED_INFO_STRING = json.dumps(CRED_INFO_JSON)
+
+
+async def mock_async_gen(data, chunk_size=1):
+ for i in range(0, len(data)): # pragma: NO COVER
+ chunk = data[i : i + chunk_size]
+ yield chunk.encode("utf-8")
+
+
def client_cert_source_callback():
return b"cert bytes", b"key bytes"
+# TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded.
+# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107.
+def async_anonymous_credentials():
+ if HAS_GOOGLE_AUTH_AIO:
+ return ga_credentials_async.AnonymousCredentials()
+ return ga_credentials.AnonymousCredentials()
+
+
# If default endpoint is localhost, then default mtls endpoint will be the same.
# This method modifies the default endpoint so the client can produce a different
# mtls endpoint for endpoint testing purposes.
@@ -124,12 +150,19 @@ def test__read_environment_variables():
with mock.patch.dict(
os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
):
- with pytest.raises(ValueError) as excinfo:
- PublisherClient._read_environment_variables()
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with pytest.raises(ValueError) as excinfo:
+ PublisherClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
+ )
+ else:
+ assert PublisherClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
assert PublisherClient._read_environment_variables() == (False, "never", None)
@@ -156,6 +189,105 @@ def test__read_environment_variables():
)
+def test_use_client_cert_effective():
+ # Test case 1: Test when `should_use_client_cert` returns True.
+ # We mock the `should_use_client_cert` function to simulate a scenario where
+ # the google-auth library supports automatic mTLS and determines that a
+ # client certificate should be used.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch(
+ "google.auth.transport.mtls.should_use_client_cert", return_value=True
+ ):
+ assert PublisherClient._use_client_cert_effective() is True
+
+ # Test case 2: Test when `should_use_client_cert` returns False.
+ # We mock the `should_use_client_cert` function to simulate a scenario where
+ # the google-auth library supports automatic mTLS and determines that a
+ # client certificate should NOT be used.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch(
+ "google.auth.transport.mtls.should_use_client_cert", return_value=False
+ ):
+ assert PublisherClient._use_client_cert_effective() is False
+
+ # Test case 3: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ assert PublisherClient._use_client_cert_effective() is True
+
+ # Test case 4: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}
+ ):
+ assert PublisherClient._use_client_cert_effective() is False
+
+ # Test case 5: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}):
+ assert PublisherClient._use_client_cert_effective() is True
+
+ # Test case 6: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"}
+ ):
+ assert PublisherClient._use_client_cert_effective() is False
+
+ # Test case 7: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}):
+ assert PublisherClient._use_client_cert_effective() is True
+
+ # Test case 8: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"}
+ ):
+ assert PublisherClient._use_client_cert_effective() is False
+
+ # Test case 9: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set.
+ # In this case, the method should return False, which is the default value.
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, clear=True):
+ assert PublisherClient._use_client_cert_effective() is False
+
+ # Test case 10: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
+ # The method should raise a ValueError as the environment variable must be either
+ # "true" or "false".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}
+ ):
+ with pytest.raises(ValueError):
+ PublisherClient._use_client_cert_effective()
+
+ # Test case 11: Test when `should_use_client_cert` is available and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
+ # The method should return False as the environment variable is set to an invalid value.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}
+ ):
+ assert PublisherClient._use_client_cert_effective() is False
+
+ # Test case 12: Test when `should_use_client_cert` is available and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also,
+ # the GOOGLE_API_CONFIG environment variable is unset.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}):
+ assert PublisherClient._use_client_cert_effective() is False
+
+
def test__get_client_cert_source():
mock_provided_cert_source = mock.Mock()
mock_default_cert_source = mock.Mock()
@@ -281,83 +413,46 @@ def test__get_universe_domain():
@pytest.mark.parametrize(
- "client_class,transport_class,transport_name",
+ "error_code,cred_info_json,show_cred_info",
[
- (PublisherClient, transports.PublisherGrpcTransport, "grpc"),
- (PublisherClient, transports.PublisherRestTransport, "rest"),
+ (401, CRED_INFO_JSON, True),
+ (403, CRED_INFO_JSON, True),
+ (404, CRED_INFO_JSON, True),
+ (500, CRED_INFO_JSON, False),
+ (401, None, False),
+ (403, None, False),
+ (404, None, False),
+ (500, None, False),
],
)
-def test__validate_universe_domain(client_class, transport_class, transport_name):
- client = client_class(
- transport=transport_class(credentials=ga_credentials.AnonymousCredentials())
- )
- assert client._validate_universe_domain() == True
-
- # Test the case when universe is already validated.
- assert client._validate_universe_domain() == True
-
- if transport_name == "grpc":
- # Test the case where credentials are provided by the
- # `local_channel_credentials`. The default universes in both match.
- channel = grpc.secure_channel(
- "http://localhost/", grpc.local_channel_credentials()
- )
- client = client_class(transport=transport_class(channel=channel))
- assert client._validate_universe_domain() == True
+def test__add_cred_info_for_auth_errors(error_code, cred_info_json, show_cred_info):
+ cred = mock.Mock(["get_cred_info"])
+ cred.get_cred_info = mock.Mock(return_value=cred_info_json)
+ client = PublisherClient(credentials=cred)
+ client._transport._credentials = cred
+
+ error = core_exceptions.GoogleAPICallError("message", details=["foo"])
+ error.code = error_code
+
+ client._add_cred_info_for_auth_errors(error)
+ if show_cred_info:
+ assert error.details == ["foo", CRED_INFO_STRING]
+ else:
+ assert error.details == ["foo"]
- # Test the case where credentials do not exist: e.g. a transport is provided
- # with no credentials. Validation should still succeed because there is no
- # mismatch with non-existent credentials.
- channel = grpc.secure_channel(
- "http://localhost/", grpc.local_channel_credentials()
- )
- transport = transport_class(channel=channel)
- transport._credentials = None
- client = client_class(transport=transport)
- assert client._validate_universe_domain() == True
- # TODO: This is needed to cater for older versions of google-auth
- # Make this test unconditional once the minimum supported version of
- # google-auth becomes 2.23.0 or higher.
- google_auth_major, google_auth_minor = [
- int(part) for part in google.auth.__version__.split(".")[0:2]
- ]
- if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23):
- credentials = ga_credentials.AnonymousCredentials()
- credentials._universe_domain = "foo.com"
- # Test the case when there is a universe mismatch from the credentials.
- client = client_class(transport=transport_class(credentials=credentials))
- with pytest.raises(ValueError) as excinfo:
- client._validate_universe_domain()
- assert (
- str(excinfo.value)
- == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default."
- )
+@pytest.mark.parametrize("error_code", [401, 403, 404, 500])
+def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code):
+ cred = mock.Mock([])
+ assert not hasattr(cred, "get_cred_info")
+ client = PublisherClient(credentials=cred)
+ client._transport._credentials = cred
- # Test the case when there is a universe mismatch from the client.
- #
- # TODO: Make this test unconditional once the minimum supported version of
- # google-api-core becomes 2.15.0 or higher.
- api_core_major, api_core_minor = [
- int(part) for part in api_core_version.__version__.split(".")[0:2]
- ]
- if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15):
- client = client_class(
- client_options={"universe_domain": "bar.com"},
- transport=transport_class(
- credentials=ga_credentials.AnonymousCredentials(),
- ),
- )
- with pytest.raises(ValueError) as excinfo:
- client._validate_universe_domain()
- assert (
- str(excinfo.value)
- == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default."
- )
+ error = core_exceptions.GoogleAPICallError("message", details=[])
+ error.code = error_code
- # Test that ValueError is raised if universe_domain is provided via client options and credentials is None
- with pytest.raises(ValueError):
- client._compare_universes("foo.bar", None)
+ client._add_cred_info_for_auth_errors(error)
+ assert error.details == []
@pytest.mark.parametrize(
@@ -556,17 +651,6 @@ def test_publisher_client_client_options(client_class, transport_class, transpor
== "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
- ):
- with pytest.raises(ValueError) as excinfo:
- client = client_class(transport=transport_name)
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
-
# Check the case quota_project_id is provided
options = client_options.ClientOptions(quota_project_id="octopus")
with mock.patch.object(transport_class, "__init__") as patched:
@@ -778,6 +862,119 @@ def test_publisher_client_get_mtls_endpoint_and_cert_source(client_class):
assert api_endpoint == mock_api_endpoint
assert cert_source is None
+ # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "Unsupported".
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
+ ):
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ mock_client_cert_source = mock.Mock()
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
+ options
+ )
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is None
+
+ # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset.
+ test_cases = [
+ (
+ # With workloads present in config, mTLS is enabled.
+ {
+ "version": 1,
+ "cert_configs": {
+ "workload": {
+ "cert_path": "path/to/cert/file",
+ "key_path": "path/to/key/file",
+ }
+ },
+ },
+ mock_client_cert_source,
+ ),
+ (
+ # With workloads not present in config, mTLS is disabled.
+ {
+ "version": 1,
+ "cert_configs": {},
+ },
+ None,
+ ),
+ ]
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ for config_data, expected_cert_source in test_cases:
+ env = os.environ.copy()
+ env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", None)
+ with mock.patch.dict(os.environ, env, clear=True):
+ config_filename = "mock_certificate_config.json"
+ config_file_content = json.dumps(config_data)
+ m = mock.mock_open(read_data=config_file_content)
+ with mock.patch("builtins.open", m):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename}
+ ):
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ (
+ api_endpoint,
+ cert_source,
+ ) = client_class.get_mtls_endpoint_and_cert_source(options)
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is expected_cert_source
+
+ # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset(empty).
+ test_cases = [
+ (
+ # With workloads present in config, mTLS is enabled.
+ {
+ "version": 1,
+ "cert_configs": {
+ "workload": {
+ "cert_path": "path/to/cert/file",
+ "key_path": "path/to/key/file",
+ }
+ },
+ },
+ mock_client_cert_source,
+ ),
+ (
+ # With workloads not present in config, mTLS is disabled.
+ {
+ "version": 1,
+ "cert_configs": {},
+ },
+ None,
+ ),
+ ]
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ for config_data, expected_cert_source in test_cases:
+ env = os.environ.copy()
+ env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", "")
+ with mock.patch.dict(os.environ, env, clear=True):
+ config_filename = "mock_certificate_config.json"
+ config_file_content = json.dumps(config_data)
+ m = mock.mock_open(read_data=config_file_content)
+ with mock.patch("builtins.open", m):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename}
+ ):
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ (
+ api_endpoint,
+ cert_source,
+ ) = client_class.get_mtls_endpoint_and_cert_source(options)
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is expected_cert_source
+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
@@ -828,18 +1025,6 @@ def test_publisher_client_get_mtls_endpoint_and_cert_source(client_class):
== "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
- ):
- with pytest.raises(ValueError) as excinfo:
- client_class.get_mtls_endpoint_and_cert_source()
-
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
-
@pytest.mark.parametrize("client_class", [PublisherClient, PublisherAsyncClient])
@mock.patch.object(
@@ -1129,25 +1314,6 @@ def test_create_topic(request_type, transport: str = "grpc"):
assert response.state == pubsub.Topic.State.ACTIVE
-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 = PublisherClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.create_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.Topic()
-
-
def test_create_topic_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -1213,32 +1379,6 @@ def test_create_topic_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_create_topic_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.create_topic), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Topic(
- name="name_value",
- kms_key_name="kms_key_name_value",
- satisfies_pzs=True,
- state=pubsub.Topic.State.ACTIVE,
- )
- )
- response = await client.create_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.Topic()
-
-
@pytest.mark.asyncio
async def test_create_topic_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -1247,7 +1387,7 @@ async def test_create_topic_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1286,7 +1426,7 @@ async def test_create_topic_async(
transport: str = "grpc_asyncio", request_type=pubsub.Topic
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1358,7 +1498,7 @@ def test_create_topic_field_headers():
@pytest.mark.asyncio
async def test_create_topic_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1426,7 +1566,7 @@ def test_create_topic_flattened_error():
@pytest.mark.asyncio
async def test_create_topic_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1453,7 +1593,7 @@ async def test_create_topic_flattened_async():
@pytest.mark.asyncio
async def test_create_topic_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1507,25 +1647,6 @@ def test_update_topic(request_type, transport: str = "grpc"):
assert response.state == pubsub.Topic.State.ACTIVE
-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 = PublisherClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.update_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.UpdateTopicRequest()
-
-
def test_update_topic_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -1585,32 +1706,6 @@ def test_update_topic_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_update_topic_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.update_topic), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Topic(
- name="name_value",
- kms_key_name="kms_key_name_value",
- satisfies_pzs=True,
- state=pubsub.Topic.State.ACTIVE,
- )
- )
- response = await client.update_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.UpdateTopicRequest()
-
-
@pytest.mark.asyncio
async def test_update_topic_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -1619,7 +1714,7 @@ async def test_update_topic_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1658,7 +1753,7 @@ async def test_update_topic_async(
transport: str = "grpc_asyncio", request_type=pubsub.UpdateTopicRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1730,7 +1825,7 @@ def test_update_topic_field_headers():
@pytest.mark.asyncio
async def test_update_topic_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1803,7 +1898,7 @@ def test_update_topic_flattened_error():
@pytest.mark.asyncio
async def test_update_topic_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1834,7 +1929,7 @@ async def test_update_topic_flattened_async():
@pytest.mark.asyncio
async def test_update_topic_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1883,25 +1978,6 @@ def test_publish(request_type, transport: str = "grpc"):
assert response.message_ids == ["message_ids_value"]
-def test_publish_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 = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.publish), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.publish()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.PublishRequest()
-
-
def test_publish_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -1965,36 +2041,13 @@ def test_publish_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_publish_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.publish), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.PublishResponse(
- message_ids=["message_ids_value"],
- )
- )
- response = await client.publish()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.PublishRequest()
-
-
@pytest.mark.asyncio
async def test_publish_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"):
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2033,7 +2086,7 @@ async def test_publish_async(
transport: str = "grpc_asyncio", request_type=pubsub.PublishRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2099,7 +2152,7 @@ def test_publish_field_headers():
@pytest.mark.asyncio
async def test_publish_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -2174,7 +2227,7 @@ def test_publish_flattened_error():
@pytest.mark.asyncio
async def test_publish_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2207,7 +2260,7 @@ async def test_publish_flattened_async():
@pytest.mark.asyncio
async def test_publish_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -2262,25 +2315,6 @@ def test_get_topic(request_type, transport: str = "grpc"):
assert response.state == pubsub.Topic.State.ACTIVE
-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 = PublisherClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.get_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.GetTopicRequest()
-
-
def test_get_topic_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -2344,39 +2378,13 @@ def test_get_topic_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_get_topic_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.get_topic), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Topic(
- name="name_value",
- kms_key_name="kms_key_name_value",
- satisfies_pzs=True,
- state=pubsub.Topic.State.ACTIVE,
- )
- )
- response = await client.get_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.GetTopicRequest()
-
-
@pytest.mark.asyncio
async def test_get_topic_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"):
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2415,7 +2423,7 @@ async def test_get_topic_async(
transport: str = "grpc_asyncio", request_type=pubsub.GetTopicRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2487,7 +2495,7 @@ def test_get_topic_field_headers():
@pytest.mark.asyncio
async def test_get_topic_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -2555,7 +2563,7 @@ def test_get_topic_flattened_error():
@pytest.mark.asyncio
async def test_get_topic_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2582,7 +2590,7 @@ async def test_get_topic_flattened_async():
@pytest.mark.asyncio
async def test_get_topic_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -2630,25 +2638,6 @@ def test_list_topics(request_type, transport: str = "grpc"):
assert response.next_page_token == "next_page_token_value"
-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 = PublisherClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.list_topics()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListTopicsRequest()
-
-
def test_list_topics_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -2714,29 +2703,6 @@ def test_list_topics_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_list_topics_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.list_topics), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.ListTopicsResponse(
- next_page_token="next_page_token_value",
- )
- )
- response = await client.list_topics()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListTopicsRequest()
-
-
@pytest.mark.asyncio
async def test_list_topics_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -2745,7 +2711,7 @@ async def test_list_topics_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2784,7 +2750,7 @@ async def test_list_topics_async(
transport: str = "grpc_asyncio", request_type=pubsub.ListTopicsRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2850,7 +2816,7 @@ def test_list_topics_field_headers():
@pytest.mark.asyncio
async def test_list_topics_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -2920,7 +2886,7 @@ def test_list_topics_flattened_error():
@pytest.mark.asyncio
async def test_list_topics_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2949,7 +2915,7 @@ async def test_list_topics_flattened_async():
@pytest.mark.asyncio
async def test_list_topics_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -3059,7 +3025,7 @@ def test_list_topics_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_topics_async_pager():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3109,7 +3075,7 @@ async def test_list_topics_async_pager():
@pytest.mark.asyncio
async def test_list_topics_async_pages():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3195,27 +3161,6 @@ def test_list_topic_subscriptions(request_type, transport: str = "grpc"):
assert response.next_page_token == "next_page_token_value"
-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 = PublisherClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.list_topic_subscriptions()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListTopicSubscriptionsRequest()
-
-
def test_list_topic_subscriptions_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3288,32 +3233,6 @@ def test_list_topic_subscriptions_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_list_topic_subscriptions_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # 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:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.ListTopicSubscriptionsResponse(
- subscriptions=["subscriptions_value"],
- next_page_token="next_page_token_value",
- )
- )
- response = await client.list_topic_subscriptions()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListTopicSubscriptionsRequest()
-
-
@pytest.mark.asyncio
async def test_list_topic_subscriptions_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3322,7 +3241,7 @@ async def test_list_topic_subscriptions_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3361,7 +3280,7 @@ async def test_list_topic_subscriptions_async(
transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSubscriptionsRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3433,7 +3352,7 @@ def test_list_topic_subscriptions_field_headers():
@pytest.mark.asyncio
async def test_list_topic_subscriptions_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -3507,7 +3426,7 @@ def test_list_topic_subscriptions_flattened_error():
@pytest.mark.asyncio
async def test_list_topic_subscriptions_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3538,7 +3457,7 @@ async def test_list_topic_subscriptions_flattened_async():
@pytest.mark.asyncio
async def test_list_topic_subscriptions_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -3654,7 +3573,7 @@ def test_list_topic_subscriptions_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_topic_subscriptions_async_pager():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3706,7 +3625,7 @@ async def test_list_topic_subscriptions_async_pager():
@pytest.mark.asyncio
async def test_list_topic_subscriptions_async_pages():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3794,27 +3713,6 @@ def test_list_topic_snapshots(request_type, transport: str = "grpc"):
assert response.next_page_token == "next_page_token_value"
-def test_list_topic_snapshots_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 = PublisherClient(
- credentials=ga_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_snapshots), "__call__"
- ) as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.list_topic_snapshots()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListTopicSnapshotsRequest()
-
-
def test_list_topic_snapshots_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3886,32 +3784,6 @@ def test_list_topic_snapshots_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_list_topic_snapshots_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.list_topic_snapshots), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.ListTopicSnapshotsResponse(
- snapshots=["snapshots_value"],
- next_page_token="next_page_token_value",
- )
- )
- response = await client.list_topic_snapshots()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListTopicSnapshotsRequest()
-
-
@pytest.mark.asyncio
async def test_list_topic_snapshots_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3920,7 +3792,7 @@ async def test_list_topic_snapshots_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3959,7 +3831,7 @@ async def test_list_topic_snapshots_async(
transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSnapshotsRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4031,7 +3903,7 @@ def test_list_topic_snapshots_field_headers():
@pytest.mark.asyncio
async def test_list_topic_snapshots_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4105,7 +3977,7 @@ def test_list_topic_snapshots_flattened_error():
@pytest.mark.asyncio
async def test_list_topic_snapshots_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4136,7 +4008,7 @@ async def test_list_topic_snapshots_flattened_async():
@pytest.mark.asyncio
async def test_list_topic_snapshots_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -4250,7 +4122,7 @@ def test_list_topic_snapshots_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_topic_snapshots_async_pager():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4302,7 +4174,7 @@ async def test_list_topic_snapshots_async_pager():
@pytest.mark.asyncio
async def test_list_topic_snapshots_async_pages():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4383,25 +4255,6 @@ def test_delete_topic(request_type, transport: str = "grpc"):
assert response is None
-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 = PublisherClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.delete_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DeleteTopicRequest()
-
-
def test_delete_topic_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -4465,25 +4318,6 @@ def test_delete_topic_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_delete_topic_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.delete_topic), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
- response = await client.delete_topic()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DeleteTopicRequest()
-
-
@pytest.mark.asyncio
async def test_delete_topic_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -4492,7 +4326,7 @@ async def test_delete_topic_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4531,7 +4365,7 @@ async def test_delete_topic_async(
transport: str = "grpc_asyncio", request_type=pubsub.DeleteTopicRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4592,7 +4426,7 @@ def test_delete_topic_field_headers():
@pytest.mark.asyncio
async def test_delete_topic_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4660,7 +4494,7 @@ def test_delete_topic_flattened_error():
@pytest.mark.asyncio
async def test_delete_topic_flattened_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4687,7 +4521,7 @@ async def test_delete_topic_flattened_async():
@pytest.mark.asyncio
async def test_delete_topic_flattened_error_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -4734,27 +4568,6 @@ def test_detach_subscription(request_type, transport: str = "grpc"):
assert isinstance(response, pubsub.DetachSubscriptionResponse)
-def test_detach_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 = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.detach_subscription), "__call__"
- ) as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.detach_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DetachSubscriptionRequest()
-
-
def test_detach_subscription_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -4824,29 +4637,6 @@ def test_detach_subscription_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_detach_subscription_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.detach_subscription), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.DetachSubscriptionResponse()
- )
- response = await client.detach_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DetachSubscriptionRequest()
-
-
@pytest.mark.asyncio
async def test_detach_subscription_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -4855,7 +4645,7 @@ async def test_detach_subscription_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4894,7 +4684,7 @@ async def test_detach_subscription_async(
transport: str = "grpc_asyncio", request_type=pubsub.DetachSubscriptionRequest
):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4961,7 +4751,7 @@ def test_detach_subscription_field_headers():
@pytest.mark.asyncio
async def test_detach_subscription_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4992,52 +4782,6 @@ async def test_detach_subscription_field_headers_async():
) in kw["metadata"]
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.Topic,
- dict,
- ],
-)
-def test_create_topic_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Topic(
- name="name_value",
- kms_key_name="kms_key_name_value",
- satisfies_pzs=True,
- state=pubsub.Topic.State.ACTIVE,
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Topic.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.create_topic(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Topic)
- assert response.name == "name_value"
- assert response.kms_key_name == "kms_key_name_value"
- assert response.satisfies_pzs is True
- assert response.state == pubsub.Topic.State.ACTIVE
-
-
def test_create_topic_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -5139,6 +4883,7 @@ def test_create_topic_rest_required_fields(request_type=pubsub.Topic):
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.create_topic(request)
@@ -5156,85 +4901,10 @@ def test_create_topic_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("name",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_create_topic_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
+def test_create_topic_rest_flattened():
+ client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
- )
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_create_topic"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_create_topic"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.Topic.pb(pubsub.Topic())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Topic.to_json(pubsub.Topic())
-
- request = pubsub.Topic()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Topic()
-
- client.create_topic(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_create_topic_rest_bad_request(
- transport: str = "rest", request_type=pubsub.Topic
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.create_topic(request)
-
-
-def test_create_topic_rest_flattened():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
+ transport="rest",
)
# Mock the http request call within the method and fake a response.
@@ -5259,6 +4929,7 @@ def test_create_topic_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.create_topic(**mock_args)
@@ -5286,58 +4957,6 @@ def test_create_topic_rest_flattened_error(transport: str = "rest"):
)
-def test_create_topic_rest_error():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.UpdateTopicRequest,
- dict,
- ],
-)
-def test_update_topic_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": {"name": "projects/sample1/topics/sample2"}}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Topic(
- name="name_value",
- kms_key_name="kms_key_name_value",
- satisfies_pzs=True,
- state=pubsub.Topic.State.ACTIVE,
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Topic.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.update_topic(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Topic)
- assert response.name == "name_value"
- assert response.kms_key_name == "kms_key_name_value"
- assert response.satisfies_pzs is True
- assert response.state == pubsub.Topic.State.ACTIVE
-
-
def test_update_topic_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -5434,6 +5053,7 @@ def test_update_topic_rest_required_fields(request_type=pubsub.UpdateTopicReques
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.update_topic(request)
@@ -5459,81 +5079,6 @@ def test_update_topic_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_update_topic_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
- )
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_update_topic"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_update_topic"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.UpdateTopicRequest.pb(pubsub.UpdateTopicRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Topic.to_json(pubsub.Topic())
-
- request = pubsub.UpdateTopicRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Topic()
-
- client.update_topic(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_update_topic_rest_bad_request(
- transport: str = "rest", request_type=pubsub.UpdateTopicRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": {"name": "projects/sample1/topics/sample2"}}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.update_topic(request)
-
-
def test_update_topic_rest_flattened():
client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -5563,6 +5108,7 @@ def test_update_topic_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.update_topic(**mock_args)
@@ -5591,52 +5137,6 @@ def test_update_topic_rest_flattened_error(transport: str = "rest"):
)
-def test_update_topic_rest_error():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.PublishRequest,
- dict,
- ],
-)
-def test_publish_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.PublishResponse(
- message_ids=["message_ids_value"],
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.PublishResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.publish(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.PublishResponse)
- assert response.message_ids == ["message_ids_value"]
-
-
def test_publish_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -5738,6 +5238,7 @@ def test_publish_rest_required_fields(request_type=pubsub.PublishRequest):
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.publish(request)
@@ -5763,87 +5264,10 @@ def test_publish_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_publish_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
+def test_publish_rest_flattened():
+ client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
- )
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_publish"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_publish"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.PublishRequest.pb(pubsub.PublishRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.PublishResponse.to_json(
- pubsub.PublishResponse()
- )
-
- request = pubsub.PublishRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.PublishResponse()
-
- client.publish(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_publish_rest_bad_request(
- transport: str = "rest", request_type=pubsub.PublishRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.publish(request)
-
-
-def test_publish_rest_flattened():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
+ transport="rest",
)
# Mock the http request call within the method and fake a response.
@@ -5869,6 +5293,7 @@ def test_publish_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.publish(**mock_args)
@@ -5898,58 +5323,6 @@ def test_publish_rest_flattened_error(transport: str = "rest"):
)
-def test_publish_rest_error():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.GetTopicRequest,
- dict,
- ],
-)
-def test_get_topic_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Topic(
- name="name_value",
- kms_key_name="kms_key_name_value",
- satisfies_pzs=True,
- state=pubsub.Topic.State.ACTIVE,
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Topic.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.get_topic(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Topic)
- assert response.name == "name_value"
- assert response.kms_key_name == "kms_key_name_value"
- assert response.satisfies_pzs is True
- assert response.state == pubsub.Topic.State.ACTIVE
-
-
def test_get_topic_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -6050,6 +5423,7 @@ def test_get_topic_rest_required_fields(request_type=pubsub.GetTopicRequest):
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.get_topic(request)
@@ -6067,81 +5441,6 @@ def test_get_topic_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("topic",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_get_topic_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
- )
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_get_topic"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_get_topic"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.GetTopicRequest.pb(pubsub.GetTopicRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Topic.to_json(pubsub.Topic())
-
- request = pubsub.GetTopicRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Topic()
-
- client.get_topic(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_get_topic_rest_bad_request(
- transport: str = "rest", request_type=pubsub.GetTopicRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.get_topic(request)
-
-
def test_get_topic_rest_flattened():
client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -6170,6 +5469,7 @@ def test_get_topic_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.get_topic(**mock_args)
@@ -6197,52 +5497,6 @@ def test_get_topic_rest_flattened_error(transport: str = "rest"):
)
-def test_get_topic_rest_error():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.ListTopicsRequest,
- dict,
- ],
-)
-def test_list_topics_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"project": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListTopicsResponse(
- next_page_token="next_page_token_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.ListTopicsResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.list_topics(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pagers.ListTopicsPager)
- assert response.next_page_token == "next_page_token_value"
-
-
def test_list_topics_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -6350,6 +5604,7 @@ def test_list_topics_rest_required_fields(request_type=pubsub.ListTopicsRequest)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.list_topics(request)
@@ -6375,96 +5630,19 @@ def test_list_topics_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_list_topics_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
+def test_list_topics_rest_flattened():
+ client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ transport="rest",
)
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_list_topics"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_list_topics"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.ListTopicsRequest.pb(pubsub.ListTopicsRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.ListTopicsResponse.to_json(
- pubsub.ListTopicsResponse()
- )
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListTopicsResponse()
- request = pubsub.ListTopicsRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.ListTopicsResponse()
-
- client.list_topics(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_list_topics_rest_bad_request(
- transport: str = "rest", request_type=pubsub.ListTopicsRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"project": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.list_topics(request)
-
-
-def test_list_topics_rest_flattened():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListTopicsResponse()
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"project": "projects/sample1"}
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"project": "projects/sample1"}
# get truthy value for each flattened field
mock_args = dict(
@@ -6480,6 +5658,7 @@ def test_list_topics_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.list_topics(**mock_args)
@@ -6568,48 +5747,6 @@ def test_list_topics_rest_pager(transport: str = "rest"):
assert page_.raw_page.next_page_token == token
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.ListTopicSubscriptionsRequest,
- dict,
- ],
-)
-def test_list_topic_subscriptions_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListTopicSubscriptionsResponse(
- subscriptions=["subscriptions_value"],
- next_page_token="next_page_token_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.ListTopicSubscriptionsResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.list_topic_subscriptions(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pagers.ListTopicSubscriptionsPager)
- assert response.subscriptions == ["subscriptions_value"]
- assert response.next_page_token == "next_page_token_value"
-
-
def test_list_topic_subscriptions_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -6724,6 +5861,7 @@ def test_list_topic_subscriptions_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.list_topic_subscriptions(request)
@@ -6749,85 +5887,6 @@ def test_list_topic_subscriptions_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_list_topic_subscriptions_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
- )
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_list_topic_subscriptions"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_list_topic_subscriptions"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.ListTopicSubscriptionsRequest.pb(
- pubsub.ListTopicSubscriptionsRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.ListTopicSubscriptionsResponse.to_json(
- pubsub.ListTopicSubscriptionsResponse()
- )
-
- request = pubsub.ListTopicSubscriptionsRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.ListTopicSubscriptionsResponse()
-
- client.list_topic_subscriptions(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_list_topic_subscriptions_rest_bad_request(
- transport: str = "rest", request_type=pubsub.ListTopicSubscriptionsRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.list_topic_subscriptions(request)
-
-
def test_list_topic_subscriptions_rest_flattened():
client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -6856,6 +5915,7 @@ def test_list_topic_subscriptions_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.list_topic_subscriptions(**mock_args)
@@ -6947,48 +6007,6 @@ def test_list_topic_subscriptions_rest_pager(transport: str = "rest"):
assert page_.raw_page.next_page_token == token
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.ListTopicSnapshotsRequest,
- dict,
- ],
-)
-def test_list_topic_snapshots_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListTopicSnapshotsResponse(
- snapshots=["snapshots_value"],
- next_page_token="next_page_token_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.ListTopicSnapshotsResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.list_topic_snapshots(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pagers.ListTopicSnapshotsPager)
- assert response.snapshots == ["snapshots_value"]
- assert response.next_page_token == "next_page_token_value"
-
-
def test_list_topic_snapshots_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7102,6 +6120,7 @@ def test_list_topic_snapshots_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.list_topic_snapshots(request)
@@ -7127,104 +6146,25 @@ def test_list_topic_snapshots_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_list_topic_snapshots_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
+def test_list_topic_snapshots_rest_flattened():
+ client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ transport="rest",
)
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_list_topic_snapshots"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_list_topic_snapshots"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.ListTopicSnapshotsRequest.pb(
- pubsub.ListTopicSnapshotsRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.ListTopicSnapshotsResponse.to_json(
- pubsub.ListTopicSnapshotsResponse()
- )
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListTopicSnapshotsResponse()
- request = pubsub.ListTopicSnapshotsRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.ListTopicSnapshotsResponse()
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"topic": "projects/sample1/topics/sample2"}
- client.list_topic_snapshots(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
+ # get truthy value for each flattened field
+ mock_args = dict(
+ topic="topic_value",
)
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_list_topic_snapshots_rest_bad_request(
- transport: str = "rest", request_type=pubsub.ListTopicSnapshotsRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.list_topic_snapshots(request)
-
-
-def test_list_topic_snapshots_rest_flattened():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListTopicSnapshotsResponse()
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"topic": "projects/sample1/topics/sample2"}
-
- # get truthy value for each flattened field
- mock_args = dict(
- topic="topic_value",
- )
- mock_args.update(sample_request)
+ mock_args.update(sample_request)
# Wrap the value into a proper Response obj
response_value = Response()
@@ -7234,6 +6174,7 @@ def test_list_topic_snapshots_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.list_topic_snapshots(**mock_args)
@@ -7323,41 +6264,6 @@ def test_list_topic_snapshots_rest_pager(transport: str = "rest"):
assert page_.raw_page.next_page_token == token
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.DeleteTopicRequest,
- dict,
- ],
-)
-def test_delete_topic_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = ""
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.delete_topic(request)
-
- # Establish that the response is the type that we expect.
- assert response is None
-
-
def test_delete_topic_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7455,6 +6361,7 @@ def test_delete_topic_rest_required_fields(request_type=pubsub.DeleteTopicReques
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.delete_topic(request)
@@ -7472,75 +6379,6 @@ def test_delete_topic_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("topic",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_delete_topic_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
- )
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_delete_topic"
- ) as pre:
- pre.assert_not_called()
- pb_message = pubsub.DeleteTopicRequest.pb(pubsub.DeleteTopicRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
-
- request = pubsub.DeleteTopicRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
-
- client.delete_topic(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
-
-
-def test_delete_topic_rest_bad_request(
- transport: str = "rest", request_type=pubsub.DeleteTopicRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"topic": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.delete_topic(request)
-
-
def test_delete_topic_rest_flattened():
client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -7567,6 +6405,7 @@ def test_delete_topic_rest_flattened():
json_return_value = ""
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.delete_topic(**mock_args)
@@ -7594,49 +6433,6 @@ def test_delete_topic_rest_flattened_error(transport: str = "rest"):
)
-def test_delete_topic_rest_error():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.DetachSubscriptionRequest,
- dict,
- ],
-)
-def test_detach_subscription_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.DetachSubscriptionResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.DetachSubscriptionResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.detach_subscription(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.DetachSubscriptionResponse)
-
-
def test_detach_subscription_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7743,6 +6539,7 @@ def test_detach_subscription_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.detach_subscription(request)
@@ -7760,114 +6557,29 @@ def test_detach_subscription_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("subscription",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_detach_subscription_rest_interceptors(null_interceptor):
- transport = transports.PublisherRestTransport(
+def test_credentials_transport_error():
+ # It is an error to provide credentials and a transport instance.
+ transport = transports.PublisherGrpcTransport(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
)
- client = PublisherClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.PublisherRestInterceptor, "post_detach_subscription"
- ) as post, mock.patch.object(
- transports.PublisherRestInterceptor, "pre_detach_subscription"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.DetachSubscriptionRequest.pb(
- pubsub.DetachSubscriptionRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.DetachSubscriptionResponse.to_json(
- pubsub.DetachSubscriptionResponse()
+ with pytest.raises(ValueError):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
)
- request = pubsub.DetachSubscriptionRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.DetachSubscriptionResponse()
-
- client.detach_subscription(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
+ # It is an error to provide a credentials file and a transport instance.
+ transport = transports.PublisherGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ with pytest.raises(ValueError):
+ client = PublisherClient(
+ client_options={"credentials_file": "credentials.json"},
+ transport=transport,
)
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_detach_subscription_rest_bad_request(
- transport: str = "rest", request_type=pubsub.DetachSubscriptionRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.detach_subscription(request)
-
-
-def test_detach_subscription_rest_error():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-def test_credentials_transport_error():
- # It is an error to provide credentials and a transport instance.
- transport = transports.PublisherGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- with pytest.raises(ValueError):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # It is an error to provide a credentials file and a transport instance.
- transport = transports.PublisherGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- with pytest.raises(ValueError):
- client = PublisherClient(
- client_options={"credentials_file": "credentials.json"},
- transport=transport,
- )
-
- # It is an error to provide an api_key and a transport instance.
- transport = transports.PublisherGrpcTransport(
+ # It is an error to provide an api_key and a transport instance.
+ transport = transports.PublisherGrpcTransport(
credentials=ga_credentials.AnonymousCredentials(),
)
options = client_options.ClientOptions()
@@ -7937,18 +6649,1979 @@ def test_transport_adc(transport_class):
adc.assert_called_once()
-@pytest.mark.parametrize(
- "transport_name",
- [
- "grpc",
- "rest",
- ],
-)
-def test_transport_kind(transport_name):
- transport = PublisherClient.get_transport_class(transport_name)(
+def test_transport_kind_grpc():
+ transport = PublisherClient.get_transport_class("grpc")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "grpc"
+
+
+def test_initialize_client_w_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="grpc"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_topic_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_topic), "__call__") as call:
+ call.return_value = pubsub.Topic()
+ client.create_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.Topic()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_update_topic_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_topic), "__call__") as call:
+ call.return_value = pubsub.Topic()
+ client.update_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_publish_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.publish), "__call__") as call:
+ call.return_value = pubsub.PublishResponse()
+ client.publish(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.PublishRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_topic_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_topic), "__call__") as call:
+ call.return_value = pubsub.Topic()
+ client.get_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_topics_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_topics), "__call__") as call:
+ call.return_value = pubsub.ListTopicsResponse()
+ client.list_topics(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_topic_subscriptions_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_topic_subscriptions), "__call__"
+ ) as call:
+ call.return_value = pubsub.ListTopicSubscriptionsResponse()
+ client.list_topic_subscriptions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicSubscriptionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_topic_snapshots_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_topic_snapshots), "__call__"
+ ) as call:
+ call.return_value = pubsub.ListTopicSnapshotsResponse()
+ client.list_topic_snapshots(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicSnapshotsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_topic_empty_call_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_topic), "__call__") as call:
+ call.return_value = None
+ client.delete_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_detach_subscription_empty_call_grpc():
+ client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.detach_subscription), "__call__"
+ ) as call:
+ call.return_value = pubsub.DetachSubscriptionResponse()
+ client.detach_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DetachSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_grpc_asyncio():
+ transport = PublisherAsyncClient.get_transport_class("grpc_asyncio")(
+ credentials=async_anonymous_credentials()
+ )
+ assert transport.kind == "grpc_asyncio"
+
+
+def test_initialize_client_w_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_create_topic_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
)
- assert transport.kind == transport_name
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_topic), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Topic(
+ name="name_value",
+ kms_key_name="kms_key_name_value",
+ satisfies_pzs=True,
+ state=pubsub.Topic.State.ACTIVE,
+ )
+ )
+ await client.create_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.Topic()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_update_topic_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_topic), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Topic(
+ name="name_value",
+ kms_key_name="kms_key_name_value",
+ satisfies_pzs=True,
+ state=pubsub.Topic.State.ACTIVE,
+ )
+ )
+ await client.update_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_publish_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.publish), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.PublishResponse(
+ message_ids=["message_ids_value"],
+ )
+ )
+ await client.publish(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.PublishRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_get_topic_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_topic), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Topic(
+ name="name_value",
+ kms_key_name="kms_key_name_value",
+ satisfies_pzs=True,
+ state=pubsub.Topic.State.ACTIVE,
+ )
+ )
+ await client.get_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_list_topics_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_topics), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.ListTopicsResponse(
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_topics(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_list_topic_subscriptions_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_topic_subscriptions), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.ListTopicSubscriptionsResponse(
+ subscriptions=["subscriptions_value"],
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_topic_subscriptions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicSubscriptionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_list_topic_snapshots_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_topic_snapshots), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.ListTopicSnapshotsResponse(
+ snapshots=["snapshots_value"],
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_topic_snapshots(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicSnapshotsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_delete_topic_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_topic), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
+ await client.delete_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_detach_subscription_empty_call_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.detach_subscription), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.DetachSubscriptionResponse()
+ )
+ await client.detach_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DetachSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_rest():
+ transport = PublisherClient.get_transport_class("rest")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "rest"
+
+
+def test_create_topic_rest_bad_request(request_type=pubsub.Topic):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.create_topic(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.Topic,
+ dict,
+ ],
+)
+def test_create_topic_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Topic(
+ name="name_value",
+ kms_key_name="kms_key_name_value",
+ satisfies_pzs=True,
+ state=pubsub.Topic.State.ACTIVE,
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Topic.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.create_topic(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Topic)
+ assert response.name == "name_value"
+ assert response.kms_key_name == "kms_key_name_value"
+ assert response.satisfies_pzs is True
+ assert response.state == pubsub.Topic.State.ACTIVE
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_create_topic_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_create_topic"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_create_topic_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_create_topic"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.Topic.pb(pubsub.Topic())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Topic.to_json(pubsub.Topic())
+ req.return_value.content = return_value
+
+ request = pubsub.Topic()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Topic()
+ post_with_metadata.return_value = pubsub.Topic(), metadata
+
+ client.create_topic(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_update_topic_rest_bad_request(request_type=pubsub.UpdateTopicRequest):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"topic": {"name": "projects/sample1/topics/sample2"}}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.update_topic(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.UpdateTopicRequest,
+ dict,
+ ],
+)
+def test_update_topic_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"topic": {"name": "projects/sample1/topics/sample2"}}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Topic(
+ name="name_value",
+ kms_key_name="kms_key_name_value",
+ satisfies_pzs=True,
+ state=pubsub.Topic.State.ACTIVE,
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Topic.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.update_topic(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Topic)
+ assert response.name == "name_value"
+ assert response.kms_key_name == "kms_key_name_value"
+ assert response.satisfies_pzs is True
+ assert response.state == pubsub.Topic.State.ACTIVE
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_update_topic_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_update_topic"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_update_topic_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_update_topic"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.UpdateTopicRequest.pb(pubsub.UpdateTopicRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Topic.to_json(pubsub.Topic())
+ req.return_value.content = return_value
+
+ request = pubsub.UpdateTopicRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Topic()
+ post_with_metadata.return_value = pubsub.Topic(), metadata
+
+ client.update_topic(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_publish_rest_bad_request(request_type=pubsub.PublishRequest):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.publish(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.PublishRequest,
+ dict,
+ ],
+)
+def test_publish_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.PublishResponse(
+ message_ids=["message_ids_value"],
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.PublishResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.publish(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.PublishResponse)
+ assert response.message_ids == ["message_ids_value"]
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_publish_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_publish"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_publish_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_publish"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.PublishRequest.pb(pubsub.PublishRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.PublishResponse.to_json(pubsub.PublishResponse())
+ req.return_value.content = return_value
+
+ request = pubsub.PublishRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.PublishResponse()
+ post_with_metadata.return_value = pubsub.PublishResponse(), metadata
+
+ client.publish(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_get_topic_rest_bad_request(request_type=pubsub.GetTopicRequest):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.get_topic(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.GetTopicRequest,
+ dict,
+ ],
+)
+def test_get_topic_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Topic(
+ name="name_value",
+ kms_key_name="kms_key_name_value",
+ satisfies_pzs=True,
+ state=pubsub.Topic.State.ACTIVE,
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Topic.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.get_topic(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Topic)
+ assert response.name == "name_value"
+ assert response.kms_key_name == "kms_key_name_value"
+ assert response.satisfies_pzs is True
+ assert response.state == pubsub.Topic.State.ACTIVE
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_get_topic_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_get_topic"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_get_topic_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_get_topic"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.GetTopicRequest.pb(pubsub.GetTopicRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Topic.to_json(pubsub.Topic())
+ req.return_value.content = return_value
+
+ request = pubsub.GetTopicRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Topic()
+ post_with_metadata.return_value = pubsub.Topic(), metadata
+
+ client.get_topic(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_topics_rest_bad_request(request_type=pubsub.ListTopicsRequest):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"project": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.list_topics(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.ListTopicsRequest,
+ dict,
+ ],
+)
+def test_list_topics_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"project": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListTopicsResponse(
+ next_page_token="next_page_token_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.ListTopicsResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.list_topics(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListTopicsPager)
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_topics_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_list_topics"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_list_topics_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_list_topics"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.ListTopicsRequest.pb(pubsub.ListTopicsRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.ListTopicsResponse.to_json(pubsub.ListTopicsResponse())
+ req.return_value.content = return_value
+
+ request = pubsub.ListTopicsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.ListTopicsResponse()
+ post_with_metadata.return_value = pubsub.ListTopicsResponse(), metadata
+
+ client.list_topics(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_topic_subscriptions_rest_bad_request(
+ request_type=pubsub.ListTopicSubscriptionsRequest,
+):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.list_topic_subscriptions(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.ListTopicSubscriptionsRequest,
+ dict,
+ ],
+)
+def test_list_topic_subscriptions_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListTopicSubscriptionsResponse(
+ subscriptions=["subscriptions_value"],
+ next_page_token="next_page_token_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.ListTopicSubscriptionsResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.list_topic_subscriptions(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListTopicSubscriptionsPager)
+ assert response.subscriptions == ["subscriptions_value"]
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_topic_subscriptions_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_list_topic_subscriptions"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor,
+ "post_list_topic_subscriptions_with_metadata",
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_list_topic_subscriptions"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.ListTopicSubscriptionsRequest.pb(
+ pubsub.ListTopicSubscriptionsRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.ListTopicSubscriptionsResponse.to_json(
+ pubsub.ListTopicSubscriptionsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = pubsub.ListTopicSubscriptionsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.ListTopicSubscriptionsResponse()
+ post_with_metadata.return_value = (
+ pubsub.ListTopicSubscriptionsResponse(),
+ metadata,
+ )
+
+ client.list_topic_subscriptions(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_topic_snapshots_rest_bad_request(
+ request_type=pubsub.ListTopicSnapshotsRequest,
+):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.list_topic_snapshots(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.ListTopicSnapshotsRequest,
+ dict,
+ ],
+)
+def test_list_topic_snapshots_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListTopicSnapshotsResponse(
+ snapshots=["snapshots_value"],
+ next_page_token="next_page_token_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.ListTopicSnapshotsResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.list_topic_snapshots(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListTopicSnapshotsPager)
+ assert response.snapshots == ["snapshots_value"]
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_topic_snapshots_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_list_topic_snapshots"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_list_topic_snapshots_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_list_topic_snapshots"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.ListTopicSnapshotsRequest.pb(
+ pubsub.ListTopicSnapshotsRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.ListTopicSnapshotsResponse.to_json(
+ pubsub.ListTopicSnapshotsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = pubsub.ListTopicSnapshotsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.ListTopicSnapshotsResponse()
+ post_with_metadata.return_value = pubsub.ListTopicSnapshotsResponse(), metadata
+
+ client.list_topic_snapshots(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_delete_topic_rest_bad_request(request_type=pubsub.DeleteTopicRequest):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.delete_topic(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.DeleteTopicRequest,
+ dict,
+ ],
+)
+def test_delete_topic_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"topic": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = ""
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.delete_topic(request)
+
+ # Establish that the response is the type that we expect.
+ assert response is None
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_delete_topic_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_delete_topic"
+ ) as pre:
+ pre.assert_not_called()
+ pb_message = pubsub.DeleteTopicRequest.pb(pubsub.DeleteTopicRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ request = pubsub.DeleteTopicRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+
+ client.delete_topic(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+
+
+def test_detach_subscription_rest_bad_request(
+ request_type=pubsub.DetachSubscriptionRequest,
+):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.detach_subscription(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.DetachSubscriptionRequest,
+ dict,
+ ],
+)
+def test_detach_subscription_rest_call_success(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.DetachSubscriptionResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.DetachSubscriptionResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.detach_subscription(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.DetachSubscriptionResponse)
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_detach_subscription_rest_interceptors(null_interceptor):
+ transport = transports.PublisherRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None if null_interceptor else transports.PublisherRestInterceptor(),
+ )
+ client = PublisherClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_detach_subscription"
+ ) as post, mock.patch.object(
+ transports.PublisherRestInterceptor, "post_detach_subscription_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.PublisherRestInterceptor, "pre_detach_subscription"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.DetachSubscriptionRequest.pb(
+ pubsub.DetachSubscriptionRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.DetachSubscriptionResponse.to_json(
+ pubsub.DetachSubscriptionResponse()
+ )
+ req.return_value.content = return_value
+
+ request = pubsub.DetachSubscriptionRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.DetachSubscriptionResponse()
+ post_with_metadata.return_value = pubsub.DetachSubscriptionResponse(), metadata
+
+ client.detach_subscription(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_get_iam_policy_rest_bad_request(
+ request_type=iam_policy_pb2.GetIamPolicyRequest,
+):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/topics/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.get_iam_policy(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.GetIamPolicyRequest,
+ dict,
+ ],
+)
+def test_get_iam_policy_rest(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = policy_pb2.Policy()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.get_iam_policy(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, policy_pb2.Policy)
+
+
+def test_set_iam_policy_rest_bad_request(
+ request_type=iam_policy_pb2.SetIamPolicyRequest,
+):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/topics/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.set_iam_policy(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.SetIamPolicyRequest,
+ dict,
+ ],
+)
+def test_set_iam_policy_rest(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = policy_pb2.Policy()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.set_iam_policy(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, policy_pb2.Policy)
+
+
+def test_test_iam_permissions_rest_bad_request(
+ request_type=iam_policy_pb2.TestIamPermissionsRequest,
+):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/subscriptions/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.test_iam_permissions(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.TestIamPermissionsRequest,
+ dict,
+ ],
+)
+def test_test_iam_permissions_rest(request_type):
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = iam_policy_pb2.TestIamPermissionsResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.test_iam_permissions(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse)
+
+
+def test_initialize_client_w_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_topic_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_topic), "__call__") as call:
+ client.create_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.Topic()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_update_topic_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_topic), "__call__") as call:
+ client.update_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_publish_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.publish), "__call__") as call:
+ client.publish(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.PublishRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_topic_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_topic), "__call__") as call:
+ client.get_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_topics_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_topics), "__call__") as call:
+ client.list_topics(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_topic_subscriptions_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_topic_subscriptions), "__call__"
+ ) as call:
+ client.list_topic_subscriptions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicSubscriptionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_topic_snapshots_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_topic_snapshots), "__call__"
+ ) as call:
+ client.list_topic_snapshots(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListTopicSnapshotsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_topic_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_topic), "__call__") as call:
+ client.delete_topic(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteTopicRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_detach_subscription_empty_call_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.detach_subscription), "__call__"
+ ) as call:
+ client.detach_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DetachSubscriptionRequest()
+
+ assert args[0] == request_msg
def test_transport_grpc_default():
@@ -8324,6 +8997,7 @@ def test_publisher_grpc_asyncio_transport_channel():
# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are
# removed from grpc/grpc_asyncio transport constructor.
+@pytest.mark.filterwarnings("ignore::FutureWarning")
@pytest.mark.parametrize(
"transport_class",
[transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport],
@@ -8418,9 +9092,38 @@ def test_publisher_transport_channel_mtls_with_adc(transport_class):
assert transport.grpc_channel == mock_grpc_channel
-def test_schema_path():
+def test_crypto_key_path():
project = "squid"
- schema = "clam"
+ location = "clam"
+ key_ring = "whelk"
+ crypto_key = "octopus"
+ expected = "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}".format(
+ project=project,
+ location=location,
+ key_ring=key_ring,
+ crypto_key=crypto_key,
+ )
+ actual = PublisherClient.crypto_key_path(project, location, key_ring, crypto_key)
+ assert expected == actual
+
+
+def test_parse_crypto_key_path():
+ expected = {
+ "project": "oyster",
+ "location": "nudibranch",
+ "key_ring": "cuttlefish",
+ "crypto_key": "mussel",
+ }
+ path = PublisherClient.crypto_key_path(**expected)
+
+ # Check that the path construction is reversible.
+ actual = PublisherClient.parse_crypto_key_path(path)
+ assert expected == actual
+
+
+def test_schema_path():
+ project = "winkle"
+ schema = "nautilus"
expected = "projects/{project}/schemas/{schema}".format(
project=project,
schema=schema,
@@ -8431,8 +9134,8 @@ def test_schema_path():
def test_parse_schema_path():
expected = {
- "project": "whelk",
- "schema": "octopus",
+ "project": "scallop",
+ "schema": "abalone",
}
path = PublisherClient.schema_path(**expected)
@@ -8441,6 +9144,29 @@ def test_parse_schema_path():
assert expected == actual
+def test_snapshot_path():
+ project = "squid"
+ snapshot = "clam"
+ expected = "projects/{project}/snapshots/{snapshot}".format(
+ project=project,
+ snapshot=snapshot,
+ )
+ actual = PublisherClient.snapshot_path(project, snapshot)
+ assert expected == actual
+
+
+def test_parse_snapshot_path():
+ expected = {
+ "project": "whelk",
+ "snapshot": "octopus",
+ }
+ path = PublisherClient.snapshot_path(**expected)
+
+ # Check that the path construction is reversible.
+ actual = PublisherClient.parse_snapshot_path(path)
+ assert expected == actual
+
+
def test_subscription_path():
project = "oyster"
subscription = "nudibranch"
@@ -8613,194 +9339,6 @@ def test_client_with_default_client_info():
prep.assert_called_once_with(client_info)
-@pytest.mark.asyncio
-async def test_transport_close_async():
- client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
- with mock.patch.object(
- type(getattr(client.transport, "grpc_channel")), "close"
- ) as close:
- async with client:
- close.assert_not_called()
- close.assert_called_once()
-
-
-def test_get_iam_policy_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.GetIamPolicyRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/topics/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.get_iam_policy(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.GetIamPolicyRequest,
- dict,
- ],
-)
-def test_get_iam_policy_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = policy_pb2.Policy()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.get_iam_policy(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, policy_pb2.Policy)
-
-
-def test_set_iam_policy_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.SetIamPolicyRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/topics/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.set_iam_policy(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.SetIamPolicyRequest,
- dict,
- ],
-)
-def test_set_iam_policy_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = policy_pb2.Policy()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.set_iam_policy(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, policy_pb2.Policy)
-
-
-def test_test_iam_permissions_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.TestIamPermissionsRequest
-):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/subscriptions/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.test_iam_permissions(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.TestIamPermissionsRequest,
- dict,
- ],
-)
-def test_test_iam_permissions_rest(request_type):
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = iam_policy_pb2.TestIamPermissionsResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.test_iam_permissions(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse)
-
-
def test_set_iam_policy(transport: str = "grpc"):
client = PublisherClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -8836,7 +9374,7 @@ def test_set_iam_policy(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_set_iam_policy_async(transport: str = "grpc_asyncio"):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -8901,7 +9439,7 @@ def test_set_iam_policy_field_headers():
@pytest.mark.asyncio
async def test_set_iam_policy_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -8949,7 +9487,7 @@ def test_set_iam_policy_from_dict():
@pytest.mark.asyncio
async def test_set_iam_policy_from_dict_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call:
@@ -9002,7 +9540,7 @@ def test_get_iam_policy(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_get_iam_policy_async(transport: str = "grpc_asyncio"):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -9068,7 +9606,7 @@ def test_get_iam_policy_field_headers():
@pytest.mark.asyncio
async def test_get_iam_policy_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -9116,7 +9654,7 @@ def test_get_iam_policy_from_dict():
@pytest.mark.asyncio
async def test_get_iam_policy_from_dict_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call:
@@ -9168,7 +9706,7 @@ def test_test_iam_permissions(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_test_iam_permissions_async(transport: str = "grpc_asyncio"):
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -9235,7 +9773,7 @@ def test_test_iam_permissions_field_headers():
@pytest.mark.asyncio
async def test_test_iam_permissions_field_headers_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -9289,7 +9827,7 @@ def test_test_iam_permissions_from_dict():
@pytest.mark.asyncio
async def test_test_iam_permissions_from_dict_async():
client = PublisherAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(
@@ -9309,22 +9847,41 @@ async def test_test_iam_permissions_from_dict_async():
call.assert_called()
-def test_transport_close():
- transports = {
- "rest": "_session",
- "grpc": "_grpc_channel",
- }
+def test_transport_close_grpc():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="grpc"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_grpc_channel")), "close"
+ ) as close:
+ with client:
+ close.assert_not_called()
+ close.assert_called_once()
- for transport, close_name in transports.items():
- client = PublisherClient(
- credentials=ga_credentials.AnonymousCredentials(), transport=transport
- )
- with mock.patch.object(
- type(getattr(client.transport, close_name)), "close"
- ) as close:
- with client:
- close.assert_not_called()
- close.assert_called_once()
+
+@pytest.mark.asyncio
+async def test_transport_close_grpc_asyncio():
+ client = PublisherAsyncClient(
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_grpc_channel")), "close"
+ ) as close:
+ async with client:
+ close.assert_not_called()
+ close.assert_called_once()
+
+
+def test_transport_close_rest():
+ client = PublisherClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_session")), "close"
+ ) as close:
+ with client:
+ close.assert_not_called()
+ close.assert_called_once()
def test_client_ctx():
diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py
index 7be518378..76dd7b1f8 100644
--- a/tests/unit/gapic/pubsub_v1/test_schema_service.py
+++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,16 +15,11 @@
#
import os
-# try/except added for compatibility with python < 3.8
-try:
- from unittest import mock
- from unittest.mock import AsyncMock # pragma: NO COVER
-except ImportError: # pragma: NO COVER
- import mock
+import mock
import grpc
from grpc.experimental import aio
-from collections.abc import Iterable
+from collections.abc import Iterable, AsyncIterable
from google.protobuf import json_format
import json
import math
@@ -37,6 +32,13 @@
from requests.sessions import Session
from google.protobuf import json_format
+try:
+ from google.auth.aio import credentials as ga_credentials_async
+
+ HAS_GOOGLE_AUTH_AIO = True
+except ImportError: # pragma: NO COVER
+ HAS_GOOGLE_AUTH_AIO = False
+
from google.api_core import client_options
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
@@ -60,10 +62,32 @@
import google.auth
+CRED_INFO_JSON = {
+ "credential_source": "/path/to/file",
+ "credential_type": "service account credentials",
+ "principal": "service-account@example.com",
+}
+CRED_INFO_STRING = json.dumps(CRED_INFO_JSON)
+
+
+async def mock_async_gen(data, chunk_size=1):
+ for i in range(0, len(data)): # pragma: NO COVER
+ chunk = data[i : i + chunk_size]
+ yield chunk.encode("utf-8")
+
+
def client_cert_source_callback():
return b"cert bytes", b"key bytes"
+# TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded.
+# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107.
+def async_anonymous_credentials():
+ if HAS_GOOGLE_AUTH_AIO:
+ return ga_credentials_async.AnonymousCredentials()
+ return ga_credentials.AnonymousCredentials()
+
+
# If default endpoint is localhost, then default mtls endpoint will be the same.
# This method modifies the default endpoint so the client can produce a different
# mtls endpoint for endpoint testing purposes.
@@ -131,12 +155,19 @@ def test__read_environment_variables():
with mock.patch.dict(
os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
):
- with pytest.raises(ValueError) as excinfo:
- SchemaServiceClient._read_environment_variables()
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with pytest.raises(ValueError) as excinfo:
+ SchemaServiceClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
+ )
+ else:
+ assert SchemaServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
assert SchemaServiceClient._read_environment_variables() == (
@@ -175,6 +206,105 @@ def test__read_environment_variables():
)
+def test_use_client_cert_effective():
+ # Test case 1: Test when `should_use_client_cert` returns True.
+ # We mock the `should_use_client_cert` function to simulate a scenario where
+ # the google-auth library supports automatic mTLS and determines that a
+ # client certificate should be used.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch(
+ "google.auth.transport.mtls.should_use_client_cert", return_value=True
+ ):
+ assert SchemaServiceClient._use_client_cert_effective() is True
+
+ # Test case 2: Test when `should_use_client_cert` returns False.
+ # We mock the `should_use_client_cert` function to simulate a scenario where
+ # the google-auth library supports automatic mTLS and determines that a
+ # client certificate should NOT be used.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch(
+ "google.auth.transport.mtls.should_use_client_cert", return_value=False
+ ):
+ assert SchemaServiceClient._use_client_cert_effective() is False
+
+ # Test case 3: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ assert SchemaServiceClient._use_client_cert_effective() is True
+
+ # Test case 4: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}
+ ):
+ assert SchemaServiceClient._use_client_cert_effective() is False
+
+ # Test case 5: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}):
+ assert SchemaServiceClient._use_client_cert_effective() is True
+
+ # Test case 6: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"}
+ ):
+ assert SchemaServiceClient._use_client_cert_effective() is False
+
+ # Test case 7: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}):
+ assert SchemaServiceClient._use_client_cert_effective() is True
+
+ # Test case 8: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"}
+ ):
+ assert SchemaServiceClient._use_client_cert_effective() is False
+
+ # Test case 9: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set.
+ # In this case, the method should return False, which is the default value.
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, clear=True):
+ assert SchemaServiceClient._use_client_cert_effective() is False
+
+ # Test case 10: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
+ # The method should raise a ValueError as the environment variable must be either
+ # "true" or "false".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}
+ ):
+ with pytest.raises(ValueError):
+ SchemaServiceClient._use_client_cert_effective()
+
+ # Test case 11: Test when `should_use_client_cert` is available and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
+ # The method should return False as the environment variable is set to an invalid value.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}
+ ):
+ assert SchemaServiceClient._use_client_cert_effective() is False
+
+ # Test case 12: Test when `should_use_client_cert` is available and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also,
+ # the GOOGLE_API_CONFIG environment variable is unset.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}):
+ assert SchemaServiceClient._use_client_cert_effective() is False
+
+
def test__get_client_cert_source():
mock_provided_cert_source = mock.Mock()
mock_default_cert_source = mock.Mock()
@@ -300,83 +430,46 @@ def test__get_universe_domain():
@pytest.mark.parametrize(
- "client_class,transport_class,transport_name",
+ "error_code,cred_info_json,show_cred_info",
[
- (SchemaServiceClient, transports.SchemaServiceGrpcTransport, "grpc"),
- (SchemaServiceClient, transports.SchemaServiceRestTransport, "rest"),
+ (401, CRED_INFO_JSON, True),
+ (403, CRED_INFO_JSON, True),
+ (404, CRED_INFO_JSON, True),
+ (500, CRED_INFO_JSON, False),
+ (401, None, False),
+ (403, None, False),
+ (404, None, False),
+ (500, None, False),
],
)
-def test__validate_universe_domain(client_class, transport_class, transport_name):
- client = client_class(
- transport=transport_class(credentials=ga_credentials.AnonymousCredentials())
- )
- assert client._validate_universe_domain() == True
-
- # Test the case when universe is already validated.
- assert client._validate_universe_domain() == True
-
- if transport_name == "grpc":
- # Test the case where credentials are provided by the
- # `local_channel_credentials`. The default universes in both match.
- channel = grpc.secure_channel(
- "http://localhost/", grpc.local_channel_credentials()
- )
- client = client_class(transport=transport_class(channel=channel))
- assert client._validate_universe_domain() == True
+def test__add_cred_info_for_auth_errors(error_code, cred_info_json, show_cred_info):
+ cred = mock.Mock(["get_cred_info"])
+ cred.get_cred_info = mock.Mock(return_value=cred_info_json)
+ client = SchemaServiceClient(credentials=cred)
+ client._transport._credentials = cred
+
+ error = core_exceptions.GoogleAPICallError("message", details=["foo"])
+ error.code = error_code
+
+ client._add_cred_info_for_auth_errors(error)
+ if show_cred_info:
+ assert error.details == ["foo", CRED_INFO_STRING]
+ else:
+ assert error.details == ["foo"]
- # Test the case where credentials do not exist: e.g. a transport is provided
- # with no credentials. Validation should still succeed because there is no
- # mismatch with non-existent credentials.
- channel = grpc.secure_channel(
- "http://localhost/", grpc.local_channel_credentials()
- )
- transport = transport_class(channel=channel)
- transport._credentials = None
- client = client_class(transport=transport)
- assert client._validate_universe_domain() == True
- # TODO: This is needed to cater for older versions of google-auth
- # Make this test unconditional once the minimum supported version of
- # google-auth becomes 2.23.0 or higher.
- google_auth_major, google_auth_minor = [
- int(part) for part in google.auth.__version__.split(".")[0:2]
- ]
- if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23):
- credentials = ga_credentials.AnonymousCredentials()
- credentials._universe_domain = "foo.com"
- # Test the case when there is a universe mismatch from the credentials.
- client = client_class(transport=transport_class(credentials=credentials))
- with pytest.raises(ValueError) as excinfo:
- client._validate_universe_domain()
- assert (
- str(excinfo.value)
- == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default."
- )
+@pytest.mark.parametrize("error_code", [401, 403, 404, 500])
+def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code):
+ cred = mock.Mock([])
+ assert not hasattr(cred, "get_cred_info")
+ client = SchemaServiceClient(credentials=cred)
+ client._transport._credentials = cred
- # Test the case when there is a universe mismatch from the client.
- #
- # TODO: Make this test unconditional once the minimum supported version of
- # google-api-core becomes 2.15.0 or higher.
- api_core_major, api_core_minor = [
- int(part) for part in api_core_version.__version__.split(".")[0:2]
- ]
- if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15):
- client = client_class(
- client_options={"universe_domain": "bar.com"},
- transport=transport_class(
- credentials=ga_credentials.AnonymousCredentials(),
- ),
- )
- with pytest.raises(ValueError) as excinfo:
- client._validate_universe_domain()
- assert (
- str(excinfo.value)
- == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default."
- )
+ error = core_exceptions.GoogleAPICallError("message", details=[])
+ error.code = error_code
- # Test that ValueError is raised if universe_domain is provided via client options and credentials is None
- with pytest.raises(ValueError):
- client._compare_universes("foo.bar", None)
+ client._add_cred_info_for_auth_errors(error)
+ assert error.details == []
@pytest.mark.parametrize(
@@ -577,17 +670,6 @@ def test_schema_service_client_client_options(
== "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
- ):
- with pytest.raises(ValueError) as excinfo:
- client = client_class(transport=transport_name)
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
-
# Check the case quota_project_id is provided
options = client_options.ClientOptions(quota_project_id="octopus")
with mock.patch.object(transport_class, "__init__") as patched:
@@ -803,6 +885,119 @@ def test_schema_service_client_get_mtls_endpoint_and_cert_source(client_class):
assert api_endpoint == mock_api_endpoint
assert cert_source is None
+ # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "Unsupported".
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
+ ):
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ mock_client_cert_source = mock.Mock()
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
+ options
+ )
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is None
+
+ # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset.
+ test_cases = [
+ (
+ # With workloads present in config, mTLS is enabled.
+ {
+ "version": 1,
+ "cert_configs": {
+ "workload": {
+ "cert_path": "path/to/cert/file",
+ "key_path": "path/to/key/file",
+ }
+ },
+ },
+ mock_client_cert_source,
+ ),
+ (
+ # With workloads not present in config, mTLS is disabled.
+ {
+ "version": 1,
+ "cert_configs": {},
+ },
+ None,
+ ),
+ ]
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ for config_data, expected_cert_source in test_cases:
+ env = os.environ.copy()
+ env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", None)
+ with mock.patch.dict(os.environ, env, clear=True):
+ config_filename = "mock_certificate_config.json"
+ config_file_content = json.dumps(config_data)
+ m = mock.mock_open(read_data=config_file_content)
+ with mock.patch("builtins.open", m):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename}
+ ):
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ (
+ api_endpoint,
+ cert_source,
+ ) = client_class.get_mtls_endpoint_and_cert_source(options)
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is expected_cert_source
+
+ # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset(empty).
+ test_cases = [
+ (
+ # With workloads present in config, mTLS is enabled.
+ {
+ "version": 1,
+ "cert_configs": {
+ "workload": {
+ "cert_path": "path/to/cert/file",
+ "key_path": "path/to/key/file",
+ }
+ },
+ },
+ mock_client_cert_source,
+ ),
+ (
+ # With workloads not present in config, mTLS is disabled.
+ {
+ "version": 1,
+ "cert_configs": {},
+ },
+ None,
+ ),
+ ]
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ for config_data, expected_cert_source in test_cases:
+ env = os.environ.copy()
+ env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", "")
+ with mock.patch.dict(os.environ, env, clear=True):
+ config_filename = "mock_certificate_config.json"
+ config_file_content = json.dumps(config_data)
+ m = mock.mock_open(read_data=config_file_content)
+ with mock.patch("builtins.open", m):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename}
+ ):
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ (
+ api_endpoint,
+ cert_source,
+ ) = client_class.get_mtls_endpoint_and_cert_source(options)
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is expected_cert_source
+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
@@ -853,18 +1048,6 @@ def test_schema_service_client_get_mtls_endpoint_and_cert_source(client_class):
== "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
- ):
- with pytest.raises(ValueError) as excinfo:
- client_class.get_mtls_endpoint_and_cert_source()
-
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
-
@pytest.mark.parametrize(
"client_class", [SchemaServiceClient, SchemaServiceAsyncClient]
@@ -1168,25 +1351,6 @@ def test_create_schema(request_type, transport: str = "grpc"):
assert response.revision_id == "revision_id_value"
-def test_create_schema_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.create_schema), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.create_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == gp_schema.CreateSchemaRequest()
-
-
def test_create_schema_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -1252,32 +1416,6 @@ def test_create_schema_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_create_schema_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.create_schema), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- gp_schema.Schema(
- name="name_value",
- type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
- )
- response = await client.create_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == gp_schema.CreateSchemaRequest()
-
-
@pytest.mark.asyncio
async def test_create_schema_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -1286,7 +1424,7 @@ async def test_create_schema_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1325,7 +1463,7 @@ async def test_create_schema_async(
transport: str = "grpc_asyncio", request_type=gp_schema.CreateSchemaRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1397,7 +1535,7 @@ def test_create_schema_field_headers():
@pytest.mark.asyncio
async def test_create_schema_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1475,7 +1613,7 @@ def test_create_schema_flattened_error():
@pytest.mark.asyncio
async def test_create_schema_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1510,7 +1648,7 @@ async def test_create_schema_flattened_async():
@pytest.mark.asyncio
async def test_create_schema_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1566,25 +1704,6 @@ def test_get_schema(request_type, transport: str = "grpc"):
assert response.revision_id == "revision_id_value"
-def test_get_schema_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.get_schema), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.get_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.GetSchemaRequest()
-
-
def test_get_schema_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -1648,39 +1767,13 @@ def test_get_schema_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_get_schema_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.get_schema), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- schema.Schema(
- name="name_value",
- type_=schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
- )
- response = await client.get_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.GetSchemaRequest()
-
-
@pytest.mark.asyncio
async def test_get_schema_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"):
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1719,7 +1812,7 @@ async def test_get_schema_async(
transport: str = "grpc_asyncio", request_type=schema.GetSchemaRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1791,7 +1884,7 @@ def test_get_schema_field_headers():
@pytest.mark.asyncio
async def test_get_schema_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1859,7 +1952,7 @@ def test_get_schema_flattened_error():
@pytest.mark.asyncio
async def test_get_schema_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1886,7 +1979,7 @@ async def test_get_schema_flattened_async():
@pytest.mark.asyncio
async def test_get_schema_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1934,25 +2027,6 @@ def test_list_schemas(request_type, transport: str = "grpc"):
assert response.next_page_token == "next_page_token_value"
-def test_list_schemas_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.list_schemas), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.list_schemas()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.ListSchemasRequest()
-
-
def test_list_schemas_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -2018,29 +2092,6 @@ def test_list_schemas_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_list_schemas_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.list_schemas), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- schema.ListSchemasResponse(
- next_page_token="next_page_token_value",
- )
- )
- response = await client.list_schemas()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.ListSchemasRequest()
-
-
@pytest.mark.asyncio
async def test_list_schemas_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -2049,7 +2100,7 @@ async def test_list_schemas_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2088,7 +2139,7 @@ async def test_list_schemas_async(
transport: str = "grpc_asyncio", request_type=schema.ListSchemasRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2154,7 +2205,7 @@ def test_list_schemas_field_headers():
@pytest.mark.asyncio
async def test_list_schemas_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -2224,7 +2275,7 @@ def test_list_schemas_flattened_error():
@pytest.mark.asyncio
async def test_list_schemas_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2253,7 +2304,7 @@ async def test_list_schemas_flattened_async():
@pytest.mark.asyncio
async def test_list_schemas_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -2363,7 +2414,7 @@ def test_list_schemas_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_schemas_async_pager():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2413,7 +2464,7 @@ async def test_list_schemas_async_pager():
@pytest.mark.asyncio
async def test_list_schemas_async_pages():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2497,27 +2548,6 @@ def test_list_schema_revisions(request_type, transport: str = "grpc"):
assert response.next_page_token == "next_page_token_value"
-def test_list_schema_revisions_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.list_schema_revisions), "__call__"
- ) as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.list_schema_revisions()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.ListSchemaRevisionsRequest()
-
-
def test_list_schema_revisions_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -2590,31 +2620,6 @@ def test_list_schema_revisions_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_list_schema_revisions_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.list_schema_revisions), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- schema.ListSchemaRevisionsResponse(
- next_page_token="next_page_token_value",
- )
- )
- response = await client.list_schema_revisions()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.ListSchemaRevisionsRequest()
-
-
@pytest.mark.asyncio
async def test_list_schema_revisions_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -2623,7 +2628,7 @@ async def test_list_schema_revisions_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2662,7 +2667,7 @@ async def test_list_schema_revisions_async(
transport: str = "grpc_asyncio", request_type=schema.ListSchemaRevisionsRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2732,7 +2737,7 @@ def test_list_schema_revisions_field_headers():
@pytest.mark.asyncio
async def test_list_schema_revisions_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -2806,7 +2811,7 @@ def test_list_schema_revisions_flattened_error():
@pytest.mark.asyncio
async def test_list_schema_revisions_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2837,7 +2842,7 @@ async def test_list_schema_revisions_flattened_async():
@pytest.mark.asyncio
async def test_list_schema_revisions_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -2951,7 +2956,7 @@ def test_list_schema_revisions_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_schema_revisions_async_pager():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3003,7 +3008,7 @@ async def test_list_schema_revisions_async_pager():
@pytest.mark.asyncio
async def test_list_schema_revisions_async_pages():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3093,25 +3098,6 @@ def test_commit_schema(request_type, transport: str = "grpc"):
assert response.revision_id == "revision_id_value"
-def test_commit_schema_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.commit_schema), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.commit_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == gp_schema.CommitSchemaRequest()
-
-
def test_commit_schema_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3175,32 +3161,6 @@ def test_commit_schema_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_commit_schema_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.commit_schema), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- gp_schema.Schema(
- name="name_value",
- type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
- )
- response = await client.commit_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == gp_schema.CommitSchemaRequest()
-
-
@pytest.mark.asyncio
async def test_commit_schema_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3209,7 +3169,7 @@ async def test_commit_schema_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3248,7 +3208,7 @@ async def test_commit_schema_async(
transport: str = "grpc_asyncio", request_type=gp_schema.CommitSchemaRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3320,7 +3280,7 @@ def test_commit_schema_field_headers():
@pytest.mark.asyncio
async def test_commit_schema_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -3393,7 +3353,7 @@ def test_commit_schema_flattened_error():
@pytest.mark.asyncio
async def test_commit_schema_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3424,7 +3384,7 @@ async def test_commit_schema_flattened_async():
@pytest.mark.asyncio
async def test_commit_schema_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -3479,25 +3439,6 @@ def test_rollback_schema(request_type, transport: str = "grpc"):
assert response.revision_id == "revision_id_value"
-def test_rollback_schema_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.rollback_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.RollbackSchemaRequest()
-
-
def test_rollback_schema_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3563,32 +3504,6 @@ def test_rollback_schema_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_rollback_schema_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- schema.Schema(
- name="name_value",
- type_=schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
- )
- response = await client.rollback_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.RollbackSchemaRequest()
-
-
@pytest.mark.asyncio
async def test_rollback_schema_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3597,7 +3512,7 @@ async def test_rollback_schema_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3636,7 +3551,7 @@ async def test_rollback_schema_async(
transport: str = "grpc_asyncio", request_type=schema.RollbackSchemaRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3708,7 +3623,7 @@ def test_rollback_schema_field_headers():
@pytest.mark.asyncio
async def test_rollback_schema_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -3781,7 +3696,7 @@ def test_rollback_schema_flattened_error():
@pytest.mark.asyncio
async def test_rollback_schema_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3812,7 +3727,7 @@ async def test_rollback_schema_flattened_async():
@pytest.mark.asyncio
async def test_rollback_schema_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -3869,27 +3784,6 @@ def test_delete_schema_revision(request_type, transport: str = "grpc"):
assert response.revision_id == "revision_id_value"
-def test_delete_schema_revision_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.delete_schema_revision), "__call__"
- ) as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.delete_schema_revision()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.DeleteSchemaRevisionRequest()
-
-
def test_delete_schema_revision_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3962,34 +3856,6 @@ def test_delete_schema_revision_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_delete_schema_revision_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.delete_schema_revision), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- schema.Schema(
- name="name_value",
- type_=schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
- )
- response = await client.delete_schema_revision()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.DeleteSchemaRevisionRequest()
-
-
@pytest.mark.asyncio
async def test_delete_schema_revision_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3998,7 +3864,7 @@ async def test_delete_schema_revision_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4037,7 +3903,7 @@ async def test_delete_schema_revision_async(
transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRevisionRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4113,7 +3979,7 @@ def test_delete_schema_revision_field_headers():
@pytest.mark.asyncio
async def test_delete_schema_revision_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4190,7 +4056,7 @@ def test_delete_schema_revision_flattened_error():
@pytest.mark.asyncio
async def test_delete_schema_revision_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4223,7 +4089,7 @@ async def test_delete_schema_revision_flattened_async():
@pytest.mark.asyncio
async def test_delete_schema_revision_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -4269,25 +4135,6 @@ def test_delete_schema(request_type, transport: str = "grpc"):
assert response is None
-def test_delete_schema_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.delete_schema), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.delete_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.DeleteSchemaRequest()
-
-
def test_delete_schema_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -4351,25 +4198,6 @@ def test_delete_schema_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_delete_schema_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.delete_schema), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
- response = await client.delete_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.DeleteSchemaRequest()
-
-
@pytest.mark.asyncio
async def test_delete_schema_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -4378,7 +4206,7 @@ async def test_delete_schema_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4417,7 +4245,7 @@ async def test_delete_schema_async(
transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4478,7 +4306,7 @@ def test_delete_schema_field_headers():
@pytest.mark.asyncio
async def test_delete_schema_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4546,7 +4374,7 @@ def test_delete_schema_flattened_error():
@pytest.mark.asyncio
async def test_delete_schema_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4573,7 +4401,7 @@ async def test_delete_schema_flattened_async():
@pytest.mark.asyncio
async def test_delete_schema_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -4618,25 +4446,6 @@ def test_validate_schema(request_type, transport: str = "grpc"):
assert isinstance(response, gp_schema.ValidateSchemaResponse)
-def test_validate_schema_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.validate_schema), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.validate_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == gp_schema.ValidateSchemaRequest()
-
-
def test_validate_schema_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -4700,27 +4509,6 @@ def test_validate_schema_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_validate_schema_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.validate_schema), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- gp_schema.ValidateSchemaResponse()
- )
- response = await client.validate_schema()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == gp_schema.ValidateSchemaRequest()
-
-
@pytest.mark.asyncio
async def test_validate_schema_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -4729,7 +4517,7 @@ async def test_validate_schema_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4768,7 +4556,7 @@ async def test_validate_schema_async(
transport: str = "grpc_asyncio", request_type=gp_schema.ValidateSchemaRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4831,7 +4619,7 @@ def test_validate_schema_field_headers():
@pytest.mark.asyncio
async def test_validate_schema_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4906,7 +4694,7 @@ def test_validate_schema_flattened_error():
@pytest.mark.asyncio
async def test_validate_schema_flattened_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4939,7 +4727,7 @@ async def test_validate_schema_flattened_async():
@pytest.mark.asyncio
async def test_validate_schema_flattened_error_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -4985,25 +4773,6 @@ def test_validate_message(request_type, transport: str = "grpc"):
assert isinstance(response, schema.ValidateMessageResponse)
-def test_validate_message_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 = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.validate_message), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.validate_message()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.ValidateMessageRequest()
-
-
def test_validate_message_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -5071,27 +4840,6 @@ def test_validate_message_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_validate_message_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.validate_message), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- schema.ValidateMessageResponse()
- )
- response = await client.validate_message()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == schema.ValidateMessageRequest()
-
-
@pytest.mark.asyncio
async def test_validate_message_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -5100,7 +4848,7 @@ async def test_validate_message_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -5139,7 +4887,7 @@ async def test_validate_message_async(
transport: str = "grpc_asyncio", request_type=schema.ValidateMessageRequest
):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -5202,7 +4950,7 @@ def test_validate_message_field_headers():
@pytest.mark.asyncio
async def test_validate_message_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -5231,134 +4979,14 @@ async def test_validate_message_field_headers_async():
) in kw["metadata"]
-@pytest.mark.parametrize(
- "request_type",
- [
- gp_schema.CreateSchemaRequest,
- dict,
- ],
-)
-def test_create_schema_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request_init["schema"] = {
- "name": "name_value",
- "type_": 1,
- "definition": "definition_value",
- "revision_id": "revision_id_value",
- "revision_create_time": {"seconds": 751, "nanos": 543},
- }
- # The version of a generated dependency at test runtime may differ from the version used during generation.
- # Delete any fields which are not present in the current runtime dependency
- # See https://github.com/googleapis/gapic-generator-python/issues/1748
-
- # Determine if the message type is proto-plus or protobuf
- test_field = gp_schema.CreateSchemaRequest.meta.fields["schema"]
-
- def get_message_fields(field):
- # Given a field which is a message (composite type), return a list with
- # all the fields of the message.
- # If the field is not a composite type, return an empty list.
- message_fields = []
-
- if hasattr(field, "message") and field.message:
- is_field_type_proto_plus_type = not hasattr(field.message, "DESCRIPTOR")
-
- if is_field_type_proto_plus_type:
- message_fields = field.message.meta.fields.values()
- # Add `# pragma: NO COVER` because there may not be any `*_pb2` field types
- else: # pragma: NO COVER
- message_fields = field.message.DESCRIPTOR.fields
- return message_fields
-
- runtime_nested_fields = [
- (field.name, nested_field.name)
- for field in get_message_fields(test_field)
- for nested_field in get_message_fields(field)
- ]
-
- subfields_not_in_runtime = []
-
- # For each item in the sample request, create a list of sub fields which are not present at runtime
- # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime
- for field, value in request_init["schema"].items(): # pragma: NO COVER
- result = None
- is_repeated = False
- # For repeated fields
- if isinstance(value, list) and len(value):
- is_repeated = True
- result = value[0]
- # For fields where the type is another message
- if isinstance(value, dict):
- result = value
-
- if result and hasattr(result, "keys"):
- for subfield in result.keys():
- if (field, subfield) not in runtime_nested_fields:
- subfields_not_in_runtime.append(
- {
- "field": field,
- "subfield": subfield,
- "is_repeated": is_repeated,
- }
- )
-
- # Remove fields from the sample request which are not present in the runtime version of the dependency
- # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime
- for subfield_to_delete in subfields_not_in_runtime: # pragma: NO COVER
- field = subfield_to_delete.get("field")
- field_repeated = subfield_to_delete.get("is_repeated")
- subfield = subfield_to_delete.get("subfield")
- if subfield:
- if field_repeated:
- for i in range(0, len(request_init["schema"][field])):
- del request_init["schema"][field][i][subfield]
- else:
- del request_init["schema"][field][subfield]
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = gp_schema.Schema(
- name="name_value",
- type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = gp_schema.Schema.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.create_schema(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, gp_schema.Schema)
- assert response.name == "name_value"
- assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER
- assert response.definition == "definition_value"
- assert response.revision_id == "revision_id_value"
-
-
-def test_create_schema_rest_use_cached_wrapped_rpc():
- # Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
- # instead of constructing them on each call
- with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn:
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
+def test_create_schema_rest_use_cached_wrapped_rpc():
+ # Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
+ # instead of constructing them on each call
+ with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn:
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
# Should wrap all calls on client creation
assert wrapper_fn.call_count > 0
@@ -5454,6 +5082,7 @@ def test_create_schema_rest_required_fields(request_type=gp_schema.CreateSchemaR
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.create_schema(request)
@@ -5479,83 +5108,6 @@ def test_create_schema_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_create_schema_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
- )
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_create_schema"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_create_schema"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = gp_schema.CreateSchemaRequest.pb(gp_schema.CreateSchemaRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = gp_schema.Schema.to_json(gp_schema.Schema())
-
- request = gp_schema.CreateSchemaRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = gp_schema.Schema()
-
- client.create_schema(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_create_schema_rest_bad_request(
- transport: str = "rest", request_type=gp_schema.CreateSchemaRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.create_schema(request)
-
-
def test_create_schema_rest_flattened():
client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -5586,6 +5138,7 @@ def test_create_schema_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.create_schema(**mock_args)
@@ -5615,58 +5168,6 @@ def test_create_schema_rest_flattened_error(transport: str = "rest"):
)
-def test_create_schema_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- schema.GetSchemaRequest,
- dict,
- ],
-)
-def test_get_schema_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.Schema(
- name="name_value",
- type_=schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = schema.Schema.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.get_schema(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, schema.Schema)
- assert response.name == "name_value"
- assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER
- assert response.definition == "definition_value"
- assert response.revision_id == "revision_id_value"
-
-
def test_get_schema_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -5769,6 +5270,7 @@ def test_get_schema_rest_required_fields(request_type=schema.GetSchemaRequest):
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.get_schema(request)
@@ -5786,87 +5288,10 @@ def test_get_schema_rest_unset_required_fields():
assert set(unset_fields) == (set(("view",)) & set(("name",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_get_schema_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
+def test_get_schema_rest_flattened():
+ client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
- )
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_get_schema"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_get_schema"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = schema.GetSchemaRequest.pb(schema.GetSchemaRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = schema.Schema.to_json(schema.Schema())
-
- request = schema.GetSchemaRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = schema.Schema()
-
- client.get_schema(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_get_schema_rest_bad_request(
- transport: str = "rest", request_type=schema.GetSchemaRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.get_schema(request)
-
-
-def test_get_schema_rest_flattened():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
+ transport="rest",
)
# Mock the http request call within the method and fake a response.
@@ -5891,6 +5316,7 @@ def test_get_schema_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.get_schema(**mock_args)
@@ -5918,52 +5344,6 @@ def test_get_schema_rest_flattened_error(transport: str = "rest"):
)
-def test_get_schema_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- schema.ListSchemasRequest,
- dict,
- ],
-)
-def test_list_schemas_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.ListSchemasResponse(
- next_page_token="next_page_token_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = schema.ListSchemasResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.list_schemas(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pagers.ListSchemasPager)
- assert response.next_page_token == "next_page_token_value"
-
-
def test_list_schemas_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -6072,6 +5452,7 @@ def test_list_schemas_rest_required_fields(request_type=schema.ListSchemasReques
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.list_schemas(request)
@@ -6098,85 +5479,6 @@ def test_list_schemas_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_list_schemas_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
- )
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_list_schemas"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_list_schemas"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = schema.ListSchemasRequest.pb(schema.ListSchemasRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = schema.ListSchemasResponse.to_json(
- schema.ListSchemasResponse()
- )
-
- request = schema.ListSchemasRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = schema.ListSchemasResponse()
-
- client.list_schemas(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_list_schemas_rest_bad_request(
- transport: str = "rest", request_type=schema.ListSchemasRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.list_schemas(request)
-
-
def test_list_schemas_rest_flattened():
client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -6205,6 +5507,7 @@ def test_list_schemas_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.list_schemas(**mock_args)
@@ -6293,46 +5596,6 @@ def test_list_schemas_rest_pager(transport: str = "rest"):
assert page_.raw_page.next_page_token == token
-@pytest.mark.parametrize(
- "request_type",
- [
- schema.ListSchemaRevisionsRequest,
- dict,
- ],
-)
-def test_list_schema_revisions_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.ListSchemaRevisionsResponse(
- next_page_token="next_page_token_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = schema.ListSchemaRevisionsResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.list_schema_revisions(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pagers.ListSchemaRevisionsPager)
- assert response.next_page_token == "next_page_token_value"
-
-
def test_list_schema_revisions_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -6448,6 +5711,7 @@ def test_list_schema_revisions_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.list_schema_revisions(request)
@@ -6474,100 +5738,19 @@ def test_list_schema_revisions_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_list_schema_revisions_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
+def test_list_schema_revisions_rest_flattened():
+ client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
+ transport="rest",
)
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_list_schema_revisions"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_list_schema_revisions"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = schema.ListSchemaRevisionsRequest.pb(
- schema.ListSchemaRevisionsRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = schema.ListSchemaRevisionsResponse.to_json(
- schema.ListSchemaRevisionsResponse()
- )
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.ListSchemaRevisionsResponse()
- request = schema.ListSchemaRevisionsRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = schema.ListSchemaRevisionsResponse()
-
- client.list_schema_revisions(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_list_schema_revisions_rest_bad_request(
- transport: str = "rest", request_type=schema.ListSchemaRevisionsRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.list_schema_revisions(request)
-
-
-def test_list_schema_revisions_rest_flattened():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.ListSchemaRevisionsResponse()
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"name": "projects/sample1/schemas/sample2"}
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"name": "projects/sample1/schemas/sample2"}
# get truthy value for each flattened field
mock_args = dict(
@@ -6583,6 +5766,7 @@ def test_list_schema_revisions_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.list_schema_revisions(**mock_args)
@@ -6674,52 +5858,6 @@ def test_list_schema_revisions_rest_pager(transport: str = "rest"):
assert page_.raw_page.next_page_token == token
-@pytest.mark.parametrize(
- "request_type",
- [
- gp_schema.CommitSchemaRequest,
- dict,
- ],
-)
-def test_commit_schema_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = gp_schema.Schema(
- name="name_value",
- type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = gp_schema.Schema.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.commit_schema(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, gp_schema.Schema)
- assert response.name == "name_value"
- assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER
- assert response.definition == "definition_value"
- assert response.revision_id == "revision_id_value"
-
-
def test_commit_schema_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -6821,6 +5959,7 @@ def test_commit_schema_rest_required_fields(request_type=gp_schema.CommitSchemaR
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.commit_schema(request)
@@ -6846,83 +5985,6 @@ def test_commit_schema_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_commit_schema_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
- )
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_commit_schema"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_commit_schema"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = gp_schema.CommitSchemaRequest.pb(gp_schema.CommitSchemaRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = gp_schema.Schema.to_json(gp_schema.Schema())
-
- request = gp_schema.CommitSchemaRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = gp_schema.Schema()
-
- client.commit_schema(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_commit_schema_rest_bad_request(
- transport: str = "rest", request_type=gp_schema.CommitSchemaRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.commit_schema(request)
-
-
def test_commit_schema_rest_flattened():
client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -6952,6 +6014,7 @@ def test_commit_schema_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.commit_schema(**mock_args)
@@ -6980,58 +6043,6 @@ def test_commit_schema_rest_flattened_error(transport: str = "rest"):
)
-def test_commit_schema_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- schema.RollbackSchemaRequest,
- dict,
- ],
-)
-def test_rollback_schema_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.Schema(
- name="name_value",
- type_=schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = schema.Schema.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.rollback_schema(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, schema.Schema)
- assert response.name == "name_value"
- assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER
- assert response.definition == "definition_value"
- assert response.revision_id == "revision_id_value"
-
-
def test_rollback_schema_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7139,6 +6150,7 @@ def test_rollback_schema_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.rollback_schema(request)
@@ -7164,96 +6176,19 @@ def test_rollback_schema_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_rollback_schema_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
+def test_rollback_schema_rest_flattened():
+ client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
+ transport="rest",
)
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_rollback_schema"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_rollback_schema"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = schema.RollbackSchemaRequest.pb(schema.RollbackSchemaRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = schema.Schema.to_json(schema.Schema())
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.Schema()
- request = schema.RollbackSchemaRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = schema.Schema()
-
- client.rollback_schema(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_rollback_schema_rest_bad_request(
- transport: str = "rest", request_type=schema.RollbackSchemaRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.rollback_schema(request)
-
-
-def test_rollback_schema_rest_flattened():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.Schema()
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"name": "projects/sample1/schemas/sample2"}
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"name": "projects/sample1/schemas/sample2"}
# get truthy value for each flattened field
mock_args = dict(
@@ -7270,6 +6205,7 @@ def test_rollback_schema_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.rollback_schema(**mock_args)
@@ -7299,58 +6235,6 @@ def test_rollback_schema_rest_flattened_error(transport: str = "rest"):
)
-def test_rollback_schema_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- schema.DeleteSchemaRevisionRequest,
- dict,
- ],
-)
-def test_delete_schema_revision_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.Schema(
- name="name_value",
- type_=schema.Schema.Type.PROTOCOL_BUFFER,
- definition="definition_value",
- revision_id="revision_id_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = schema.Schema.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.delete_schema_revision(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, schema.Schema)
- assert response.name == "name_value"
- assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER
- assert response.definition == "definition_value"
- assert response.revision_id == "revision_id_value"
-
-
def test_delete_schema_revision_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7460,6 +6344,7 @@ def test_delete_schema_revision_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.delete_schema_revision(request)
@@ -7477,85 +6362,6 @@ def test_delete_schema_revision_rest_unset_required_fields():
assert set(unset_fields) == (set(("revisionId",)) & set(("name",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_delete_schema_revision_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
- )
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_delete_schema_revision"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_delete_schema_revision"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = schema.DeleteSchemaRevisionRequest.pb(
- schema.DeleteSchemaRevisionRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = schema.Schema.to_json(schema.Schema())
-
- request = schema.DeleteSchemaRevisionRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = schema.Schema()
-
- client.delete_schema_revision(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_delete_schema_revision_rest_bad_request(
- transport: str = "rest", request_type=schema.DeleteSchemaRevisionRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.delete_schema_revision(request)
-
-
def test_delete_schema_revision_rest_flattened():
client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -7585,6 +6391,7 @@ def test_delete_schema_revision_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.delete_schema_revision(**mock_args)
@@ -7614,47 +6421,6 @@ def test_delete_schema_revision_rest_flattened_error(transport: str = "rest"):
)
-def test_delete_schema_revision_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- schema.DeleteSchemaRequest,
- dict,
- ],
-)
-def test_delete_schema_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = ""
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.delete_schema(request)
-
- # Establish that the response is the type that we expect.
- assert response is None
-
-
def test_delete_schema_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7752,6 +6518,7 @@ def test_delete_schema_rest_required_fields(request_type=schema.DeleteSchemaRequ
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.delete_schema(request)
@@ -7769,90 +6536,19 @@ def test_delete_schema_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("name",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_delete_schema_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
+def test_delete_schema_rest_flattened():
+ client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
+ transport="rest",
)
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_delete_schema"
- ) as pre:
- pre.assert_not_called()
- pb_message = schema.DeleteSchemaRequest.pb(schema.DeleteSchemaRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
- request = schema.DeleteSchemaRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
-
- client.delete_schema(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
-
-
-def test_delete_schema_rest_bad_request(
- transport: str = "rest", request_type=schema.DeleteSchemaRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/schemas/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.delete_schema(request)
-
-
-def test_delete_schema_rest_flattened():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"name": "projects/sample1/schemas/sample2"}
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"name": "projects/sample1/schemas/sample2"}
# get truthy value for each flattened field
mock_args = dict(
@@ -7866,6 +6562,7 @@ def test_delete_schema_rest_flattened():
json_return_value = ""
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.delete_schema(**mock_args)
@@ -7893,49 +6590,6 @@ def test_delete_schema_rest_flattened_error(transport: str = "rest"):
)
-def test_delete_schema_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- gp_schema.ValidateSchemaRequest,
- dict,
- ],
-)
-def test_validate_schema_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = gp_schema.ValidateSchemaResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = gp_schema.ValidateSchemaResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.validate_schema(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, gp_schema.ValidateSchemaResponse)
-
-
def test_validate_schema_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -8039,6 +6693,7 @@ def test_validate_schema_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.validate_schema(request)
@@ -8064,87 +6719,6 @@ def test_validate_schema_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_validate_schema_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
- )
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_validate_schema"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_validate_schema"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = gp_schema.ValidateSchemaRequest.pb(
- gp_schema.ValidateSchemaRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = gp_schema.ValidateSchemaResponse.to_json(
- gp_schema.ValidateSchemaResponse()
- )
-
- request = gp_schema.ValidateSchemaRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = gp_schema.ValidateSchemaResponse()
-
- client.validate_schema(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_validate_schema_rest_bad_request(
- transport: str = "rest", request_type=gp_schema.ValidateSchemaRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.validate_schema(request)
-
-
def test_validate_schema_rest_flattened():
client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -8174,6 +6748,7 @@ def test_validate_schema_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.validate_schema(**mock_args)
@@ -8203,49 +6778,6 @@ def test_validate_schema_rest_flattened_error(transport: str = "rest"):
)
-def test_validate_schema_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- schema.ValidateMessageRequest,
- dict,
- ],
-)
-def test_validate_message_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = schema.ValidateMessageResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = schema.ValidateMessageResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.validate_message(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, schema.ValidateMessageResponse)
-
-
def test_validate_message_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -8351,6 +6883,7 @@ def test_validate_message_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.validate_message(request)
@@ -8368,110 +6901,25 @@ def test_validate_message_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("parent",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_validate_message_rest_interceptors(null_interceptor):
- transport = transports.SchemaServiceRestTransport(
+def test_credentials_transport_error():
+ # It is an error to provide credentials and a transport instance.
+ transport = transports.SchemaServiceGrpcTransport(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SchemaServiceRestInterceptor(),
)
- client = SchemaServiceClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "post_validate_message"
- ) as post, mock.patch.object(
- transports.SchemaServiceRestInterceptor, "pre_validate_message"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = schema.ValidateMessageRequest.pb(schema.ValidateMessageRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = schema.ValidateMessageResponse.to_json(
- schema.ValidateMessageResponse()
+ with pytest.raises(ValueError):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
)
- request = schema.ValidateMessageRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = schema.ValidateMessageResponse()
-
- client.validate_message(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_validate_message_rest_bad_request(
- transport: str = "rest", request_type=schema.ValidateMessageRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"parent": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.validate_message(request)
-
-
-def test_validate_message_rest_error():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-def test_credentials_transport_error():
- # It is an error to provide credentials and a transport instance.
- transport = transports.SchemaServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- with pytest.raises(ValueError):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # It is an error to provide a credentials file and a transport instance.
- transport = transports.SchemaServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- with pytest.raises(ValueError):
- client = SchemaServiceClient(
- client_options={"credentials_file": "credentials.json"},
- transport=transport,
+ # It is an error to provide a credentials file and a transport instance.
+ transport = transports.SchemaServiceGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ with pytest.raises(ValueError):
+ client = SchemaServiceClient(
+ client_options={"credentials_file": "credentials.json"},
+ transport=transport,
)
# It is an error to provide an api_key and a transport instance.
@@ -8529,34 +6977,2275 @@ def test_transport_get_channel():
assert channel
-@pytest.mark.parametrize(
- "transport_class",
- [
- transports.SchemaServiceGrpcTransport,
- transports.SchemaServiceGrpcAsyncIOTransport,
- transports.SchemaServiceRestTransport,
- ],
-)
-def test_transport_adc(transport_class):
- # Test default credentials are used if not provided.
- with mock.patch.object(google.auth, "default") as adc:
- adc.return_value = (ga_credentials.AnonymousCredentials(), None)
- transport_class()
- adc.assert_called_once()
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.SchemaServiceGrpcTransport,
+ transports.SchemaServiceGrpcAsyncIOTransport,
+ transports.SchemaServiceRestTransport,
+ ],
+)
+def test_transport_adc(transport_class):
+ # Test default credentials are used if not provided.
+ with mock.patch.object(google.auth, "default") as adc:
+ adc.return_value = (ga_credentials.AnonymousCredentials(), None)
+ transport_class()
+ adc.assert_called_once()
+
+
+def test_transport_kind_grpc():
+ transport = SchemaServiceClient.get_transport_class("grpc")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "grpc"
+
+
+def test_initialize_client_w_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="grpc"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_schema_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_schema), "__call__") as call:
+ call.return_value = gp_schema.Schema()
+ client.create_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.CreateSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_schema_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_schema), "__call__") as call:
+ call.return_value = schema.Schema()
+ client.get_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.GetSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_schemas_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_schemas), "__call__") as call:
+ call.return_value = schema.ListSchemasResponse()
+ client.list_schemas(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ListSchemasRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_schema_revisions_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_schema_revisions), "__call__"
+ ) as call:
+ call.return_value = schema.ListSchemaRevisionsResponse()
+ client.list_schema_revisions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ListSchemaRevisionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_commit_schema_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.commit_schema), "__call__") as call:
+ call.return_value = gp_schema.Schema()
+ client.commit_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.CommitSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_rollback_schema_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call:
+ call.return_value = schema.Schema()
+ client.rollback_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.RollbackSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_schema_revision_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.delete_schema_revision), "__call__"
+ ) as call:
+ call.return_value = schema.Schema()
+ client.delete_schema_revision(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.DeleteSchemaRevisionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_schema_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_schema), "__call__") as call:
+ call.return_value = None
+ client.delete_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.DeleteSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_validate_schema_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.validate_schema), "__call__") as call:
+ call.return_value = gp_schema.ValidateSchemaResponse()
+ client.validate_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.ValidateSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_validate_message_empty_call_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.validate_message), "__call__") as call:
+ call.return_value = schema.ValidateMessageResponse()
+ client.validate_message(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ValidateMessageRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_grpc_asyncio():
+ transport = SchemaServiceAsyncClient.get_transport_class("grpc_asyncio")(
+ credentials=async_anonymous_credentials()
+ )
+ assert transport.kind == "grpc_asyncio"
+
+
+def test_initialize_client_w_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_create_schema_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_schema), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ gp_schema.Schema(
+ name="name_value",
+ type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+ )
+ await client.create_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.CreateSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_get_schema_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_schema), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ schema.Schema(
+ name="name_value",
+ type_=schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+ )
+ await client.get_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.GetSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_list_schemas_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_schemas), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ schema.ListSchemasResponse(
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_schemas(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ListSchemasRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_list_schema_revisions_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_schema_revisions), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ schema.ListSchemaRevisionsResponse(
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_schema_revisions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ListSchemaRevisionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_commit_schema_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.commit_schema), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ gp_schema.Schema(
+ name="name_value",
+ type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+ )
+ await client.commit_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.CommitSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_rollback_schema_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ schema.Schema(
+ name="name_value",
+ type_=schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+ )
+ await client.rollback_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.RollbackSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_delete_schema_revision_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.delete_schema_revision), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ schema.Schema(
+ name="name_value",
+ type_=schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+ )
+ await client.delete_schema_revision(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.DeleteSchemaRevisionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_delete_schema_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_schema), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
+ await client.delete_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.DeleteSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_validate_schema_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.validate_schema), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ gp_schema.ValidateSchemaResponse()
+ )
+ await client.validate_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.ValidateSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_validate_message_empty_call_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.validate_message), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ schema.ValidateMessageResponse()
+ )
+ await client.validate_message(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ValidateMessageRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_rest():
+ transport = SchemaServiceClient.get_transport_class("rest")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "rest"
+
+
+def test_create_schema_rest_bad_request(request_type=gp_schema.CreateSchemaRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.create_schema(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ gp_schema.CreateSchemaRequest,
+ dict,
+ ],
+)
+def test_create_schema_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request_init["schema"] = {
+ "name": "name_value",
+ "type_": 1,
+ "definition": "definition_value",
+ "revision_id": "revision_id_value",
+ "revision_create_time": {"seconds": 751, "nanos": 543},
+ }
+ # The version of a generated dependency at test runtime may differ from the version used during generation.
+ # Delete any fields which are not present in the current runtime dependency
+ # See https://github.com/googleapis/gapic-generator-python/issues/1748
+
+ # Determine if the message type is proto-plus or protobuf
+ test_field = gp_schema.CreateSchemaRequest.meta.fields["schema"]
+
+ def get_message_fields(field):
+ # Given a field which is a message (composite type), return a list with
+ # all the fields of the message.
+ # If the field is not a composite type, return an empty list.
+ message_fields = []
+
+ if hasattr(field, "message") and field.message:
+ is_field_type_proto_plus_type = not hasattr(field.message, "DESCRIPTOR")
+
+ if is_field_type_proto_plus_type:
+ message_fields = field.message.meta.fields.values()
+ # Add `# pragma: NO COVER` because there may not be any `*_pb2` field types
+ else: # pragma: NO COVER
+ message_fields = field.message.DESCRIPTOR.fields
+ return message_fields
+
+ runtime_nested_fields = [
+ (field.name, nested_field.name)
+ for field in get_message_fields(test_field)
+ for nested_field in get_message_fields(field)
+ ]
+
+ subfields_not_in_runtime = []
+
+ # For each item in the sample request, create a list of sub fields which are not present at runtime
+ # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime
+ for field, value in request_init["schema"].items(): # pragma: NO COVER
+ result = None
+ is_repeated = False
+ # For repeated fields
+ if isinstance(value, list) and len(value):
+ is_repeated = True
+ result = value[0]
+ # For fields where the type is another message
+ if isinstance(value, dict):
+ result = value
+
+ if result and hasattr(result, "keys"):
+ for subfield in result.keys():
+ if (field, subfield) not in runtime_nested_fields:
+ subfields_not_in_runtime.append(
+ {
+ "field": field,
+ "subfield": subfield,
+ "is_repeated": is_repeated,
+ }
+ )
+
+ # Remove fields from the sample request which are not present in the runtime version of the dependency
+ # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime
+ for subfield_to_delete in subfields_not_in_runtime: # pragma: NO COVER
+ field = subfield_to_delete.get("field")
+ field_repeated = subfield_to_delete.get("is_repeated")
+ subfield = subfield_to_delete.get("subfield")
+ if subfield:
+ if field_repeated:
+ for i in range(0, len(request_init["schema"][field])):
+ del request_init["schema"][field][i][subfield]
+ else:
+ del request_init["schema"][field][subfield]
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = gp_schema.Schema(
+ name="name_value",
+ type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = gp_schema.Schema.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.create_schema(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, gp_schema.Schema)
+ assert response.name == "name_value"
+ assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER
+ assert response.definition == "definition_value"
+ assert response.revision_id == "revision_id_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_create_schema_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_create_schema"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_create_schema_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_create_schema"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = gp_schema.CreateSchemaRequest.pb(gp_schema.CreateSchemaRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = gp_schema.Schema.to_json(gp_schema.Schema())
+ req.return_value.content = return_value
+
+ request = gp_schema.CreateSchemaRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = gp_schema.Schema()
+ post_with_metadata.return_value = gp_schema.Schema(), metadata
+
+ client.create_schema(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_get_schema_rest_bad_request(request_type=schema.GetSchemaRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.get_schema(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ schema.GetSchemaRequest,
+ dict,
+ ],
+)
+def test_get_schema_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.Schema(
+ name="name_value",
+ type_=schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = schema.Schema.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.get_schema(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, schema.Schema)
+ assert response.name == "name_value"
+ assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER
+ assert response.definition == "definition_value"
+ assert response.revision_id == "revision_id_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_get_schema_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_get_schema"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_get_schema_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_get_schema"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = schema.GetSchemaRequest.pb(schema.GetSchemaRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = schema.Schema.to_json(schema.Schema())
+ req.return_value.content = return_value
+
+ request = schema.GetSchemaRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = schema.Schema()
+ post_with_metadata.return_value = schema.Schema(), metadata
+
+ client.get_schema(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_schemas_rest_bad_request(request_type=schema.ListSchemasRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.list_schemas(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ schema.ListSchemasRequest,
+ dict,
+ ],
+)
+def test_list_schemas_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.ListSchemasResponse(
+ next_page_token="next_page_token_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = schema.ListSchemasResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.list_schemas(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListSchemasPager)
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_schemas_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_list_schemas"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_list_schemas_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_list_schemas"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = schema.ListSchemasRequest.pb(schema.ListSchemasRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = schema.ListSchemasResponse.to_json(schema.ListSchemasResponse())
+ req.return_value.content = return_value
+
+ request = schema.ListSchemasRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = schema.ListSchemasResponse()
+ post_with_metadata.return_value = schema.ListSchemasResponse(), metadata
+
+ client.list_schemas(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_schema_revisions_rest_bad_request(
+ request_type=schema.ListSchemaRevisionsRequest,
+):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.list_schema_revisions(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ schema.ListSchemaRevisionsRequest,
+ dict,
+ ],
+)
+def test_list_schema_revisions_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.ListSchemaRevisionsResponse(
+ next_page_token="next_page_token_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = schema.ListSchemaRevisionsResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.list_schema_revisions(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListSchemaRevisionsPager)
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_schema_revisions_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_list_schema_revisions"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor,
+ "post_list_schema_revisions_with_metadata",
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_list_schema_revisions"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = schema.ListSchemaRevisionsRequest.pb(
+ schema.ListSchemaRevisionsRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = schema.ListSchemaRevisionsResponse.to_json(
+ schema.ListSchemaRevisionsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = schema.ListSchemaRevisionsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = schema.ListSchemaRevisionsResponse()
+ post_with_metadata.return_value = schema.ListSchemaRevisionsResponse(), metadata
+
+ client.list_schema_revisions(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_commit_schema_rest_bad_request(request_type=gp_schema.CommitSchemaRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.commit_schema(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ gp_schema.CommitSchemaRequest,
+ dict,
+ ],
+)
+def test_commit_schema_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = gp_schema.Schema(
+ name="name_value",
+ type_=gp_schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = gp_schema.Schema.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.commit_schema(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, gp_schema.Schema)
+ assert response.name == "name_value"
+ assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER
+ assert response.definition == "definition_value"
+ assert response.revision_id == "revision_id_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_commit_schema_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_commit_schema"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_commit_schema_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_commit_schema"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = gp_schema.CommitSchemaRequest.pb(gp_schema.CommitSchemaRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = gp_schema.Schema.to_json(gp_schema.Schema())
+ req.return_value.content = return_value
+
+ request = gp_schema.CommitSchemaRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = gp_schema.Schema()
+ post_with_metadata.return_value = gp_schema.Schema(), metadata
+
+ client.commit_schema(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_rollback_schema_rest_bad_request(request_type=schema.RollbackSchemaRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.rollback_schema(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ schema.RollbackSchemaRequest,
+ dict,
+ ],
+)
+def test_rollback_schema_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.Schema(
+ name="name_value",
+ type_=schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = schema.Schema.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.rollback_schema(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, schema.Schema)
+ assert response.name == "name_value"
+ assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER
+ assert response.definition == "definition_value"
+ assert response.revision_id == "revision_id_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_rollback_schema_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_rollback_schema"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_rollback_schema_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_rollback_schema"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = schema.RollbackSchemaRequest.pb(schema.RollbackSchemaRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = schema.Schema.to_json(schema.Schema())
+ req.return_value.content = return_value
+
+ request = schema.RollbackSchemaRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = schema.Schema()
+ post_with_metadata.return_value = schema.Schema(), metadata
+
+ client.rollback_schema(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_delete_schema_revision_rest_bad_request(
+ request_type=schema.DeleteSchemaRevisionRequest,
+):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.delete_schema_revision(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ schema.DeleteSchemaRevisionRequest,
+ dict,
+ ],
+)
+def test_delete_schema_revision_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.Schema(
+ name="name_value",
+ type_=schema.Schema.Type.PROTOCOL_BUFFER,
+ definition="definition_value",
+ revision_id="revision_id_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = schema.Schema.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.delete_schema_revision(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, schema.Schema)
+ assert response.name == "name_value"
+ assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER
+ assert response.definition == "definition_value"
+ assert response.revision_id == "revision_id_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_delete_schema_revision_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_delete_schema_revision"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor,
+ "post_delete_schema_revision_with_metadata",
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_delete_schema_revision"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = schema.DeleteSchemaRevisionRequest.pb(
+ schema.DeleteSchemaRevisionRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = schema.Schema.to_json(schema.Schema())
+ req.return_value.content = return_value
+
+ request = schema.DeleteSchemaRevisionRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = schema.Schema()
+ post_with_metadata.return_value = schema.Schema(), metadata
+
+ client.delete_schema_revision(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_delete_schema_rest_bad_request(request_type=schema.DeleteSchemaRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.delete_schema(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ schema.DeleteSchemaRequest,
+ dict,
+ ],
+)
+def test_delete_schema_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/schemas/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = ""
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.delete_schema(request)
+
+ # Establish that the response is the type that we expect.
+ assert response is None
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_delete_schema_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_delete_schema"
+ ) as pre:
+ pre.assert_not_called()
+ pb_message = schema.DeleteSchemaRequest.pb(schema.DeleteSchemaRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ request = schema.DeleteSchemaRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+
+ client.delete_schema(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+
+
+def test_validate_schema_rest_bad_request(request_type=gp_schema.ValidateSchemaRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.validate_schema(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ gp_schema.ValidateSchemaRequest,
+ dict,
+ ],
+)
+def test_validate_schema_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = gp_schema.ValidateSchemaResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = gp_schema.ValidateSchemaResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.validate_schema(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, gp_schema.ValidateSchemaResponse)
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_validate_schema_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_validate_schema"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_validate_schema_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_validate_schema"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = gp_schema.ValidateSchemaRequest.pb(
+ gp_schema.ValidateSchemaRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = gp_schema.ValidateSchemaResponse.to_json(
+ gp_schema.ValidateSchemaResponse()
+ )
+ req.return_value.content = return_value
+
+ request = gp_schema.ValidateSchemaRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = gp_schema.ValidateSchemaResponse()
+ post_with_metadata.return_value = gp_schema.ValidateSchemaResponse(), metadata
+
+ client.validate_schema(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_validate_message_rest_bad_request(request_type=schema.ValidateMessageRequest):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.validate_message(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ schema.ValidateMessageRequest,
+ dict,
+ ],
+)
+def test_validate_message_rest_call_success(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"parent": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = schema.ValidateMessageResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = schema.ValidateMessageResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.validate_message(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, schema.ValidateMessageResponse)
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_validate_message_rest_interceptors(null_interceptor):
+ transport = transports.SchemaServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SchemaServiceRestInterceptor(),
+ )
+ client = SchemaServiceClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_validate_message"
+ ) as post, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "post_validate_message_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SchemaServiceRestInterceptor, "pre_validate_message"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = schema.ValidateMessageRequest.pb(schema.ValidateMessageRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = schema.ValidateMessageResponse.to_json(
+ schema.ValidateMessageResponse()
+ )
+ req.return_value.content = return_value
+
+ request = schema.ValidateMessageRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = schema.ValidateMessageResponse()
+ post_with_metadata.return_value = schema.ValidateMessageResponse(), metadata
+
+ client.validate_message(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_get_iam_policy_rest_bad_request(
+ request_type=iam_policy_pb2.GetIamPolicyRequest,
+):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/topics/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.get_iam_policy(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.GetIamPolicyRequest,
+ dict,
+ ],
+)
+def test_get_iam_policy_rest(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = policy_pb2.Policy()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.get_iam_policy(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, policy_pb2.Policy)
+
+
+def test_set_iam_policy_rest_bad_request(
+ request_type=iam_policy_pb2.SetIamPolicyRequest,
+):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/topics/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.set_iam_policy(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.SetIamPolicyRequest,
+ dict,
+ ],
+)
+def test_set_iam_policy_rest(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = policy_pb2.Policy()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.set_iam_policy(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, policy_pb2.Policy)
+
+
+def test_test_iam_permissions_rest_bad_request(
+ request_type=iam_policy_pb2.TestIamPermissionsRequest,
+):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/subscriptions/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.test_iam_permissions(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.TestIamPermissionsRequest,
+ dict,
+ ],
+)
+def test_test_iam_permissions_rest(request_type):
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = iam_policy_pb2.TestIamPermissionsResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.test_iam_permissions(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse)
+
+
+def test_initialize_client_w_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_schema_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_schema), "__call__") as call:
+ client.create_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.CreateSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_schema_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_schema), "__call__") as call:
+ client.get_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.GetSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_schemas_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_schemas), "__call__") as call:
+ client.list_schemas(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ListSchemasRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_schema_revisions_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_schema_revisions), "__call__"
+ ) as call:
+ client.list_schema_revisions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ListSchemaRevisionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_commit_schema_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.commit_schema), "__call__") as call:
+ client.commit_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.CommitSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_rollback_schema_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call:
+ client.rollback_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.RollbackSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_schema_revision_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.delete_schema_revision), "__call__"
+ ) as call:
+ client.delete_schema_revision(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.DeleteSchemaRevisionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_schema_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_schema), "__call__") as call:
+ client.delete_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.DeleteSchemaRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_validate_schema_empty_call_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.validate_schema), "__call__") as call:
+ client.validate_schema(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = gp_schema.ValidateSchemaRequest()
+ assert args[0] == request_msg
-@pytest.mark.parametrize(
- "transport_name",
- [
- "grpc",
- "rest",
- ],
-)
-def test_transport_kind(transport_name):
- transport = SchemaServiceClient.get_transport_class(transport_name)(
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_validate_message_empty_call_rest():
+ client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
)
- assert transport.kind == transport_name
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.validate_message), "__call__") as call:
+ client.validate_message(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = schema.ValidateMessageRequest()
+
+ assert args[0] == request_msg
def test_transport_grpc_default():
@@ -8939,6 +9628,7 @@ def test_schema_service_grpc_asyncio_transport_channel():
# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are
# removed from grpc/grpc_asyncio transport constructor.
+@pytest.mark.filterwarnings("ignore::FutureWarning")
@pytest.mark.parametrize(
"transport_class",
[
@@ -9188,194 +9878,6 @@ def test_client_with_default_client_info():
prep.assert_called_once_with(client_info)
-@pytest.mark.asyncio
-async def test_transport_close_async():
- client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
- with mock.patch.object(
- type(getattr(client.transport, "grpc_channel")), "close"
- ) as close:
- async with client:
- close.assert_not_called()
- close.assert_called_once()
-
-
-def test_get_iam_policy_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.GetIamPolicyRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/topics/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.get_iam_policy(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.GetIamPolicyRequest,
- dict,
- ],
-)
-def test_get_iam_policy_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = policy_pb2.Policy()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.get_iam_policy(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, policy_pb2.Policy)
-
-
-def test_set_iam_policy_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.SetIamPolicyRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/topics/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.set_iam_policy(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.SetIamPolicyRequest,
- dict,
- ],
-)
-def test_set_iam_policy_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = policy_pb2.Policy()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.set_iam_policy(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, policy_pb2.Policy)
-
-
-def test_test_iam_permissions_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.TestIamPermissionsRequest
-):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/subscriptions/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.test_iam_permissions(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.TestIamPermissionsRequest,
- dict,
- ],
-)
-def test_test_iam_permissions_rest(request_type):
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = iam_policy_pb2.TestIamPermissionsResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.test_iam_permissions(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse)
-
-
def test_set_iam_policy(transport: str = "grpc"):
client = SchemaServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -9411,7 +9913,7 @@ def test_set_iam_policy(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_set_iam_policy_async(transport: str = "grpc_asyncio"):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -9476,7 +9978,7 @@ def test_set_iam_policy_field_headers():
@pytest.mark.asyncio
async def test_set_iam_policy_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -9524,7 +10026,7 @@ def test_set_iam_policy_from_dict():
@pytest.mark.asyncio
async def test_set_iam_policy_from_dict_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call:
@@ -9577,7 +10079,7 @@ def test_get_iam_policy(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_get_iam_policy_async(transport: str = "grpc_asyncio"):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -9643,7 +10145,7 @@ def test_get_iam_policy_field_headers():
@pytest.mark.asyncio
async def test_get_iam_policy_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -9691,7 +10193,7 @@ def test_get_iam_policy_from_dict():
@pytest.mark.asyncio
async def test_get_iam_policy_from_dict_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call:
@@ -9743,7 +10245,7 @@ def test_test_iam_permissions(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_test_iam_permissions_async(transport: str = "grpc_asyncio"):
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -9810,7 +10312,7 @@ def test_test_iam_permissions_field_headers():
@pytest.mark.asyncio
async def test_test_iam_permissions_field_headers_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -9864,7 +10366,7 @@ def test_test_iam_permissions_from_dict():
@pytest.mark.asyncio
async def test_test_iam_permissions_from_dict_async():
client = SchemaServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(
@@ -9884,22 +10386,41 @@ async def test_test_iam_permissions_from_dict_async():
call.assert_called()
-def test_transport_close():
- transports = {
- "rest": "_session",
- "grpc": "_grpc_channel",
- }
+def test_transport_close_grpc():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="grpc"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_grpc_channel")), "close"
+ ) as close:
+ with client:
+ close.assert_not_called()
+ close.assert_called_once()
- for transport, close_name in transports.items():
- client = SchemaServiceClient(
- credentials=ga_credentials.AnonymousCredentials(), transport=transport
- )
- with mock.patch.object(
- type(getattr(client.transport, close_name)), "close"
- ) as close:
- with client:
- close.assert_not_called()
- close.assert_called_once()
+
+@pytest.mark.asyncio
+async def test_transport_close_grpc_asyncio():
+ client = SchemaServiceAsyncClient(
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_grpc_channel")), "close"
+ ) as close:
+ async with client:
+ close.assert_not_called()
+ close.assert_called_once()
+
+
+def test_transport_close_rest():
+ client = SchemaServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_session")), "close"
+ ) as close:
+ with client:
+ close.assert_not_called()
+ close.assert_called_once()
def test_client_ctx():
diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py
index 4bef862ec..1aa2e55c9 100644
--- a/tests/unit/gapic/pubsub_v1/test_subscriber.py
+++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2024 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,16 +16,11 @@
import os
import warnings
-# try/except added for compatibility with python < 3.8
-try:
- from unittest import mock
- from unittest.mock import AsyncMock # pragma: NO COVER
-except ImportError: # pragma: NO COVER
- import mock
+import mock
import grpc
from grpc.experimental import aio
-from collections.abc import Iterable
+from collections.abc import Iterable, AsyncIterable
from google.protobuf import json_format
import json
import math
@@ -38,6 +33,13 @@
from requests.sessions import Session
from google.protobuf import json_format
+try:
+ from google.auth.aio import credentials as ga_credentials_async
+
+ HAS_GOOGLE_AUTH_AIO = True
+except ImportError: # pragma: NO COVER
+ HAS_GOOGLE_AUTH_AIO = False
+
from google.api_core import client_options
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
@@ -53,6 +55,7 @@
from google.oauth2 import service_account
from google.protobuf import duration_pb2 # type: ignore
from google.protobuf import field_mask_pb2 # type: ignore
+from google.protobuf import struct_pb2 # type: ignore
from google.protobuf import timestamp_pb2 # type: ignore
from google.pubsub_v1.services.subscriber import SubscriberAsyncClient
from google.pubsub_v1.services.subscriber import SubscriberClient
@@ -62,10 +65,32 @@
import google.auth
+CRED_INFO_JSON = {
+ "credential_source": "/path/to/file",
+ "credential_type": "service account credentials",
+ "principal": "service-account@example.com",
+}
+CRED_INFO_STRING = json.dumps(CRED_INFO_JSON)
+
+
+async def mock_async_gen(data, chunk_size=1):
+ for i in range(0, len(data)): # pragma: NO COVER
+ chunk = data[i : i + chunk_size]
+ yield chunk.encode("utf-8")
+
+
def client_cert_source_callback():
return b"cert bytes", b"key bytes"
+# TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded.
+# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107.
+def async_anonymous_credentials():
+ if HAS_GOOGLE_AUTH_AIO:
+ return ga_credentials_async.AnonymousCredentials()
+ return ga_credentials.AnonymousCredentials()
+
+
# If default endpoint is localhost, then default mtls endpoint will be the same.
# This method modifies the default endpoint so the client can produce a different
# mtls endpoint for endpoint testing purposes.
@@ -126,12 +151,19 @@ def test__read_environment_variables():
with mock.patch.dict(
os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
):
- with pytest.raises(ValueError) as excinfo:
- SubscriberClient._read_environment_variables()
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with pytest.raises(ValueError) as excinfo:
+ SubscriberClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
+ )
+ else:
+ assert SubscriberClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
assert SubscriberClient._read_environment_variables() == (False, "never", None)
@@ -158,6 +190,105 @@ def test__read_environment_variables():
)
+def test_use_client_cert_effective():
+ # Test case 1: Test when `should_use_client_cert` returns True.
+ # We mock the `should_use_client_cert` function to simulate a scenario where
+ # the google-auth library supports automatic mTLS and determines that a
+ # client certificate should be used.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch(
+ "google.auth.transport.mtls.should_use_client_cert", return_value=True
+ ):
+ assert SubscriberClient._use_client_cert_effective() is True
+
+ # Test case 2: Test when `should_use_client_cert` returns False.
+ # We mock the `should_use_client_cert` function to simulate a scenario where
+ # the google-auth library supports automatic mTLS and determines that a
+ # client certificate should NOT be used.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch(
+ "google.auth.transport.mtls.should_use_client_cert", return_value=False
+ ):
+ assert SubscriberClient._use_client_cert_effective() is False
+
+ # Test case 3: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ assert SubscriberClient._use_client_cert_effective() is True
+
+ # Test case 4: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}
+ ):
+ assert SubscriberClient._use_client_cert_effective() is False
+
+ # Test case 5: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}):
+ assert SubscriberClient._use_client_cert_effective() is True
+
+ # Test case 6: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"}
+ ):
+ assert SubscriberClient._use_client_cert_effective() is False
+
+ # Test case 7: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}):
+ assert SubscriberClient._use_client_cert_effective() is True
+
+ # Test case 8: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"}
+ ):
+ assert SubscriberClient._use_client_cert_effective() is False
+
+ # Test case 9: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set.
+ # In this case, the method should return False, which is the default value.
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, clear=True):
+ assert SubscriberClient._use_client_cert_effective() is False
+
+ # Test case 10: Test when `should_use_client_cert` is unavailable and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
+ # The method should raise a ValueError as the environment variable must be either
+ # "true" or "false".
+ if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}
+ ):
+ with pytest.raises(ValueError):
+ SubscriberClient._use_client_cert_effective()
+
+ # Test case 11: Test when `should_use_client_cert` is available and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
+ # The method should return False as the environment variable is set to an invalid value.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}
+ ):
+ assert SubscriberClient._use_client_cert_effective() is False
+
+ # Test case 12: Test when `should_use_client_cert` is available and the
+ # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also,
+ # the GOOGLE_API_CONFIG environment variable is unset.
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}):
+ with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}):
+ assert SubscriberClient._use_client_cert_effective() is False
+
+
def test__get_client_cert_source():
mock_provided_cert_source = mock.Mock()
mock_default_cert_source = mock.Mock()
@@ -283,83 +414,46 @@ def test__get_universe_domain():
@pytest.mark.parametrize(
- "client_class,transport_class,transport_name",
+ "error_code,cred_info_json,show_cred_info",
[
- (SubscriberClient, transports.SubscriberGrpcTransport, "grpc"),
- (SubscriberClient, transports.SubscriberRestTransport, "rest"),
+ (401, CRED_INFO_JSON, True),
+ (403, CRED_INFO_JSON, True),
+ (404, CRED_INFO_JSON, True),
+ (500, CRED_INFO_JSON, False),
+ (401, None, False),
+ (403, None, False),
+ (404, None, False),
+ (500, None, False),
],
)
-def test__validate_universe_domain(client_class, transport_class, transport_name):
- client = client_class(
- transport=transport_class(credentials=ga_credentials.AnonymousCredentials())
- )
- assert client._validate_universe_domain() == True
-
- # Test the case when universe is already validated.
- assert client._validate_universe_domain() == True
-
- if transport_name == "grpc":
- # Test the case where credentials are provided by the
- # `local_channel_credentials`. The default universes in both match.
- channel = grpc.secure_channel(
- "http://localhost/", grpc.local_channel_credentials()
- )
- client = client_class(transport=transport_class(channel=channel))
- assert client._validate_universe_domain() == True
+def test__add_cred_info_for_auth_errors(error_code, cred_info_json, show_cred_info):
+ cred = mock.Mock(["get_cred_info"])
+ cred.get_cred_info = mock.Mock(return_value=cred_info_json)
+ client = SubscriberClient(credentials=cred)
+ client._transport._credentials = cred
+
+ error = core_exceptions.GoogleAPICallError("message", details=["foo"])
+ error.code = error_code
+
+ client._add_cred_info_for_auth_errors(error)
+ if show_cred_info:
+ assert error.details == ["foo", CRED_INFO_STRING]
+ else:
+ assert error.details == ["foo"]
- # Test the case where credentials do not exist: e.g. a transport is provided
- # with no credentials. Validation should still succeed because there is no
- # mismatch with non-existent credentials.
- channel = grpc.secure_channel(
- "http://localhost/", grpc.local_channel_credentials()
- )
- transport = transport_class(channel=channel)
- transport._credentials = None
- client = client_class(transport=transport)
- assert client._validate_universe_domain() == True
- # TODO: This is needed to cater for older versions of google-auth
- # Make this test unconditional once the minimum supported version of
- # google-auth becomes 2.23.0 or higher.
- google_auth_major, google_auth_minor = [
- int(part) for part in google.auth.__version__.split(".")[0:2]
- ]
- if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23):
- credentials = ga_credentials.AnonymousCredentials()
- credentials._universe_domain = "foo.com"
- # Test the case when there is a universe mismatch from the credentials.
- client = client_class(transport=transport_class(credentials=credentials))
- with pytest.raises(ValueError) as excinfo:
- client._validate_universe_domain()
- assert (
- str(excinfo.value)
- == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default."
- )
+@pytest.mark.parametrize("error_code", [401, 403, 404, 500])
+def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code):
+ cred = mock.Mock([])
+ assert not hasattr(cred, "get_cred_info")
+ client = SubscriberClient(credentials=cred)
+ client._transport._credentials = cred
- # Test the case when there is a universe mismatch from the client.
- #
- # TODO: Make this test unconditional once the minimum supported version of
- # google-api-core becomes 2.15.0 or higher.
- api_core_major, api_core_minor = [
- int(part) for part in api_core_version.__version__.split(".")[0:2]
- ]
- if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15):
- client = client_class(
- client_options={"universe_domain": "bar.com"},
- transport=transport_class(
- credentials=ga_credentials.AnonymousCredentials(),
- ),
- )
- with pytest.raises(ValueError) as excinfo:
- client._validate_universe_domain()
- assert (
- str(excinfo.value)
- == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default."
- )
+ error = core_exceptions.GoogleAPICallError("message", details=[])
+ error.code = error_code
- # Test that ValueError is raised if universe_domain is provided via client options and credentials is None
- with pytest.raises(ValueError):
- client._compare_universes("foo.bar", None)
+ client._add_cred_info_for_auth_errors(error)
+ assert error.details == []
@pytest.mark.parametrize(
@@ -560,17 +654,6 @@ def test_subscriber_client_client_options(
== "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
- ):
- with pytest.raises(ValueError) as excinfo:
- client = client_class(transport=transport_name)
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
-
# Check the case quota_project_id is provided
options = client_options.ClientOptions(quota_project_id="octopus")
with mock.patch.object(transport_class, "__init__") as patched:
@@ -782,6 +865,119 @@ def test_subscriber_client_get_mtls_endpoint_and_cert_source(client_class):
assert api_endpoint == mock_api_endpoint
assert cert_source is None
+ # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "Unsupported".
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
+ ):
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ mock_client_cert_source = mock.Mock()
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
+ options
+ )
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is None
+
+ # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset.
+ test_cases = [
+ (
+ # With workloads present in config, mTLS is enabled.
+ {
+ "version": 1,
+ "cert_configs": {
+ "workload": {
+ "cert_path": "path/to/cert/file",
+ "key_path": "path/to/key/file",
+ }
+ },
+ },
+ mock_client_cert_source,
+ ),
+ (
+ # With workloads not present in config, mTLS is disabled.
+ {
+ "version": 1,
+ "cert_configs": {},
+ },
+ None,
+ ),
+ ]
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ for config_data, expected_cert_source in test_cases:
+ env = os.environ.copy()
+ env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", None)
+ with mock.patch.dict(os.environ, env, clear=True):
+ config_filename = "mock_certificate_config.json"
+ config_file_content = json.dumps(config_data)
+ m = mock.mock_open(read_data=config_file_content)
+ with mock.patch("builtins.open", m):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename}
+ ):
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ (
+ api_endpoint,
+ cert_source,
+ ) = client_class.get_mtls_endpoint_and_cert_source(options)
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is expected_cert_source
+
+ # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset(empty).
+ test_cases = [
+ (
+ # With workloads present in config, mTLS is enabled.
+ {
+ "version": 1,
+ "cert_configs": {
+ "workload": {
+ "cert_path": "path/to/cert/file",
+ "key_path": "path/to/key/file",
+ }
+ },
+ },
+ mock_client_cert_source,
+ ),
+ (
+ # With workloads not present in config, mTLS is disabled.
+ {
+ "version": 1,
+ "cert_configs": {},
+ },
+ None,
+ ),
+ ]
+ if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
+ for config_data, expected_cert_source in test_cases:
+ env = os.environ.copy()
+ env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", "")
+ with mock.patch.dict(os.environ, env, clear=True):
+ config_filename = "mock_certificate_config.json"
+ config_file_content = json.dumps(config_data)
+ m = mock.mock_open(read_data=config_file_content)
+ with mock.patch("builtins.open", m):
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename}
+ ):
+ mock_api_endpoint = "foo"
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source,
+ api_endpoint=mock_api_endpoint,
+ )
+ (
+ api_endpoint,
+ cert_source,
+ ) = client_class.get_mtls_endpoint_and_cert_source(options)
+ assert api_endpoint == mock_api_endpoint
+ assert cert_source is expected_cert_source
+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
@@ -832,18 +1028,6 @@ def test_subscriber_client_get_mtls_endpoint_and_cert_source(client_class):
== "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
)
- # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
- ):
- with pytest.raises(ValueError) as excinfo:
- client_class.get_mtls_endpoint_and_cert_source()
-
- assert (
- str(excinfo.value)
- == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
- )
-
@pytest.mark.parametrize("client_class", [SubscriberClient, SubscriberAsyncClient])
@mock.patch.object(
@@ -1145,27 +1329,6 @@ def test_create_subscription(request_type, transport: str = "grpc"):
assert response.state == pubsub.Subscription.State.ACTIVE
-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 = SubscriberClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.create_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.Subscription()
-
-
def test_create_subscription_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -1239,39 +1402,6 @@ def test_create_subscription_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_create_subscription_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.create_subscription), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Subscription(
- name="name_value",
- topic="topic_value",
- ack_deadline_seconds=2066,
- retain_acked_messages=True,
- enable_message_ordering=True,
- filter="filter_value",
- detached=True,
- enable_exactly_once_delivery=True,
- state=pubsub.Subscription.State.ACTIVE,
- )
- )
- response = await client.create_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.Subscription()
-
-
@pytest.mark.asyncio
async def test_create_subscription_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -1280,7 +1410,7 @@ async def test_create_subscription_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1319,7 +1449,7 @@ async def test_create_subscription_async(
transport: str = "grpc_asyncio", request_type=pubsub.Subscription
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1405,7 +1535,7 @@ def test_create_subscription_field_headers():
@pytest.mark.asyncio
async def test_create_subscription_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1492,7 +1622,7 @@ def test_create_subscription_flattened_error():
@pytest.mark.asyncio
async def test_create_subscription_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1533,7 +1663,7 @@ async def test_create_subscription_flattened_async():
@pytest.mark.asyncio
async def test_create_subscription_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1600,25 +1730,6 @@ def test_get_subscription(request_type, transport: str = "grpc"):
assert response.state == pubsub.Subscription.State.ACTIVE
-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 = SubscriberClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.get_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.GetSubscriptionRequest()
-
-
def test_get_subscription_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -1684,37 +1795,6 @@ def test_get_subscription_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_get_subscription_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.get_subscription), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Subscription(
- name="name_value",
- topic="topic_value",
- ack_deadline_seconds=2066,
- retain_acked_messages=True,
- enable_message_ordering=True,
- filter="filter_value",
- detached=True,
- enable_exactly_once_delivery=True,
- state=pubsub.Subscription.State.ACTIVE,
- )
- )
- response = await client.get_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.GetSubscriptionRequest()
-
-
@pytest.mark.asyncio
async def test_get_subscription_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -1723,7 +1803,7 @@ async def test_get_subscription_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1762,7 +1842,7 @@ async def test_get_subscription_async(
transport: str = "grpc_asyncio", request_type=pubsub.GetSubscriptionRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1844,7 +1924,7 @@ def test_get_subscription_field_headers():
@pytest.mark.asyncio
async def test_get_subscription_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1912,7 +1992,7 @@ def test_get_subscription_flattened_error():
@pytest.mark.asyncio
async def test_get_subscription_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1939,7 +2019,7 @@ async def test_get_subscription_flattened_async():
@pytest.mark.asyncio
async def test_get_subscription_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -2005,27 +2085,6 @@ def test_update_subscription(request_type, transport: str = "grpc"):
assert response.state == pubsub.Subscription.State.ACTIVE
-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 = SubscriberClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.update_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.UpdateSubscriptionRequest()
-
-
def test_update_subscription_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -2091,39 +2150,6 @@ def test_update_subscription_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_update_subscription_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.update_subscription), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Subscription(
- name="name_value",
- topic="topic_value",
- ack_deadline_seconds=2066,
- retain_acked_messages=True,
- enable_message_ordering=True,
- filter="filter_value",
- detached=True,
- enable_exactly_once_delivery=True,
- state=pubsub.Subscription.State.ACTIVE,
- )
- )
- response = await client.update_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.UpdateSubscriptionRequest()
-
-
@pytest.mark.asyncio
async def test_update_subscription_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -2132,7 +2158,7 @@ async def test_update_subscription_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2171,7 +2197,7 @@ async def test_update_subscription_async(
transport: str = "grpc_asyncio", request_type=pubsub.UpdateSubscriptionRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2257,7 +2283,7 @@ def test_update_subscription_field_headers():
@pytest.mark.asyncio
async def test_update_subscription_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -2334,7 +2360,7 @@ def test_update_subscription_flattened_error():
@pytest.mark.asyncio
async def test_update_subscription_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2367,7 +2393,7 @@ async def test_update_subscription_flattened_async():
@pytest.mark.asyncio
async def test_update_subscription_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -2418,27 +2444,6 @@ def test_list_subscriptions(request_type, transport: str = "grpc"):
assert response.next_page_token == "next_page_token_value"
-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 = SubscriberClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.list_subscriptions()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListSubscriptionsRequest()
-
-
def test_list_subscriptions_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -2510,31 +2515,6 @@ def test_list_subscriptions_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_list_subscriptions_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.list_subscriptions), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.ListSubscriptionsResponse(
- next_page_token="next_page_token_value",
- )
- )
- response = await client.list_subscriptions()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListSubscriptionsRequest()
-
-
@pytest.mark.asyncio
async def test_list_subscriptions_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -2543,7 +2523,7 @@ async def test_list_subscriptions_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2582,7 +2562,7 @@ async def test_list_subscriptions_async(
transport: str = "grpc_asyncio", request_type=pubsub.ListSubscriptionsRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -2652,7 +2632,7 @@ def test_list_subscriptions_field_headers():
@pytest.mark.asyncio
async def test_list_subscriptions_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -2726,7 +2706,7 @@ def test_list_subscriptions_flattened_error():
@pytest.mark.asyncio
async def test_list_subscriptions_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2757,7 +2737,7 @@ async def test_list_subscriptions_flattened_async():
@pytest.mark.asyncio
async def test_list_subscriptions_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -2871,7 +2851,7 @@ def test_list_subscriptions_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_subscriptions_async_pager():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -2923,7 +2903,7 @@ async def test_list_subscriptions_async_pager():
@pytest.mark.asyncio
async def test_list_subscriptions_async_pages():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3006,27 +2986,6 @@ def test_delete_subscription(request_type, transport: str = "grpc"):
assert response is None
-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 = SubscriberClient(
- credentials=ga_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:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.delete_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DeleteSubscriptionRequest()
-
-
def test_delete_subscription_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3096,27 +3055,6 @@ def test_delete_subscription_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_delete_subscription_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.delete_subscription), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
- response = await client.delete_subscription()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DeleteSubscriptionRequest()
-
-
@pytest.mark.asyncio
async def test_delete_subscription_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3125,7 +3063,7 @@ async def test_delete_subscription_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3164,7 +3102,7 @@ async def test_delete_subscription_async(
transport: str = "grpc_asyncio", request_type=pubsub.DeleteSubscriptionRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3229,7 +3167,7 @@ def test_delete_subscription_field_headers():
@pytest.mark.asyncio
async def test_delete_subscription_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -3301,7 +3239,7 @@ def test_delete_subscription_flattened_error():
@pytest.mark.asyncio
async def test_delete_subscription_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3330,7 +3268,7 @@ async def test_delete_subscription_flattened_async():
@pytest.mark.asyncio
async def test_delete_subscription_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -3377,27 +3315,6 @@ def test_modify_ack_deadline(request_type, transport: str = "grpc"):
assert response is None
-def test_modify_ack_deadline_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.modify_ack_deadline), "__call__"
- ) as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.modify_ack_deadline()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ModifyAckDeadlineRequest()
-
-
def test_modify_ack_deadline_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3467,27 +3384,6 @@ def test_modify_ack_deadline_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_modify_ack_deadline_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.modify_ack_deadline), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
- response = await client.modify_ack_deadline()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ModifyAckDeadlineRequest()
-
-
@pytest.mark.asyncio
async def test_modify_ack_deadline_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3496,7 +3392,7 @@ async def test_modify_ack_deadline_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3535,7 +3431,7 @@ async def test_modify_ack_deadline_async(
transport: str = "grpc_asyncio", request_type=pubsub.ModifyAckDeadlineRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3600,7 +3496,7 @@ def test_modify_ack_deadline_field_headers():
@pytest.mark.asyncio
async def test_modify_ack_deadline_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -3682,7 +3578,7 @@ def test_modify_ack_deadline_flattened_error():
@pytest.mark.asyncio
async def test_modify_ack_deadline_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -3719,7 +3615,7 @@ async def test_modify_ack_deadline_flattened_async():
@pytest.mark.asyncio
async def test_modify_ack_deadline_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -3766,25 +3662,6 @@ def test_acknowledge(request_type, transport: str = "grpc"):
assert response is None
-def test_acknowledge_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.acknowledge), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.acknowledge()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.AcknowledgeRequest()
-
-
def test_acknowledge_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -3848,25 +3725,6 @@ def test_acknowledge_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_acknowledge_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.acknowledge), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
- response = await client.acknowledge()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.AcknowledgeRequest()
-
-
@pytest.mark.asyncio
async def test_acknowledge_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -3875,7 +3733,7 @@ async def test_acknowledge_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3914,7 +3772,7 @@ async def test_acknowledge_async(
transport: str = "grpc_asyncio", request_type=pubsub.AcknowledgeRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -3975,7 +3833,7 @@ def test_acknowledge_field_headers():
@pytest.mark.asyncio
async def test_acknowledge_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4048,7 +3906,7 @@ def test_acknowledge_flattened_error():
@pytest.mark.asyncio
async def test_acknowledge_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4079,7 +3937,7 @@ async def test_acknowledge_flattened_async():
@pytest.mark.asyncio
async def test_acknowledge_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -4125,25 +3983,6 @@ def test_pull(request_type, transport: str = "grpc"):
assert isinstance(response, pubsub.PullResponse)
-def test_pull_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.pull), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.pull()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.PullRequest()
-
-
def test_pull_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -4207,32 +4046,13 @@ def test_pull_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_pull_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.pull), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.PullResponse())
- response = await client.pull()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.PullRequest()
-
-
@pytest.mark.asyncio
async def test_pull_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"):
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4270,7 +4090,7 @@ async def test_pull_async(
transport: str = "grpc_asyncio", request_type=pubsub.PullRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4331,7 +4151,7 @@ def test_pull_field_headers():
@pytest.mark.asyncio
async def test_pull_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4411,7 +4231,7 @@ def test_pull_flattened_error():
@pytest.mark.asyncio
async def test_pull_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4448,7 +4268,7 @@ async def test_pull_flattened_async():
@pytest.mark.asyncio
async def test_pull_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -4539,7 +4359,7 @@ async def test_streaming_pull_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4578,7 +4398,7 @@ async def test_streaming_pull_async(
transport: str = "grpc_asyncio", request_type=pubsub.StreamingPullRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4646,27 +4466,6 @@ def test_modify_push_config(request_type, transport: str = "grpc"):
assert response is None
-def test_modify_push_config_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.modify_push_config), "__call__"
- ) as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.modify_push_config()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ModifyPushConfigRequest()
-
-
def test_modify_push_config_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -4736,27 +4535,6 @@ def test_modify_push_config_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_modify_push_config_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(
- type(client.transport.modify_push_config), "__call__"
- ) as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
- response = await client.modify_push_config()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ModifyPushConfigRequest()
-
-
@pytest.mark.asyncio
async def test_modify_push_config_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -4765,7 +4543,7 @@ async def test_modify_push_config_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4804,7 +4582,7 @@ async def test_modify_push_config_async(
transport: str = "grpc_asyncio", request_type=pubsub.ModifyPushConfigRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -4869,7 +4647,7 @@ def test_modify_push_config_field_headers():
@pytest.mark.asyncio
async def test_modify_push_config_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -4946,7 +4724,7 @@ def test_modify_push_config_flattened_error():
@pytest.mark.asyncio
async def test_modify_push_config_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -4979,7 +4757,7 @@ async def test_modify_push_config_flattened_async():
@pytest.mark.asyncio
async def test_modify_push_config_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -5030,25 +4808,6 @@ def test_get_snapshot(request_type, transport: str = "grpc"):
assert response.topic == "topic_value"
-def test_get_snapshot_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.get_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.GetSnapshotRequest()
-
-
def test_get_snapshot_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -5112,30 +4871,6 @@ def test_get_snapshot_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_get_snapshot_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Snapshot(
- name="name_value",
- topic="topic_value",
- )
- )
- response = await client.get_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.GetSnapshotRequest()
-
-
@pytest.mark.asyncio
async def test_get_snapshot_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -5144,7 +4879,7 @@ async def test_get_snapshot_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -5183,7 +4918,7 @@ async def test_get_snapshot_async(
transport: str = "grpc_asyncio", request_type=pubsub.GetSnapshotRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -5251,7 +4986,7 @@ def test_get_snapshot_field_headers():
@pytest.mark.asyncio
async def test_get_snapshot_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -5319,7 +5054,7 @@ def test_get_snapshot_flattened_error():
@pytest.mark.asyncio
async def test_get_snapshot_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -5346,7 +5081,7 @@ async def test_get_snapshot_flattened_async():
@pytest.mark.asyncio
async def test_get_snapshot_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -5394,47 +5129,28 @@ def test_list_snapshots(request_type, transport: str = "grpc"):
assert response.next_page_token == "next_page_token_value"
-def test_list_snapshots_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.
+def test_list_snapshots_non_empty_request_with_auto_populated_field():
+ # This test is a coverage failsafe to make sure that UUID4 fields are
+ # automatically populated, according to AIP-4235, with non-empty requests.
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
transport="grpc",
)
+ # Populate all string fields in the request which are not UUID4
+ # since we want to check that UUID4 are populated automatically
+ # if they meet the requirements of AIP 4235.
+ request = pubsub.ListSnapshotsRequest(
+ project="project_value",
+ page_token="page_token_value",
+ )
+
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call:
call.return_value.name = (
"foo" # operation_request.operation in compute client(s) expect a string.
)
- client.list_snapshots()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListSnapshotsRequest()
-
-
-def test_list_snapshots_non_empty_request_with_auto_populated_field():
- # This test is a coverage failsafe to make sure that UUID4 fields are
- # automatically populated, according to AIP-4235, with non-empty requests.
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Populate all string fields in the request which are not UUID4
- # since we want to check that UUID4 are populated automatically
- # if they meet the requirements of AIP 4235.
- request = pubsub.ListSnapshotsRequest(
- project="project_value",
- page_token="page_token_value",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.list_snapshots(request=request)
+ client.list_snapshots(request=request)
call.assert_called()
_, args, _ = call.mock_calls[0]
assert args[0] == pubsub.ListSnapshotsRequest(
@@ -5478,29 +5194,6 @@ def test_list_snapshots_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_list_snapshots_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.ListSnapshotsResponse(
- next_page_token="next_page_token_value",
- )
- )
- response = await client.list_snapshots()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.ListSnapshotsRequest()
-
-
@pytest.mark.asyncio
async def test_list_snapshots_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -5509,7 +5202,7 @@ async def test_list_snapshots_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -5548,7 +5241,7 @@ async def test_list_snapshots_async(
transport: str = "grpc_asyncio", request_type=pubsub.ListSnapshotsRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -5614,7 +5307,7 @@ def test_list_snapshots_field_headers():
@pytest.mark.asyncio
async def test_list_snapshots_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -5684,7 +5377,7 @@ def test_list_snapshots_flattened_error():
@pytest.mark.asyncio
async def test_list_snapshots_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -5713,7 +5406,7 @@ async def test_list_snapshots_flattened_async():
@pytest.mark.asyncio
async def test_list_snapshots_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -5823,7 +5516,7 @@ def test_list_snapshots_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_snapshots_async_pager():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -5873,7 +5566,7 @@ async def test_list_snapshots_async_pager():
@pytest.mark.asyncio
async def test_list_snapshots_async_pages():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -5957,25 +5650,6 @@ def test_create_snapshot(request_type, transport: str = "grpc"):
assert response.topic == "topic_value"
-def test_create_snapshot_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.create_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.CreateSnapshotRequest()
-
-
def test_create_snapshot_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -6041,30 +5715,6 @@ def test_create_snapshot_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_create_snapshot_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Snapshot(
- name="name_value",
- topic="topic_value",
- )
- )
- response = await client.create_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.CreateSnapshotRequest()
-
-
@pytest.mark.asyncio
async def test_create_snapshot_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -6073,7 +5723,7 @@ async def test_create_snapshot_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -6112,7 +5762,7 @@ async def test_create_snapshot_async(
transport: str = "grpc_asyncio", request_type=pubsub.CreateSnapshotRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -6180,7 +5830,7 @@ def test_create_snapshot_field_headers():
@pytest.mark.asyncio
async def test_create_snapshot_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -6253,7 +5903,7 @@ def test_create_snapshot_flattened_error():
@pytest.mark.asyncio
async def test_create_snapshot_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -6284,7 +5934,7 @@ async def test_create_snapshot_flattened_async():
@pytest.mark.asyncio
async def test_create_snapshot_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -6335,25 +5985,6 @@ def test_update_snapshot(request_type, transport: str = "grpc"):
assert response.topic == "topic_value"
-def test_update_snapshot_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.update_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.UpdateSnapshotRequest()
-
-
def test_update_snapshot_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -6413,30 +6044,6 @@ def test_update_snapshot_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_update_snapshot_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
- pubsub.Snapshot(
- name="name_value",
- topic="topic_value",
- )
- )
- response = await client.update_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.UpdateSnapshotRequest()
-
-
@pytest.mark.asyncio
async def test_update_snapshot_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -6445,7 +6052,7 @@ async def test_update_snapshot_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -6484,7 +6091,7 @@ async def test_update_snapshot_async(
transport: str = "grpc_asyncio", request_type=pubsub.UpdateSnapshotRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -6552,7 +6159,7 @@ def test_update_snapshot_field_headers():
@pytest.mark.asyncio
async def test_update_snapshot_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -6625,7 +6232,7 @@ def test_update_snapshot_flattened_error():
@pytest.mark.asyncio
async def test_update_snapshot_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -6656,7 +6263,7 @@ async def test_update_snapshot_flattened_async():
@pytest.mark.asyncio
async def test_update_snapshot_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -6702,25 +6309,6 @@ def test_delete_snapshot(request_type, transport: str = "grpc"):
assert response is None
-def test_delete_snapshot_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.delete_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DeleteSnapshotRequest()
-
-
def test_delete_snapshot_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -6784,25 +6372,6 @@ def test_delete_snapshot_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_delete_snapshot_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
- response = await client.delete_snapshot()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.DeleteSnapshotRequest()
-
-
@pytest.mark.asyncio
async def test_delete_snapshot_async_use_cached_wrapped_rpc(
transport: str = "grpc_asyncio",
@@ -6811,7 +6380,7 @@ async def test_delete_snapshot_async_use_cached_wrapped_rpc(
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -6850,7 +6419,7 @@ async def test_delete_snapshot_async(
transport: str = "grpc_asyncio", request_type=pubsub.DeleteSnapshotRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -6911,7 +6480,7 @@ def test_delete_snapshot_field_headers():
@pytest.mark.asyncio
async def test_delete_snapshot_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -6979,7 +6548,7 @@ def test_delete_snapshot_flattened_error():
@pytest.mark.asyncio
async def test_delete_snapshot_flattened_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -7006,7 +6575,7 @@ async def test_delete_snapshot_flattened_async():
@pytest.mark.asyncio
async def test_delete_snapshot_flattened_error_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -7051,25 +6620,6 @@ def test_seek(request_type, transport: str = "grpc"):
assert isinstance(response, pubsub.SeekResponse)
-def test_seek_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 = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.seek), "__call__") as call:
- call.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client.seek()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.SeekRequest()
-
-
def test_seek_non_empty_request_with_auto_populated_field():
# This test is a coverage failsafe to make sure that UUID4 fields are
# automatically populated, according to AIP-4235, with non-empty requests.
@@ -7135,32 +6685,13 @@ def test_seek_use_cached_wrapped_rpc():
assert mock_rpc.call_count == 2
-@pytest.mark.asyncio
-async def test_seek_empty_call_async():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
-
- # Mock the actual call within the gRPC stub, and fake the request.
- with mock.patch.object(type(client.transport.seek), "__call__") as call:
- # Designate an appropriate return value for the call.
- call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.SeekResponse())
- response = await client.seek()
- call.assert_called()
- _, args, _ = call.mock_calls[0]
- assert args[0] == pubsub.SeekRequest()
-
-
@pytest.mark.asyncio
async def test_seek_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"):
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn:
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -7198,7 +6729,7 @@ async def test_seek_async(
transport: str = "grpc_asyncio", request_type=pubsub.SeekRequest
):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -7259,7 +6790,7 @@ def test_seek_field_headers():
@pytest.mark.asyncio
async def test_seek_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -7286,62 +6817,6 @@ async def test_seek_field_headers_async():
) in kw["metadata"]
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.Subscription,
- dict,
- ],
-)
-def test_create_subscription_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Subscription(
- name="name_value",
- topic="topic_value",
- ack_deadline_seconds=2066,
- retain_acked_messages=True,
- enable_message_ordering=True,
- filter="filter_value",
- detached=True,
- enable_exactly_once_delivery=True,
- state=pubsub.Subscription.State.ACTIVE,
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Subscription.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.create_subscription(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Subscription)
- assert response.name == "name_value"
- assert response.topic == "topic_value"
- assert response.ack_deadline_seconds == 2066
- assert response.retain_acked_messages is True
- assert response.enable_message_ordering is True
- assert response.filter == "filter_value"
- assert response.detached is True
- assert response.enable_exactly_once_delivery is True
- assert response.state == pubsub.Subscription.State.ACTIVE
-
-
def test_create_subscription_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7451,6 +6926,7 @@ def test_create_subscription_rest_required_fields(request_type=pubsub.Subscripti
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.create_subscription(request)
@@ -7476,85 +6952,8 @@ def test_create_subscription_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_create_subscription_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_create_subscription"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_create_subscription"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.Subscription.pb(pubsub.Subscription())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Subscription.to_json(pubsub.Subscription())
-
- request = pubsub.Subscription()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Subscription()
-
- client.create_subscription(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_create_subscription_rest_bad_request(
- transport: str = "rest", request_type=pubsub.Subscription
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.create_subscription(request)
-
-
-def test_create_subscription_rest_flattened():
- client = SubscriberClient(
+def test_create_subscription_rest_flattened():
+ client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
transport="rest",
)
@@ -7584,6 +6983,7 @@ def test_create_subscription_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.create_subscription(**mock_args)
@@ -7614,68 +7014,6 @@ def test_create_subscription_rest_flattened_error(transport: str = "rest"):
)
-def test_create_subscription_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.GetSubscriptionRequest,
- dict,
- ],
-)
-def test_get_subscription_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Subscription(
- name="name_value",
- topic="topic_value",
- ack_deadline_seconds=2066,
- retain_acked_messages=True,
- enable_message_ordering=True,
- filter="filter_value",
- detached=True,
- enable_exactly_once_delivery=True,
- state=pubsub.Subscription.State.ACTIVE,
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Subscription.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.get_subscription(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Subscription)
- assert response.name == "name_value"
- assert response.topic == "topic_value"
- assert response.ack_deadline_seconds == 2066
- assert response.retain_acked_messages is True
- assert response.enable_message_ordering is True
- assert response.filter == "filter_value"
- assert response.detached is True
- assert response.enable_exactly_once_delivery is True
- assert response.state == pubsub.Subscription.State.ACTIVE
-
-
def test_get_subscription_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -7780,6 +7118,7 @@ def test_get_subscription_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.get_subscription(request)
@@ -7797,83 +7136,6 @@ def test_get_subscription_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("subscription",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_get_subscription_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_get_subscription"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_get_subscription"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.GetSubscriptionRequest.pb(pubsub.GetSubscriptionRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Subscription.to_json(pubsub.Subscription())
-
- request = pubsub.GetSubscriptionRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Subscription()
-
- client.get_subscription(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_get_subscription_rest_bad_request(
- transport: str = "rest", request_type=pubsub.GetSubscriptionRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.get_subscription(request)
-
-
def test_get_subscription_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -7902,6 +7164,7 @@ def test_get_subscription_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.get_subscription(**mock_args)
@@ -7930,94 +7193,32 @@ def test_get_subscription_rest_flattened_error(transport: str = "rest"):
)
-def test_get_subscription_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
+def test_update_subscription_rest_use_cached_wrapped_rpc():
+ # Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
+ # instead of constructing them on each call
+ with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn:
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.UpdateSubscriptionRequest,
- dict,
- ],
-)
-def test_update_subscription_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
- # send a request that will satisfy transcoding
- request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}}
- request = request_type(**request_init)
+ # Ensure method has been cached
+ assert (
+ client._transport.update_subscription in client._transport._wrapped_methods
+ )
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Subscription(
- name="name_value",
- topic="topic_value",
- ack_deadline_seconds=2066,
- retain_acked_messages=True,
- enable_message_ordering=True,
- filter="filter_value",
- detached=True,
- enable_exactly_once_delivery=True,
- state=pubsub.Subscription.State.ACTIVE,
+ # Replace cached wrapped function with mock
+ mock_rpc = mock.Mock()
+ mock_rpc.return_value.name = (
+ "foo" # operation_request.operation in compute client(s) expect a string.
)
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Subscription.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.update_subscription(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Subscription)
- assert response.name == "name_value"
- assert response.topic == "topic_value"
- assert response.ack_deadline_seconds == 2066
- assert response.retain_acked_messages is True
- assert response.enable_message_ordering is True
- assert response.filter == "filter_value"
- assert response.detached is True
- assert response.enable_exactly_once_delivery is True
- assert response.state == pubsub.Subscription.State.ACTIVE
-
-
-def test_update_subscription_rest_use_cached_wrapped_rpc():
- # Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
- # instead of constructing them on each call
- with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn:
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Should wrap all calls on client creation
- assert wrapper_fn.call_count > 0
- wrapper_fn.reset_mock()
-
- # Ensure method has been cached
- assert (
- client._transport.update_subscription in client._transport._wrapped_methods
- )
-
- # Replace cached wrapped function with mock
- mock_rpc = mock.Mock()
- mock_rpc.return_value.name = (
- "foo" # operation_request.operation in compute client(s) expect a string.
- )
- client._transport._wrapped_methods[
- client._transport.update_subscription
- ] = mock_rpc
+ client._transport._wrapped_methods[
+ client._transport.update_subscription
+ ] = mock_rpc
request = {}
client.update_subscription(request)
@@ -8094,6 +7295,7 @@ def test_update_subscription_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.update_subscription(request)
@@ -8119,85 +7321,6 @@ def test_update_subscription_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_update_subscription_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_update_subscription"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_update_subscription"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.UpdateSubscriptionRequest.pb(
- pubsub.UpdateSubscriptionRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Subscription.to_json(pubsub.Subscription())
-
- request = pubsub.UpdateSubscriptionRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Subscription()
-
- client.update_subscription(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_update_subscription_rest_bad_request(
- transport: str = "rest", request_type=pubsub.UpdateSubscriptionRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.update_subscription(request)
-
-
def test_update_subscription_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -8229,6 +7352,7 @@ def test_update_subscription_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.update_subscription(**mock_args)
@@ -8259,52 +7383,6 @@ def test_update_subscription_rest_flattened_error(transport: str = "rest"):
)
-def test_update_subscription_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.ListSubscriptionsRequest,
- dict,
- ],
-)
-def test_list_subscriptions_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"project": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListSubscriptionsResponse(
- next_page_token="next_page_token_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.ListSubscriptionsResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.list_subscriptions(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pagers.ListSubscriptionsPager)
- assert response.next_page_token == "next_page_token_value"
-
-
def test_list_subscriptions_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -8418,6 +7496,7 @@ def test_list_subscriptions_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.list_subscriptions(request)
@@ -8443,87 +7522,6 @@ def test_list_subscriptions_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_list_subscriptions_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_list_subscriptions"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_list_subscriptions"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.ListSubscriptionsRequest.pb(
- pubsub.ListSubscriptionsRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.ListSubscriptionsResponse.to_json(
- pubsub.ListSubscriptionsResponse()
- )
-
- request = pubsub.ListSubscriptionsRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.ListSubscriptionsResponse()
-
- client.list_subscriptions(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_list_subscriptions_rest_bad_request(
- transport: str = "rest", request_type=pubsub.ListSubscriptionsRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"project": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.list_subscriptions(request)
-
-
def test_list_subscriptions_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -8552,6 +7550,7 @@ def test_list_subscriptions_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.list_subscriptions(**mock_args)
@@ -8640,41 +7639,6 @@ def test_list_subscriptions_rest_pager(transport: str = "rest"):
assert page_.raw_page.next_page_token == token
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.DeleteSubscriptionRequest,
- dict,
- ],
-)
-def test_delete_subscription_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = ""
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.delete_subscription(request)
-
- # Establish that the response is the type that we expect.
- assert response is None
-
-
def test_delete_subscription_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -8778,6 +7742,7 @@ def test_delete_subscription_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.delete_subscription(request)
@@ -8795,79 +7760,6 @@ def test_delete_subscription_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("subscription",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_delete_subscription_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_delete_subscription"
- ) as pre:
- pre.assert_not_called()
- pb_message = pubsub.DeleteSubscriptionRequest.pb(
- pubsub.DeleteSubscriptionRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
-
- request = pubsub.DeleteSubscriptionRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
-
- client.delete_subscription(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
-
-
-def test_delete_subscription_rest_bad_request(
- transport: str = "rest", request_type=pubsub.DeleteSubscriptionRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.delete_subscription(request)
-
-
def test_delete_subscription_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -8894,6 +7786,7 @@ def test_delete_subscription_rest_flattened():
json_return_value = ""
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.delete_subscription(**mock_args)
@@ -8922,47 +7815,6 @@ def test_delete_subscription_rest_flattened_error(transport: str = "rest"):
)
-def test_delete_subscription_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.ModifyAckDeadlineRequest,
- dict,
- ],
-)
-def test_modify_ack_deadline_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = ""
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.modify_ack_deadline(request)
-
- # Establish that the response is the type that we expect.
- assert response is None
-
-
def test_modify_ack_deadline_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -9075,6 +7927,7 @@ def test_modify_ack_deadline_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.modify_ack_deadline(request)
@@ -9101,79 +7954,6 @@ def test_modify_ack_deadline_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_modify_ack_deadline_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_modify_ack_deadline"
- ) as pre:
- pre.assert_not_called()
- pb_message = pubsub.ModifyAckDeadlineRequest.pb(
- pubsub.ModifyAckDeadlineRequest()
- )
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
-
- request = pubsub.ModifyAckDeadlineRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
-
- client.modify_ack_deadline(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
-
-
-def test_modify_ack_deadline_rest_bad_request(
- transport: str = "rest", request_type=pubsub.ModifyAckDeadlineRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.modify_ack_deadline(request)
-
-
def test_modify_ack_deadline_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -9202,6 +7982,7 @@ def test_modify_ack_deadline_rest_flattened():
json_return_value = ""
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.modify_ack_deadline(**mock_args)
@@ -9233,47 +8014,6 @@ def test_modify_ack_deadline_rest_flattened_error(transport: str = "rest"):
)
-def test_modify_ack_deadline_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.AcknowledgeRequest,
- dict,
- ],
-)
-def test_acknowledge_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = ""
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.acknowledge(request)
-
- # Establish that the response is the type that we expect.
- assert response is None
-
-
def test_acknowledge_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -9376,6 +8116,7 @@ def test_acknowledge_rest_required_fields(request_type=pubsub.AcknowledgeRequest
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.acknowledge(request)
@@ -9401,81 +8142,10 @@ def test_acknowledge_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_acknowledge_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
+def test_acknowledge_rest_flattened():
+ client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_acknowledge"
- ) as pre:
- pre.assert_not_called()
- pb_message = pubsub.AcknowledgeRequest.pb(pubsub.AcknowledgeRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
-
- request = pubsub.AcknowledgeRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
-
- client.acknowledge(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
-
-
-def test_acknowledge_rest_bad_request(
- transport: str = "rest", request_type=pubsub.AcknowledgeRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.acknowledge(request)
-
-
-def test_acknowledge_rest_flattened():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
+ transport="rest",
)
# Mock the http request call within the method and fake a response.
@@ -9499,6 +8169,7 @@ def test_acknowledge_rest_flattened():
json_return_value = ""
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.acknowledge(**mock_args)
@@ -9529,49 +8200,6 @@ def test_acknowledge_rest_flattened_error(transport: str = "rest"):
)
-def test_acknowledge_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.PullRequest,
- dict,
- ],
-)
-def test_pull_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.PullResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.PullResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.pull(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.PullResponse)
-
-
def test_pull_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -9677,6 +8305,7 @@ def test_pull_rest_required_fields(request_type=pubsub.PullRequest):
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.pull(request)
@@ -9702,83 +8331,6 @@ def test_pull_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_pull_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_pull"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_pull"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.PullRequest.pb(pubsub.PullRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.PullResponse.to_json(pubsub.PullResponse())
-
- request = pubsub.PullRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.PullResponse()
-
- client.pull(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_pull_rest_bad_request(
- transport: str = "rest", request_type=pubsub.PullRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.pull(request)
-
-
def test_pull_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -9809,6 +8361,7 @@ def test_pull_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.pull(**mock_args)
@@ -9840,12 +8393,6 @@ def test_pull_rest_flattened_error(transport: str = "rest"):
)
-def test_pull_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
def test_streaming_pull_rest_no_http_options():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -9857,41 +8404,6 @@ def test_streaming_pull_rest_no_http_options():
client.streaming_pull(requests)
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.ModifyPushConfigRequest,
- dict,
- ],
-)
-def test_modify_push_config_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = ""
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.modify_push_config(request)
-
- # Establish that the response is the type that we expect.
- assert response is None
-
-
def test_modify_push_config_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -9996,6 +8508,7 @@ def test_modify_push_config_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.modify_push_config(request)
@@ -10021,90 +8534,19 @@ def test_modify_push_config_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_modify_push_config_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
+def test_modify_push_config_rest_flattened():
+ client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
+ transport="rest",
)
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_modify_push_config"
- ) as pre:
- pre.assert_not_called()
- pb_message = pubsub.ModifyPushConfigRequest.pb(pubsub.ModifyPushConfigRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- request = pubsub.ModifyPushConfigRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
- client.modify_push_config(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
-
-
-def test_modify_push_config_rest_bad_request(
- transport: str = "rest", request_type=pubsub.ModifyPushConfigRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.modify_push_config(request)
-
-
-def test_modify_push_config_rest_flattened():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"subscription": "projects/sample1/subscriptions/sample2"}
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"subscription": "projects/sample1/subscriptions/sample2"}
# get truthy value for each flattened field
mock_args = dict(
@@ -10119,6 +8561,7 @@ def test_modify_push_config_rest_flattened():
json_return_value = ""
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.modify_push_config(**mock_args)
@@ -10149,54 +8592,6 @@ def test_modify_push_config_rest_flattened_error(transport: str = "rest"):
)
-def test_modify_push_config_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.GetSnapshotRequest,
- dict,
- ],
-)
-def test_get_snapshot_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Snapshot(
- name="name_value",
- topic="topic_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Snapshot.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.get_snapshot(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Snapshot)
- assert response.name == "name_value"
- assert response.topic == "topic_value"
-
-
def test_get_snapshot_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -10297,6 +8692,7 @@ def test_get_snapshot_rest_required_fields(request_type=pubsub.GetSnapshotReques
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.get_snapshot(request)
@@ -10314,83 +8710,6 @@ def test_get_snapshot_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("snapshot",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_get_snapshot_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_get_snapshot"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_get_snapshot"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.GetSnapshotRequest.pb(pubsub.GetSnapshotRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Snapshot.to_json(pubsub.Snapshot())
-
- request = pubsub.GetSnapshotRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Snapshot()
-
- client.get_snapshot(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_get_snapshot_rest_bad_request(
- transport: str = "rest", request_type=pubsub.GetSnapshotRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.get_snapshot(request)
-
-
def test_get_snapshot_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -10419,6 +8738,7 @@ def test_get_snapshot_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.get_snapshot(**mock_args)
@@ -10446,52 +8766,6 @@ def test_get_snapshot_rest_flattened_error(transport: str = "rest"):
)
-def test_get_snapshot_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.ListSnapshotsRequest,
- dict,
- ],
-)
-def test_list_snapshots_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"project": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListSnapshotsResponse(
- next_page_token="next_page_token_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.ListSnapshotsResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.list_snapshots(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pagers.ListSnapshotsPager)
- assert response.next_page_token == "next_page_token_value"
-
-
def test_list_snapshots_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -10599,6 +8873,7 @@ def test_list_snapshots_rest_required_fields(request_type=pubsub.ListSnapshotsRe
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.list_snapshots(request)
@@ -10624,104 +8899,25 @@ def test_list_snapshots_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_list_snapshots_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
+def test_list_snapshots_rest_flattened():
+ client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
+ transport="rest",
)
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_list_snapshots"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_list_snapshots"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.ListSnapshotsRequest.pb(pubsub.ListSnapshotsRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.ListSnapshotsResponse.to_json(
- pubsub.ListSnapshotsResponse()
- )
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListSnapshotsResponse()
- request = pubsub.ListSnapshotsRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.ListSnapshotsResponse()
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"project": "projects/sample1"}
- client.list_snapshots(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
+ # get truthy value for each flattened field
+ mock_args = dict(
+ project="project_value",
)
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_list_snapshots_rest_bad_request(
- transport: str = "rest", request_type=pubsub.ListSnapshotsRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"project": "projects/sample1"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.list_snapshots(request)
-
-
-def test_list_snapshots_rest_flattened():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.ListSnapshotsResponse()
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"project": "projects/sample1"}
-
- # get truthy value for each flattened field
- mock_args = dict(
- project="project_value",
- )
- mock_args.update(sample_request)
+ mock_args.update(sample_request)
# Wrap the value into a proper Response obj
response_value = Response()
@@ -10731,6 +8927,7 @@ def test_list_snapshots_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.list_snapshots(**mock_args)
@@ -10819,48 +9016,6 @@ def test_list_snapshots_rest_pager(transport: str = "rest"):
assert page_.raw_page.next_page_token == token
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.CreateSnapshotRequest,
- dict,
- ],
-)
-def test_create_snapshot_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/snapshots/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Snapshot(
- name="name_value",
- topic="topic_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Snapshot.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.create_snapshot(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Snapshot)
- assert response.name == "name_value"
- assert response.topic == "topic_value"
-
-
def test_create_snapshot_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -10968,6 +9123,7 @@ def test_create_snapshot_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.create_snapshot(request)
@@ -10993,83 +9149,6 @@ def test_create_snapshot_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_create_snapshot_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_create_snapshot"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_create_snapshot"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.CreateSnapshotRequest.pb(pubsub.CreateSnapshotRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Snapshot.to_json(pubsub.Snapshot())
-
- request = pubsub.CreateSnapshotRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Snapshot()
-
- client.create_snapshot(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_create_snapshot_rest_bad_request(
- transport: str = "rest", request_type=pubsub.CreateSnapshotRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"name": "projects/sample1/snapshots/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.create_snapshot(request)
-
-
def test_create_snapshot_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -11099,6 +9178,7 @@ def test_create_snapshot_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.create_snapshot(**mock_args)
@@ -11127,54 +9207,6 @@ def test_create_snapshot_rest_flattened_error(transport: str = "rest"):
)
-def test_create_snapshot_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.UpdateSnapshotRequest,
- dict,
- ],
-)
-def test_update_snapshot_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Snapshot(
- name="name_value",
- topic="topic_value",
- )
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.Snapshot.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.update_snapshot(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.Snapshot)
- assert response.name == "name_value"
- assert response.topic == "topic_value"
-
-
def test_update_snapshot_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -11273,6 +9305,7 @@ def test_update_snapshot_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.update_snapshot(request)
@@ -11298,101 +9331,24 @@ def test_update_snapshot_rest_unset_required_fields():
)
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_update_snapshot_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
+def test_update_snapshot_rest_flattened():
+ client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
+ transport="rest",
)
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_update_snapshot"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_update_snapshot"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.UpdateSnapshotRequest.pb(pubsub.UpdateSnapshotRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.Snapshot.to_json(pubsub.Snapshot())
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Snapshot()
- request = pubsub.UpdateSnapshotRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.Snapshot()
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}}
- client.update_snapshot(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-
-
-def test_update_snapshot_rest_bad_request(
- transport: str = "rest", request_type=pubsub.UpdateSnapshotRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.update_snapshot(request)
-
-
-def test_update_snapshot_rest_flattened():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.Snapshot()
-
- # get arguments that satisfy an http rule for this method
- sample_request = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}}
-
- # get truthy value for each flattened field
- mock_args = dict(
- snapshot=pubsub.Snapshot(name="name_value"),
- update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]),
+ # get truthy value for each flattened field
+ mock_args = dict(
+ snapshot=pubsub.Snapshot(name="name_value"),
+ update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]),
)
mock_args.update(sample_request)
@@ -11404,6 +9360,7 @@ def test_update_snapshot_rest_flattened():
json_return_value = json_format.MessageToJson(return_value)
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.update_snapshot(**mock_args)
@@ -11433,47 +9390,6 @@ def test_update_snapshot_rest_flattened_error(transport: str = "rest"):
)
-def test_update_snapshot_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.DeleteSnapshotRequest,
- dict,
- ],
-)
-def test_delete_snapshot_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = None
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = ""
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.delete_snapshot(request)
-
- # Establish that the response is the type that we expect.
- assert response is None
-
-
def test_delete_snapshot_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -11573,6 +9489,7 @@ def test_delete_snapshot_rest_required_fields(
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.delete_snapshot(request)
@@ -11590,77 +9507,6 @@ def test_delete_snapshot_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("snapshot",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_delete_snapshot_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
- )
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_delete_snapshot"
- ) as pre:
- pre.assert_not_called()
- pb_message = pubsub.DeleteSnapshotRequest.pb(pubsub.DeleteSnapshotRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
-
- request = pubsub.DeleteSnapshotRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
-
- client.delete_snapshot(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
-
-
-def test_delete_snapshot_rest_bad_request(
- transport: str = "rest", request_type=pubsub.DeleteSnapshotRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.delete_snapshot(request)
-
-
def test_delete_snapshot_rest_flattened():
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -11687,6 +9533,7 @@ def test_delete_snapshot_rest_flattened():
json_return_value = ""
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
client.delete_snapshot(**mock_args)
@@ -11714,49 +9561,6 @@ def test_delete_snapshot_rest_flattened_error(transport: str = "rest"):
)
-def test_delete_snapshot_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- pubsub.SeekRequest,
- dict,
- ],
-)
-def test_seek_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = pubsub.SeekResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- # Convert return value to protobuf type
- return_value = pubsub.SeekResponse.pb(return_value)
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
- response = client.seek(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, pubsub.SeekResponse)
-
-
def test_seek_rest_use_cached_wrapped_rpc():
# Clients should use _prep_wrapped_messages to create cached wrapped rpcs,
# instead of constructing them on each call
@@ -11858,6 +9662,7 @@ def test_seek_rest_required_fields(request_type=pubsub.SeekRequest):
response_value._content = json_return_value.encode("UTF-8")
req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
response = client.seek(request)
@@ -11875,105 +9680,22 @@ def test_seek_rest_unset_required_fields():
assert set(unset_fields) == (set(()) & set(("subscription",)))
-@pytest.mark.parametrize("null_interceptor", [True, False])
-def test_seek_rest_interceptors(null_interceptor):
- transport = transports.SubscriberRestTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- interceptor=None
- if null_interceptor
- else transports.SubscriberRestInterceptor(),
+def test_streaming_pull_rest_error():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # Since a `google.api.http` annotation is required for using a rest transport
+ # method, this should error.
+ with pytest.raises(NotImplementedError) as not_implemented_error:
+ client.streaming_pull({})
+ assert "Method StreamingPull is not available over REST transport" in str(
+ not_implemented_error.value
)
- client = SubscriberClient(transport=transport)
- with mock.patch.object(
- type(client.transport._session), "request"
- ) as req, mock.patch.object(
- path_template, "transcode"
- ) as transcode, mock.patch.object(
- transports.SubscriberRestInterceptor, "post_seek"
- ) as post, mock.patch.object(
- transports.SubscriberRestInterceptor, "pre_seek"
- ) as pre:
- pre.assert_not_called()
- post.assert_not_called()
- pb_message = pubsub.SeekRequest.pb(pubsub.SeekRequest())
- transcode.return_value = {
- "method": "post",
- "uri": "my_uri",
- "body": pb_message,
- "query_params": pb_message,
- }
-
- req.return_value = Response()
- req.return_value.status_code = 200
- req.return_value.request = PreparedRequest()
- req.return_value._content = pubsub.SeekResponse.to_json(pubsub.SeekResponse())
-
- request = pubsub.SeekRequest()
- metadata = [
- ("key", "val"),
- ("cephalopod", "squid"),
- ]
- pre.return_value = request, metadata
- post.return_value = pubsub.SeekResponse()
-
- client.seek(
- request,
- metadata=[
- ("key", "val"),
- ("cephalopod", "squid"),
- ],
- )
-
- pre.assert_called_once()
- post.assert_called_once()
-def test_seek_rest_bad_request(
- transport: str = "rest", request_type=pubsub.SeekRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- # send a request that will satisfy transcoding
- request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.seek(request)
-
-
-def test_seek_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
-
-
-def test_streaming_pull_rest_error():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport="rest"
- )
- # Since a `google.api.http` annotation is required for using a rest transport
- # method, this should error.
- with pytest.raises(NotImplementedError) as not_implemented_error:
- client.streaming_pull({})
- assert "Method StreamingPull is not available over REST transport" in str(
- not_implemented_error.value
- )
-
-
-def test_credentials_transport_error():
- # It is an error to provide credentials and a transport instance.
- transport = transports.SubscriberGrpcTransport(
+def test_credentials_transport_error():
+ # It is an error to provide credentials and a transport instance.
+ transport = transports.SubscriberGrpcTransport(
credentials=ga_credentials.AnonymousCredentials(),
)
with pytest.raises(ValueError):
@@ -12063,18 +9785,3124 @@ def test_transport_adc(transport_class):
adc.assert_called_once()
-@pytest.mark.parametrize(
- "transport_name",
- [
- "grpc",
- "rest",
- ],
-)
-def test_transport_kind(transport_name):
- transport = SubscriberClient.get_transport_class(transport_name)(
+def test_transport_kind_grpc():
+ transport = SubscriberClient.get_transport_class("grpc")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "grpc"
+
+
+def test_initialize_client_w_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="grpc"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_subscription_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.create_subscription), "__call__"
+ ) as call:
+ call.return_value = pubsub.Subscription()
+ client.create_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.Subscription()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_subscription_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_subscription), "__call__") as call:
+ call.return_value = pubsub.Subscription()
+ client.get_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_update_subscription_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.update_subscription), "__call__"
+ ) as call:
+ call.return_value = pubsub.Subscription()
+ client.update_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_subscriptions_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_subscriptions), "__call__"
+ ) as call:
+ call.return_value = pubsub.ListSubscriptionsResponse()
+ client.list_subscriptions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListSubscriptionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_subscription_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.delete_subscription), "__call__"
+ ) as call:
+ call.return_value = None
+ client.delete_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_modify_ack_deadline_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.modify_ack_deadline), "__call__"
+ ) as call:
+ call.return_value = None
+ client.modify_ack_deadline(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ModifyAckDeadlineRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_acknowledge_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.acknowledge), "__call__") as call:
+ call.return_value = None
+ client.acknowledge(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.AcknowledgeRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_pull_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.pull), "__call__") as call:
+ call.return_value = pubsub.PullResponse()
+ client.pull(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.PullRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_modify_push_config_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.modify_push_config), "__call__"
+ ) as call:
+ call.return_value = None
+ client.modify_push_config(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ModifyPushConfigRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_snapshot_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call:
+ call.return_value = pubsub.Snapshot()
+ client.get_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_snapshots_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call:
+ call.return_value = pubsub.ListSnapshotsResponse()
+ client.list_snapshots(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListSnapshotsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_snapshot_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call:
+ call.return_value = pubsub.Snapshot()
+ client.create_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.CreateSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_update_snapshot_empty_call_grpc():
+ client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call:
+ call.return_value = pubsub.Snapshot()
+ client.update_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_snapshot_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call:
+ call.return_value = None
+ client.delete_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_seek_empty_call_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
)
- assert transport.kind == transport_name
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.seek), "__call__") as call:
+ call.return_value = pubsub.SeekResponse()
+ client.seek(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.SeekRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_grpc_asyncio():
+ transport = SubscriberAsyncClient.get_transport_class("grpc_asyncio")(
+ credentials=async_anonymous_credentials()
+ )
+ assert transport.kind == "grpc_asyncio"
+
+
+def test_initialize_client_w_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_create_subscription_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.create_subscription), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Subscription(
+ name="name_value",
+ topic="topic_value",
+ ack_deadline_seconds=2066,
+ retain_acked_messages=True,
+ enable_message_ordering=True,
+ filter="filter_value",
+ detached=True,
+ enable_exactly_once_delivery=True,
+ state=pubsub.Subscription.State.ACTIVE,
+ )
+ )
+ await client.create_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.Subscription()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_get_subscription_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_subscription), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Subscription(
+ name="name_value",
+ topic="topic_value",
+ ack_deadline_seconds=2066,
+ retain_acked_messages=True,
+ enable_message_ordering=True,
+ filter="filter_value",
+ detached=True,
+ enable_exactly_once_delivery=True,
+ state=pubsub.Subscription.State.ACTIVE,
+ )
+ )
+ await client.get_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_update_subscription_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.update_subscription), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Subscription(
+ name="name_value",
+ topic="topic_value",
+ ack_deadline_seconds=2066,
+ retain_acked_messages=True,
+ enable_message_ordering=True,
+ filter="filter_value",
+ detached=True,
+ enable_exactly_once_delivery=True,
+ state=pubsub.Subscription.State.ACTIVE,
+ )
+ )
+ await client.update_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_list_subscriptions_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_subscriptions), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.ListSubscriptionsResponse(
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_subscriptions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListSubscriptionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_delete_subscription_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.delete_subscription), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
+ await client.delete_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_modify_ack_deadline_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.modify_ack_deadline), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
+ await client.modify_ack_deadline(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ModifyAckDeadlineRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_acknowledge_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.acknowledge), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
+ await client.acknowledge(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.AcknowledgeRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_pull_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.pull), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.PullResponse())
+ await client.pull(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.PullRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_modify_push_config_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.modify_push_config), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
+ await client.modify_push_config(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ModifyPushConfigRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_get_snapshot_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Snapshot(
+ name="name_value",
+ topic="topic_value",
+ )
+ )
+ await client.get_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_list_snapshots_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.ListSnapshotsResponse(
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_snapshots(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListSnapshotsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_create_snapshot_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Snapshot(
+ name="name_value",
+ topic="topic_value",
+ )
+ )
+ await client.create_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.CreateSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_update_snapshot_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ pubsub.Snapshot(
+ name="name_value",
+ topic="topic_value",
+ )
+ )
+ await client.update_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_delete_snapshot_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None)
+ await client.delete_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+@pytest.mark.asyncio
+async def test_seek_empty_call_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.seek), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.SeekResponse())
+ await client.seek(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.SeekRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_rest():
+ transport = SubscriberClient.get_transport_class("rest")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "rest"
+
+
+def test_create_subscription_rest_bad_request(request_type=pubsub.Subscription):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.create_subscription(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.Subscription,
+ dict,
+ ],
+)
+def test_create_subscription_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Subscription(
+ name="name_value",
+ topic="topic_value",
+ ack_deadline_seconds=2066,
+ retain_acked_messages=True,
+ enable_message_ordering=True,
+ filter="filter_value",
+ detached=True,
+ enable_exactly_once_delivery=True,
+ state=pubsub.Subscription.State.ACTIVE,
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Subscription.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.create_subscription(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Subscription)
+ assert response.name == "name_value"
+ assert response.topic == "topic_value"
+ assert response.ack_deadline_seconds == 2066
+ assert response.retain_acked_messages is True
+ assert response.enable_message_ordering is True
+ assert response.filter == "filter_value"
+ assert response.detached is True
+ assert response.enable_exactly_once_delivery is True
+ assert response.state == pubsub.Subscription.State.ACTIVE
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_create_subscription_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_create_subscription"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_create_subscription_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_create_subscription"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.Subscription.pb(pubsub.Subscription())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Subscription.to_json(pubsub.Subscription())
+ req.return_value.content = return_value
+
+ request = pubsub.Subscription()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Subscription()
+ post_with_metadata.return_value = pubsub.Subscription(), metadata
+
+ client.create_subscription(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_get_subscription_rest_bad_request(request_type=pubsub.GetSubscriptionRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.get_subscription(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.GetSubscriptionRequest,
+ dict,
+ ],
+)
+def test_get_subscription_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Subscription(
+ name="name_value",
+ topic="topic_value",
+ ack_deadline_seconds=2066,
+ retain_acked_messages=True,
+ enable_message_ordering=True,
+ filter="filter_value",
+ detached=True,
+ enable_exactly_once_delivery=True,
+ state=pubsub.Subscription.State.ACTIVE,
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Subscription.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.get_subscription(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Subscription)
+ assert response.name == "name_value"
+ assert response.topic == "topic_value"
+ assert response.ack_deadline_seconds == 2066
+ assert response.retain_acked_messages is True
+ assert response.enable_message_ordering is True
+ assert response.filter == "filter_value"
+ assert response.detached is True
+ assert response.enable_exactly_once_delivery is True
+ assert response.state == pubsub.Subscription.State.ACTIVE
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_get_subscription_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_get_subscription"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_get_subscription_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_get_subscription"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.GetSubscriptionRequest.pb(pubsub.GetSubscriptionRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Subscription.to_json(pubsub.Subscription())
+ req.return_value.content = return_value
+
+ request = pubsub.GetSubscriptionRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Subscription()
+ post_with_metadata.return_value = pubsub.Subscription(), metadata
+
+ client.get_subscription(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_update_subscription_rest_bad_request(
+ request_type=pubsub.UpdateSubscriptionRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.update_subscription(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.UpdateSubscriptionRequest,
+ dict,
+ ],
+)
+def test_update_subscription_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Subscription(
+ name="name_value",
+ topic="topic_value",
+ ack_deadline_seconds=2066,
+ retain_acked_messages=True,
+ enable_message_ordering=True,
+ filter="filter_value",
+ detached=True,
+ enable_exactly_once_delivery=True,
+ state=pubsub.Subscription.State.ACTIVE,
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Subscription.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.update_subscription(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Subscription)
+ assert response.name == "name_value"
+ assert response.topic == "topic_value"
+ assert response.ack_deadline_seconds == 2066
+ assert response.retain_acked_messages is True
+ assert response.enable_message_ordering is True
+ assert response.filter == "filter_value"
+ assert response.detached is True
+ assert response.enable_exactly_once_delivery is True
+ assert response.state == pubsub.Subscription.State.ACTIVE
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_update_subscription_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_update_subscription"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_update_subscription_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_update_subscription"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.UpdateSubscriptionRequest.pb(
+ pubsub.UpdateSubscriptionRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Subscription.to_json(pubsub.Subscription())
+ req.return_value.content = return_value
+
+ request = pubsub.UpdateSubscriptionRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Subscription()
+ post_with_metadata.return_value = pubsub.Subscription(), metadata
+
+ client.update_subscription(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_subscriptions_rest_bad_request(
+ request_type=pubsub.ListSubscriptionsRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"project": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.list_subscriptions(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.ListSubscriptionsRequest,
+ dict,
+ ],
+)
+def test_list_subscriptions_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"project": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListSubscriptionsResponse(
+ next_page_token="next_page_token_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.ListSubscriptionsResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.list_subscriptions(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListSubscriptionsPager)
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_subscriptions_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_list_subscriptions"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_list_subscriptions_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_list_subscriptions"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.ListSubscriptionsRequest.pb(
+ pubsub.ListSubscriptionsRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.ListSubscriptionsResponse.to_json(
+ pubsub.ListSubscriptionsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = pubsub.ListSubscriptionsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.ListSubscriptionsResponse()
+ post_with_metadata.return_value = pubsub.ListSubscriptionsResponse(), metadata
+
+ client.list_subscriptions(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_delete_subscription_rest_bad_request(
+ request_type=pubsub.DeleteSubscriptionRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.delete_subscription(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.DeleteSubscriptionRequest,
+ dict,
+ ],
+)
+def test_delete_subscription_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = ""
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.delete_subscription(request)
+
+ # Establish that the response is the type that we expect.
+ assert response is None
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_delete_subscription_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_delete_subscription"
+ ) as pre:
+ pre.assert_not_called()
+ pb_message = pubsub.DeleteSubscriptionRequest.pb(
+ pubsub.DeleteSubscriptionRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ request = pubsub.DeleteSubscriptionRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+
+ client.delete_subscription(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+
+
+def test_modify_ack_deadline_rest_bad_request(
+ request_type=pubsub.ModifyAckDeadlineRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.modify_ack_deadline(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.ModifyAckDeadlineRequest,
+ dict,
+ ],
+)
+def test_modify_ack_deadline_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = ""
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.modify_ack_deadline(request)
+
+ # Establish that the response is the type that we expect.
+ assert response is None
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_modify_ack_deadline_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_modify_ack_deadline"
+ ) as pre:
+ pre.assert_not_called()
+ pb_message = pubsub.ModifyAckDeadlineRequest.pb(
+ pubsub.ModifyAckDeadlineRequest()
+ )
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ request = pubsub.ModifyAckDeadlineRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+
+ client.modify_ack_deadline(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+
+
+def test_acknowledge_rest_bad_request(request_type=pubsub.AcknowledgeRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.acknowledge(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.AcknowledgeRequest,
+ dict,
+ ],
+)
+def test_acknowledge_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = ""
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.acknowledge(request)
+
+ # Establish that the response is the type that we expect.
+ assert response is None
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_acknowledge_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_acknowledge"
+ ) as pre:
+ pre.assert_not_called()
+ pb_message = pubsub.AcknowledgeRequest.pb(pubsub.AcknowledgeRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ request = pubsub.AcknowledgeRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+
+ client.acknowledge(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+
+
+def test_pull_rest_bad_request(request_type=pubsub.PullRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.pull(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.PullRequest,
+ dict,
+ ],
+)
+def test_pull_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.PullResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.PullResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.pull(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.PullResponse)
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_pull_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_pull"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_pull_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_pull"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.PullRequest.pb(pubsub.PullRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.PullResponse.to_json(pubsub.PullResponse())
+ req.return_value.content = return_value
+
+ request = pubsub.PullRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.PullResponse()
+ post_with_metadata.return_value = pubsub.PullResponse(), metadata
+
+ client.pull(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_streaming_pull_rest_error():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ with pytest.raises(NotImplementedError) as not_implemented_error:
+ client.streaming_pull({})
+ assert "Method StreamingPull is not available over REST transport" in str(
+ not_implemented_error.value
+ )
+
+
+def test_modify_push_config_rest_bad_request(
+ request_type=pubsub.ModifyPushConfigRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.modify_push_config(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.ModifyPushConfigRequest,
+ dict,
+ ],
+)
+def test_modify_push_config_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = ""
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.modify_push_config(request)
+
+ # Establish that the response is the type that we expect.
+ assert response is None
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_modify_push_config_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_modify_push_config"
+ ) as pre:
+ pre.assert_not_called()
+ pb_message = pubsub.ModifyPushConfigRequest.pb(pubsub.ModifyPushConfigRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ request = pubsub.ModifyPushConfigRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+
+ client.modify_push_config(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+
+
+def test_get_snapshot_rest_bad_request(request_type=pubsub.GetSnapshotRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.get_snapshot(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.GetSnapshotRequest,
+ dict,
+ ],
+)
+def test_get_snapshot_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Snapshot(
+ name="name_value",
+ topic="topic_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Snapshot.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.get_snapshot(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Snapshot)
+ assert response.name == "name_value"
+ assert response.topic == "topic_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_get_snapshot_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_get_snapshot"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_get_snapshot_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_get_snapshot"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.GetSnapshotRequest.pb(pubsub.GetSnapshotRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Snapshot.to_json(pubsub.Snapshot())
+ req.return_value.content = return_value
+
+ request = pubsub.GetSnapshotRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Snapshot()
+ post_with_metadata.return_value = pubsub.Snapshot(), metadata
+
+ client.get_snapshot(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_snapshots_rest_bad_request(request_type=pubsub.ListSnapshotsRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"project": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.list_snapshots(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.ListSnapshotsRequest,
+ dict,
+ ],
+)
+def test_list_snapshots_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"project": "projects/sample1"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.ListSnapshotsResponse(
+ next_page_token="next_page_token_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.ListSnapshotsResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.list_snapshots(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListSnapshotsPager)
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_snapshots_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_list_snapshots"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_list_snapshots_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_list_snapshots"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.ListSnapshotsRequest.pb(pubsub.ListSnapshotsRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.ListSnapshotsResponse.to_json(
+ pubsub.ListSnapshotsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = pubsub.ListSnapshotsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.ListSnapshotsResponse()
+ post_with_metadata.return_value = pubsub.ListSnapshotsResponse(), metadata
+
+ client.list_snapshots(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_create_snapshot_rest_bad_request(request_type=pubsub.CreateSnapshotRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/snapshots/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.create_snapshot(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.CreateSnapshotRequest,
+ dict,
+ ],
+)
+def test_create_snapshot_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"name": "projects/sample1/snapshots/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Snapshot(
+ name="name_value",
+ topic="topic_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Snapshot.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.create_snapshot(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Snapshot)
+ assert response.name == "name_value"
+ assert response.topic == "topic_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_create_snapshot_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_create_snapshot"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_create_snapshot_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_create_snapshot"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.CreateSnapshotRequest.pb(pubsub.CreateSnapshotRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Snapshot.to_json(pubsub.Snapshot())
+ req.return_value.content = return_value
+
+ request = pubsub.CreateSnapshotRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Snapshot()
+ post_with_metadata.return_value = pubsub.Snapshot(), metadata
+
+ client.create_snapshot(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_update_snapshot_rest_bad_request(request_type=pubsub.UpdateSnapshotRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.update_snapshot(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.UpdateSnapshotRequest,
+ dict,
+ ],
+)
+def test_update_snapshot_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.Snapshot(
+ name="name_value",
+ topic="topic_value",
+ )
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.Snapshot.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.update_snapshot(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.Snapshot)
+ assert response.name == "name_value"
+ assert response.topic == "topic_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_update_snapshot_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_update_snapshot"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_update_snapshot_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_update_snapshot"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.UpdateSnapshotRequest.pb(pubsub.UpdateSnapshotRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.Snapshot.to_json(pubsub.Snapshot())
+ req.return_value.content = return_value
+
+ request = pubsub.UpdateSnapshotRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.Snapshot()
+ post_with_metadata.return_value = pubsub.Snapshot(), metadata
+
+ client.update_snapshot(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_delete_snapshot_rest_bad_request(request_type=pubsub.DeleteSnapshotRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.delete_snapshot(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.DeleteSnapshotRequest,
+ dict,
+ ],
+)
+def test_delete_snapshot_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"snapshot": "projects/sample1/snapshots/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = None
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = ""
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.delete_snapshot(request)
+
+ # Establish that the response is the type that we expect.
+ assert response is None
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_delete_snapshot_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_delete_snapshot"
+ ) as pre:
+ pre.assert_not_called()
+ pb_message = pubsub.DeleteSnapshotRequest.pb(pubsub.DeleteSnapshotRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ request = pubsub.DeleteSnapshotRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+
+ client.delete_snapshot(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+
+
+def test_seek_rest_bad_request(request_type=pubsub.SeekRequest):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = mock.Mock()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.seek(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ pubsub.SeekRequest,
+ dict,
+ ],
+)
+def test_seek_rest_call_success(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"subscription": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(type(client.transport._session), "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = pubsub.SeekResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = pubsub.SeekResponse.pb(return_value)
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ response = client.seek(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pubsub.SeekResponse)
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_seek_rest_interceptors(null_interceptor):
+ transport = transports.SubscriberRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.SubscriberRestInterceptor(),
+ )
+ client = SubscriberClient(transport=transport)
+
+ with mock.patch.object(
+ type(client.transport._session), "request"
+ ) as req, mock.patch.object(
+ path_template, "transcode"
+ ) as transcode, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_seek"
+ ) as post, mock.patch.object(
+ transports.SubscriberRestInterceptor, "post_seek_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.SubscriberRestInterceptor, "pre_seek"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = pubsub.SeekRequest.pb(pubsub.SeekRequest())
+ transcode.return_value = {
+ "method": "post",
+ "uri": "my_uri",
+ "body": pb_message,
+ "query_params": pb_message,
+ }
+
+ req.return_value = mock.Mock()
+ req.return_value.status_code = 200
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ return_value = pubsub.SeekResponse.to_json(pubsub.SeekResponse())
+ req.return_value.content = return_value
+
+ request = pubsub.SeekRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = pubsub.SeekResponse()
+ post_with_metadata.return_value = pubsub.SeekResponse(), metadata
+
+ client.seek(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_get_iam_policy_rest_bad_request(
+ request_type=iam_policy_pb2.GetIamPolicyRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/topics/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.get_iam_policy(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.GetIamPolicyRequest,
+ dict,
+ ],
+)
+def test_get_iam_policy_rest(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = policy_pb2.Policy()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.get_iam_policy(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, policy_pb2.Policy)
+
+
+def test_set_iam_policy_rest_bad_request(
+ request_type=iam_policy_pb2.SetIamPolicyRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/topics/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.set_iam_policy(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.SetIamPolicyRequest,
+ dict,
+ ],
+)
+def test_set_iam_policy_rest(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/topics/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = policy_pb2.Policy()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.set_iam_policy(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, policy_pb2.Policy)
+
+
+def test_test_iam_permissions_rest_bad_request(
+ request_type=iam_policy_pb2.TestIamPermissionsRequest,
+):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type()
+ request = json_format.ParseDict(
+ {"resource": "projects/sample1/subscriptions/sample2"}, request
+ )
+
+ # Mock the http request call within the method and fake a BadRequest error.
+ with mock.patch.object(Session, "request") as req, pytest.raises(
+ core_exceptions.BadRequest
+ ):
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ json_return_value = ""
+ response_value.json = mock.Mock(return_value={})
+ response_value.status_code = 400
+ response_value.request = Request()
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+ client.test_iam_permissions(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ iam_policy_pb2.TestIamPermissionsRequest,
+ dict,
+ ],
+)
+def test_test_iam_permissions_rest(request_type):
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ request_init = {"resource": "projects/sample1/subscriptions/sample2"}
+ request = request_type(**request_init)
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # Designate an appropriate value for the returned response.
+ return_value = iam_policy_pb2.TestIamPermissionsResponse()
+
+ # Wrap the value into a proper Response obj
+ response_value = mock.Mock()
+ response_value.status_code = 200
+ json_return_value = json_format.MessageToJson(return_value)
+ response_value.content = json_return_value.encode("UTF-8")
+
+ req.return_value = response_value
+ req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"}
+
+ response = client.test_iam_permissions(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse)
+
+
+def test_initialize_client_w_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ assert client is not None
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_subscription_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.create_subscription), "__call__"
+ ) as call:
+ client.create_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.Subscription()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_subscription_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_subscription), "__call__") as call:
+ client.get_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_update_subscription_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.update_subscription), "__call__"
+ ) as call:
+ client.update_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_subscriptions_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_subscriptions), "__call__"
+ ) as call:
+ client.list_subscriptions(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListSubscriptionsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_subscription_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.delete_subscription), "__call__"
+ ) as call:
+ client.delete_subscription(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteSubscriptionRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_modify_ack_deadline_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.modify_ack_deadline), "__call__"
+ ) as call:
+ client.modify_ack_deadline(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ModifyAckDeadlineRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_acknowledge_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.acknowledge), "__call__") as call:
+ client.acknowledge(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.AcknowledgeRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_pull_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.pull), "__call__") as call:
+ client.pull(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.PullRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_modify_push_config_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.modify_push_config), "__call__"
+ ) as call:
+ client.modify_push_config(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ModifyPushConfigRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_get_snapshot_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call:
+ client.get_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.GetSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_list_snapshots_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call:
+ client.list_snapshots(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.ListSnapshotsRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_create_snapshot_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call:
+ client.create_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.CreateSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_update_snapshot_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call:
+ client.update_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.UpdateSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_delete_snapshot_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call:
+ client.delete_snapshot(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.DeleteSnapshotRequest()
+
+ assert args[0] == request_msg
+
+
+# This test is a coverage failsafe to make sure that totally empty calls,
+# i.e. request == None and no flattened fields passed, work.
+def test_seek_empty_call_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.seek), "__call__") as call:
+ client.seek(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = pubsub.SeekRequest()
+
+ assert args[0] == request_msg
def test_transport_grpc_default():
@@ -12478,6 +13306,7 @@ def test_subscriber_grpc_asyncio_transport_channel():
# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are
# removed from grpc/grpc_asyncio transport constructor.
+@pytest.mark.filterwarnings("ignore::FutureWarning")
@pytest.mark.parametrize(
"transport_class",
[transports.SubscriberGrpcTransport, transports.SubscriberGrpcAsyncIOTransport],
@@ -12572,9 +13401,38 @@ def test_subscriber_transport_channel_mtls_with_adc(transport_class):
assert transport.grpc_channel == mock_grpc_channel
-def test_snapshot_path():
+def test_listing_path():
project = "squid"
- snapshot = "clam"
+ location = "clam"
+ data_exchange = "whelk"
+ listing = "octopus"
+ expected = "projects/{project}/locations/{location}/dataExchanges/{data_exchange}/listings/{listing}".format(
+ project=project,
+ location=location,
+ data_exchange=data_exchange,
+ listing=listing,
+ )
+ actual = SubscriberClient.listing_path(project, location, data_exchange, listing)
+ assert expected == actual
+
+
+def test_parse_listing_path():
+ expected = {
+ "project": "oyster",
+ "location": "nudibranch",
+ "data_exchange": "cuttlefish",
+ "listing": "mussel",
+ }
+ path = SubscriberClient.listing_path(**expected)
+
+ # Check that the path construction is reversible.
+ actual = SubscriberClient.parse_listing_path(path)
+ assert expected == actual
+
+
+def test_snapshot_path():
+ project = "winkle"
+ snapshot = "nautilus"
expected = "projects/{project}/snapshots/{snapshot}".format(
project=project,
snapshot=snapshot,
@@ -12585,8 +13443,8 @@ def test_snapshot_path():
def test_parse_snapshot_path():
expected = {
- "project": "whelk",
- "snapshot": "octopus",
+ "project": "scallop",
+ "snapshot": "abalone",
}
path = SubscriberClient.snapshot_path(**expected)
@@ -12596,8 +13454,8 @@ def test_parse_snapshot_path():
def test_subscription_path():
- project = "oyster"
- subscription = "nudibranch"
+ project = "squid"
+ subscription = "clam"
expected = "projects/{project}/subscriptions/{subscription}".format(
project=project,
subscription=subscription,
@@ -12608,8 +13466,8 @@ def test_subscription_path():
def test_parse_subscription_path():
expected = {
- "project": "cuttlefish",
- "subscription": "mussel",
+ "project": "whelk",
+ "subscription": "octopus",
}
path = SubscriberClient.subscription_path(**expected)
@@ -12619,8 +13477,8 @@ def test_parse_subscription_path():
def test_topic_path():
- project = "winkle"
- topic = "nautilus"
+ project = "oyster"
+ topic = "nudibranch"
expected = "projects/{project}/topics/{topic}".format(
project=project,
topic=topic,
@@ -12631,8 +13489,8 @@ def test_topic_path():
def test_parse_topic_path():
expected = {
- "project": "scallop",
- "topic": "abalone",
+ "project": "cuttlefish",
+ "topic": "mussel",
}
path = SubscriberClient.topic_path(**expected)
@@ -12642,7 +13500,7 @@ def test_parse_topic_path():
def test_common_billing_account_path():
- billing_account = "squid"
+ billing_account = "winkle"
expected = "billingAccounts/{billing_account}".format(
billing_account=billing_account,
)
@@ -12652,7 +13510,7 @@ def test_common_billing_account_path():
def test_parse_common_billing_account_path():
expected = {
- "billing_account": "clam",
+ "billing_account": "nautilus",
}
path = SubscriberClient.common_billing_account_path(**expected)
@@ -12662,7 +13520,7 @@ def test_parse_common_billing_account_path():
def test_common_folder_path():
- folder = "whelk"
+ folder = "scallop"
expected = "folders/{folder}".format(
folder=folder,
)
@@ -12672,7 +13530,7 @@ def test_common_folder_path():
def test_parse_common_folder_path():
expected = {
- "folder": "octopus",
+ "folder": "abalone",
}
path = SubscriberClient.common_folder_path(**expected)
@@ -12682,7 +13540,7 @@ def test_parse_common_folder_path():
def test_common_organization_path():
- organization = "oyster"
+ organization = "squid"
expected = "organizations/{organization}".format(
organization=organization,
)
@@ -12692,7 +13550,7 @@ def test_common_organization_path():
def test_parse_common_organization_path():
expected = {
- "organization": "nudibranch",
+ "organization": "clam",
}
path = SubscriberClient.common_organization_path(**expected)
@@ -12702,7 +13560,7 @@ def test_parse_common_organization_path():
def test_common_project_path():
- project = "cuttlefish"
+ project = "whelk"
expected = "projects/{project}".format(
project=project,
)
@@ -12712,7 +13570,7 @@ def test_common_project_path():
def test_parse_common_project_path():
expected = {
- "project": "mussel",
+ "project": "octopus",
}
path = SubscriberClient.common_project_path(**expected)
@@ -12722,8 +13580,8 @@ def test_parse_common_project_path():
def test_common_location_path():
- project = "winkle"
- location = "nautilus"
+ project = "oyster"
+ location = "nudibranch"
expected = "projects/{project}/locations/{location}".format(
project=project,
location=location,
@@ -12734,8 +13592,8 @@ def test_common_location_path():
def test_parse_common_location_path():
expected = {
- "project": "scallop",
- "location": "abalone",
+ "project": "cuttlefish",
+ "location": "mussel",
}
path = SubscriberClient.common_location_path(**expected)
@@ -12767,194 +13625,6 @@ def test_client_with_default_client_info():
prep.assert_called_once_with(client_info)
-@pytest.mark.asyncio
-async def test_transport_close_async():
- client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
- )
- with mock.patch.object(
- type(getattr(client.transport, "grpc_channel")), "close"
- ) as close:
- async with client:
- close.assert_not_called()
- close.assert_called_once()
-
-
-def test_get_iam_policy_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.GetIamPolicyRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/topics/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.get_iam_policy(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.GetIamPolicyRequest,
- dict,
- ],
-)
-def test_get_iam_policy_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = policy_pb2.Policy()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.get_iam_policy(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, policy_pb2.Policy)
-
-
-def test_set_iam_policy_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.SetIamPolicyRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/topics/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.set_iam_policy(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.SetIamPolicyRequest,
- dict,
- ],
-)
-def test_set_iam_policy_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/topics/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = policy_pb2.Policy()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.set_iam_policy(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, policy_pb2.Policy)
-
-
-def test_test_iam_permissions_rest_bad_request(
- transport: str = "rest", request_type=iam_policy_pb2.TestIamPermissionsRequest
-):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
- )
-
- request = request_type()
- request = json_format.ParseDict(
- {"resource": "projects/sample1/subscriptions/sample2"}, request
- )
-
- # Mock the http request call within the method and fake a BadRequest error.
- with mock.patch.object(Session, "request") as req, pytest.raises(
- core_exceptions.BadRequest
- ):
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 400
- response_value.request = Request()
- req.return_value = response_value
- client.test_iam_permissions(request)
-
-
-@pytest.mark.parametrize(
- "request_type",
- [
- iam_policy_pb2.TestIamPermissionsRequest,
- dict,
- ],
-)
-def test_test_iam_permissions_rest(request_type):
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="rest",
- )
- request_init = {"resource": "projects/sample1/subscriptions/sample2"}
- request = request_type(**request_init)
- # Mock the http request call within the method and fake a response.
- with mock.patch.object(type(client.transport._session), "request") as req:
- # Designate an appropriate value for the returned response.
- return_value = iam_policy_pb2.TestIamPermissionsResponse()
-
- # Wrap the value into a proper Response obj
- response_value = Response()
- response_value.status_code = 200
- json_return_value = json_format.MessageToJson(return_value)
-
- response_value._content = json_return_value.encode("UTF-8")
- req.return_value = response_value
-
- response = client.test_iam_permissions(request)
-
- # Establish that the response is the type that we expect.
- assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse)
-
-
def test_set_iam_policy(transport: str = "grpc"):
client = SubscriberClient(
credentials=ga_credentials.AnonymousCredentials(),
@@ -12990,7 +13660,7 @@ def test_set_iam_policy(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_set_iam_policy_async(transport: str = "grpc_asyncio"):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -13055,7 +13725,7 @@ def test_set_iam_policy_field_headers():
@pytest.mark.asyncio
async def test_set_iam_policy_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -13103,7 +13773,7 @@ def test_set_iam_policy_from_dict():
@pytest.mark.asyncio
async def test_set_iam_policy_from_dict_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call:
@@ -13156,7 +13826,7 @@ def test_get_iam_policy(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_get_iam_policy_async(transport: str = "grpc_asyncio"):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -13222,7 +13892,7 @@ def test_get_iam_policy_field_headers():
@pytest.mark.asyncio
async def test_get_iam_policy_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -13270,7 +13940,7 @@ def test_get_iam_policy_from_dict():
@pytest.mark.asyncio
async def test_get_iam_policy_from_dict_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call:
@@ -13322,7 +13992,7 @@ def test_test_iam_permissions(transport: str = "grpc"):
@pytest.mark.asyncio
async def test_test_iam_permissions_async(transport: str = "grpc_asyncio"):
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -13389,7 +14059,7 @@ def test_test_iam_permissions_field_headers():
@pytest.mark.asyncio
async def test_test_iam_permissions_field_headers_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -13443,7 +14113,7 @@ def test_test_iam_permissions_from_dict():
@pytest.mark.asyncio
async def test_test_iam_permissions_from_dict_async():
client = SubscriberAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(
@@ -13463,22 +14133,41 @@ async def test_test_iam_permissions_from_dict_async():
call.assert_called()
-def test_transport_close():
- transports = {
- "rest": "_session",
- "grpc": "_grpc_channel",
- }
+def test_transport_close_grpc():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="grpc"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_grpc_channel")), "close"
+ ) as close:
+ with client:
+ close.assert_not_called()
+ close.assert_called_once()
- for transport, close_name in transports.items():
- client = SubscriberClient(
- credentials=ga_credentials.AnonymousCredentials(), transport=transport
- )
- with mock.patch.object(
- type(getattr(client.transport, close_name)), "close"
- ) as close:
- with client:
- close.assert_not_called()
- close.assert_called_once()
+
+@pytest.mark.asyncio
+async def test_transport_close_grpc_asyncio():
+ client = SubscriberAsyncClient(
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_grpc_channel")), "close"
+ ) as close:
+ async with client:
+ close.assert_not_called()
+ close.assert_called_once()
+
+
+def test_transport_close_rest():
+ client = SubscriberClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ with mock.patch.object(
+ type(getattr(client.transport, "_session")), "close"
+ ) as close:
+ with client:
+ close.assert_not_called()
+ close.assert_called_once()
def test_client_ctx():
diff --git a/tests/unit/pubsub_v1/conftest.py b/tests/unit/pubsub_v1/conftest.py
index dc4192931..ab73ab26c 100644
--- a/tests/unit/pubsub_v1/conftest.py
+++ b/tests/unit/pubsub_v1/conftest.py
@@ -12,9 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import google.auth.credentials
+import logging
import pytest
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry import trace
+import google.auth.credentials
+
@pytest.fixture
def creds():
@@ -23,3 +29,34 @@ def creds():
GOOGLE_APPLICATION_CREDENTIALS set.
"""
yield google.auth.credentials.AnonymousCredentials()
+
+
+@pytest.fixture(scope="session", autouse=True)
+def set_trace_provider():
+ provider = TracerProvider()
+ trace.set_tracer_provider(provider)
+
+
+@pytest.fixture(scope="function")
+def span_exporter():
+ exporter = InMemorySpanExporter()
+ processor = SimpleSpanProcessor(exporter)
+ provider = trace.get_tracer_provider()
+ provider.add_span_processor(processor)
+ yield exporter
+
+
+@pytest.fixture()
+def modify_google_logger_propagation():
+ """
+ Allow propagation of logs to the root logger for tests
+ that depend on the caplog fixture. Restore the default
+ propagation setting after the test finishes.
+ """
+ logger = logging.getLogger("google")
+ original_propagate = logger.propagate
+ logger.propagate = True
+ try:
+ yield
+ finally:
+ logger.propagate = original_propagate
diff --git a/tests/unit/pubsub_v1/publisher/batch/test_base.py b/tests/unit/pubsub_v1/publisher/batch/test_base.py
index a95d72c12..ae5dbea04 100644
--- a/tests/unit/pubsub_v1/publisher/batch/test_base.py
+++ b/tests/unit/pubsub_v1/publisher/batch/test_base.py
@@ -21,6 +21,9 @@
from google.cloud.pubsub_v1.publisher._batch.base import BatchStatus
from google.cloud.pubsub_v1.publisher._batch.thread import Batch
from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
def create_batch(status, settings=types.BatchSettings()):
@@ -41,5 +44,5 @@ def create_batch(status, settings=types.BatchSettings()):
def test_len():
batch = create_batch(status=BatchStatus.ACCEPTING_MESSAGES)
assert len(batch) == 0
- batch.publish(gapic_types.PubsubMessage(data=b"foo"))
+ batch.publish(PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo")))
assert len(batch) == 1
diff --git a/tests/unit/pubsub_v1/publisher/batch/test_thread.py b/tests/unit/pubsub_v1/publisher/batch/test_thread.py
index 2752d62a2..dc6d25fad 100644
--- a/tests/unit/pubsub_v1/publisher/batch/test_thread.py
+++ b/tests/unit/pubsub_v1/publisher/batch/test_thread.py
@@ -17,17 +17,17 @@
import threading
import time
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
+from opentelemetry import trace
+from opentelemetry.trace import SpanContext
+
import google.api_core.exceptions
from google.api_core import gapic_v1
from google.auth import credentials
+from google.auth import exceptions as auth_exceptions
from google.cloud.pubsub_v1 import publisher
from google.cloud.pubsub_v1 import types
from google.cloud.pubsub_v1.publisher import exceptions
@@ -36,10 +36,18 @@
from google.cloud.pubsub_v1.publisher._batch import thread
from google.cloud.pubsub_v1.publisher._batch.thread import Batch
from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
-def create_client():
- return publisher.Client(credentials=credentials.AnonymousCredentials())
+def create_client(enable_open_telemetry: bool = False):
+ return publisher.Client(
+ credentials=credentials.AnonymousCredentials(),
+ publisher_options=types.PublisherOptions(
+ enable_open_telemetry_tracing=enable_open_telemetry,
+ ),
+ )
def create_batch(
@@ -48,7 +56,8 @@ def create_batch(
commit_when_full=True,
commit_retry=gapic_v1.method.DEFAULT,
commit_timeout: gapic_types.TimeoutType = gapic_v1.method.DEFAULT,
- **batch_settings
+ enable_open_telemetry: bool = False,
+ **batch_settings,
):
"""Return a batch object suitable for testing.
@@ -62,13 +71,14 @@ def create_batch(
for the batch commit call.
commit_timeout (:class:`~.pubsub_v1.types.TimeoutType`):
The timeout to apply to the batch commit call.
+ enable_open_telemetry (bool): Whether to enable OpenTelemetry.
batch_settings (Mapping[str, str]): Arguments passed on to the
:class:``~.pubsub_v1.types.BatchSettings`` constructor.
Returns:
~.pubsub_v1.publisher.batch.thread.Batch: A batch object.
"""
- client = create_client()
+ client = create_client(enable_open_telemetry=enable_open_telemetry)
settings = types.BatchSettings(**batch_settings)
return Batch(
client,
@@ -126,8 +136,16 @@ def test_commit_no_op():
def test_blocking__commit():
batch = create_batch()
futures = (
- batch.publish({"data": b"This is my message."}),
- batch.publish({"data": b"This is another message."}),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"This is my message.")
+ )
+ ),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"This is another message.")
+ )
+ ),
)
# Set up the underlying API publish method to return a PublishResponse.
@@ -160,7 +178,11 @@ def test_blocking__commit():
def test_blocking__commit_custom_retry():
batch = create_batch(commit_retry=mock.sentinel.custom_retry)
- batch.publish({"data": b"This is my message."})
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"This is my message.")
+ )
+ )
# Set up the underlying API publish method to return a PublishResponse.
publish_response = gapic_types.PublishResponse(message_ids=["a"])
@@ -182,7 +204,11 @@ def test_blocking__commit_custom_retry():
def test_blocking__commit_custom_timeout():
batch = create_batch(commit_timeout=mock.sentinel.custom_timeout)
- batch.publish({"data": b"This is my message."})
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"This is my message.")
+ )
+ )
# Set up the underlying API publish method to return a PublishResponse.
publish_response = gapic_types.PublishResponse(message_ids=["a"])
@@ -217,13 +243,21 @@ def api_publish_delay(topic="", messages=(), retry=None, timeout=None):
)
with api_publish_patch:
- batch.publish({"data": b"first message"})
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"first message")
+ )
+ )
start = datetime.datetime.now()
event_set = api_publish_called.wait(timeout=1.0)
if not event_set: # pragma: NO COVER
pytest.fail("API publish was not called in time")
- batch.publish({"data": b"second message"})
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"second message")
+ )
+ )
end = datetime.datetime.now()
# While a batch commit in progress, waiting for the API publish call to
@@ -266,8 +300,16 @@ def test_blocking__commit_no_messages():
def test_blocking__commit_wrong_messageid_length():
batch = create_batch()
futures = (
- batch.publish({"data": b"blah blah blah"}),
- batch.publish({"data": b"blah blah blah blah"}),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"blah blah blah")
+ )
+ ),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"blah blah blah blah")
+ )
+ ),
)
# Set up a PublishResponse that only returns one message ID.
@@ -284,15 +326,29 @@ def test_blocking__commit_wrong_messageid_length():
assert isinstance(future.exception(), exceptions.PublishError)
-def test_block__commmit_api_error():
+@pytest.mark.parametrize(
+ "error",
+ [
+ (google.api_core.exceptions.InternalServerError("Internal server error"),),
+ (auth_exceptions.TransportError("some transport error"),),
+ ],
+)
+def test_block__commmit_api_error(error):
batch = create_batch()
futures = (
- batch.publish({"data": b"blah blah blah"}),
- batch.publish({"data": b"blah blah blah blah"}),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"blah blah blah")
+ )
+ ),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"blah blah blah blah")
+ )
+ ),
)
# Make the API throw an error when publishing.
- error = google.api_core.exceptions.InternalServerError("uh oh")
patch = mock.patch.object(type(batch.client), "_gapic_publish", side_effect=error)
with patch:
@@ -300,14 +356,22 @@ def test_block__commmit_api_error():
for future in futures:
assert future.done()
- assert future.exception() == error
+ assert future.exception() == error[0]
def test_block__commmit_retry_error():
batch = create_batch()
futures = (
- batch.publish({"data": b"blah blah blah"}),
- batch.publish({"data": b"blah blah blah blah"}),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"blah blah blah")
+ )
+ ),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"blah blah blah blah")
+ )
+ ),
)
# Make the API throw an error when publishing.
@@ -324,24 +388,31 @@ def test_block__commmit_retry_error():
def test_publish_updating_batch_size():
batch = create_batch(topic="topic_foo")
- messages = (
- gapic_types.PubsubMessage(data=b"foobarbaz"),
- gapic_types.PubsubMessage(data=b"spameggs"),
- gapic_types.PubsubMessage(data=b"1335020400"),
+ wrappers = (
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz"),
+ ),
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"spameggs"),
+ ),
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"1335020400"),
+ ),
)
# Publish each of the messages, which should save them to the batch.
- futures = [batch.publish(message) for message in messages]
+ futures = [batch.publish(wrapper) for wrapper in wrappers]
# There should be three messages on the batch, and three futures.
- assert len(batch.messages) == 3
+ assert len(batch.message_wrappers) == 3
assert batch._futures == futures
# The size should have been incremented by the sum of the size
# contributions of each message to the PublishRequest.
base_request_size = gapic_types.PublishRequest(topic="topic_foo")._pb.ByteSize()
expected_request_size = base_request_size + sum(
- gapic_types.PublishRequest(messages=[msg])._pb.ByteSize() for msg in messages
+ gapic_types.PublishRequest(messages=[wrapper.message])._pb.ByteSize()
+ for wrapper in wrappers
)
assert batch.size == expected_request_size
@@ -350,68 +421,82 @@ def test_publish_updating_batch_size():
def test_publish():
batch = create_batch()
- message = gapic_types.PubsubMessage()
- future = batch.publish(message)
+ wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage())
+ future = batch.publish(wrapper)
- assert len(batch.messages) == 1
+ assert len(batch.message_wrappers) == 1
assert batch._futures == [future]
def test_publish_max_messages_zero():
batch = create_batch(topic="topic_foo", max_messages=0)
-
- message = gapic_types.PubsubMessage(data=b"foobarbaz")
+ wrapper = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz"),
+ )
with mock.patch.object(batch, "commit") as commit:
- future = batch.publish(message)
+ future = batch.publish(wrapper)
assert future is not None
- assert len(batch.messages) == 1
+ assert len(batch.message_wrappers) == 1
assert batch._futures == [future]
commit.assert_called_once()
def test_publish_max_messages_enforced():
batch = create_batch(topic="topic_foo", max_messages=1)
+ wrapper = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz")
+ )
+ wrapper2 = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz2")
+ )
- message = gapic_types.PubsubMessage(data=b"foobarbaz")
- message2 = gapic_types.PubsubMessage(data=b"foobarbaz2")
-
- future = batch.publish(message)
- future2 = batch.publish(message2)
+ future = batch.publish(wrapper)
+ future2 = batch.publish(wrapper2)
assert future is not None
assert future2 is None
- assert len(batch.messages) == 1
+ assert len(batch.message_wrappers) == 1
assert len(batch._futures) == 1
def test_publish_max_bytes_enforced():
batch = create_batch(topic="topic_foo", max_bytes=15)
- message = gapic_types.PubsubMessage(data=b"foobarbaz")
- message2 = gapic_types.PubsubMessage(data=b"foobarbaz2")
+ wrapper = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz")
+ )
+ wrapper2 = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz2")
+ )
- future = batch.publish(message)
- future2 = batch.publish(message2)
+ future = batch.publish(wrapper)
+ future2 = batch.publish(wrapper2)
assert future is not None
assert future2 is None
- assert len(batch.messages) == 1
+ assert len(batch.message_wrappers) == 1
assert len(batch._futures) == 1
def test_publish_exceed_max_messages():
max_messages = 4
batch = create_batch(max_messages=max_messages)
- messages = (
- gapic_types.PubsubMessage(data=b"foobarbaz"),
- gapic_types.PubsubMessage(data=b"spameggs"),
- gapic_types.PubsubMessage(data=b"1335020400"),
+ wrappers = (
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz"),
+ ),
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"spameggs"),
+ ),
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"1335020400"),
+ ),
)
# Publish each of the messages, which should save them to the batch.
with mock.patch.object(batch, "commit") as commit:
- futures = [batch.publish(message) for message in messages]
+ futures = [batch.publish(wrapper) for wrapper in wrappers]
assert batch._futures == futures
assert len(futures) == max_messages - 1
@@ -420,7 +505,11 @@ def test_publish_exceed_max_messages():
# When a fourth message is published, commit should be called.
# No future will be returned in this case.
- future = batch.publish(gapic_types.PubsubMessage(data=b"last one"))
+ future = batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"last one")
+ )
+ )
commit.assert_called_once_with()
assert future is None
@@ -443,28 +532,32 @@ def test_publish_single_message_size_exceeds_server_size_limit():
assert request_size == 1001 # sanity check, just above the (mocked) server limit
with pytest.raises(exceptions.MessageTooLargeError):
- batch.publish(big_message)
+ batch.publish(wrapper=PublishMessageWrapper(message=big_message))
@mock.patch.object(thread, "_SERVER_PUBLISH_MAX_BYTES", 1000)
def test_publish_total_messages_size_exceeds_server_size_limit():
batch = create_batch(topic="topic_foo", max_messages=10, max_bytes=1500)
- messages = (
- gapic_types.PubsubMessage(data=b"x" * 500),
- gapic_types.PubsubMessage(data=b"x" * 600),
+ wrappers = (
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"x" * 500),
+ ),
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"x" * 600),
+ ),
)
# Sanity check - request size is still below BatchSettings.max_bytes,
# but it exceeds the server-side size limit.
request_size = gapic_types.PublishRequest(
- topic="topic_foo", messages=messages
+ topic="topic_foo", messages=[wrapper.message for wrapper in wrappers]
)._pb.ByteSize()
assert 1000 < request_size < 1500
with mock.patch.object(batch, "commit") as fake_commit:
- batch.publish(messages[0])
- batch.publish(messages[1])
+ batch.publish(wrappers[0])
+ batch.publish(wrappers[1])
# The server side limit should kick in and cause a commit.
fake_commit.assert_called_once()
@@ -472,21 +565,40 @@ def test_publish_total_messages_size_exceeds_server_size_limit():
def test_publish_dict():
batch = create_batch()
- future = batch.publish({"data": b"foobarbaz", "attributes": {"spam": "eggs"}})
+ future = batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(
+ data=b"foobarbaz",
+ attributes={"spam": "eggs"},
+ ),
+ )
+ )
# There should be one message on the batch.
- expected_message = gapic_types.PubsubMessage(
- data=b"foobarbaz", attributes={"spam": "eggs"}
+ expected_message_wrapper = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(
+ data=b"foobarbaz",
+ attributes={"spam": "eggs"},
+ )
)
- assert batch.messages == [expected_message]
+
+ assert batch.message_wrappers == [expected_message_wrapper]
assert batch._futures == [future]
def test_cancel():
batch = create_batch()
futures = (
- batch.publish({"data": b"This is my message."}),
- batch.publish({"data": b"This is another message."}),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"This is my message."),
+ ),
+ ),
+ batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"This is another message."),
+ ),
+ ),
)
batch.cancel(BatchCancellationReason.PRIOR_ORDERED_MESSAGE_FAILED)
@@ -502,19 +614,29 @@ def test_do_not_commit_when_full_when_flag_is_off():
max_messages = 4
# Set commit_when_full flag to False
batch = create_batch(max_messages=max_messages, commit_when_full=False)
- messages = (
- gapic_types.PubsubMessage(data=b"foobarbaz"),
- gapic_types.PubsubMessage(data=b"spameggs"),
- gapic_types.PubsubMessage(data=b"1335020400"),
+ wrappers = (
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz"),
+ ),
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"spameggs"),
+ ),
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"1335020400"),
+ ),
)
with mock.patch.object(batch, "commit") as commit:
# Publish 3 messages.
- futures = [batch.publish(message) for message in messages]
+ futures = [batch.publish(wrapper) for wrapper in wrappers]
assert len(futures) == 3
# When a fourth message is published, commit should not be called.
- future = batch.publish(gapic_types.PubsubMessage(data=b"last one"))
+ future = batch.publish(
+ wrapper=PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"last one"),
+ )
+ )
assert commit.call_count == 0
assert future is None
@@ -534,8 +656,10 @@ def test_batch_done_callback_called_on_success():
batch = create_batch(batch_done_callback=batch_done_callback_tracker)
# Ensure messages exist.
- message = gapic_types.PubsubMessage(data=b"foobarbaz")
- batch.publish(message)
+ wrapper = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz")
+ )
+ batch.publish(wrapper)
# One response for one published message.
publish_response = gapic_types.PublishResponse(message_ids=["a"])
@@ -554,8 +678,10 @@ def test_batch_done_callback_called_on_publish_failure():
batch = create_batch(batch_done_callback=batch_done_callback_tracker)
# Ensure messages exist.
- message = gapic_types.PubsubMessage(data=b"foobarbaz")
- batch.publish(message)
+ wrapper = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz")
+ )
+ batch.publish(wrapper)
# One response for one published message.
publish_response = gapic_types.PublishResponse(message_ids=["a"])
@@ -580,8 +706,10 @@ def test_batch_done_callback_called_on_publish_response_invalid():
batch = create_batch(batch_done_callback=batch_done_callback_tracker)
# Ensure messages exist.
- message = gapic_types.PubsubMessage(data=b"foobarbaz")
- batch.publish(message)
+ wrapper = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foobarbaz"),
+ )
+ batch.publish(wrapper)
# No message ids returned in successful publish response -> invalid.
publish_response = gapic_types.PublishResponse(message_ids=[])
@@ -593,3 +721,249 @@ def test_batch_done_callback_called_on_publish_response_invalid():
assert batch_done_callback_tracker.called
assert not batch_done_callback_tracker.success
+
+
+# Refer https://opentelemetry.io/docs/languages/python/#version-support
+@pytest.mark.skipif(
+ sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
+)
+def test_open_telemetry_commit_publish_rpc_span_none(span_exporter):
+ """
+ Test scenario where OpenTelemetry is enabled, publish RPC
+ span creation fails(unexpected) and hence batch._rpc_span is None when
+ attempting to close it. Required for code coverage.
+ """
+ TOPIC = "projects/projectID/topics/topicID"
+ batch = create_batch(topic=TOPIC, enable_open_telemetry=True)
+
+ message = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foo"),
+ )
+ message.start_create_span(topic=TOPIC, ordering_key=None)
+ batch.publish(message)
+
+ # Mock error when publish RPC span creation is attempted.
+ error = google.api_core.exceptions.InternalServerError("error")
+
+ with mock.patch.object(
+ type(batch),
+ "_start_publish_rpc_span",
+ side_effect=error,
+ ):
+ batch._commit()
+
+ assert batch._rpc_span is None
+ spans = span_exporter.get_finished_spans()
+
+ # Only Create span should be exported, since publish RPC span creation
+ # should fail with a mock error.
+ assert len(spans) == 1
+
+ publish_create_span = spans[0]
+ assert publish_create_span.status.status_code == trace.status.StatusCode.ERROR
+ assert publish_create_span.end_time is not None
+
+ assert publish_create_span.name == "topicID create"
+ # Publish start event and exception event should be present in publish
+ # create span.
+ assert len(publish_create_span.events) == 2
+ assert publish_create_span.events[0].name == "publish start"
+ assert publish_create_span.events[1].name == "exception"
+
+
+# Refer https://opentelemetry.io/docs/languages/python/#version-support
+@pytest.mark.skipif(
+ sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
+)
+def test_open_telemetry_commit_publish_rpc_exception(span_exporter):
+ TOPIC = "projects/projectID/topics/topicID"
+ batch = create_batch(topic=TOPIC, enable_open_telemetry=True)
+
+ message = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foo"),
+ )
+ message.start_create_span(topic=TOPIC, ordering_key=None)
+ batch.publish(message)
+
+ # Mock publish error.
+ error = google.api_core.exceptions.InternalServerError("error")
+
+ with mock.patch.object(
+ type(batch.client),
+ "_gapic_publish",
+ side_effect=error,
+ ):
+ batch._commit()
+
+ spans = span_exporter.get_finished_spans()
+ # Span 1: Publish RPC span
+ # Span 2: Create span.
+ assert len(spans) == 2
+
+ # Verify both spans recorded error and have ended.
+ for span in spans:
+ assert span.status.status_code == trace.status.StatusCode.ERROR
+ assert span.end_time is not None
+
+ publish_rpc_span = spans[0]
+ assert publish_rpc_span.name == "topicID publish"
+ assert len(publish_rpc_span.events) == 1
+ assert publish_rpc_span.events[0].name == "exception"
+
+ publish_create_span = spans[1]
+ assert publish_create_span.name == "topicID create"
+ # Publish start event and exception event should be present in publish
+ # create span.
+ assert len(publish_create_span.events) == 2
+ assert publish_create_span.events[0].name == "publish start"
+ assert publish_create_span.events[1].name == "exception"
+
+
+# Refer https://opentelemetry.io/docs/languages/python/#version-support
+@pytest.mark.skipif(
+ sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
+)
+def test_opentelemetry_commit_sampling(span_exporter):
+ TOPIC = "projects/projectID/topics/topic"
+ batch = create_batch(
+ topic=TOPIC,
+ enable_open_telemetry=True,
+ )
+
+ message1 = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foo"),
+ )
+ message1.start_create_span(topic=TOPIC, ordering_key=None)
+
+ message2 = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"bar"),
+ )
+ message2.start_create_span(topic=TOPIC, ordering_key=None)
+
+ # Mock the 'get_span_context' method to return a mock SpanContext
+ mock_span_context = mock.Mock(spec=SpanContext)
+ mock_span_context.trace_flags.sampled = False
+
+ batch.publish(message1)
+ batch.publish(message2)
+
+ publish_response = gapic_types.PublishResponse(message_ids=["a", "b"])
+
+ # Patch the 'create_span' method to return the mock SpanContext
+ with mock.patch.object(
+ message1.create_span, "get_span_context", return_value=mock_span_context
+ ):
+ with mock.patch.object(
+ type(batch.client), "_gapic_publish", return_value=publish_response
+ ):
+ batch._commit()
+
+ spans = span_exporter.get_finished_spans()
+
+ # Span 1: Publish RPC span of both messages
+ # Span 2: Create span of message 1
+ # Span 3: Create span of message 2
+ assert len(spans) == 3
+
+ publish_rpc_span, create_span1, create_span2 = spans
+
+ # Verify publish RPC span has only one link corresponding to
+ # message 2 which is included in the sample.
+ assert len(publish_rpc_span.links) == 1
+ assert len(create_span1.links) == 0
+ assert len(create_span2.links) == 1
+ assert publish_rpc_span.links[0].context == create_span2.context
+ assert create_span2.links[0].context == publish_rpc_span.context
+
+ # Verify all spans have ended.
+ for span in spans:
+ assert span.end_time is not None
+
+ # Verify both publish create spans have 2 events - publish start and publish
+ # end.
+ for span in spans[1:]:
+ assert len(span.events) == 2
+ assert span.events[0].name == "publish start"
+ assert span.events[1].name == "publish end"
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
+)
+def test_opentelemetry_commit(span_exporter):
+ TOPIC = "projects/projectID/topics/topic"
+ batch = create_batch(
+ topic=TOPIC,
+ enable_open_telemetry=True,
+ )
+
+ msg1 = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foo"),
+ )
+ msg2 = PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"bar"),
+ )
+ msg1.start_create_span(topic=TOPIC, ordering_key=None)
+ msg2.start_create_span(topic=TOPIC, ordering_key=None)
+
+ # Add both messages to the batch.
+ batch.publish(msg1)
+ batch.publish(msg2)
+
+ publish_response = gapic_types.PublishResponse(message_ids=["a", "b"])
+ with mock.patch.object(
+ type(batch.client), "_gapic_publish", return_value=publish_response
+ ):
+ batch._commit()
+
+ spans = span_exporter.get_finished_spans()
+
+ # Span 1: publish RPC span - closed after publish RPC success.
+ # Span 2: publisher create span of message 1 - closed after publish RPC success.
+ # Span 3: publisher create span of message 2 - closed after publish RPC success.
+ assert len(spans) == 3
+ publish_rpc_span, create_span1, create_span2 = spans
+
+ # Verify publish RPC span
+ assert publish_rpc_span.name == "topic publish"
+ assert publish_rpc_span.kind == trace.SpanKind.CLIENT
+ assert publish_rpc_span.end_time is not None
+ attributes = publish_rpc_span.attributes
+ assert attributes["messaging.system"] == "gcp_pubsub"
+ assert attributes["messaging.destination.name"] == "topic"
+ assert attributes["gcp.project_id"] == "projectID"
+ assert attributes["messaging.batch.message_count"] == 2
+ assert attributes["messaging.operation"] == "publish"
+ assert attributes["code.function"] == "_commit"
+ assert publish_rpc_span.parent is None
+ # Verify the links correspond to the spans of the published messages.
+ assert len(publish_rpc_span.links) == 2
+ assert publish_rpc_span.links[0].context == create_span1.context
+ assert publish_rpc_span.links[1].context == create_span2.context
+ assert len(create_span1.links) == 1
+ assert create_span1.links[0].context == publish_rpc_span.get_span_context()
+ assert len(create_span2.links) == 1
+ assert create_span2.links[0].context == publish_rpc_span.get_span_context()
+
+ # Verify spans of the published messages.
+ assert create_span1.name == "topic create"
+ assert create_span2.name == "topic create"
+
+ # Verify the publish create spans have been closed after publish success.
+ assert create_span1.end_time is not None
+ assert create_span2.end_time is not None
+
+ # Verify message IDs returned from gapic publish are added as attributes
+ # to the publisher create spans of the messages.
+ assert "messaging.message.id" in create_span1.attributes
+ assert create_span1.attributes["messaging.message.id"] == "a"
+ assert "messaging.message.id" in create_span2.attributes
+ assert create_span2.attributes["messaging.message.id"] == "b"
+
+ # Verify publish end event added to the span
+ assert len(create_span1.events) == 2
+ assert len(create_span2.events) == 2
+ assert create_span1.events[0].name == "publish start"
+ assert create_span1.events[1].name == "publish end"
+ assert create_span2.events[0].name == "publish start"
+ assert create_span2.events[1].name == "publish end"
diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py
index 7570c2970..f7c166aab 100644
--- a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py
+++ b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py
@@ -13,26 +13,25 @@
# limitations under the License.
import concurrent.futures as futures
-import sys
-
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
from google.auth import credentials
from google.cloud.pubsub_v1 import publisher
from google.cloud.pubsub_v1.publisher._sequencer import ordered_sequencer
from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
_ORDERING_KEY = "ordering_key_1"
def create_message():
- return gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
+ return PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
+ )
def create_client():
diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py
index 01d9d6ca4..054e66da0 100644
--- a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py
+++ b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py
@@ -11,14 +11,8 @@
# 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 sys
-
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
from google.auth import credentials
@@ -27,10 +21,15 @@
from google.cloud.pubsub_v1.publisher._batch import base
from google.cloud.pubsub_v1.publisher._sequencer import unordered_sequencer
from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
def create_message():
- return gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
+ return PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
+ )
def create_client():
@@ -140,7 +139,9 @@ def test_publish_after_batch_error():
batch = client._batch_class(
client, "topic_name", types.BatchSettings(max_latency=float("inf"))
)
- batch._messages.append(mock.Mock(name="message")) # Make batch truthy (non-empty).
+ batch._message_wrappers.append(
+ mock.Mock(name="message")
+ ) # Make batch truthy (non-empty).
sequencer = unordered_sequencer.UnorderedSequencer(client, "topic_name")
sequencer._set_batch(batch)
diff --git a/tests/unit/pubsub_v1/publisher/test_publish_message_wrapper.py b/tests/unit/pubsub_v1/publisher/test_publish_message_wrapper.py
new file mode 100644
index 000000000..e100950ad
--- /dev/null
+++ b/tests/unit/pubsub_v1/publisher/test_publish_message_wrapper.py
@@ -0,0 +1,55 @@
+# Copyright 2019, Google LLC All rights reserved.
+#
+# 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 pytest
+
+from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
+
+
+def test_message_setter():
+ wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo"))
+ another_message = gapic_types.PubsubMessage(data=b"bar")
+ wrapper.message = another_message
+
+ assert wrapper.message == another_message
+
+
+def test_eq():
+ wrapper1 = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo"))
+ wrapper2 = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"bar"))
+ wrapper3 = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo"))
+
+ assert wrapper1.__eq__(wrapper2) is False
+ assert wrapper1.__eq__(wrapper3) is True
+
+
+def test_end_create_span():
+ wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo"))
+ with pytest.raises(AssertionError):
+ wrapper.end_create_span()
+
+
+def test_end_publisher_flow_control_span():
+ wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo"))
+ with pytest.raises(AssertionError):
+ wrapper.end_publisher_flow_control_span()
+
+
+def test_end_publisher_batching_span():
+ wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo"))
+ with pytest.raises(AssertionError):
+ wrapper.end_publisher_batching_span()
diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py
index 9db5e0ef8..cc417d492 100644
--- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py
+++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py
@@ -19,28 +19,50 @@
import sys
import grpc
+import math
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
import time
+from flaky import flaky
+from typing import cast, Callable, Any, TypeVar
+from opentelemetry import trace
from google.api_core import gapic_v1
from google.api_core import retry as retries
from google.api_core.gapic_v1.client_info import METRICS_METADATA_KEY
+from google.api_core.timeout import ConstantTimeout
+
from google.cloud.pubsub_v1 import publisher
from google.cloud.pubsub_v1 import types
-
from google.cloud.pubsub_v1.publisher import exceptions
from google.cloud.pubsub_v1.publisher._sequencer import ordered_sequencer
-
from google.pubsub_v1 import types as gapic_types
from google.pubsub_v1.services.publisher import client as publisher_client
from google.pubsub_v1.services.publisher.transports.grpc import PublisherGrpcTransport
+from google.cloud.pubsub_v1.open_telemetry.context_propagation import (
+ OpenTelemetryContextSetter,
+)
+from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
+ PublishMessageWrapper,
+)
+
+
+C = TypeVar("C", bound=Callable[..., Any])
+typed_flaky = cast(Callable[[C], C], flaky(max_runs=5, min_passes=1))
+
+
+# Attempt to use `_thunk` to obtain the underlying grpc channel from
+# the intercept channel. Default to obtaining the grpc channel directly
+# for backwards compatibility.
+# TODO(https://github.com/grpc/grpc/issues/38519): Workaround to obtain a channel
+# until a public API is available.
+def get_publish_channel(client):
+ try:
+ return client._transport.publish._thunk("")._channel
+ except AttributeError:
+ return client._transport.publish._channel
def _assert_retries_equal(retry, retry2):
@@ -129,6 +151,231 @@ def test_init_w_custom_transport(creds):
assert client.batch_settings.max_messages == 100
+@pytest.mark.parametrize(
+ "enable_open_telemetry",
+ [
+ True,
+ False,
+ ],
+)
+@typed_flaky
+def test_open_telemetry_publisher_options(creds, enable_open_telemetry):
+ if sys.version_info >= (3, 8) or enable_open_telemetry is False:
+ client = publisher.Client(
+ publisher_options=types.PublisherOptions(
+ enable_open_telemetry_tracing=enable_open_telemetry
+ ),
+ credentials=creds,
+ )
+ assert client._open_telemetry_enabled == enable_open_telemetry
+ else:
+ # Open Telemetry is not supported and hence disabled for Python
+ # versions 3.7 or below
+ with pytest.warns(
+ RuntimeWarning,
+ match="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.",
+ ):
+ client = publisher.Client(
+ publisher_options=types.PublisherOptions(
+ enable_open_telemetry_tracing=enable_open_telemetry
+ ),
+ credentials=creds,
+ )
+ assert client._open_telemetry_enabled is False
+
+
+def test_opentelemetry_context_setter():
+ msg = gapic_types.PubsubMessage(data=b"foo")
+ OpenTelemetryContextSetter().set(carrier=msg, key="key", value="bar")
+
+ assert "googclient_key" in msg.attributes.keys()
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry_context_propagation(creds, span_exporter):
+ TOPIC = "projects/projectID/topics/topicID"
+ client = publisher.Client(
+ credentials=creds,
+ publisher_options=types.PublisherOptions(
+ enable_open_telemetry_tracing=True,
+ ),
+ )
+
+ message_mock = mock.Mock(spec=publisher.flow_controller.FlowController.add)
+ client._flow_controller.add = message_mock
+ client.publish(TOPIC, b"data")
+
+ message_mock.assert_called_once()
+ args = message_mock.call_args.args
+ assert len(args) == 1
+ assert "googclient_traceparent" in args[0].attributes
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+@pytest.mark.parametrize(
+ "enable_open_telemetry",
+ [
+ True,
+ False,
+ ],
+)
+def test_opentelemetry_publisher_batching_exception(
+ creds, span_exporter, enable_open_telemetry
+):
+ client = publisher.Client(
+ credentials=creds,
+ publisher_options=types.PublisherOptions(
+ enable_open_telemetry_tracing=enable_open_telemetry,
+ ),
+ )
+
+ # Throw an exception when sequencer.publish() is called
+ sequencer = mock.Mock(spec=ordered_sequencer.OrderedSequencer)
+ sequencer.publish = mock.Mock(side_effect=RuntimeError("some error"))
+ client._get_or_create_sequencer = mock.Mock(return_value=sequencer)
+
+ TOPIC = "projects/projectID/topics/topicID"
+ with pytest.raises(RuntimeError):
+ client.publish(TOPIC, b"message")
+
+ spans = span_exporter.get_finished_spans()
+
+ if enable_open_telemetry:
+ # Span 1: Publisher Flow Control span
+ # Span 2: Publisher Batching span
+ # Span 3: Create Publish span
+ assert len(spans) == 3
+
+ flow_control_span, batching_span, create_span = spans
+
+ # Verify batching span contents.
+ assert batching_span.name == "publisher batching"
+ assert batching_span.kind == trace.SpanKind.INTERNAL
+ assert batching_span.parent.span_id == create_span.get_span_context().span_id
+
+ # Verify exception recorded by the publisher batching span.
+ assert batching_span.status.status_code == trace.StatusCode.ERROR
+ assert len(batching_span.events) == 1
+ assert batching_span.events[0].name == "exception"
+
+ # Verify exception recorded by the publisher create span.
+ assert create_span.status.status_code == trace.StatusCode.ERROR
+ assert len(create_span.events) == 2
+ assert create_span.events[0].name == "publish start"
+ assert create_span.events[1].name == "exception"
+
+ # Verify the finished flow control span.
+ assert flow_control_span.name == "publisher flow control"
+ assert len(flow_control_span.events) == 0
+ else:
+ assert len(spans) == 0
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry_flow_control_exception(creds, span_exporter):
+ publisher_options = types.PublisherOptions(
+ flow_control=types.PublishFlowControl(
+ message_limit=10,
+ byte_limit=150,
+ limit_exceeded_behavior=types.LimitExceededBehavior.ERROR,
+ ),
+ enable_open_telemetry_tracing=True,
+ )
+ client = publisher.Client(credentials=creds, publisher_options=publisher_options)
+
+ mock_batch = mock.Mock(spec=client._batch_class)
+ topic = "projects/projectID/topics/topicID"
+ client._set_batch(topic, mock_batch)
+
+ future1 = client.publish(topic, b"a" * 60)
+ future2 = client.publish(topic, b"b" * 100)
+
+ future1.result() # no error, still within flow control limits
+ with pytest.raises(exceptions.FlowControlLimitError):
+ future2.result()
+
+ spans = span_exporter.get_finished_spans()
+
+ # Find the spans related to the second, failing publish call
+ failed_create_span = None
+ failed_fc_span = None
+ for span in spans:
+ if span.name == "topicID create":
+ if span.status.status_code == trace.StatusCode.ERROR:
+ failed_create_span = span
+ elif span.name == "publisher flow control":
+ if span.status.status_code == trace.StatusCode.ERROR:
+ failed_fc_span = span
+
+ assert failed_create_span is not None, "Failed 'topicID create' span not found"
+ assert failed_fc_span is not None, "Failed 'publisher flow control' span not found"
+
+ # Verify failed flow control span values.
+ assert failed_fc_span.kind == trace.SpanKind.INTERNAL
+ assert (
+ failed_fc_span.parent.span_id == failed_create_span.get_span_context().span_id
+ )
+ assert len(failed_fc_span.events) == 1
+ assert failed_fc_span.events[0].name == "exception"
+
+ # Verify finished publish create span values
+ assert failed_create_span.status.status_code == trace.StatusCode.ERROR
+ assert len(failed_create_span.events) >= 1 # Should have at least 'publish start'
+ assert failed_create_span.events[0].name == "publish start"
+ # Check for exception event
+ has_exception_event = any(
+ event.name == "exception" for event in failed_create_span.events
+ )
+ assert has_exception_event, "Exception event not found in failed create span"
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry_publish(creds, span_exporter):
+ TOPIC = "projects/projectID/topics/topicID"
+ client = publisher.Client(
+ credentials=creds,
+ publisher_options=types.PublisherOptions(
+ enable_open_telemetry_tracing=True,
+ ),
+ )
+
+ client.publish(TOPIC, b"message")
+ spans = span_exporter.get_finished_spans()
+
+ # Publisher Flow control and batching spans would be ended in the
+ # publish() function and are deterministically expected to be in the
+ # list of exported spans. The Publish Create span and Publish RPC span
+ # are run async and end at a non-deterministic time. Hence,
+ # asserting that we have atleast two spans(flow control and batching span)
+ assert len(spans) >= 2
+ flow_control_span = None
+ batching_span = None
+ for span in spans:
+ if span.name == "publisher flow control":
+ flow_control_span = span
+ assert flow_control_span.kind == trace.SpanKind.INTERNAL
+ assert flow_control_span.parent is not None
+ if span.name == "publisher batching":
+ batching_span = span
+ assert batching_span.kind == trace.SpanKind.INTERNAL
+ assert batching_span.parent is not None
+
+ assert flow_control_span is not None
+ assert batching_span is not None
+
+
def test_init_w_api_endpoint(creds):
client_options = {"api_endpoint": "testendpoint.google.com"}
client = publisher.Client(client_options=client_options, credentials=creds)
@@ -192,7 +439,7 @@ def test_init_emulator(monkeypatch):
#
# Sadly, there seems to be no good way to do this without poking at
# the private API of gRPC.
- channel = client._transport.publish._channel
+ channel = get_publish_channel(client)
# Behavior to include dns prefix changed in gRPCv1.63
grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]]
if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63):
@@ -240,9 +487,17 @@ def test_publish(creds):
# Check mock.
batch.publish.assert_has_calls(
[
- mock.call(gapic_types.PubsubMessage(data=b"spam")),
mock.call(
- gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"spam"),
+ )
+ ),
+ mock.call(
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(
+ data=b"foo", attributes={"bar": "baz"}
+ )
+ )
),
]
)
@@ -381,7 +636,9 @@ def test_publish_attrs_bytestring(creds):
# The attributes should have been sent as text.
batch.publish.assert_called_once_with(
- gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
+ )
)
@@ -410,6 +667,8 @@ def test_publish_new_batch_needed(creds):
future = client.publish(topic, b"foo", bar=b"baz")
assert future is mock.sentinel.future
+ call_args = batch_class.call_args
+
# Check the mocks.
batch_class.assert_called_once_with(
client=mock.ANY,
@@ -418,11 +677,16 @@ def test_publish_new_batch_needed(creds):
batch_done_callback=None,
commit_when_full=True,
commit_retry=gapic_v1.method.DEFAULT,
- commit_timeout=gapic_v1.method.DEFAULT,
+ commit_timeout=mock.ANY,
)
+ commit_timeout_arg = call_args[1]["commit_timeout"]
+ assert isinstance(commit_timeout_arg, ConstantTimeout)
+ assert math.isclose(commit_timeout_arg._timeout, 60) is True
+
message_pb = gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"})
- batch1.publish.assert_called_once_with(message_pb)
- batch2.publish.assert_called_once_with(message_pb)
+ wrapper = PublishMessageWrapper(message=message_pb)
+ batch1.publish.assert_called_once_with(wrapper)
+ batch2.publish.assert_called_once_with(wrapper)
def test_publish_attrs_type_error(creds):
@@ -445,9 +709,9 @@ def test_publish_custom_retry_overrides_configured_retry(creds):
client.publish(topic, b"hello!", retry=mock.sentinel.custom_retry)
fake_sequencer.publish.assert_called_once_with(
- mock.ANY, retry=mock.sentinel.custom_retry, timeout=mock.ANY
+ wrapper=mock.ANY, retry=mock.sentinel.custom_retry, timeout=mock.ANY
)
- message = fake_sequencer.publish.call_args.args[0]
+ message = fake_sequencer.publish.call_args.kwargs["wrapper"].message
assert message.data == b"hello!"
@@ -464,9 +728,9 @@ def test_publish_custom_timeout_overrides_configured_timeout(creds):
client.publish(topic, b"hello!", timeout=mock.sentinel.custom_timeout)
fake_sequencer.publish.assert_called_once_with(
- mock.ANY, retry=mock.ANY, timeout=mock.sentinel.custom_timeout
+ wrapper=mock.ANY, retry=mock.ANY, timeout=mock.sentinel.custom_timeout
)
- message = fake_sequencer.publish.call_args.args[0]
+ message = fake_sequencer.publish.call_args.kwargs["wrapper"].message
assert message.data == b"hello!"
@@ -626,10 +890,16 @@ def test_publish_with_ordering_key(creds):
# Check mock.
batch.publish.assert_has_calls(
[
- mock.call(gapic_types.PubsubMessage(data=b"spam", ordering_key="k1")),
mock.call(
- gapic_types.PubsubMessage(
- data=b"foo", attributes={"bar": "baz"}, ordering_key="k1"
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(data=b"spam", ordering_key="k1")
+ ),
+ ),
+ mock.call(
+ PublishMessageWrapper(
+ message=gapic_types.PubsubMessage(
+ data=b"foo", attributes={"bar": "baz"}, ordering_key="k1"
+ )
)
),
]
diff --git a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py
index 89d72c61d..23e1a6c18 100644
--- a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py
+++ b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py
@@ -17,17 +17,19 @@
import sys
import threading
+from opentelemetry import trace
+
from google.cloud.pubsub_v1.subscriber._protocol import dispatcher
from google.cloud.pubsub_v1.subscriber._protocol import helper_threads
from google.cloud.pubsub_v1.subscriber._protocol import requests
from google.cloud.pubsub_v1.subscriber._protocol import streaming_pull_manager
from google.cloud.pubsub_v1.subscriber import futures
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
+from google.pubsub_v1.types import PubsubMessage
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
from google.cloud.pubsub_v1.subscriber.exceptions import (
@@ -365,6 +367,125 @@ def test_unknown_request_type():
dispatcher_.dispatch_callback(items)
+def test_opentelemetry_modify_ack_deadline(span_exporter):
+ manager = mock.create_autospec(
+ streaming_pull_manager.StreamingPullManager, instance=True
+ )
+ dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue)
+ opentelemetry_data = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=5,
+ )
+
+ items = [
+ requests.ModAckRequest(
+ ack_id="ack_id_string",
+ seconds=60,
+ future=None,
+ opentelemetry_data=opentelemetry_data,
+ )
+ ]
+ manager.send_unary_modack.return_value = (items, [])
+ dispatcher_.modify_ack_deadline(items)
+
+ # Subscribe span would not have ended as part of a modack. So, end it
+ # in the test, so that we can export and assert its contents.
+ opentelemetry_data.end_subscribe_span()
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 1
+ subscribe_span = spans[0]
+
+ assert len(subscribe_span.events) == 2
+ assert subscribe_span.events[0].name == "modack start"
+ assert subscribe_span.events[1].name == "modack end"
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry_ack(span_exporter):
+ manager = mock.create_autospec(
+ streaming_pull_manager.StreamingPullManager, instance=True
+ )
+ dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue)
+
+ data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data1.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=5,
+ )
+ data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data2.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=5,
+ )
+ items = [
+ requests.AckRequest(
+ ack_id="ack_id_string",
+ byte_size=0,
+ time_to_ack=20,
+ ordering_key="",
+ future=None,
+ opentelemetry_data=data1,
+ ),
+ requests.AckRequest(
+ ack_id="ack_id_string2",
+ byte_size=0,
+ time_to_ack=20,
+ ordering_key="",
+ future=None,
+ opentelemetry_data=data2,
+ ),
+ ]
+ manager.send_unary_ack.return_value = (items, [])
+ mock_span_context = mock.Mock(spec=trace.SpanContext)
+ mock_span_context.trace_flags.sampled = False
+ with mock.patch.object(
+ data2._subscribe_span, "get_span_context", return_value=mock_span_context
+ ):
+ dispatcher_.ack(items)
+
+ spans = span_exporter.get_finished_spans()
+
+ assert len(spans) == 3
+ ack_span = spans[0]
+
+ for subscribe_span in spans[1:]:
+ assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "acked"
+ assert len(subscribe_span.events) == 2
+ assert subscribe_span.events[0].name == "ack start"
+ assert subscribe_span.events[1].name == "ack end"
+
+ # This subscribe span is sampled, so we expect it to be linked to the ack
+ # span.
+ assert len(spans[1].links) == 1
+ assert spans[1].links[0].context == ack_span.context
+ assert len(spans[1].links[0].attributes) == 1
+ assert spans[1].links[0].attributes["messaging.operation.name"] == "ack"
+ # This subscribe span is not sampled, so we expect it to not be linked to
+ # the ack span
+ assert len(spans[2].links) == 0
+
+ assert ack_span.name == "subscriptionID ack"
+ assert ack_span.kind == trace.SpanKind.CLIENT
+ assert ack_span.parent is None
+ assert len(ack_span.links) == 1
+ assert ack_span.attributes["messaging.system"] == "gcp_pubsub"
+ assert ack_span.attributes["messaging.batch.message_count"] == 2
+ assert ack_span.attributes["messaging.operation"] == "ack"
+ assert ack_span.attributes["gcp.project_id"] == "projectID"
+ assert ack_span.attributes["messaging.destination.name"] == "subscriptionID"
+ assert ack_span.attributes["code.function"] == "ack"
+
+
def test_ack():
manager = mock.create_autospec(
streaming_pull_manager.StreamingPullManager, instance=True
@@ -481,6 +602,92 @@ def test_retry_acks_in_new_thread():
assert ctor_call.kwargs["daemon"]
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry_retry_acks(span_exporter):
+ manager = mock.create_autospec(
+ streaming_pull_manager.StreamingPullManager, instance=True
+ )
+ dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue)
+ data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data1.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=5,
+ )
+ data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data2.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=5,
+ )
+
+ f = futures.Future()
+ items = [
+ requests.AckRequest(
+ ack_id="ack_id_string",
+ byte_size=0,
+ time_to_ack=20,
+ ordering_key="",
+ future=f,
+ opentelemetry_data=data1,
+ ),
+ requests.AckRequest(
+ ack_id="ack_id_string2",
+ byte_size=0,
+ time_to_ack=20,
+ ordering_key="",
+ future=f,
+ opentelemetry_data=data2,
+ ),
+ ]
+ manager.send_unary_ack.side_effect = [(items, [])]
+ mock_span_context = mock.Mock(spec=trace.SpanContext)
+ mock_span_context.trace_flags.sampled = False
+ with mock.patch("time.sleep", return_value=None):
+ with mock.patch.object(
+ data2._subscribe_span, "get_span_context", return_value=mock_span_context
+ ):
+ dispatcher_._retry_acks(items)
+
+ spans = span_exporter.get_finished_spans()
+
+ assert len(spans) == 3
+ ack_span = spans[0]
+
+ for subscribe_span in spans[1:]:
+ assert "messaging.gcp_pubsub.result" in subscribe_span.attributes
+ assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "acked"
+ assert len(subscribe_span.events) == 2
+ assert subscribe_span.events[0].name == "ack start"
+ assert subscribe_span.events[1].name == "ack end"
+
+ # This subscribe span is sampled, so we expect it to be linked to the ack
+ # span.
+ assert len(spans[1].links) == 1
+ assert spans[1].links[0].context == ack_span.context
+ assert len(spans[1].links[0].attributes) == 1
+ assert spans[1].links[0].attributes["messaging.operation.name"] == "ack"
+ # This subscribe span is not sampled, so we expect it to not be linked to
+ # the ack span
+ assert len(spans[2].links) == 0
+
+ assert ack_span.name == "subscriptionID ack"
+ assert ack_span.kind == trace.SpanKind.CLIENT
+ assert ack_span.parent is None
+ assert len(ack_span.links) == 1
+ assert ack_span.attributes["messaging.system"] == "gcp_pubsub"
+ assert ack_span.attributes["messaging.batch.message_count"] == 2
+ assert ack_span.attributes["messaging.operation"] == "ack"
+ assert ack_span.attributes["gcp.project_id"] == "projectID"
+ assert ack_span.attributes["messaging.destination.name"] == "subscriptionID"
+ assert ack_span.attributes["code.function"] == "ack"
+
+
def test_retry_acks():
manager = mock.create_autospec(
streaming_pull_manager.StreamingPullManager, instance=True
@@ -544,6 +751,125 @@ def test_retry_modacks_in_new_thread():
assert ctor_call.kwargs["daemon"]
+def test_opentelemetry_retry_modacks(span_exporter):
+ manager = mock.create_autospec(
+ streaming_pull_manager.StreamingPullManager, instance=True
+ )
+ dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue)
+
+ opentelemetry_data = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=5,
+ )
+
+ f = futures.Future()
+ items = [
+ requests.ModAckRequest(
+ ack_id="ack_id_string",
+ seconds=20,
+ future=f,
+ opentelemetry_data=opentelemetry_data,
+ )
+ ]
+ manager.send_unary_modack.side_effect = [(items, [])]
+ with mock.patch("time.sleep", return_value=None):
+ dispatcher_._retry_modacks(items)
+
+ # Subscribe span wouldn't be ended for modacks. So, end it in the test, so
+ # that we can export and assert its contents.
+ opentelemetry_data.end_subscribe_span()
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 1
+ subscribe_span = spans[0]
+
+ assert len(subscribe_span.events) == 1
+ assert subscribe_span.events[0].name == "modack end"
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry_retry_nacks(span_exporter):
+ manager = mock.create_autospec(
+ streaming_pull_manager.StreamingPullManager, instance=True
+ )
+ dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue)
+
+ data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data1.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id1",
+ delivery_attempt=5,
+ )
+ data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data2.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id2",
+ delivery_attempt=5,
+ )
+
+ f = futures.Future()
+ items = [
+ requests.ModAckRequest(
+ ack_id="ack_id1",
+ seconds=0,
+ future=f,
+ opentelemetry_data=data1,
+ ),
+ requests.ModAckRequest(
+ ack_id="ack_id2",
+ seconds=0,
+ future=f,
+ opentelemetry_data=data2,
+ ),
+ ]
+ manager.send_unary_modack.side_effect = [(items, [])]
+ mock_span_context = mock.Mock(spec=trace.SpanContext)
+ mock_span_context.trace_flags.sampled = False
+ with mock.patch("time.sleep", return_value=None):
+ with mock.patch.object(
+ data2._subscribe_span, "get_span_context", return_value=mock_span_context
+ ):
+ dispatcher_._retry_modacks(items)
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 3
+ nack_span = spans[0]
+
+ for subscribe_span in spans[1:]:
+ assert "messaging.gcp_pubsub.result" in subscribe_span.attributes
+ assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "nacked"
+ assert len(subscribe_span.events) == 1
+ assert subscribe_span.events[0].name == "nack end"
+
+ # This subscribe span is sampled, so we expect it to be linked to the nack
+ # span.
+ assert len(spans[1].links) == 1
+ assert spans[1].links[0].context == nack_span.context
+ assert len(spans[1].links[0].attributes) == 1
+ assert spans[1].links[0].attributes["messaging.operation.name"] == "nack"
+ # This subscribe span is not sampled, so we expect it to not be linked to
+ # the nack span
+ assert len(spans[2].links) == 0
+
+ assert nack_span.name == "subscriptionID nack"
+ assert nack_span.kind == trace.SpanKind.CLIENT
+ assert nack_span.parent is None
+ assert len(nack_span.links) == 1
+ assert nack_span.attributes["messaging.system"] == "gcp_pubsub"
+ assert nack_span.attributes["messaging.batch.message_count"] == 2
+ assert nack_span.attributes["messaging.operation"] == "nack"
+ assert nack_span.attributes["gcp.project_id"] == "projectID"
+ assert nack_span.attributes["messaging.destination.name"] == "subscriptionID"
+ assert nack_span.attributes["code.function"] == "modify_ack_deadline"
+
+
def test_retry_modacks():
manager = mock.create_autospec(
streaming_pull_manager.StreamingPullManager, instance=True
@@ -633,6 +959,103 @@ def test_drop_ordered_messages():
manager.maybe_resume_consumer.assert_called_once()
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry_nack(span_exporter):
+ manager = mock.create_autospec(
+ streaming_pull_manager.StreamingPullManager, instance=True
+ )
+ dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue)
+
+ data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data1.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=5,
+ )
+ data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo"))
+ data2.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id2",
+ delivery_attempt=5,
+ )
+
+ items = [
+ requests.NackRequest(
+ ack_id="ack_id",
+ byte_size=10,
+ ordering_key="",
+ future=None,
+ opentelemetry_data=data1,
+ ),
+ requests.NackRequest(
+ ack_id="ack_id2",
+ byte_size=10,
+ ordering_key="",
+ future=None,
+ opentelemetry_data=data2,
+ ),
+ ]
+ response_items = [
+ requests.ModAckRequest(
+ ack_id="ack_id",
+ seconds=0,
+ future=None,
+ opentelemetry_data=data1,
+ ),
+ requests.ModAckRequest(
+ ack_id="ack_id2",
+ seconds=0,
+ future=None,
+ opentelemetry_data=data2,
+ ),
+ ]
+ manager.send_unary_modack.return_value = (response_items, [])
+
+ mock_span_context = mock.Mock(spec=trace.SpanContext)
+ mock_span_context.trace_flags.sampled = False
+ with mock.patch.object(
+ data2._subscribe_span, "get_span_context", return_value=mock_span_context
+ ):
+ dispatcher_.nack(items)
+
+ spans = span_exporter.get_finished_spans()
+
+ assert len(spans) == 3
+ nack_span = spans[0]
+ for subscribe_span in spans[1:]:
+ assert "messaging.gcp_pubsub.result" in subscribe_span.attributes
+ assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "nacked"
+ assert len(subscribe_span.events) == 2
+ assert subscribe_span.events[0].name == "nack start"
+ assert subscribe_span.events[1].name == "nack end"
+
+ # This subscribe span is sampled, so we expect it to be linked to the nack
+ # span.
+ assert len(spans[1].links) == 1
+ assert spans[1].links[0].context == nack_span.context
+ assert len(spans[1].links[0].attributes) == 1
+ assert spans[1].links[0].attributes["messaging.operation.name"] == "nack"
+ # This subscribe span is not sampled, so we expect it to not be linked to
+ # the nack span
+ assert len(spans[2].links) == 0
+
+ assert nack_span.name == "subscriptionID nack"
+ assert nack_span.kind == trace.SpanKind.CLIENT
+ assert nack_span.parent is None
+ assert len(nack_span.links) == 1
+ assert nack_span.attributes["messaging.system"] == "gcp_pubsub"
+ assert nack_span.attributes["messaging.batch.message_count"] == 2
+ assert nack_span.attributes["messaging.operation"] == "nack"
+ assert nack_span.attributes["gcp.project_id"] == "projectID"
+ assert nack_span.attributes["messaging.destination.name"] == "subscriptionID"
+ assert nack_span.attributes["code.function"] == "modify_ack_deadline"
+
+
def test_nack():
manager = mock.create_autospec(
streaming_pull_manager.StreamingPullManager, instance=True
diff --git a/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py b/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py
index d10da6fb1..c4c539f96 100644
--- a/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py
+++ b/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py
@@ -13,14 +13,8 @@
# limitations under the License.
from __future__ import absolute_import
-import sys
-
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
from google.cloud.pubsub_v1.subscriber import futures
diff --git a/tests/unit/pubsub_v1/subscriber/test_heartbeater.py b/tests/unit/pubsub_v1/subscriber/test_heartbeater.py
index 503fde2c9..cd9fd9762 100644
--- a/tests/unit/pubsub_v1/subscriber/test_heartbeater.py
+++ b/tests/unit/pubsub_v1/subscriber/test_heartbeater.py
@@ -13,22 +13,19 @@
# limitations under the License.
import logging
-import sys
import threading
from google.cloud.pubsub_v1.subscriber._protocol import heartbeater
from google.cloud.pubsub_v1.subscriber._protocol import streaming_pull_manager
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
-def test_heartbeat_inactive_manager_active_rpc(caplog):
+def test_heartbeat_inactive_manager_active_rpc(
+ caplog, modify_google_logger_propagation
+):
caplog.set_level(logging.DEBUG)
manager = mock.create_autospec(
@@ -46,7 +43,10 @@ def test_heartbeat_inactive_manager_active_rpc(caplog):
assert "exiting" in caplog.text
-def test_heartbeat_inactive_manager_inactive_rpc(caplog):
+def test_heartbeat_inactive_manager_inactive_rpc(
+ caplog,
+ modify_google_logger_propagation,
+):
caplog.set_level(logging.DEBUG)
manager = mock.create_autospec(
@@ -64,7 +64,7 @@ def test_heartbeat_inactive_manager_inactive_rpc(caplog):
assert "exiting" in caplog.text
-def test_heartbeat_stopped(caplog):
+def test_heartbeat_stopped(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
manager = mock.create_autospec(
streaming_pull_manager.StreamingPullManager, instance=True
diff --git a/tests/unit/pubsub_v1/subscriber/test_helper_threads.py b/tests/unit/pubsub_v1/subscriber/test_helper_threads.py
index bfbaf3e56..54659a5a3 100644
--- a/tests/unit/pubsub_v1/subscriber/test_helper_threads.py
+++ b/tests/unit/pubsub_v1/subscriber/test_helper_threads.py
@@ -12,13 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
-
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import queue
diff --git a/tests/unit/pubsub_v1/subscriber/test_leaser.py b/tests/unit/pubsub_v1/subscriber/test_leaser.py
index f38717c6f..3d2a96151 100644
--- a/tests/unit/pubsub_v1/subscriber/test_leaser.py
+++ b/tests/unit/pubsub_v1/subscriber/test_leaser.py
@@ -13,7 +13,6 @@
# limitations under the License.
import logging
-import sys
import threading
from google.cloud.pubsub_v1 import types
@@ -22,12 +21,12 @@
from google.cloud.pubsub_v1.subscriber._protocol import leaser
from google.cloud.pubsub_v1.subscriber._protocol import requests
from google.cloud.pubsub_v1.subscriber._protocol import streaming_pull_manager
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
+from google.cloud.pubsub_v1.subscriber import message
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
@@ -49,7 +48,7 @@ def test_add_and_remove():
assert leaser_.bytes == 25
-def test_add_already_managed(caplog):
+def test_add_already_managed(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
leaser_ = leaser.Leaser(mock.sentinel.manager)
@@ -60,7 +59,7 @@ def test_add_already_managed(caplog):
assert "already lease managed" in caplog.text
-def test_remove_not_managed(caplog):
+def test_remove_not_managed(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
leaser_ = leaser.Leaser(mock.sentinel.manager)
@@ -70,7 +69,7 @@ def test_remove_not_managed(caplog):
assert "not managed" in caplog.text
-def test_remove_negative_bytes(caplog):
+def test_remove_negative_bytes(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
leaser_ = leaser.Leaser(mock.sentinel.manager)
@@ -94,7 +93,7 @@ def create_manager(flow_control=types.FlowControl()):
return manager
-def test_maintain_leases_inactive_manager(caplog):
+def test_maintain_leases_inactive_manager(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
manager = create_manager()
manager.is_active = False
@@ -113,7 +112,7 @@ def test_maintain_leases_inactive_manager(caplog):
assert "exiting" in caplog.text
-def test_maintain_leases_stopped(caplog):
+def test_maintain_leases_stopped(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
manager = create_manager()
@@ -136,6 +135,101 @@ def trigger_done(timeout):
leaser._stop_event.wait = trigger_done
+def test_opentelemetry_dropped_message_process_span(span_exporter):
+ manager = create_manager()
+ leaser_ = leaser.Leaser(manager)
+ make_sleep_mark_event_as_done(leaser_)
+ msg = mock.create_autospec(
+ message.Message, instance=True, ack_id="ack_foo", size=10
+ )
+ msg.message_id = 3
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ opentelemetry_data.start_process_span()
+ leaser_.add(
+ [
+ requests.LeaseRequest(
+ ack_id="my ack id",
+ byte_size=50,
+ ordering_key="",
+ opentelemetry_data=opentelemetry_data,
+ )
+ ]
+ )
+ leased_messages_dict = leaser_._leased_messages
+
+ # Setting the `sent_time`` to be less than `cutoff` in order to make the leased message expire.
+ # This will exercise the code path where the message would be dropped from the leaser
+ leased_messages_dict["my ack id"] = leased_messages_dict["my ack id"]._replace(
+ sent_time=0
+ )
+
+ manager._send_lease_modacks.return_value = set()
+ leaser_.maintain_leases()
+
+ opentelemetry_data.end_subscribe_span()
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+ process_span, subscribe_span = spans
+
+ assert process_span.name == "subscriptionID process"
+ assert subscribe_span.name == "subscriptionID subscribe"
+
+ assert len(process_span.events) == 1
+ assert process_span.events[0].name == "expired"
+
+ assert process_span.parent == subscribe_span.context
+
+
+def test_opentelemetry_expired_message_exactly_once_process_span(span_exporter):
+ manager = create_manager()
+ leaser_ = leaser.Leaser(manager)
+ make_sleep_mark_event_as_done(leaser_)
+ msg = mock.create_autospec(
+ message.Message, instance=True, ack_id="ack_foo", size=10
+ )
+ msg.message_id = 3
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ opentelemetry_data.start_process_span()
+ leaser_.add(
+ [
+ requests.LeaseRequest(
+ ack_id="my ack id",
+ byte_size=50,
+ ordering_key="",
+ opentelemetry_data=opentelemetry_data,
+ )
+ ]
+ )
+
+ manager._send_lease_modacks.return_value = ["my ack id"]
+ leaser_.maintain_leases()
+
+ opentelemetry_data.end_subscribe_span()
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+ process_span, subscribe_span = spans
+
+ assert process_span.name == "subscriptionID process"
+ assert subscribe_span.name == "subscriptionID subscribe"
+
+ assert len(process_span.events) == 1
+ assert process_span.events[0].name == "expired"
+
+ assert process_span.parent == subscribe_span.context
+
+
def test_maintain_leases_ack_ids():
manager = create_manager()
leaser_ = leaser.Leaser(manager)
diff --git a/tests/unit/pubsub_v1/subscriber/test_message.py b/tests/unit/pubsub_v1/subscriber/test_message.py
index 49b07b7fd..676536f01 100644
--- a/tests/unit/pubsub_v1/subscriber/test_message.py
+++ b/tests/unit/pubsub_v1/subscriber/test_message.py
@@ -14,14 +14,9 @@
import datetime
import queue
-import sys
import time
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
from google.api_core import datetime_helpers
from google.cloud.pubsub_v1.subscriber import message
@@ -29,6 +24,9 @@
from google.protobuf import timestamp_pb2
from google.pubsub_v1 import types as gapic_types
from google.cloud.pubsub_v1.subscriber.exceptions import AcknowledgeStatus
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
RECEIVED = datetime.datetime(2012, 4, 21, 15, 0, tzinfo=datetime.timezone.utc)
@@ -131,12 +129,162 @@ def check_call_types(mock, *args, **kwargs):
assert isinstance(call_args[n], argtype)
+def test_opentelemetry_ack(span_exporter):
+ SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID"
+ msg = create_message(b"data", ack_id="ack_id")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription=SUBSCRIPTION,
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=2,
+ )
+ opentelemetry_data.start_process_span()
+ msg.opentelemetry_data = opentelemetry_data
+ msg.ack()
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+ process_span, subscribe_span = spans
+
+ assert subscribe_span.name == "subscriptionID subscribe"
+ assert len(subscribe_span.events) == 0
+
+ assert process_span.name == "subscriptionID process"
+ assert len(process_span.events) == 1
+ assert process_span.events[0].name == "ack called"
+
+
+def test_opentelemetry_ack_with_response(span_exporter):
+ SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID"
+ msg = create_message(b"data", ack_id="ack_id")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription=SUBSCRIPTION,
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=2,
+ )
+ opentelemetry_data.start_process_span()
+ msg.opentelemetry_data = opentelemetry_data
+ msg.ack_with_response()
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+ process_span, subscribe_span = spans
+
+ assert subscribe_span.name == "subscriptionID subscribe"
+ assert len(subscribe_span.events) == 0
+
+ assert process_span.name == "subscriptionID process"
+ assert len(process_span.events) == 1
+ assert process_span.events[0].name == "ack called"
+
+
+def test_opentelemetry_nack(span_exporter):
+ SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID"
+ msg = create_message(b"data", ack_id="ack_id")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription=SUBSCRIPTION,
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=2,
+ )
+ opentelemetry_data.start_process_span()
+ msg.opentelemetry_data = opentelemetry_data
+ msg.nack()
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+ process_span, subscribe_span = spans
+
+ assert subscribe_span.name == "subscriptionID subscribe"
+ assert len(subscribe_span.events) == 0
+
+ assert process_span.name == "subscriptionID process"
+ assert len(process_span.events) == 1
+ assert process_span.events[0].name == "nack called"
+
+
+def test_opentelemetry_nack_with_response(span_exporter):
+ SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID"
+ msg = create_message(b"data", ack_id="ack_id")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription=SUBSCRIPTION,
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=2,
+ )
+ opentelemetry_data.start_process_span()
+ msg.opentelemetry_data = opentelemetry_data
+ msg.nack_with_response()
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+
+ process_span, subscribe_span = spans
+
+ assert subscribe_span.name == "subscriptionID subscribe"
+ assert len(subscribe_span.events) == 0
+
+ assert process_span.name == "subscriptionID process"
+ assert len(process_span.events) == 1
+ assert process_span.events[0].name == "nack called"
+
+
+def test_opentelemetry_modack(span_exporter):
+ SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID"
+ msg = create_message(b"data", ack_id="ack_id")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription=SUBSCRIPTION,
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=2,
+ )
+ msg.opentelemetry_data = opentelemetry_data
+ msg.modify_ack_deadline(3)
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 1
+
+ assert len(spans[0].events) == 0
+
+
+def test_opentelemetry_modack_with_response(span_exporter):
+ SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID"
+ msg = create_message(b"data", ack_id="ack_id")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription=SUBSCRIPTION,
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=2,
+ )
+ msg.opentelemetry_data = opentelemetry_data
+ msg.modify_ack_deadline_with_response(3)
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 1
+
+ assert len(spans[0].events) == 0
+
+
def test_ack():
msg = create_message(b"foo", ack_id="bogus_ack_id")
with mock.patch.object(msg._request_queue, "put") as put:
msg.ack()
put.assert_called_once_with(
requests.AckRequest(
+ message_id=msg.message_id,
ack_id="bogus_ack_id",
byte_size=30,
time_to_ack=mock.ANY,
@@ -153,6 +301,7 @@ def test_ack_with_response_exactly_once_delivery_disabled():
future = msg.ack_with_response()
put.assert_called_once_with(
requests.AckRequest(
+ message_id=msg.message_id,
ack_id="bogus_ack_id",
byte_size=30,
time_to_ack=mock.ANY,
@@ -173,6 +322,7 @@ def test_ack_with_response_exactly_once_delivery_enabled():
future = msg.ack_with_response()
put.assert_called_once_with(
requests.AckRequest(
+ message_id=msg.message_id,
ack_id="bogus_ack_id",
byte_size=30,
time_to_ack=mock.ANY,
@@ -198,7 +348,12 @@ def test_modify_ack_deadline():
with mock.patch.object(msg._request_queue, "put") as put:
msg.modify_ack_deadline(60)
put.assert_called_once_with(
- requests.ModAckRequest(ack_id="bogus_ack_id", seconds=60, future=None)
+ requests.ModAckRequest(
+ message_id=msg.message_id,
+ ack_id="bogus_ack_id",
+ seconds=60,
+ future=None,
+ )
)
check_call_types(put, requests.ModAckRequest)
@@ -208,7 +363,12 @@ def test_modify_ack_deadline_with_response_exactly_once_delivery_disabled():
with mock.patch.object(msg._request_queue, "put") as put:
future = msg.modify_ack_deadline_with_response(60)
put.assert_called_once_with(
- requests.ModAckRequest(ack_id="bogus_ack_id", seconds=60, future=None)
+ requests.ModAckRequest(
+ message_id=msg.message_id,
+ ack_id="bogus_ack_id",
+ seconds=60,
+ future=None,
+ )
)
assert future.result() == AcknowledgeStatus.SUCCESS
assert future == message._SUCCESS_FUTURE
@@ -222,7 +382,12 @@ def test_modify_ack_deadline_with_response_exactly_once_delivery_enabled():
with mock.patch.object(msg._request_queue, "put") as put:
future = msg.modify_ack_deadline_with_response(60)
put.assert_called_once_with(
- requests.ModAckRequest(ack_id="bogus_ack_id", seconds=60, future=future)
+ requests.ModAckRequest(
+ message_id=msg.message_id,
+ ack_id="bogus_ack_id",
+ seconds=60,
+ future=future,
+ )
)
check_call_types(put, requests.ModAckRequest)
diff --git a/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py b/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py
index 5e1dcf91b..0f060e4ea 100644
--- a/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py
+++ b/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py
@@ -14,9 +14,14 @@
import queue
+from opentelemetry import trace
+
+from google.pubsub_v1 import types as gapic_types
from google.cloud.pubsub_v1.subscriber import message
from google.cloud.pubsub_v1.subscriber._protocol import messages_on_hold
-from google.pubsub_v1 import types as gapic_types
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
def make_message(ack_id, ordering_key):
@@ -37,6 +42,32 @@ def test_init():
assert moh.get() is None
+def test_opentelemetry_subscriber_scheduler_span(span_exporter):
+ moh = messages_on_hold.MessagesOnHold()
+ msg = make_message(ack_id="ack1", ordering_key="")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ msg.opentelemetry_data = opentelemetry_data
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ moh.put(msg)
+ opentelemetry_data.end_subscribe_scheduler_span()
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+
+ assert len(spans) == 2
+
+ subscribe_scheduler_span, subscribe_span = spans
+
+ assert subscribe_scheduler_span.name == "subscriber scheduler"
+ assert subscribe_scheduler_span.kind == trace.SpanKind.INTERNAL
+ assert subscribe_scheduler_span.parent == subscribe_span.context
+
+
def test_put_and_get_unordered_messages():
moh = messages_on_hold.MessagesOnHold()
@@ -109,7 +140,7 @@ def test_ordered_messages_one_key():
assert moh.size == 0
-def test_ordered_messages_drop_duplicate_keys(caplog):
+def test_ordered_messages_drop_duplicate_keys(caplog, modify_google_logger_propagation):
moh = messages_on_hold.MessagesOnHold()
msg1 = make_message(ack_id="ack1", ordering_key="key1")
@@ -346,7 +377,7 @@ def test_ordered_and_unordered_messages_interleaved():
assert moh.size == 0
-def test_cleanup_nonexistent_key(caplog):
+def test_cleanup_nonexistent_key(caplog, modify_google_logger_propagation):
moh = messages_on_hold.MessagesOnHold()
moh._clean_up_ordering_key("non-existent-key")
assert (
@@ -355,7 +386,7 @@ def test_cleanup_nonexistent_key(caplog):
)
-def test_cleanup_key_with_messages(caplog):
+def test_cleanup_key_with_messages(caplog, modify_google_logger_propagation):
moh = messages_on_hold.MessagesOnHold()
# Put message with "key1".
diff --git a/tests/unit/pubsub_v1/subscriber/test_scheduler.py b/tests/unit/pubsub_v1/subscriber/test_scheduler.py
index 3ed1978c1..22bd53729 100644
--- a/tests/unit/pubsub_v1/subscriber/test_scheduler.py
+++ b/tests/unit/pubsub_v1/subscriber/test_scheduler.py
@@ -15,15 +15,10 @@
import concurrent.futures
import queue
import pytest
-import sys
import threading
import time
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
from google.cloud.pubsub_v1.subscriber import scheduler
diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py
index 278f3e88e..953b882f8 100644
--- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py
+++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py
@@ -12,18 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import functools
import logging
import sys
import threading
import time
import types as stdlib_types
+import datetime
+import queue
+import math
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from opentelemetry import trace
+from google.protobuf import timestamp_pb2
+from google.api_core import datetime_helpers
+
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
+from google.cloud.pubsub_v1.subscriber.message import Message
+from google.cloud.pubsub_v1.types import PubsubMessage
+
+
+from unittest import mock
import pytest
from google.api_core import bidi
@@ -47,6 +56,15 @@
from google.rpc import error_details_pb2
+def create_mock_message(**kwargs):
+ _message_mock = mock.create_autospec(message.Message, instance=True)
+ msg = _message_mock.return_value
+ for k, v in kwargs.items():
+ setattr(msg, k, v)
+
+ return msg
+
+
@pytest.mark.parametrize(
"exception,expected_cls",
[
@@ -66,7 +84,7 @@ def test__wrap_as_exception(exception, expected_cls):
def test__wrap_callback_errors_no_error():
- msg = mock.create_autospec(message.Message, instance=True)
+ msg = create_mock_message()
callback = mock.Mock()
on_callback_error = mock.Mock()
@@ -85,7 +103,7 @@ def test__wrap_callback_errors_no_error():
],
)
def test__wrap_callback_errors_error(callback_error):
- msg = mock.create_autospec(message.Message, instance=True)
+ msg = create_mock_message()
callback = mock.Mock(side_effect=callback_error)
on_callback_error = mock.Mock()
@@ -179,11 +197,16 @@ def test_constructor_with_max_duration_per_lease_extension_too_high():
assert manager._stream_ack_deadline == 600
-def make_manager(**kwargs):
+def make_manager(
+ enable_open_telemetry: bool = False,
+ subscription_name: str = "subscription-name",
+ **kwargs,
+):
client_ = mock.create_autospec(client.Client, instance=True)
+ client_.open_telemetry_enabled = enable_open_telemetry
scheduler_ = mock.create_autospec(scheduler.Scheduler, instance=True)
return streaming_pull_manager.StreamingPullManager(
- client_, "subscription-name", scheduler=scheduler_, **kwargs
+ client_, subscription_name, scheduler=scheduler_, **kwargs
)
@@ -492,8 +515,8 @@ def test__maybe_release_messages_on_overload():
manager = make_manager(
flow_control=types.FlowControl(max_messages=10, max_bytes=1000)
)
+ msg = create_mock_message(ack_id="ack", size=11)
- msg = mock.create_autospec(message.Message, instance=True, ack_id="ack", size=11)
manager._messages_on_hold.put(msg)
manager._on_hold_bytes = msg.size
@@ -509,6 +532,44 @@ def test__maybe_release_messages_on_overload():
manager._scheduler.schedule.assert_not_called()
+def test_opentelemetry__maybe_release_messages_subscribe_scheduler_span(span_exporter):
+ manager = make_manager(
+ flow_control=types.FlowControl(max_messages=10, max_bytes=1000)
+ )
+ manager._callback = mock.sentinel.callback
+
+ # Init leaser message count to 11, so that when subtracting the 3 messages
+ # that are on hold, there is still room for another 2 messages before the
+ # max load is hit.
+ _leaser = manager._leaser = mock.create_autospec(leaser.Leaser)
+ fake_leaser_add(_leaser, init_msg_count=8, assumed_msg_size=10)
+ msg = create_mock_message(ack_id="ack_foo", size=10)
+
+ msg.message_id = 3
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ msg.opentelemetry_data = opentelemetry_data
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ manager._messages_on_hold.put(msg)
+ manager._maybe_release_messages()
+ opentelemetry_data.end_subscribe_span()
+ spans = span_exporter.get_finished_spans()
+
+ assert len(spans) == 2
+
+ subscriber_scheduler_span, subscribe_span = spans
+
+ assert subscriber_scheduler_span.name == "subscriber scheduler"
+ assert subscribe_span.name == "subscriptionID subscribe"
+
+ assert subscriber_scheduler_span.parent == subscribe_span.context
+ assert subscriber_scheduler_span.kind == trace.SpanKind.INTERNAL
+
+
def test__maybe_release_messages_below_overload():
manager = make_manager(
flow_control=types.FlowControl(max_messages=10, max_bytes=1000)
@@ -545,13 +606,15 @@ def test__maybe_release_messages_below_overload():
assert call_args[1].ack_id in ("ack_foo", "ack_bar")
-def test__maybe_release_messages_negative_on_hold_bytes_warning(caplog):
+def test__maybe_release_messages_negative_on_hold_bytes_warning(
+ caplog, modify_google_logger_propagation
+):
manager = make_manager(
flow_control=types.FlowControl(max_messages=10, max_bytes=1000)
)
manager._callback = lambda msg: msg # pragma: NO COVER
- msg = mock.create_autospec(message.Message, instance=True, ack_id="ack", size=17)
+ msg = create_mock_message(ack_id="ack", size=17)
manager._messages_on_hold.put(msg)
manager._on_hold_bytes = 5 # too low for some reason
@@ -574,6 +637,86 @@ def test__maybe_release_messages_negative_on_hold_bytes_warning(caplog):
assert manager._on_hold_bytes == 0 # should be auto-corrected
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+@pytest.mark.parametrize(
+ "receipt_modack",
+ [
+ True,
+ False,
+ ],
+)
+def test_opentelemetry__send_lease_modacks(span_exporter, receipt_modack):
+ manager, _, _, _, _, _ = make_running_manager(
+ enable_open_telemetry=True,
+ subscription_name="projects/projectID/subscriptions/subscriptionID",
+ )
+ data1 = SubscribeOpenTelemetry(
+ message=gapic_types.PubsubMessage(data=b"foo", message_id="1")
+ )
+ data2 = SubscribeOpenTelemetry(
+ message=gapic_types.PubsubMessage(data=b"bar", message_id="2")
+ )
+
+ data1.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id1",
+ delivery_attempt=2,
+ )
+ data2.start_subscribe_span(
+ subscription="projects/projectID/subscriptions/subscriptionID",
+ exactly_once_enabled=True,
+ ack_id="ack_id1",
+ delivery_attempt=2,
+ )
+ mock_span_context = mock.Mock(spec=trace.SpanContext)
+ mock_span_context.trace_flags.sampled = False
+ with mock.patch.object(
+ data1._subscribe_span, "get_span_context", return_value=mock_span_context
+ ):
+ manager._send_lease_modacks(
+ ack_ids=["ack_id1", "ack_id2"],
+ ack_deadline=20,
+ opentelemetry_data=[data1, data2],
+ receipt_modack=receipt_modack,
+ )
+ data1.end_subscribe_span()
+ data2.end_subscribe_span()
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 3
+ modack_span, subscribe_span1, subscribe_span2 = spans
+
+ assert len(subscribe_span1.events) == 0
+ assert len(subscribe_span2.events) == 0
+
+ assert len(subscribe_span1.links) == 0
+ assert len(subscribe_span2.links) == 1
+ assert subscribe_span2.links[0].context == modack_span.context
+ assert subscribe_span2.links[0].attributes["messaging.operation.name"] == "modack"
+
+ assert modack_span.name == "subscriptionID modack"
+ assert modack_span.parent is None
+ assert modack_span.kind == trace.SpanKind.CLIENT
+ assert len(modack_span.links) == 1
+ modack_span_attributes = modack_span.attributes
+ assert modack_span_attributes["messaging.system"] == "gcp_pubsub"
+ assert modack_span_attributes["messaging.batch.message_count"] == 2
+ assert math.isclose(
+ modack_span_attributes["messaging.gcp_pubsub.message.ack_deadline"], 20
+ )
+ assert modack_span_attributes["messaging.destination.name"] == "subscriptionID"
+ assert modack_span_attributes["gcp.project_id"] == "projectID"
+ assert modack_span_attributes["messaging.operation.name"] == "modack"
+ assert modack_span_attributes["code.function"] == "_send_lease_modacks"
+ assert (
+ modack_span_attributes["messaging.gcp_pubsub.is_receipt_modack"]
+ == receipt_modack
+ )
+
+
def test_send_unary_ack():
manager = make_manager()
@@ -786,7 +929,7 @@ def test_send_unary_modack_exactly_once_disabled_with_futures():
assert future3.result() == subscriber_exceptions.AcknowledgeStatus.SUCCESS
-def test_send_unary_ack_api_call_error(caplog):
+def test_send_unary_ack_api_call_error(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
manager = make_manager()
@@ -807,7 +950,7 @@ def test_send_unary_ack_api_call_error(caplog):
assert "The front fell off" in caplog.text
-def test_send_unary_modack_api_call_error(caplog):
+def test_send_unary_modack_api_call_error(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
manager = make_manager()
@@ -840,7 +983,9 @@ def test_send_unary_modack_api_call_error(caplog):
assert "The front fell off" in caplog.text
-def test_send_unary_ack_retry_error_exactly_once_disabled_no_futures(caplog):
+def test_send_unary_ack_retry_error_exactly_once_disabled_no_futures(
+ caplog, modify_google_logger_propagation
+):
caplog.set_level(logging.DEBUG)
manager, _, _, _, _, _ = make_running_manager()
@@ -876,7 +1021,9 @@ def test_send_unary_ack_retry_error_exactly_once_disabled_no_futures(caplog):
assert "signaled streaming pull manager shutdown" in caplog.text
-def test_send_unary_ack_retry_error_exactly_once_disabled_with_futures(caplog):
+def test_send_unary_ack_retry_error_exactly_once_disabled_with_futures(
+ caplog, modify_google_logger_propagation
+):
caplog.set_level(logging.DEBUG)
manager, _, _, _, _, _ = make_running_manager()
@@ -916,7 +1063,9 @@ def test_send_unary_ack_retry_error_exactly_once_disabled_with_futures(caplog):
assert future2.result() == subscriber_exceptions.AcknowledgeStatus.SUCCESS
-def test_send_unary_ack_retry_error_exactly_once_enabled_no_futures(caplog):
+def test_send_unary_ack_retry_error_exactly_once_enabled_no_futures(
+ caplog, modify_google_logger_propagation
+):
caplog.set_level(logging.DEBUG)
manager, _, _, _, _, _ = make_running_manager()
@@ -952,7 +1101,9 @@ def test_send_unary_ack_retry_error_exactly_once_enabled_no_futures(caplog):
assert "signaled streaming pull manager shutdown" in caplog.text
-def test_send_unary_ack_retry_error_exactly_once_enabled_with_futures(caplog):
+def test_send_unary_ack_retry_error_exactly_once_enabled_with_futures(
+ caplog, modify_google_logger_propagation
+):
caplog.set_level(logging.DEBUG)
manager, _, _, _, _, _ = make_running_manager()
@@ -998,7 +1149,9 @@ def test_send_unary_ack_retry_error_exactly_once_enabled_with_futures(caplog):
)
-def test_send_unary_modack_retry_error_exactly_once_disabled_no_future(caplog):
+def test_send_unary_modack_retry_error_exactly_once_disabled_no_future(
+ caplog, modify_google_logger_propagation
+):
caplog.set_level(logging.DEBUG)
manager, _, _, _, _, _ = make_running_manager()
@@ -1024,7 +1177,7 @@ def test_send_unary_modack_retry_error_exactly_once_disabled_no_future(caplog):
def test_send_unary_modack_retry_error_exactly_once_disabled_with_futures(
- caplog,
+ caplog, modify_google_logger_propagation
):
caplog.set_level(logging.DEBUG)
@@ -1053,7 +1206,7 @@ def test_send_unary_modack_retry_error_exactly_once_disabled_with_futures(
def test_send_unary_modack_retry_error_exactly_once_enabled_no_futures(
- caplog,
+ caplog, modify_google_logger_propagation
):
caplog.set_level(logging.DEBUG)
@@ -1080,7 +1233,7 @@ def test_send_unary_modack_retry_error_exactly_once_enabled_no_futures(
def test_send_unary_modack_retry_error_exactly_once_enabled_with_futures(
- caplog,
+ caplog, modify_google_logger_propagation
):
caplog.set_level(logging.DEBUG)
@@ -1133,7 +1286,9 @@ def test_heartbeat_inactive():
assert not result
-def test_heartbeat_stream_ack_deadline_seconds(caplog):
+def test_heartbeat_stream_ack_deadline_seconds(
+ caplog, modify_google_logger_propagation
+):
caplog.set_level(logging.DEBUG)
manager = make_manager()
manager._rpc = mock.create_autospec(bidi.BidiRpc, instance=True)
@@ -1181,7 +1336,13 @@ def test_open(heartbeater, dispatcher, leaser, background_consumer, resumable_bi
leaser.return_value.start.assert_called_once()
assert manager.leaser == leaser.return_value
- background_consumer.assert_called_once_with(manager._rpc, manager._on_response)
+ if streaming_pull_manager._SHOULD_USE_ON_FATAL_ERROR_CALLBACK:
+ background_consumer.assert_called_once_with(
+ manager._rpc, manager._on_response, manager._on_fatal_exception
+ )
+ else:
+ background_consumer.assert_called_once_with(manager._rpc, manager._on_response)
+
background_consumer.return_value.start.assert_called_once()
assert manager._consumer == background_consumer.return_value
@@ -1224,14 +1385,17 @@ def test_open_has_been_closed():
manager.open(mock.sentinel.callback, mock.sentinel.on_callback_error)
-def make_running_manager(**kwargs):
- manager = make_manager(**kwargs)
+def make_running_manager(
+ enable_open_telemetry: bool = False,
+ subscription_name: str = "subscription-name",
+ **kwargs,
+):
+ manager = make_manager(enable_open_telemetry, subscription_name, **kwargs)
manager._consumer = mock.create_autospec(bidi.BackgroundConsumer, instance=True)
manager._consumer.is_active = True
manager._dispatcher = mock.create_autospec(dispatcher.Dispatcher, instance=True)
manager._leaser = mock.create_autospec(leaser.Leaser, instance=True)
manager._heartbeater = mock.create_autospec(heartbeater.Heartbeater, instance=True)
-
return (
manager,
manager._consumer,
@@ -1277,6 +1441,31 @@ def test_close():
assert manager.is_active is False
+def test_closes_on_fatal_consumer_error():
+ (
+ manager,
+ consumer,
+ dispatcher,
+ leaser,
+ heartbeater,
+ scheduler,
+ ) = make_running_manager()
+
+ if streaming_pull_manager._SHOULD_USE_ON_FATAL_ERROR_CALLBACK:
+ error = ValueError("some fatal exception")
+ manager._on_fatal_exception(error)
+
+ await_manager_shutdown(manager, timeout=3)
+
+ consumer.stop.assert_called_once()
+ leaser.stop.assert_called_once()
+ dispatcher.stop.assert_called_once()
+ heartbeater.stop.assert_called_once()
+ scheduler.shutdown.assert_called_once()
+
+ assert manager.is_active is False
+
+
def test_close_inactive_consumer():
(
manager,
@@ -1375,6 +1564,62 @@ def test_close_blocking_scheduler_shutdown():
scheduler.shutdown.assert_called_once_with(await_msg_callbacks=True)
+def test__on_response_none_scheduler():
+ manager, _, _, _, _, _ = make_running_manager()
+
+ manager._callback = mock.sentinel.callback
+ manager._scheduler = None
+ # Set up the messages.
+ response = gapic_types.StreamingPullResponse(
+ received_messages=[
+ gapic_types.ReceivedMessage(
+ ack_id="ack1",
+ message=gapic_types.PubsubMessage(data=b"foo", message_id="1"),
+ ),
+ gapic_types.ReceivedMessage(
+ ack_id="ack2",
+ message=gapic_types.PubsubMessage(data=b"bar", message_id="2"),
+ delivery_attempt=6,
+ ),
+ ]
+ )
+
+ manager._maybe_release_messages = mock.Mock()
+
+ # adjust message bookkeeping in leaser
+ fake_leaser_add(leaser, init_msg_count=0, assumed_msg_size=42)
+ manager._on_response(response)
+
+ manager._maybe_release_messages.assert_not_called
+
+
+def test__on_response_none_leaser():
+ manager, _, _, _, _, _ = make_running_manager()
+
+ manager._callback = mock.sentinel.callback
+ manager._leaser = None
+ # Set up the messages.
+ response = gapic_types.StreamingPullResponse(
+ received_messages=[
+ gapic_types.ReceivedMessage(
+ ack_id="ack1",
+ message=gapic_types.PubsubMessage(data=b"foo", message_id="1"),
+ ),
+ gapic_types.ReceivedMessage(
+ ack_id="ack2",
+ message=gapic_types.PubsubMessage(data=b"bar", message_id="2"),
+ delivery_attempt=6,
+ ),
+ ]
+ )
+
+ manager._maybe_release_messages = mock.Mock()
+
+ manager._on_response(response)
+
+ manager._maybe_release_messages.assert_not_called
+
+
def test_close_nonblocking_scheduler_shutdown():
manager, _, _, _, _, _ = make_running_manager(await_callbacks_on_shutdown=False)
scheduler = manager._scheduler
@@ -1391,8 +1636,11 @@ def test_close_nacks_internally_queued_messages():
def fake_nack(self):
nacked_messages.append(self.data)
- MockMsg = functools.partial(mock.create_autospec, message.Message, instance=True)
- messages = [MockMsg(data=b"msg1"), MockMsg(data=b"msg2"), MockMsg(data=b"msg3")]
+ messages = [
+ create_message(data=b"msg1"),
+ create_message(data=b"msg2"),
+ create_message(data=b"msg3"),
+ ]
for msg in messages:
msg.nack = stdlib_types.MethodType(fake_nack, msg)
@@ -1725,7 +1973,7 @@ def test__on_response_with_leaser_overload():
assert msg.message_id in ("2", "3")
-def test__on_response_none_data(caplog):
+def test__on_response_none_data(caplog, modify_google_logger_propagation):
caplog.set_level(logging.DEBUG)
manager, _, dispatcher, leaser, _, scheduler = make_running_manager()
@@ -1890,7 +2138,10 @@ def test__on_response_disable_exactly_once():
assert manager._stream_ack_deadline == 60
-def test__on_response_exactly_once_immediate_modacks_fail(caplog):
+def test__on_response_exactly_once_immediate_modacks_fail(
+ caplog,
+ modify_google_logger_propagation,
+):
manager, _, dispatcher, leaser, _, scheduler = make_running_manager()
manager._callback = mock.sentinel.callback
@@ -1962,7 +2213,9 @@ def complete_futures_with_error(*args, **kwargs):
assert manager.load == 0.001
-def test__on_response_exactly_once_immediate_modacks_fail_non_invalid(caplog):
+def test__on_response_exactly_once_immediate_modacks_fail_non_invalid(
+ caplog, modify_google_logger_propagation
+):
manager, _, dispatcher, leaser, _, scheduler = make_running_manager()
manager._callback = mock.sentinel.callback
@@ -2054,18 +2307,24 @@ def test__should_recover_false():
def test__should_terminate_true():
manager = make_manager()
- details = "Cancelled. Go away, before I taunt you a second time."
- exc = exceptions.Cancelled(details)
-
- assert manager._should_terminate(exc) is True
+ for exc in [
+ exceptions.Cancelled(""),
+ exceptions.PermissionDenied(""),
+ TypeError(),
+ ValueError(),
+ ]:
+ assert manager._should_terminate(exc)
def test__should_terminate_false():
manager = make_manager()
- exc = TypeError("wahhhhhh")
-
- assert manager._should_terminate(exc) is False
+ for exc in [
+ exceptions.ResourceExhausted(""),
+ exceptions.ServiceUnavailable(""),
+ exceptions.DeadlineExceeded(""),
+ ]:
+ assert not manager._should_terminate(exc)
@mock.patch("threading.Thread", autospec=True)
@@ -2570,3 +2829,148 @@ def test_process_requests_mixed_success_and_failure_modacks():
# message with ack_id 'ackid3' succeeds
assert requests_completed[1].ack_id == "ackid3"
assert future3.result() == subscriber_exceptions.AcknowledgeStatus.SUCCESS
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3, 8),
+ reason="Open Telemetry not supported below Python version 3.8",
+)
+def test_opentelemetry__on_response_subscribe_span_create(span_exporter):
+ manager, _, _, leaser, _, _ = make_running_manager(
+ enable_open_telemetry=True,
+ subscription_name="projects/projectID/subscriptions/subscriptionID",
+ )
+
+ fake_leaser_add(leaser, init_msg_count=0, assumed_msg_size=42)
+ manager._callback = mock.sentinel.callback
+
+ response = gapic_types.StreamingPullResponse(
+ received_messages=[
+ gapic_types.ReceivedMessage(
+ ack_id="ack1",
+ message=gapic_types.PubsubMessage(data=b"foo", message_id="1"),
+ ),
+ gapic_types.ReceivedMessage(
+ ack_id="ack2",
+ message=gapic_types.PubsubMessage(data=b"bar", message_id="2"),
+ delivery_attempt=6,
+ ),
+ ]
+ )
+
+ manager._on_response(response)
+
+ spans = span_exporter.get_finished_spans()
+
+ # Subscribe span is still active, hence unexported.
+ # Subscriber scheduler spans corresponding to the two messages would be started in `messages_on_hold.put()``
+ # and ended in `_maybe_release_messages`
+ assert len(spans) == 3
+ modack_span = spans[0]
+
+ for span in spans[1:]:
+ assert span.name == "subscriber scheduler"
+ assert span.kind == trace.SpanKind.INTERNAL
+ assert span.parent is not None
+ assert len(span.attributes) == 0
+
+ assert modack_span.name == "subscriptionID modack"
+ assert modack_span.kind == trace.SpanKind.CLIENT
+ assert modack_span.parent is None
+ assert len(modack_span.links) == 2
+
+
+RECEIVED = datetime.datetime(2012, 4, 21, 15, 0, tzinfo=datetime.timezone.utc)
+RECEIVED_SECONDS = datetime_helpers.to_milliseconds(RECEIVED) // 1000
+PUBLISHED_MICROS = 123456
+PUBLISHED = RECEIVED + datetime.timedelta(days=1, microseconds=PUBLISHED_MICROS)
+PUBLISHED_SECONDS = datetime_helpers.to_milliseconds(PUBLISHED) // 1000
+
+
+def create_message(
+ data,
+ ack_id="ACKID",
+ delivery_attempt=0,
+ ordering_key="",
+ exactly_once_delivery_enabled=False,
+ **attrs,
+): # pragma: NO COVER
+ with mock.patch.object(time, "time") as time_:
+ time_.return_value = RECEIVED_SECONDS
+ gapic_pubsub_message = PubsubMessage(
+ attributes=attrs,
+ data=data,
+ message_id="message_id",
+ publish_time=timestamp_pb2.Timestamp(
+ seconds=PUBLISHED_SECONDS, nanos=PUBLISHED_MICROS * 1000
+ ),
+ ordering_key=ordering_key,
+ )
+ msg = Message(
+ # The code under test uses a raw protobuf PubsubMessage, i.e. w/o additional
+ # Python class wrappers, hence the "_pb"
+ message=gapic_pubsub_message._pb,
+ ack_id=ack_id,
+ delivery_attempt=delivery_attempt,
+ request_queue=queue.Queue(),
+ exactly_once_delivery_enabled_func=lambda: exactly_once_delivery_enabled,
+ )
+ return msg
+
+
+def test_opentelemetry_subscriber_concurrency_control_span(span_exporter):
+ manager, _, _, leaser, _, _ = make_running_manager(
+ enable_open_telemetry=True,
+ subscription_name="projects/projectID/subscriptions/subscriptionID",
+ )
+ manager._callback = mock.Mock()
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ msg.opentelemetry_data = opentelemetry_data
+ manager._schedule_message_on_hold(msg)
+ opentelemetry_data.end_subscribe_concurrency_control_span()
+ opentelemetry_data.end_subscribe_span()
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+
+ concurrency_control_span, subscribe_span = spans
+ assert concurrency_control_span.name == "subscriber concurrency control"
+ assert subscribe_span.name == "subscriptionID subscribe"
+ assert opentelemetry_data.subscription_id == "subscriptionID"
+
+ assert concurrency_control_span.parent == subscribe_span.context
+
+
+def test_opentelemetry_subscriber_concurrency_control_span_end(span_exporter):
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ opentelemetry_data.start_subscribe_concurrency_control_span()
+ msg.opentelemetry_data = opentelemetry_data
+ streaming_pull_manager._wrap_callback_errors(mock.Mock(), mock.Mock(), msg)
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 2
+
+ concurrency_control_span = spans[0]
+ assert concurrency_control_span.name == "subscriber concurrency control"
+
+
+def test_opentelemetry_wrap_callback_error(span_exporter):
+ msg = create_message(b"foo")
+ streaming_pull_manager._wrap_callback_errors(mock.Mock(), mock.Mock(), msg)
+
+ spans = span_exporter.get_finished_spans()
+ assert len(spans) == 0
diff --git a/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py b/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py
new file mode 100644
index 000000000..efa3e4f71
--- /dev/null
+++ b/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py
@@ -0,0 +1,197 @@
+# Copyright 2024, Google LLC All rights reserved.
+#
+# 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 datetime
+import time
+import queue
+import pytest
+
+from google.protobuf import timestamp_pb2
+from google.api_core import datetime_helpers
+from opentelemetry import trace
+from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
+from google.cloud.pubsub_v1.open_telemetry.context_propagation import (
+ OpenTelemetryContextSetter,
+)
+
+from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import (
+ SubscribeOpenTelemetry,
+)
+from google.cloud.pubsub_v1.subscriber.message import Message
+from google.cloud.pubsub_v1.types import PubsubMessage
+
+from unittest import mock
+
+RECEIVED = datetime.datetime(2012, 4, 21, 15, 0, tzinfo=datetime.timezone.utc)
+RECEIVED_SECONDS = datetime_helpers.to_milliseconds(RECEIVED) // 1000
+PUBLISHED_MICROS = 123456
+PUBLISHED = RECEIVED + datetime.timedelta(days=1, microseconds=PUBLISHED_MICROS)
+PUBLISHED_SECONDS = datetime_helpers.to_milliseconds(PUBLISHED) // 1000
+
+
+def create_message(
+ data,
+ ack_id="ACKID",
+ delivery_attempt=0,
+ ordering_key="",
+ exactly_once_delivery_enabled=False,
+ **attrs
+): # pragma: NO COVER
+ with mock.patch.object(time, "time") as time_:
+ time_.return_value = RECEIVED_SECONDS
+ gapic_pubsub_message = PubsubMessage(
+ attributes=attrs,
+ data=data,
+ message_id="message_id",
+ publish_time=timestamp_pb2.Timestamp(
+ seconds=PUBLISHED_SECONDS, nanos=PUBLISHED_MICROS * 1000
+ ),
+ ordering_key=ordering_key,
+ )
+ msg = Message(
+ # The code under test uses a raw protobuf PubsubMessage, i.e. w/o additional
+ # Python class wrappers, hence the "_pb"
+ message=gapic_pubsub_message._pb,
+ ack_id=ack_id,
+ delivery_attempt=delivery_attempt,
+ request_queue=queue.Queue(),
+ exactly_once_delivery_enabled_func=lambda: exactly_once_delivery_enabled,
+ )
+ return msg
+
+
+def test_opentelemetry_set_subscribe_span_result(span_exporter):
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ msg.opentelemetry_data = opentelemetry_data
+ opentelemetry_data.set_subscribe_span_result("acked")
+ opentelemetry_data.end_subscribe_span()
+ spans = span_exporter.get_finished_spans()
+
+ assert len(spans) == 1
+
+ assert "messaging.gcp_pubsub.result" in spans[0].attributes
+ assert spans[0].attributes["messaging.gcp_pubsub.result"] == "acked"
+
+
+def test_opentelemetry_set_subscribe_span_result_assert_error():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ with pytest.raises(AssertionError):
+ opentelemetry_data.set_subscribe_span_result("hi")
+
+
+def test_opentelemetry_start_subscribe_concurrency_control_span_no_subscribe_span():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ with pytest.raises(AssertionError):
+ opentelemetry_data.start_subscribe_concurrency_control_span()
+
+
+def test_opentelemetry_end_subscribe_concurrency_control_span_assertion_error():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ with pytest.raises(AssertionError):
+ opentelemetry_data.end_subscribe_concurrency_control_span()
+
+
+def test_opentelemetry_start_subscribe_scheduler_span_assertion_error():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ with pytest.raises(AssertionError):
+ opentelemetry_data.start_subscribe_scheduler_span()
+
+
+def test_opentelemetry_end_subscribe_scheduler_span_assertion_error():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ with pytest.raises(AssertionError):
+ opentelemetry_data.end_subscribe_scheduler_span()
+
+
+def test_opentelemetry_start_process_span_assertion_error():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ with pytest.raises(AssertionError):
+ opentelemetry_data.start_process_span()
+
+
+def test_opentelemetry_end_process_span_assertion_error():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ with pytest.raises(AssertionError):
+ opentelemetry_data.end_process_span()
+
+
+def test_opentelemetry_start_process_span_publisher_link():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ msg.opentelemetry_data = opentelemetry_data
+ tracer = trace.get_tracer("foo")
+ publisher_create_span = None
+ with tracer.start_as_current_span(name="name") as span:
+ publisher_create_span = span
+ TraceContextTextMapPropagator().inject(
+ carrier=msg._message,
+ setter=OpenTelemetryContextSetter(),
+ )
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ opentelemetry_data.start_process_span()
+ assert len(opentelemetry_data._process_span.links) == 1
+ assert (
+ opentelemetry_data._process_span.links[0].context.span_id
+ == publisher_create_span.get_span_context().span_id
+ )
+
+
+def test_opentelemetry_start_process_span_no_publisher_span():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ msg.opentelemetry_data = opentelemetry_data
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ opentelemetry_data.start_process_span()
+ # Assert that when no context is propagated, the subscriber span has no parent.
+ assert opentelemetry_data._subscribe_span.parent is None
+ # Assert that when there is no publisher create span context propagated,
+ # There are no links created in the process span.
+ assert len(opentelemetry_data._process_span.links) == 0
+
+
+def test_opentelemetry_project_id_set_after_create_subscribe_span():
+ msg = create_message(b"foo")
+ opentelemetry_data = SubscribeOpenTelemetry(msg)
+ msg.opentelemetry_data = opentelemetry_data
+ opentelemetry_data.start_subscribe_span(
+ subscription="projects/projectId/subscriptions/subscriptionID",
+ exactly_once_enabled=False,
+ ack_id="ack_id",
+ delivery_attempt=4,
+ )
+ assert opentelemetry_data.project_id == "projectId"
diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py
index 16a6150af..ecaf23cf9 100644
--- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py
+++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py
@@ -16,11 +16,7 @@
import grpc
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest
@@ -30,6 +26,22 @@
from google.cloud.pubsub_v1.subscriber import futures
from google.pubsub_v1.services.subscriber import client as subscriber_client
from google.pubsub_v1.services.subscriber.transports.grpc import SubscriberGrpcTransport
+from google.cloud.pubsub_v1.open_telemetry.context_propagation import (
+ OpenTelemetryContextGetter,
+)
+from google.pubsub_v1.types import PubsubMessage
+
+
+# Attempt to use `_thunk` to obtain the underlying grpc channel from
+# the intercept channel. Default to obtaining the grpc channel directly
+# for backwards compatibility.
+# TODO(https://github.com/grpc/grpc/issues/38519): Workaround to obtain a channel
+# until a public API is available.
+def get_pull_channel(client):
+ try:
+ return client._transport.pull._thunk("")._channel
+ except AttributeError:
+ return client._transport.pull._channel
def test_init_default_client_info(creds):
@@ -125,7 +137,7 @@ def test_init_emulator(monkeypatch):
#
# Sadly, there seems to be no good way to do this without poking at
# the private API of gRPC.
- channel = client._transport.pull._channel
+ channel = get_pull_channel(client)
# Behavior to include dns prefix changed in gRPCv1.63
grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]]
if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63):
@@ -317,3 +329,48 @@ async def test_sync_pull_warning_if_return_immediately_async(creds):
warning_msg = str(warned[0].message)
assert "return_immediately" in warning_msg
assert "deprecated" in warning_msg
+
+
+@pytest.mark.parametrize(
+ "enable_open_telemetry",
+ [
+ True,
+ False,
+ ],
+)
+def test_opentelemetry_subscriber_setting(creds, enable_open_telemetry):
+ options = types.SubscriberOptions(
+ enable_open_telemetry_tracing=enable_open_telemetry,
+ )
+ if sys.version_info >= (3, 8) or enable_open_telemetry is False:
+ client = subscriber.Client(credentials=creds, subscriber_options=options)
+ assert client.subscriber_options == options
+ assert client._open_telemetry_enabled == enable_open_telemetry
+ else:
+ with pytest.warns(
+ RuntimeWarning,
+ match="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.",
+ ):
+ client = subscriber.Client(credentials=creds, subscriber_options=options)
+ assert client._open_telemetry_enabled is False
+
+
+def test_opentelemetry_propagator_get():
+ message = PubsubMessage(data=b"foo")
+ message.attributes["key1"] = "value1"
+ message.attributes["googclient_key2"] = "value2"
+
+ assert OpenTelemetryContextGetter().get(message, "key2") == ["value2"]
+
+ assert OpenTelemetryContextGetter().get(message, "key1") is None
+
+
+def test_opentelemetry_propagator_keys():
+ message = PubsubMessage(data=b"foo")
+ message.attributes["key1"] = "value1"
+ message.attributes["googclient_key2"] = "value2"
+
+ assert sorted(OpenTelemetryContextGetter().keys(message)) == [
+ "googclient_key2",
+ "key1",
+ ]
diff --git a/tests/unit/pubsub_v1/test_futures.py b/tests/unit/pubsub_v1/test_futures.py
index 5a4dad41a..d6af2f359 100644
--- a/tests/unit/pubsub_v1/test_futures.py
+++ b/tests/unit/pubsub_v1/test_futures.py
@@ -17,11 +17,7 @@
import threading
import time
-# special case python < 3.8
-if sys.version_info.major == 3 and sys.version_info.minor < 8:
- import mock
-else:
- from unittest import mock
+from unittest import mock
import pytest