diff --git a/.coveragerc b/.coveragerc
index f72fce1e..9aac2710 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -5,13 +5,9 @@ branch = True
show_missing = True
omit =
google/cloud/errorreporting/__init__.py
+ google/cloud/errorreporting/gapic_version.py
exclude_lines =
# Re-enable the standard pragma
pragma: NO COVER
# Ignore debug-only repr
def __repr__
- # Ignore pkg_resources exceptions.
- # This is added at the module level as a safeguard for if someone
- # generates the code and tries to run it without pip installing. This
- # makes it virtually impossible to test properly.
- except pkg_resources.DistributionNotFound
diff --git a/.flake8 b/.flake8
index 2e438749..32986c79 100644
--- a/.flake8
+++ b/.flake8
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright 2020 Google LLC
+# 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.
diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index 1ce60852..c4e82889 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -1,4 +1,4 @@
-# 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,5 +13,5 @@
# limitations under the License.
docker:
image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
- digest: sha256:e7bb19d47c13839fe8c147e50e02e8b6cf5da8edd1af8b82208cd6f66cc2829c
-# created: 2022-07-05T18:31:20.838186805Z
+ digest: sha256:023a21377a2a00008057f99f0118edadc30a19d1636a3fee47189ebec2f3921c
+# created: 2025-03-31T16:51:40.130756953Z
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 2a3b4205..0738e11e 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-logging are the default owners for changes in this repo
-* @googleapis/yoshi-python @googleapis/api-logging
+# @googleapis/yoshi-python @googleapis/api-logging @googleapis/api-logging-partners are the default owners for changes in this repo
+* @googleapis/yoshi-python @googleapis/api-logging @googleapis/api-logging-partners
-# @googleapis/python-samples-reviewers @googleapis/api-logging are the default owners for samples changes
-/samples/ @googleapis/python-samples-reviewers @googleapis/api-logging
+# @googleapis/python-samples-reviewers @googleapis/api-logging @googleapis/api-logging-partners are the default owners for samples changes
+/samples/ @googleapis/python-samples-reviewers @googleapis/api-logging @googleapis/api-logging-partners
diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml
index 148ebf4e..d5f69b10 100644
--- a/.github/blunderbuss.yml
+++ b/.github/blunderbuss.yml
@@ -1,4 +1,20 @@
+# Blunderbuss config
+#
+# This file controls who is assigned for pull requests and issues.
+# Note: This file is autogenerated. To make changes to the assignee
+# team, please update `codeowner_team` in `.repo-metadata.json`.
assign_issues:
- - Daniel-Sanche
+ - googleapis/api-logging
+ - googleapis/api-logging-partners
+
+assign_issues_by:
+ - labels:
+ - "samples"
+ to:
+ - googleapis/python-samples-reviewers
+ - googleapis/api-logging
+ - googleapis/api-logging-partners
+
assign_prs:
- - Daniel-Sanche
+ - googleapis/api-logging
+ - googleapis/api-logging-partners
diff --git a/samples/snippets/fluent_on_compute/main_test.py b/.github/flakybot.yaml
similarity index 64%
rename from samples/snippets/fluent_on_compute/main_test.py
rename to .github/flakybot.yaml
index 11a24d03..2159a1bc 100644
--- a/samples/snippets/fluent_on_compute/main_test.py
+++ b/.github/flakybot.yaml
@@ -1,10 +1,10 @@
-# Copyright 2016 Google Inc. All rights reserved.
+# 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
+# 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,
@@ -12,12 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import mock
-
-import main
-
-
-@mock.patch("fluent.event")
-def test_error_sends(event_mock):
- main.simulate_error()
- event_mock.Event.assert_called_once_with(mock.ANY, mock.ANY)
+issuePriority: p2
\ No newline at end of file
diff --git a/.github/release-please.yml b/.github/release-please.yml
index 6def37a8..e9a4f008 100644
--- a/.github/release-please.yml
+++ b/.github/release-please.yml
@@ -1,5 +1,6 @@
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:
diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml
index d4ca9418..c1b20096 100644
--- a/.github/release-trigger.yml
+++ b/.github/release-trigger.yml
@@ -1 +1,2 @@
enabled: true
+multiScmName: python-error-reporting
diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml
index 37438d33..1ca6d927 100644
--- a/.github/sync-repo-settings.yaml
+++ b/.github/sync-repo-settings.yaml
@@ -12,3 +12,17 @@ branchProtectionRules:
- 'Samples - Lint'
- 'Samples - Python 3.7'
- 'Samples - Python 3.8'
+ - 'Samples - Python 3.9'
+ - 'Samples - Python 3.10'
+ - 'Samples - Python 3.11'
+ - 'Samples - Python 3.12'
+ - 'docs'
+ - 'docfx'
+ - 'lint'
+ - 'unit (3.7)'
+ - 'unit (3.8)'
+ - 'unit (3.9)'
+ - 'unit (3.10)'
+ - 'unit (3.11)'
+ - 'unit (3.12)'
+ - 'cover'
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index b46d7305..2833fe98 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -8,9 +8,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install nox
@@ -24,9 +24,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install nox
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index f512a496..4866193a 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@v3
+ uses: actions/checkout@v4
- name: Setup Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
- python-version: "3.10"
+ python-version: "3.8"
- 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 5531b014..c66b757c 100644
--- a/.github/workflows/unittest.yml
+++ b/.github/workflows/unittest.yml
@@ -5,15 +5,18 @@ 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']
+ python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install nox
@@ -26,10 +29,11 @@ jobs:
run: |
nox -s unit-${{ matrix.python }}
- name: Upload coverage results
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: coverage-artifacts
+ name: coverage-artifact-${{ matrix.python }}
path: .coverage-${{ matrix.python }}
+ include-hidden-files: true
cover:
runs-on: ubuntu-latest
@@ -37,21 +41,21 @@ jobs:
- unit
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
- python-version: "3.10"
+ python-version: "3.8"
- name: Install coverage
run: |
python -m pip install --upgrade setuptools pip wheel
python -m pip install coverage
- name: Download coverage results
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
- name: coverage-artifacts
path: .coverage-results/
- name: Report coverage results
run: |
- coverage combine .coverage-results/.coverage*
+ find .coverage-results -type f -name '*.zip' -exec unzip {} \;
+ coverage combine .coverage-results/**/.coverage*
coverage report --show-missing --fail-under=100
diff --git a/.gitignore b/.gitignore
index b4243ced..d083ea1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,6 +50,7 @@ docs.metadata
# Virtual environment
env/
+venv/
# Test logs
coverage.xml
diff --git a/.kokoro/build.sh b/.kokoro/build.sh
index 165d0e08..d41b45aa 100755
--- a/.kokoro/build.sh
+++ b/.kokoro/build.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright 2018 Google LLC
+# 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.
@@ -15,11 +15,13 @@
set -eo pipefail
+CURRENT_DIR=$(dirname "${BASH_SOURCE[0]}")
+
if [[ -z "${PROJECT_ROOT:-}" ]]; then
- PROJECT_ROOT="github/python-error-reporting"
+ PROJECT_ROOT=$(realpath "${CURRENT_DIR}/..")
fi
-cd "${PROJECT_ROOT}"
+pushd "${PROJECT_ROOT}"
# Disable buffering, so that the logs stream through.
export PYTHONUNBUFFERED=1
@@ -28,17 +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")
-
-# Remove old nox
-python3 -m pip uninstall --yes --quiet nox-automation
-
-# Install nox
-python3 -m pip install --upgrade --quiet nox
-python3 -m nox --version
+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.
@@ -53,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/common_env_vars.cfg b/.kokoro/common_env_vars.cfg
new file mode 100644
index 00000000..b5c22b80
--- /dev/null
+++ b/.kokoro/common_env_vars.cfg
@@ -0,0 +1,19 @@
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/continuous/common.cfg b/.kokoro/continuous/common.cfg
index ccbc23c7..c337b6d8 100644
--- a/.kokoro/continuous/common.cfg
+++ b/.kokoro/continuous/common.cfg
@@ -25,3 +25,23 @@ env_vars: {
key: "TRAMPOLINE_BUILD_FILE"
value: "github/python-error-reporting/.kokoro/build.sh"
}
+
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile
deleted file mode 100644
index 238b87b9..00000000
--- a/.kokoro/docker/docs/Dockerfile
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from ubuntu:22.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 \
- python3-distutils \
- 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.8.11
-
-# Download python 3.8.11
-RUN wget https://www.python.org/ftp/python/3.8.11/Python-3.8.11.tgz
-
-# Extract files
-RUN tar -xvf Python-3.8.11.tgz
-
-# Install python 3.8.11
-RUN ./Python-3.8.11/configure --enable-optimizations
-RUN make altinstall
-
-###################### 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
-
-CMD ["python3.8"]
diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg
deleted file mode 100644
index 2603e22d..00000000
--- 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-error-reporting/.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-error-reporting/.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 6d7b5186..00000000
--- 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-error-reporting/.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 8f43917d..00000000
--- 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/populate-secrets.sh b/.kokoro/populate-secrets.sh
index f5251425..c435402f 100755
--- a/.kokoro/populate-secrets.sh
+++ b/.kokoro/populate-secrets.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright 2020 Google LLC.
+# 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.
diff --git a/.kokoro/presubmit/common.cfg b/.kokoro/presubmit/common.cfg
index ccbc23c7..c337b6d8 100644
--- a/.kokoro/presubmit/common.cfg
+++ b/.kokoro/presubmit/common.cfg
@@ -25,3 +25,23 @@ env_vars: {
key: "TRAMPOLINE_BUILD_FILE"
value: "github/python-error-reporting/.kokoro/build.sh"
}
+
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh
deleted file mode 100755
index 8acb14e8..00000000
--- a/.kokoro/publish-docs.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-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 --user --upgrade --quiet nox
-python3 -m nox --version
-
-# build docs
-nox -s docs
-
-python3 -m pip install --user gcp-docuploader
-
-# 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 086e7e24..00000000
--- a/.kokoro/release.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -eo pipefail
-
-# Start the releasetool reporter
-python3 -m pip install gcp-releasetool
-python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script
-
-# Ensure that we have the latest versions of Twine, Wheel, and Setuptools.
-python3 -m pip install --upgrade twine wheel setuptools
-
-# 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-error-reporting
-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 c4f805b8..00000000
--- a/.kokoro/release/common.cfg
+++ /dev/null
@@ -1,40 +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-error-reporting/.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-error-reporting/.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"
-}
diff --git a/.kokoro/release/release.cfg b/.kokoro/release/release.cfg
deleted file mode 100644
index 8f43917d..00000000
--- 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/samples/lint/common.cfg b/.kokoro/samples/lint/common.cfg
index a38013a4..3f98b562 100644
--- a/.kokoro/samples/lint/common.cfg
+++ b/.kokoro/samples/lint/common.cfg
@@ -31,4 +31,23 @@ 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-error-reporting/.kokoro/trampoline_v2.sh"
\ No newline at end of file
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/samples/python3.10/common.cfg b/.kokoro/samples/python3.10/common.cfg
index 2ed420e5..d19172ae 100644
--- a/.kokoro/samples/python3.10/common.cfg
+++ b/.kokoro/samples/python3.10/common.cfg
@@ -37,4 +37,23 @@ 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-error-reporting/.kokoro/trampoline_v2.sh"
\ No newline at end of file
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/samples/python3.11/common.cfg b/.kokoro/samples/python3.11/common.cfg
new file mode 100644
index 00000000..d83d3eab
--- /dev/null
+++ b/.kokoro/samples/python3.11/common.cfg
@@ -0,0 +1,59 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+# Build logs will be here
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.xml"
+ }
+}
+
+# Specify which tests to run
+env_vars: {
+ key: "RUN_TESTS_SESSION"
+ value: "py-3.11"
+}
+
+# Declare build specific Cloud project.
+env_vars: {
+ key: "BUILD_SPECIFIC_GCLOUD_PROJECT"
+ value: "python-docs-samples-tests-311"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-error-reporting/.kokoro/test-samples.sh"
+}
+
+# Configure the docker image for kokoro-trampoline.
+env_vars: {
+ key: "TRAMPOLINE_IMAGE"
+ value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker"
+}
+
+# Download secrets for samples
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples"
+
+# Download trampoline resources.
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
+
+# Use the trampoline script to run in docker.
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/samples/python3.11/continuous.cfg b/.kokoro/samples/python3.11/continuous.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.11/continuous.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/.kokoro/samples/python3.11/periodic-head.cfg b/.kokoro/samples/python3.11/periodic-head.cfg
new file mode 100644
index 00000000..0ab001ca
--- /dev/null
+++ b/.kokoro/samples/python3.11/periodic-head.cfg
@@ -0,0 +1,11 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-error-reporting/.kokoro/test-samples-against-head.sh"
+}
diff --git a/.kokoro/samples/python3.11/periodic.cfg b/.kokoro/samples/python3.11/periodic.cfg
new file mode 100644
index 00000000..71cd1e59
--- /dev/null
+++ b/.kokoro/samples/python3.11/periodic.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "False"
+}
diff --git a/.kokoro/samples/python3.11/presubmit.cfg b/.kokoro/samples/python3.11/presubmit.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.11/presubmit.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/.kokoro/samples/python3.12/common.cfg b/.kokoro/samples/python3.12/common.cfg
new file mode 100644
index 00000000..67d66075
--- /dev/null
+++ b/.kokoro/samples/python3.12/common.cfg
@@ -0,0 +1,59 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+# Build logs will be here
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.xml"
+ }
+}
+
+# Specify which tests to run
+env_vars: {
+ key: "RUN_TESTS_SESSION"
+ value: "py-3.12"
+}
+
+# Declare build specific Cloud project.
+env_vars: {
+ key: "BUILD_SPECIFIC_GCLOUD_PROJECT"
+ value: "python-docs-samples-tests-312"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-error-reporting/.kokoro/test-samples.sh"
+}
+
+# Configure the docker image for kokoro-trampoline.
+env_vars: {
+ key: "TRAMPOLINE_IMAGE"
+ value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker"
+}
+
+# Download secrets for samples
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples"
+
+# Download trampoline resources.
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
+
+# Use the trampoline script to run in docker.
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/samples/python3.12/continuous.cfg b/.kokoro/samples/python3.12/continuous.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.12/continuous.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/.kokoro/samples/python3.12/periodic-head.cfg b/.kokoro/samples/python3.12/periodic-head.cfg
new file mode 100644
index 00000000..0ab001ca
--- /dev/null
+++ b/.kokoro/samples/python3.12/periodic-head.cfg
@@ -0,0 +1,11 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-error-reporting/.kokoro/test-samples-against-head.sh"
+}
diff --git a/.kokoro/samples/python3.12/periodic.cfg b/.kokoro/samples/python3.12/periodic.cfg
new file mode 100644
index 00000000..71cd1e59
--- /dev/null
+++ b/.kokoro/samples/python3.12/periodic.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "False"
+}
diff --git a/.kokoro/samples/python3.12/presubmit.cfg b/.kokoro/samples/python3.12/presubmit.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.12/presubmit.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/.kokoro/samples/python3.13/common.cfg b/.kokoro/samples/python3.13/common.cfg
new file mode 100644
index 00000000..e83c889e
--- /dev/null
+++ b/.kokoro/samples/python3.13/common.cfg
@@ -0,0 +1,60 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+# Build logs will be here
+action {
+ define_artifacts {
+ regex: "**/*sponge_log.xml"
+ }
+}
+
+# Specify which tests to run
+env_vars: {
+ key: "RUN_TESTS_SESSION"
+ value: "py-3.13"
+}
+
+# Declare build specific Cloud project.
+env_vars: {
+ key: "BUILD_SPECIFIC_GCLOUD_PROJECT"
+ value: "python-docs-samples-tests-313"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-error-reporting/.kokoro/test-samples.sh"
+}
+
+# Configure the docker image for kokoro-trampoline.
+env_vars: {
+ key: "TRAMPOLINE_IMAGE"
+ value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker"
+}
+
+# Download secrets for samples
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples"
+
+# Download trampoline resources.
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
+
+# Use the trampoline script to run in docker.
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/samples/python3.13/continuous.cfg b/.kokoro/samples/python3.13/continuous.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.13/continuous.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/.kokoro/samples/python3.13/periodic-head.cfg b/.kokoro/samples/python3.13/periodic-head.cfg
new file mode 100644
index 00000000..0ab001ca
--- /dev/null
+++ b/.kokoro/samples/python3.13/periodic-head.cfg
@@ -0,0 +1,11 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-error-reporting/.kokoro/test-samples-against-head.sh"
+}
diff --git a/.kokoro/samples/python3.13/periodic.cfg b/.kokoro/samples/python3.13/periodic.cfg
new file mode 100644
index 00000000..71cd1e59
--- /dev/null
+++ b/.kokoro/samples/python3.13/periodic.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "False"
+}
diff --git a/.kokoro/samples/python3.13/presubmit.cfg b/.kokoro/samples/python3.13/presubmit.cfg
new file mode 100644
index 00000000..a1c8d975
--- /dev/null
+++ b/.kokoro/samples/python3.13/presubmit.cfg
@@ -0,0 +1,6 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+env_vars: {
+ key: "INSTALL_LIBRARY_FROM_SOURCE"
+ value: "True"
+}
\ No newline at end of file
diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg
index 161c018b..ad162db9 100644
--- a/.kokoro/samples/python3.7/common.cfg
+++ b/.kokoro/samples/python3.7/common.cfg
@@ -37,4 +37,23 @@ 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-error-reporting/.kokoro/trampoline_v2.sh"
\ No newline at end of file
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg
index abd2a5bb..b495960b 100644
--- a/.kokoro/samples/python3.8/common.cfg
+++ b/.kokoro/samples/python3.8/common.cfg
@@ -37,4 +37,23 @@ 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-error-reporting/.kokoro/trampoline_v2.sh"
\ No newline at end of file
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/samples/python3.9/common.cfg b/.kokoro/samples/python3.9/common.cfg
index 4889669b..01a9e9d3 100644
--- a/.kokoro/samples/python3.9/common.cfg
+++ b/.kokoro/samples/python3.9/common.cfg
@@ -37,4 +37,23 @@ 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-error-reporting/.kokoro/trampoline_v2.sh"
\ No newline at end of file
+build_file: "python-error-reporting/.kokoro/trampoline_v2.sh"
+
+#############################################
+# this section merged from .kokoro/common_env_vars.cfg using owlbot.py
+
+env_vars: {
+ key: "PRODUCT_AREA_LABEL"
+ value: "observability"
+}
+env_vars: {
+ key: "PRODUCT_LABEL"
+ value: "error-reporting"
+}
+env_vars: {
+ key: "LANGUAGE_LABEL"
+ value: "python"
+}
+
+###################################################
+
diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh
index ba3a707b..e9d8bd79 100755
--- a/.kokoro/test-samples-against-head.sh
+++ b/.kokoro/test-samples-against-head.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright 2020 Google LLC
+# 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.
diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh
index 2c6500ca..53e365bc 100755
--- a/.kokoro/test-samples-impl.sh
+++ b/.kokoro/test-samples-impl.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright 2021 Google LLC
+# 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.
@@ -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/test-samples.sh b/.kokoro/test-samples.sh
index 11c042d3..7933d820 100755
--- a/.kokoro/test-samples.sh
+++ b/.kokoro/test-samples.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright 2020 Google LLC
+# 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.
diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh
index f39236e9..48f79699 100755
--- a/.kokoro/trampoline.sh
+++ b/.kokoro/trampoline.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright 2017 Google Inc.
+# 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.
diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh
index 4af6cdc2..35fa5292 100755
--- a/.kokoro/trampoline_v2.sh
+++ b/.kokoro/trampoline_v2.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-# Copyright 2020 Google LLC
+# 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.
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 46d23716..1d74695f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,4 +1,4 @@
-# Copyright 2021 Google LLC
+# 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.
@@ -22,10 +22,10 @@ repos:
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/psf/black
- rev: 22.3.0
+ rev: 23.7.0
hooks:
- id: black
-- repo: https://gitlab.com/pycqa/flake8
- rev: 3.9.2
+- repo: https://github.com/pycqa/flake8
+ rev: 6.1.0
hooks:
- id: flake8
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
new file mode 100644
index 00000000..b3960c05
--- /dev/null
+++ b/.release-please-manifest.json
@@ -0,0 +1,3 @@
+{
+ ".": "1.12.0"
+}
diff --git a/.repo-metadata.json b/.repo-metadata.json
index ea61a2bb..e032093f 100644
--- a/.repo-metadata.json
+++ b/.repo-metadata.json
@@ -11,7 +11,7 @@
"distribution_name": "google-cloud-error-reporting",
"api_id": "clouderrorreporting.googleapis.com",
"requires_billing": false,
- "codeowner_team": "@googleapis/api-logging",
+ "codeowner_team": "@googleapis/api-logging @googleapis/api-logging-partners",
"default_version": "v1beta1",
"api_shortname": "clouderrorreporting",
"api_description": "counts, analyzes and aggregates the crashes in your running cloud services. A centralized error management interface displays the results with sorting and filtering capabilities. A dedicated view shows the error details: time chart, occurrences, affected user count, first and last seen dates and a cleaned exception stack trace. Opt-in to receive email and mobile alerts on new errors."
diff --git a/.trampolinerc b/.trampolinerc
index 0eee72ab..00801523 100644
--- a/.trampolinerc
+++ b/.trampolinerc
@@ -1,4 +1,4 @@
-# Copyright 2020 Google LLC
+# 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.
@@ -12,8 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Template for .trampolinerc
-
# Add required env vars here.
required_envvars+=(
)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30c79b90..a09f8129 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,165 @@
[1]: https://pypi.org/project/google-cloud-error-reporting/#history
+## [1.12.0](https://github.com/googleapis/python-error-reporting/compare/v1.11.1...v1.12.0) (2025-05-21)
+
+
+### Features
+
+* Add REST Interceptors which support reading metadata ([1d120c7](https://github.com/googleapis/python-error-reporting/commit/1d120c77c73b566796b32cab017a0d4cdfa28713))
+* Add support for opt-in debug logging ([1d120c7](https://github.com/googleapis/python-error-reporting/commit/1d120c77c73b566796b32cab017a0d4cdfa28713))
+
+
+### Bug Fixes
+
+* Allow Protobuf 6.x ([#557](https://github.com/googleapis/python-error-reporting/issues/557)) ([9f8faeb](https://github.com/googleapis/python-error-reporting/commit/9f8faeba223a0e1834c0750d21b5cafdee74d327))
+* Fix typing issue with gRPC metadata when key ends in -bin ([1d120c7](https://github.com/googleapis/python-error-reporting/commit/1d120c77c73b566796b32cab017a0d4cdfa28713))
+* Remove setup.cfg configuration for creating universal wheels ([#562](https://github.com/googleapis/python-error-reporting/issues/562)) ([0738c03](https://github.com/googleapis/python-error-reporting/commit/0738c03fa4321fd29c0915da2336bf77947367ae))
+* Require proto-plus >= 1.25.0 for Python 3.13 ([#567](https://github.com/googleapis/python-error-reporting/issues/567)) ([d5cd225](https://github.com/googleapis/python-error-reporting/commit/d5cd225fd71d8b54197c4f02c30d32eb8cd24dfa))
+
+## [1.11.1](https://github.com/googleapis/python-error-reporting/compare/v1.11.0...v1.11.1) (2024-09-17)
+
+
+### Bug Fixes
+
+* Allow Protobuf 5.x ([#507](https://github.com/googleapis/python-error-reporting/issues/507)) ([8d33168](https://github.com/googleapis/python-error-reporting/commit/8d3316866c0825911b26f17fd4f703cabbc3c396))
+* Retry and timeout values do not propagate in requests during pagination ([#518](https://github.com/googleapis/python-error-reporting/issues/518)) ([cbe41fd](https://github.com/googleapis/python-error-reporting/commit/cbe41fd1fa4edbb1c574cd2c07dc99d4415c3078))
+
+
+### Documentation
+
+* Add summary_overview template ([#496](https://github.com/googleapis/python-error-reporting/issues/496)) ([988def5](https://github.com/googleapis/python-error-reporting/commit/988def551df3f95577519450470a7cbf5f3b6b7d))
+* Removes references as a "global-only" service ([1fef616](https://github.com/googleapis/python-error-reporting/commit/1fef61635e3f9d7297f9fe28d18568cf6c2a0fc1))
+* Updates documentation with regional resource names for multiple requests ([1fef616](https://github.com/googleapis/python-error-reporting/commit/1fef61635e3f9d7297f9fe28d18568cf6c2a0fc1))
+
+## [1.11.0](https://github.com/googleapis/python-error-reporting/compare/v1.10.0...v1.11.0) (2024-03-26)
+
+
+### Features
+
+* Allow users to explicitly configure universe domain ([#475](https://github.com/googleapis/python-error-reporting/issues/475)) ([e4c3454](https://github.com/googleapis/python-error-reporting/commit/e4c3454bd5ba9f452479b0bc956c6ef011766d14))
+
+## [1.10.0](https://github.com/googleapis/python-error-reporting/compare/v1.9.2...v1.10.0) (2023-12-10)
+
+
+### Features
+
+* Add support for Python 3.12 ([#459](https://github.com/googleapis/python-error-reporting/issues/459)) ([36c1b59](https://github.com/googleapis/python-error-reporting/commit/36c1b598b35561e56815ce729884410134c3357d))
+* Use native namespaces instead of pkg_resources ([#463](https://github.com/googleapis/python-error-reporting/issues/463)) ([a63e3f2](https://github.com/googleapis/python-error-reporting/commit/a63e3f25ce5ef0cd0077838cdbb6ceff0f15ce31))
+
+
+### Bug Fixes
+
+* Use `retry_async` instead of `retry` in async client ([#462](https://github.com/googleapis/python-error-reporting/issues/462)) ([44c2b14](https://github.com/googleapis/python-error-reporting/commit/44c2b146aec92e272134ebaa6945fe78f98753bd))
+
+
+### Documentation
+
+* Minor formatting ([#448](https://github.com/googleapis/python-error-reporting/issues/448)) ([48823d4](https://github.com/googleapis/python-error-reporting/commit/48823d4529fc2a2ac7b6c3f745c3ea5cb0ec9d38))
+
+## [1.9.2](https://github.com/googleapis/python-error-reporting/compare/v1.9.1...v1.9.2) (2023-07-04)
+
+
+### Bug Fixes
+
+* Add async context manager return types ([#434](https://github.com/googleapis/python-error-reporting/issues/434)) ([319fa54](https://github.com/googleapis/python-error-reporting/commit/319fa54a19cb9aca3a079439fd7c6bd2c3163f71))
+
+## [1.9.1](https://github.com/googleapis/python-error-reporting/compare/v1.9.0...v1.9.1) (2023-03-23)
+
+
+### Documentation
+
+* Fix formatting of request arg in docstring ([#422](https://github.com/googleapis/python-error-reporting/issues/422)) ([561dc39](https://github.com/googleapis/python-error-reporting/commit/561dc395302f7989820db79f1c82aad04b7f6f7c))
+
+## [1.9.0](https://github.com/googleapis/python-error-reporting/compare/v1.8.2...v1.9.0) (2023-02-28)
+
+
+### Features
+
+* Enable "rest" transport in Python for services supporting numeric enums ([#418](https://github.com/googleapis/python-error-reporting/issues/418)) ([b2b9eab](https://github.com/googleapis/python-error-reporting/commit/b2b9eab649f413d7ad8a47fd660f9e2bcc32a820))
+
+## [1.8.2](https://github.com/googleapis/python-error-reporting/compare/v1.8.1...v1.8.2) (2023-02-04)
+
+
+### Documentation
+
+* Removed link to the regionalization page ([#411](https://github.com/googleapis/python-error-reporting/issues/411)) ([3a0d82d](https://github.com/googleapis/python-error-reporting/commit/3a0d82db6a425b91430be0ee84fd9c957f39af00))
+
+## [1.8.1](https://github.com/googleapis/python-error-reporting/compare/v1.8.0...v1.8.1) (2023-01-20)
+
+
+### Bug Fixes
+
+* Add context manager return types ([26a0749](https://github.com/googleapis/python-error-reporting/commit/26a074998b0c0a0697ed03086f7e1f6c4b77e35a))
+
+
+### Documentation
+
+* Add documentation for enums ([26a0749](https://github.com/googleapis/python-error-reporting/commit/26a074998b0c0a0697ed03086f7e1f6c4b77e35a))
+
+## [1.8.0](https://github.com/googleapis/python-error-reporting/compare/v1.7.0...v1.8.0) (2023-01-10)
+
+
+### Features
+
+* Add support for python 3.11 ([#405](https://github.com/googleapis/python-error-reporting/issues/405)) ([d5e0c4c](https://github.com/googleapis/python-error-reporting/commit/d5e0c4cf0cb40aab80146cad16141217b1998b30))
+
+## [1.7.0](https://github.com/googleapis/python-error-reporting/compare/v1.6.3...v1.7.0) (2022-12-15)
+
+
+### Features
+
+* Add typing to proto.Message based class attributes ([ccaa40f](https://github.com/googleapis/python-error-reporting/commit/ccaa40f1eca7d001cb58cd340189d521e93632ff))
+
+
+### Bug Fixes
+
+* Add dict typing for client_options ([ccaa40f](https://github.com/googleapis/python-error-reporting/commit/ccaa40f1eca7d001cb58cd340189d521e93632ff))
+* **deps:** Require google-api-core >=1.34.0, >=2.11.0 ([ccaa40f](https://github.com/googleapis/python-error-reporting/commit/ccaa40f1eca7d001cb58cd340189d521e93632ff))
+* Drop usage of pkg_resources ([ccaa40f](https://github.com/googleapis/python-error-reporting/commit/ccaa40f1eca7d001cb58cd340189d521e93632ff))
+* Fix timeout default values ([ccaa40f](https://github.com/googleapis/python-error-reporting/commit/ccaa40f1eca7d001cb58cd340189d521e93632ff))
+
+
+### Documentation
+
+* **samples:** Snippetgen handling of repeated enum field ([ccaa40f](https://github.com/googleapis/python-error-reporting/commit/ccaa40f1eca7d001cb58cd340189d521e93632ff))
+* **samples:** Snippetgen should call await on the operation coroutine before calling result ([ccaa40f](https://github.com/googleapis/python-error-reporting/commit/ccaa40f1eca7d001cb58cd340189d521e93632ff))
+
+## [1.6.3](https://github.com/googleapis/python-error-reporting/compare/v1.6.2...v1.6.3) (2022-10-07)
+
+
+### Bug Fixes
+
+* **deps:** Allow protobuf 3.19.5 ([#391](https://github.com/googleapis/python-error-reporting/issues/391)) ([6a42c05](https://github.com/googleapis/python-error-reporting/commit/6a42c056a7535f4c43d7698525e19df655c91092))
+
+## [1.6.2](https://github.com/googleapis/python-error-reporting/compare/v1.6.1...v1.6.2) (2022-10-03)
+
+
+### Bug Fixes
+
+* **deps:** Require protobuf >= 3.20.2 ([#388](https://github.com/googleapis/python-error-reporting/issues/388)) ([adde212](https://github.com/googleapis/python-error-reporting/commit/adde212c5c37ecbfac9a7ccda9e1fa027c670e52))
+
+## [1.6.1](https://github.com/googleapis/python-error-reporting/compare/v1.6.0...v1.6.1) (2022-08-12)
+
+
+### Bug Fixes
+
+* **deps:** allow protobuf < 5.0.0 ([#366](https://github.com/googleapis/python-error-reporting/issues/366)) ([9535a28](https://github.com/googleapis/python-error-reporting/commit/9535a289a458badf7406688d3e9a77f0e580d0a8))
+* **deps:** require proto-plus >= 1.22.0 ([9535a28](https://github.com/googleapis/python-error-reporting/commit/9535a289a458badf7406688d3e9a77f0e580d0a8))
+
+## [1.6.0](https://github.com/googleapis/python-error-reporting/compare/v1.5.3...v1.6.0) (2022-07-14)
+
+
+### Features
+
+* add audience parameter ([f53a2fa](https://github.com/googleapis/python-error-reporting/commit/f53a2fa49567035a1a3bb94d13444dd65bd104c6))
+
+
+### Bug Fixes
+
+* **deps:** require google-api-core>=1.32.0,>=2.8.0 ([#354](https://github.com/googleapis/python-error-reporting/issues/354)) ([f53a2fa](https://github.com/googleapis/python-error-reporting/commit/f53a2fa49567035a1a3bb94d13444dd65bd104c6))
+* require python 3.7+ ([#358](https://github.com/googleapis/python-error-reporting/issues/358)) ([ab0a9ba](https://github.com/googleapis/python-error-reporting/commit/ab0a9bacf9594ce3ff4c521413a20a2995533032))
+
## [1.5.3](https://github.com/googleapis/python-error-reporting/compare/v1.5.2...v1.5.3) (2022-06-07)
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 795ac991..5307292a 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 and 3.10 on both UNIX and Windows.
+ 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 and 3.13 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.10 -- -k
+ $ nox -s unit-3.13 -- -k
.. note::
@@ -225,11 +225,17 @@ We support:
- `Python 3.8`_
- `Python 3.9`_
- `Python 3.10`_
+- `Python 3.11`_
+- `Python 3.12`_
+- `Python 3.13`_
.. _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/
Supported versions can be found in our ``noxfile.py`` `config`_.
diff --git a/MANIFEST.in b/MANIFEST.in
index e783f4c6..d6814cd6 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright 2020 Google LLC
+# 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.
diff --git a/README.rst b/README.rst
index a258b7d9..a1b9e25e 100644
--- a/README.rst
+++ b/README.rst
@@ -15,7 +15,7 @@ Python Client for Error Reporting API
.. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-error-reporting.svg
:target: https://pypi.org/project/google-cloud-error-reporting/
.. _Error Reporting API: https://cloud.google.com/error-reporting
-.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/clouderrorreporting/latest
+.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/clouderrorreporting/latest/summary_overview
.. _Product Documentation: https://cloud.google.com/error-reporting
Quick Start
@@ -26,57 +26,63 @@ In order to use this library, you first need to go through the following steps:
1. `Select or create a Cloud Platform project.`_
2. `Enable billing for your project.`_
3. `Enable the Error Reporting API.`_
-4. `Setup Authentication.`_
+4. `Set up Authentication.`_
.. _Select or create a Cloud Platform project.: https://console.cloud.google.com/project
.. _Enable billing for your project.: https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_project
.. _Enable the Error Reporting API.: https://cloud.google.com/error-reporting
-.. _Setup Authentication.: https://googleapis.dev/python/google-api-core/latest/auth.html
+.. _Set up Authentication.: https://googleapis.dev/python/google-api-core/latest/auth.html
Installation
~~~~~~~~~~~~
-Install this library in a `virtualenv`_ using pip. `virtualenv`_ is a tool to
-create isolated Python environments. The basic problem it addresses is one of
-dependencies and versions, and indirectly permissions.
+Install this library in a virtual environment using `venv`_. `venv`_ is a tool that
+creates isolated Python environments. These isolated environments can have separate
+versions of Python packages, which allows you to isolate one project's dependencies
+from the dependencies of other projects.
-With `virtualenv`_, it's possible to install this library without needing system
+With `venv`_, it's possible to install this library without needing system
install permissions, and without clashing with the installed system
dependencies.
-.. _`virtualenv`: https://virtualenv.pypa.io/en/latest/
+.. _`venv`: https://docs.python.org/3/library/venv.html
Code samples and snippets
~~~~~~~~~~~~~~~~~~~~~~~~~
-Code samples and snippets live in the `samples/` folder.
+Code samples and snippets live in the `samples/`_ folder.
+
+.. _samples/: https://github.com/googleapis/python-error-reporting/tree/main/samples
Supported Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^
-Our client libraries are compatible with all current [active](https://devguide.python.org/devcycle/#in-development-main-branch) and [maintenance](https://devguide.python.org/devcycle/#maintenance-branches) versions of
+Our client libraries are compatible with all current `active`_ and `maintenance`_ versions of
Python.
Python >= 3.7
+.. _active: https://devguide.python.org/devcycle/#in-development-main-branch
+.. _maintenance: https://devguide.python.org/devcycle/#maintenance-branches
+
Unsupported Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Python <= 3.6
-If you are using an [end-of-life](https://devguide.python.org/devcycle/#end-of-life-branches)
+If you are using an `end-of-life`_
version of Python, we recommend that you update as soon as possible to an actively supported version.
+.. _end-of-life: https://devguide.python.org/devcycle/#end-of-life-branches
Mac/Linux
^^^^^^^^^
.. code-block:: console
- pip install virtualenv
- virtualenv
+ python3 -m venv
source /bin/activate
- /bin/pip install google-cloud-error-reporting
+ pip install google-cloud-error-reporting
Windows
@@ -84,10 +90,9 @@ Windows
.. code-block:: console
- pip install virtualenv
- virtualenv
- \Scripts\activate
- \Scripts\pip.exe install google-cloud-error-reporting
+ py -m venv
+ .\\Scripts\activate
+ pip install google-cloud-error-reporting
Next Steps
~~~~~~~~~~
@@ -101,3 +106,92 @@ Next Steps
.. _Error Reporting API Product documentation: https://cloud.google.com/error-reporting
.. _README: https://github.com/googleapis/google-cloud-python/blob/main/README.rst
+
+Logging
+-------
+
+This library uses the standard Python :code:`logging` functionality to log some RPC events that could be of interest for debugging and monitoring purposes.
+Note the following:
+
+#. Logs may contain sensitive information. Take care to **restrict access to the logs** if they are saved, whether it be on local storage or on Google Cloud Logging.
+#. Google may refine the occurrence, level, and content of various log messages in this library without flagging such changes as breaking. **Do not depend on immutability of the logging events**.
+#. By default, the logging events from this library are not handled. You must **explicitly configure log handling** using one of the mechanisms below.
+
+Simple, environment-based configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To enable logging for this library without any changes in your code, set the :code:`GOOGLE_SDK_PYTHON_LOGGING_SCOPE` environment variable to a valid Google
+logging scope. This configures handling of logging events (at level :code:`logging.DEBUG` or higher) from this library in a default manner, emitting the logged
+messages in a structured format. It does not currently allow customizing the logging levels captured nor the handlers, formatters, etc. used for any logging
+event.
+
+A logging scope is a period-separated namespace that begins with :code:`google`, identifying the Python module or package to log.
+
+- Valid logging scopes: :code:`google`, :code:`google.cloud.asset.v1`, :code:`google.api`, :code:`google.auth`, etc.
+- Invalid logging scopes: :code:`foo`, :code:`123`, etc.
+
+**NOTE**: If the logging scope is invalid, the library does not set up any logging handlers.
+
+Environment-Based Examples
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Enabling the default handler for all Google-based loggers
+
+.. code-block:: console
+
+ export GOOGLE_SDK_PYTHON_LOGGING_SCOPE=google
+
+- Enabling the default handler for a specific Google module (for a client library called :code:`library_v1`):
+
+.. code-block:: console
+
+ export GOOGLE_SDK_PYTHON_LOGGING_SCOPE=google.cloud.library_v1
+
+
+Advanced, code-based configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can also configure a valid logging scope using Python's standard `logging` mechanism.
+
+Code-Based Examples
+^^^^^^^^^^^^^^^^^^^
+
+- Configuring a handler for all Google-based loggers
+
+.. code-block:: python
+
+ import logging
+
+ from google.cloud import library_v1
+
+ base_logger = logging.getLogger("google")
+ base_logger.addHandler(logging.StreamHandler())
+ base_logger.setLevel(logging.DEBUG)
+
+- Configuring a handler for a specific Google module (for a client library called :code:`library_v1`):
+
+.. code-block:: python
+
+ import logging
+
+ from google.cloud import library_v1
+
+ base_logger = logging.getLogger("google.cloud.library_v1")
+ base_logger.addHandler(logging.StreamHandler())
+ base_logger.setLevel(logging.DEBUG)
+
+Logging details
+~~~~~~~~~~~~~~~
+
+#. Regardless of which of the mechanisms above you use to configure logging for this library, by default logging events are not propagated up to the root
+ logger from the `google`-level logger. If you need the events to be propagated to the root logger, you must explicitly set
+ :code:`logging.getLogger("google").propagate = True` in your code.
+#. You can mix the different logging configurations above for different Google modules. For example, you may want use a code-based logging configuration for
+ one library, but decide you need to also set up environment-based logging configuration for another library.
+
+ #. If you attempt to use both code-based and environment-based configuration for the same module, the environment-based configuration will be ineffectual
+ if the code -based configuration gets applied first.
+
+#. The Google-specific logging configurations (default handlers for environment-based configuration; not propagating logging events to the root logger) get
+ executed the first time *any* client library is instantiated in your application, and only if the affected loggers have not been previously configured.
+ (This is the reason for 2.i. above.)
diff --git a/docs/conf.py b/docs/conf.py
index c8f4857a..fd79abb7 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2021 Google LLC
+# 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.
diff --git a/docs/errorreporting_v1beta1/services.rst b/docs/errorreporting_v1beta1/services_.rst
similarity index 100%
rename from docs/errorreporting_v1beta1/services.rst
rename to docs/errorreporting_v1beta1/services_.rst
diff --git a/docs/errorreporting_v1beta1/types.rst b/docs/errorreporting_v1beta1/types_.rst
similarity index 90%
rename from docs/errorreporting_v1beta1/types.rst
rename to docs/errorreporting_v1beta1/types_.rst
index 179256c7..08851dbe 100644
--- a/docs/errorreporting_v1beta1/types.rst
+++ b/docs/errorreporting_v1beta1/types_.rst
@@ -3,5 +3,4 @@ Types for Google Cloud Errorreporting v1beta1 API
.. automodule:: google.cloud.errorreporting_v1beta1.types
:members:
- :undoc-members:
:show-inheritance:
diff --git a/docs/index.rst b/docs/index.rst
index 949b1c9d..a33c831a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -19,8 +19,8 @@ API Reference
client
util
- errorreporting_v1beta1/services
- errorreporting_v1beta1/types
+ errorreporting_v1beta1/services_
+ errorreporting_v1beta1/types_
Changelog
@@ -32,3 +32,8 @@ For a list of all ``google-cloud-error-reporting`` releases:
:maxdepth: 2
changelog
+
+.. toctree::
+ :hidden:
+
+ summary_overview.md
diff --git a/docs/summary_overview.md b/docs/summary_overview.md
new file mode 100644
index 00000000..dd16632d
--- /dev/null
+++ b/docs/summary_overview.md
@@ -0,0 +1,22 @@
+[
+This is a templated file. Adding content to this file may result in it being
+reverted. Instead, if you want to place additional content, create an
+"overview_content.md" file in `docs/` directory. The Sphinx tool will
+pick up on the content and merge the content.
+]: #
+
+# Error Reporting API
+
+Overview of the APIs available for Error Reporting API.
+
+## All entries
+
+Classes, methods and properties & attributes for
+Error Reporting API.
+
+[classes](https://cloud.google.com/python/docs/reference/clouderrorreporting/latest/summary_class.html)
+
+[methods](https://cloud.google.com/python/docs/reference/clouderrorreporting/latest/summary_method.html)
+
+[properties and
+attributes](https://cloud.google.com/python/docs/reference/clouderrorreporting/latest/summary_property.html)
diff --git a/google/cloud/error_reporting/__init__.py b/google/cloud/error_reporting/__init__.py
index 8cbe3963..1b729821 100644
--- a/google/cloud/error_reporting/__init__.py
+++ b/google/cloud/error_reporting/__init__.py
@@ -15,9 +15,9 @@
"""Client library for Error Reporting"""
-from pkg_resources import get_distribution
+from google.cloud.error_reporting import gapic_version as package_version
-__version__ = get_distribution("google-cloud-error-reporting").version
+__version__ = package_version.__version__
from google.cloud.error_reporting.client import Client
from google.cloud.error_reporting.client import HTTPContext
diff --git a/setup.cfg b/google/cloud/error_reporting/gapic_version.py
similarity index 78%
rename from setup.cfg
rename to google/cloud/error_reporting/gapic_version.py
index c3a2b39f..7138f214 100644
--- a/setup.cfg
+++ b/google/cloud/error_reporting/gapic_version.py
@@ -1,19 +1,16 @@
# -*- coding: utf-8 -*-
-#
-# Copyright 2020 Google LLC
+# 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
#
-# 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
+#
+__version__ = "1.12.0" # {x-release-please-version}
diff --git a/google/cloud/errorreporting_v1beta1/__init__.py b/google/cloud/errorreporting_v1beta1/__init__.py
index 04baaaa4..e6ff221d 100644
--- a/google/cloud/errorreporting_v1beta1/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/__init__.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,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
+
+__version__ = package_version.__version__
+
from .services.error_group_service import ErrorGroupServiceClient
from .services.error_group_service import ErrorGroupServiceAsyncClient
diff --git a/google/cloud/errorreporting_v1beta1/gapic_metadata.json b/google/cloud/errorreporting_v1beta1/gapic_metadata.json
index 1b6fc5e2..825275e6 100644
--- a/google/cloud/errorreporting_v1beta1/gapic_metadata.json
+++ b/google/cloud/errorreporting_v1beta1/gapic_metadata.json
@@ -36,6 +36,21 @@
]
}
}
+ },
+ "rest": {
+ "libraryClient": "ErrorGroupServiceClient",
+ "rpcs": {
+ "GetGroup": {
+ "methods": [
+ "get_group"
+ ]
+ },
+ "UpdateGroup": {
+ "methods": [
+ "update_group"
+ ]
+ }
+ }
}
}
},
@@ -80,6 +95,26 @@
]
}
}
+ },
+ "rest": {
+ "libraryClient": "ErrorStatsServiceClient",
+ "rpcs": {
+ "DeleteEvents": {
+ "methods": [
+ "delete_events"
+ ]
+ },
+ "ListEvents": {
+ "methods": [
+ "list_events"
+ ]
+ },
+ "ListGroupStats": {
+ "methods": [
+ "list_group_stats"
+ ]
+ }
+ }
}
}
},
@@ -104,6 +139,16 @@
]
}
}
+ },
+ "rest": {
+ "libraryClient": "ReportErrorsServiceClient",
+ "rpcs": {
+ "ReportErrorEvent": {
+ "methods": [
+ "report_error_event"
+ ]
+ }
+ }
}
}
}
diff --git a/samples/snippets/api/report_exception_test.py b/google/cloud/errorreporting_v1beta1/gapic_version.py
similarity index 71%
rename from samples/snippets/api/report_exception_test.py
rename to google/cloud/errorreporting_v1beta1/gapic_version.py
index 042951e9..7138f214 100644
--- a/samples/snippets/api/report_exception_test.py
+++ b/google/cloud/errorreporting_v1beta1/gapic_version.py
@@ -1,4 +1,5 @@
-# Copyright 2016 Google Inc. All rights reserved.
+# -*- 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.
@@ -11,13 +12,5 @@
# 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 report_exception
-
-
-def test_error_sends():
- report_exception.simulate_error()
-
-
-def test_manual_error_sends():
- report_exception.report_manual_error()
+#
+__version__ = "1.12.0" # {x-release-please-version}
diff --git a/google/cloud/errorreporting_v1beta1/py.typed b/google/cloud/errorreporting_v1beta1/py.typed
index 20bf6ac6..01870137 100644
--- a/google/cloud/errorreporting_v1beta1/py.typed
+++ b/google/cloud/errorreporting_v1beta1/py.typed
@@ -1,2 +1,2 @@
# Marker file for PEP 561.
-# The google-cloud-errorreporting package uses inline types.
+# The google-cloud-error-reporting package uses inline types.
diff --git a/google/cloud/errorreporting_v1beta1/services/__init__.py b/google/cloud/errorreporting_v1beta1/services/__init__.py
index e8e1c384..cbf94b28 100644
--- a/google/cloud/errorreporting_v1beta1/services/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/services/__init__.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.
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/__init__.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/__init__.py
index 62563e79..1e8d42ee 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_group_service/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/__init__.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.
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/async_client.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/async_client.py
index 0a03d9a3..3e9cf737 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_group_service/async_client.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/async_client.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,23 +13,37 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging as std_logging
from collections import OrderedDict
-import functools
import re
-from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union
-import pkg_resources
+from typing import (
+ Dict,
+ Callable,
+ Mapping,
+ MutableMapping,
+ MutableSequence,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+)
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
from google.api_core.client_options import ClientOptions
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
-from google.api_core import retry as retries
+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:
- OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault]
+ OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
- OptionalRetry = Union[retries.Retry, object] # type: ignore
+ OptionalRetry = Union[retries.AsyncRetry, object, None] # type: ignore
from google.cloud.errorreporting_v1beta1.types import common
from google.cloud.errorreporting_v1beta1.types import error_group_service
@@ -37,14 +51,27 @@
from .transports.grpc_asyncio import ErrorGroupServiceGrpcAsyncIOTransport
from .client import ErrorGroupServiceClient
+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 ErrorGroupServiceAsyncClient:
"""Service for retrieving and updating individual error groups."""
_client: ErrorGroupServiceClient
+ # Copy defaults from the synchronous client for use here.
+ # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
DEFAULT_ENDPOINT = ErrorGroupServiceClient.DEFAULT_ENDPOINT
DEFAULT_MTLS_ENDPOINT = ErrorGroupServiceClient.DEFAULT_MTLS_ENDPOINT
+ _DEFAULT_ENDPOINT_TEMPLATE = ErrorGroupServiceClient._DEFAULT_ENDPOINT_TEMPLATE
+ _DEFAULT_UNIVERSE = ErrorGroupServiceClient._DEFAULT_UNIVERSE
error_group_path = staticmethod(ErrorGroupServiceClient.error_group_path)
parse_error_group_path = staticmethod(
@@ -124,7 +151,7 @@ def get_mtls_endpoint_and_cert_source(
The API endpoint is determined in the following order:
(1) if `client_options.api_endpoint` if provided, use the provided one.
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
- default mTLS endpoint; if the environment variabel is "never", use the default API
+ default mTLS endpoint; if the environment variable is "never", use the default API
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
use the default API endpoint.
@@ -153,19 +180,42 @@ def transport(self) -> ErrorGroupServiceTransport:
"""
return self._client.transport
- get_transport_class = functools.partial(
- type(ErrorGroupServiceClient).get_transport_class, type(ErrorGroupServiceClient)
- )
+ @property
+ def api_endpoint(self):
+ """Return the API endpoint used by the client instance.
+
+ Returns:
+ str: The API endpoint used by the client instance.
+ """
+ return self._client._api_endpoint
+
+ @property
+ def universe_domain(self) -> str:
+ """Return the universe domain used by the client instance.
+
+ Returns:
+ str: The universe domain used
+ by the client instance.
+ """
+ return self._client._universe_domain
+
+ get_transport_class = ErrorGroupServiceClient.get_transport_class
def __init__(
self,
*,
- credentials: ga_credentials.Credentials = None,
- transport: Union[str, ErrorGroupServiceTransport] = "grpc_asyncio",
- client_options: ClientOptions = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ transport: Optional[
+ Union[
+ str,
+ ErrorGroupServiceTransport,
+ Callable[..., ErrorGroupServiceTransport],
+ ]
+ ] = "grpc_asyncio",
+ client_options: Optional[ClientOptions] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
- """Instantiates the error group service client.
+ """Instantiates the error group service async client.
Args:
credentials (Optional[google.auth.credentials.Credentials]): The
@@ -173,26 +223,43 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ~.ErrorGroupServiceTransport]): The
- transport to use. If set to None, a transport is chosen
- automatically.
- client_options (ClientOptions): Custom options for the client. It
- won't take effect if a ``transport`` instance is provided.
- (1) The ``api_endpoint`` property can be used to override the
- default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
- environment variable can also be used to override the endpoint:
+ transport (Optional[Union[str,ErrorGroupServiceTransport,Callable[..., ErrorGroupServiceTransport]]]):
+ The transport to use, or a Callable that constructs and returns a new transport to use.
+ If a Callable is given, it will be called with the same set of initialization
+ arguments as used in the ErrorGroupServiceTransport constructor.
+ If set to None, a transport is chosen automatically.
+ client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]):
+ Custom options for the client.
+
+ 1. The ``api_endpoint`` property can be used to override the
+ default endpoint provided by the client when ``transport`` is
+ not explicitly provided. Only if this property is not set and
+ ``transport`` was not explicitly provided, the endpoint is
+ determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment
+ variable, which have one of the following values:
"always" (always use the default mTLS endpoint), "never" (always
- use the default regular endpoint) and "auto" (auto switch to the
- default mTLS endpoint if client certificate is present, this is
- the default value). However, the ``api_endpoint`` property takes
- precedence if provided.
- (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
+ use the default regular endpoint) and "auto" (auto-switch to the
+ default mTLS endpoint if client certificate is present; this is
+ the default value).
+
+ 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
is "true", then the ``client_cert_source`` property can be used
- to provide client certificate for mutual TLS transport. If
+ to provide a client certificate for mTLS transport. If
not provided, the default SSL client certificate will be used if
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
set, no client certificate will be used.
+ 3. The ``universe_domain`` property can be used to override the
+ default "googleapis.com" universe. Note that ``api_endpoint``
+ property still takes precedence; and ``universe_domain`` is
+ currently not supported for mTLS.
+
+ 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're developing
+ your own client library.
+
Raises:
google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
creation failed for any reason.
@@ -204,19 +271,48 @@ def __init__(
client_info=client_info,
)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.devtools.clouderrorreporting_v1beta1.ErrorGroupServiceAsyncClient`.",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "credentialsType": None,
+ },
+ )
+
async def get_group(
self,
- request: Union[error_group_service.GetGroupRequest, dict] = None,
+ request: Optional[Union[error_group_service.GetGroupRequest, dict]] = None,
*,
- group_name: str = None,
+ group_name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> common.ErrorGroup:
r"""Get the specified group.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
async def sample_get_group():
@@ -235,25 +331,41 @@ async def sample_get_group():
print(response)
Args:
- request (Union[google.cloud.errorreporting_v1beta1.types.GetGroupRequest, dict]):
+ request (Optional[Union[google.cloud.errorreporting_v1beta1.types.GetGroupRequest, dict]]):
The request object. A request to return an individual
group.
group_name (:class:`str`):
- Required. The group resource name. Written as
- ``projects/{projectID}/groups/{group_name}``. Call
- ```groupStats.list`` `__
+ Required. The group resource name. Written as either
+ ``projects/{projectID}/groups/{group_id}`` or
+ ``projects/{projectID}/locations/{location}/groups/{group_id}``.
+ Call [groupStats.list]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorStatsService.ListGroupStats]
to return a list of groups belonging to this project.
- Example: ``projects/my-project-123/groups/my-group``
+ Examples: ``projects/my-project-123/groups/my-group``,
+ ``projects/my-project-123/locations/global/groups/my-group``
+
+ In the group resource name, the ``group_id`` is a unique
+ identifier for a particular error group. The identifier
+ is derived from key parts of the error-log content and
+ is treated as Service Data. For information about how
+ Service Data is handled, see `Google Cloud Privacy
+ Notice `__.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
This corresponds to the ``group_name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ 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.cloud.errorreporting_v1beta1.types.ErrorGroup:
@@ -262,16 +374,22 @@ async def sample_get_group():
"""
# 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([group_name])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [group_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 "
"the individual field arguments should be set."
)
- request = error_group_service.GetGroupRequest(request)
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
+ if not isinstance(request, error_group_service.GetGroupRequest):
+ request = error_group_service.GetGroupRequest(request)
# If we have keyword arguments corresponding to fields on the
# request, apply these.
@@ -280,11 +398,9 @@ async def sample_get_group():
# 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_group,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._client._transport._wrapped_methods[
+ self._client._transport.get_group
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -294,6 +410,9 @@ async def sample_get_group():
),
)
+ # Validate the universe domain.
+ self._client._validate_universe_domain()
+
# Send the request.
response = await rpc(
request,
@@ -307,18 +426,25 @@ async def sample_get_group():
async def update_group(
self,
- request: Union[error_group_service.UpdateGroupRequest, dict] = None,
+ request: Optional[Union[error_group_service.UpdateGroupRequest, dict]] = None,
*,
- group: common.ErrorGroup = None,
+ group: Optional[common.ErrorGroup] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> common.ErrorGroup:
r"""Replace the data for the specified group.
Fails if the group does not exist.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
async def sample_update_group():
@@ -336,7 +462,7 @@ async def sample_update_group():
print(response)
Args:
- request (Union[google.cloud.errorreporting_v1beta1.types.UpdateGroupRequest, dict]):
+ request (Optional[Union[google.cloud.errorreporting_v1beta1.types.UpdateGroupRequest, dict]]):
The request object. A request to replace the existing
data for the given group.
group (:class:`google.cloud.errorreporting_v1beta1.types.ErrorGroup`):
@@ -346,11 +472,13 @@ async def sample_update_group():
This corresponds to the ``group`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ 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.cloud.errorreporting_v1beta1.types.ErrorGroup:
@@ -359,16 +487,22 @@ async def sample_update_group():
"""
# 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([group])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [group]
+ 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 "
"the individual field arguments should be set."
)
- request = error_group_service.UpdateGroupRequest(request)
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
+ if not isinstance(request, error_group_service.UpdateGroupRequest):
+ request = error_group_service.UpdateGroupRequest(request)
# If we have keyword arguments corresponding to fields on the
# request, apply these.
@@ -377,11 +511,9 @@ async def sample_update_group():
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.update_group,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._client._transport._wrapped_methods[
+ self._client._transport.update_group
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -391,6 +523,9 @@ async def sample_update_group():
),
)
+ # Validate the universe domain.
+ self._client._validate_universe_domain()
+
# Send the request.
response = await rpc(
request,
@@ -402,21 +537,19 @@ async def sample_update_group():
# Done; return the response.
return response
- async def __aenter__(self):
+ async def __aenter__(self) -> "ErrorGroupServiceAsyncClient":
return self
async def __aexit__(self, exc_type, exc, tb):
await self.transport.close()
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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__ = ("ErrorGroupServiceAsyncClient",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/client.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/client.py
index eec0a0ae..cc4b15df 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_group_service/client.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/client.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.
@@ -14,10 +14,27 @@
# limitations under the License.
#
from collections import OrderedDict
+from http import HTTPStatus
+import json
+import logging as std_logging
import os
import re
-from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union
-import pkg_resources
+from typing import (
+ Dict,
+ Callable,
+ Mapping,
+ MutableMapping,
+ MutableSequence,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+ cast,
+)
+import warnings
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
from google.api_core import client_options as client_options_lib
from google.api_core import exceptions as core_exceptions
@@ -28,17 +45,28 @@
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]
+ OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
- OptionalRetry = Union[retries.Retry, object] # type: ignore
+ 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.cloud.errorreporting_v1beta1.types import common
from google.cloud.errorreporting_v1beta1.types import error_group_service
from .transports.base import ErrorGroupServiceTransport, DEFAULT_CLIENT_INFO
from .transports.grpc import ErrorGroupServiceGrpcTransport
from .transports.grpc_asyncio import ErrorGroupServiceGrpcAsyncIOTransport
+from .transports.rest import ErrorGroupServiceRestTransport
class ErrorGroupServiceClientMeta(type):
@@ -54,10 +82,11 @@ class ErrorGroupServiceClientMeta(type):
) # type: Dict[str, Type[ErrorGroupServiceTransport]]
_transport_registry["grpc"] = ErrorGroupServiceGrpcTransport
_transport_registry["grpc_asyncio"] = ErrorGroupServiceGrpcAsyncIOTransport
+ _transport_registry["rest"] = ErrorGroupServiceRestTransport
def get_transport_class(
cls,
- label: str = None,
+ label: Optional[str] = None,
) -> Type[ErrorGroupServiceTransport]:
"""Returns an appropriate transport class.
@@ -110,11 +139,15 @@ def _get_default_mtls_endpoint(api_endpoint):
return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
+ # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
DEFAULT_ENDPOINT = "clouderrorreporting.googleapis.com"
DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore
DEFAULT_ENDPOINT
)
+ _DEFAULT_ENDPOINT_TEMPLATE = "clouderrorreporting.{UNIVERSE_DOMAIN}"
+ _DEFAULT_UNIVERSE = "googleapis.com"
+
@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -260,7 +293,7 @@ def parse_common_location_path(path: str) -> Dict[str, str]:
def get_mtls_endpoint_and_cert_source(
cls, client_options: Optional[client_options_lib.ClientOptions] = None
):
- """Return the API endpoint and client cert source for mutual TLS.
+ """Deprecated. Return the API endpoint and client cert source for mutual TLS.
The client cert source is determined in the following order:
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
@@ -272,7 +305,7 @@ def get_mtls_endpoint_and_cert_source(
The API endpoint is determined in the following order:
(1) if `client_options.api_endpoint` if provided, use the provided one.
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
- default mTLS endpoint; if the environment variabel is "never", use the default API
+ default mTLS endpoint; if the environment variable is "never", use the default API
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
use the default API endpoint.
@@ -290,6 +323,11 @@ def get_mtls_endpoint_and_cert_source(
Raises:
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
"""
+
+ warnings.warn(
+ "get_mtls_endpoint_and_cert_source is deprecated. Use the api_endpoint property instead.",
+ DeprecationWarning,
+ )
if client_options is None:
client_options = client_options_lib.ClientOptions()
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
@@ -323,12 +361,183 @@ def get_mtls_endpoint_and_cert_source(
return api_endpoint, client_cert_source
+ @staticmethod
+ def _read_environment_variables():
+ """Returns the environment variables used by the client.
+
+ Returns:
+ Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE,
+ GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables.
+
+ Raises:
+ ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not
+ any of ["true", "false"].
+ 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_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
+
+ @staticmethod
+ def _get_client_cert_source(provided_cert_source, use_cert_flag):
+ """Return the client cert source to be used by the client.
+
+ Args:
+ provided_cert_source (bytes): The client certificate source provided.
+ use_cert_flag (bool): A flag indicating whether to use the client certificate.
+
+ Returns:
+ bytes or None: The client cert source to be used by the client.
+ """
+ client_cert_source = None
+ if use_cert_flag:
+ if provided_cert_source:
+ client_cert_source = provided_cert_source
+ elif mtls.has_default_client_cert_source():
+ client_cert_source = mtls.default_client_cert_source()
+ return client_cert_source
+
+ @staticmethod
+ def _get_api_endpoint(
+ api_override, client_cert_source, universe_domain, use_mtls_endpoint
+ ):
+ """Return the API endpoint used by the client.
+
+ Args:
+ api_override (str): The API endpoint override. If specified, this is always
+ the return value of this function and the other arguments are not used.
+ client_cert_source (bytes): The client certificate source used by the client.
+ universe_domain (str): The universe domain used by the client.
+ use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters.
+ Possible values are "always", "auto", or "never".
+
+ Returns:
+ str: The API endpoint to be used by the client.
+ """
+ if api_override is not None:
+ api_endpoint = api_override
+ elif use_mtls_endpoint == "always" or (
+ use_mtls_endpoint == "auto" and client_cert_source
+ ):
+ _default_universe = ErrorGroupServiceClient._DEFAULT_UNIVERSE
+ if universe_domain != _default_universe:
+ raise MutualTLSChannelError(
+ f"mTLS is not supported in any universe other than {_default_universe}."
+ )
+ api_endpoint = ErrorGroupServiceClient.DEFAULT_MTLS_ENDPOINT
+ else:
+ api_endpoint = ErrorGroupServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=universe_domain
+ )
+ return api_endpoint
+
+ @staticmethod
+ def _get_universe_domain(
+ client_universe_domain: Optional[str], universe_domain_env: Optional[str]
+ ) -> str:
+ """Return the universe domain used by the client.
+
+ Args:
+ client_universe_domain (Optional[str]): The universe domain configured via the client options.
+ universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable.
+
+ Returns:
+ str: The universe domain to be used by the client.
+
+ Raises:
+ ValueError: If the universe domain is an empty string.
+ """
+ universe_domain = ErrorGroupServiceClient._DEFAULT_UNIVERSE
+ if client_universe_domain is not None:
+ universe_domain = client_universe_domain
+ elif universe_domain_env is not None:
+ universe_domain = universe_domain_env
+ if len(universe_domain.strip()) == 0:
+ raise ValueError("Universe Domain cannot be an empty string.")
+ return universe_domain
+
+ def _validate_universe_domain(self):
+ """Validates client's and credentials' universe domains are consistent.
+
+ Returns:
+ bool: True iff the configured universe domain is valid.
+
+ Raises:
+ ValueError: If the configured universe domain is not valid.
+ """
+
+ # NOTE (b/349488459): universe validation is disabled until further notice.
+ return True
+
+ 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.
+
+ Args:
+ error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info.
+ """
+ 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):
+ """Return the API endpoint used by the client instance.
+
+ Returns:
+ str: The API endpoint used by the client instance.
+ """
+ return self._api_endpoint
+
+ @property
+ def universe_domain(self) -> str:
+ """Return the universe domain used by the client instance.
+
+ Returns:
+ str: The universe domain used by the client instance.
+ """
+ return self._universe_domain
+
def __init__(
self,
*,
credentials: Optional[ga_credentials.Credentials] = None,
- transport: Union[str, ErrorGroupServiceTransport, None] = None,
- client_options: Optional[client_options_lib.ClientOptions] = None,
+ transport: Optional[
+ Union[
+ str,
+ ErrorGroupServiceTransport,
+ Callable[..., ErrorGroupServiceTransport],
+ ]
+ ] = None,
+ client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
"""Instantiates the error group service client.
@@ -339,25 +548,37 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ErrorGroupServiceTransport]): The
- transport to use. If set to None, a transport is chosen
- automatically.
- client_options (google.api_core.client_options.ClientOptions): Custom options for the
- client. It won't take effect if a ``transport`` instance is provided.
- (1) The ``api_endpoint`` property can be used to override the
- default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
- environment variable can also be used to override the endpoint:
+ transport (Optional[Union[str,ErrorGroupServiceTransport,Callable[..., ErrorGroupServiceTransport]]]):
+ The transport to use, or a Callable that constructs and returns a new transport.
+ If a Callable is given, it will be called with the same set of initialization
+ arguments as used in the ErrorGroupServiceTransport constructor.
+ If set to None, a transport is chosen automatically.
+ client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]):
+ Custom options for the client.
+
+ 1. The ``api_endpoint`` property can be used to override the
+ default endpoint provided by the client when ``transport`` is
+ not explicitly provided. Only if this property is not set and
+ ``transport`` was not explicitly provided, the endpoint is
+ determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment
+ variable, which have one of the following values:
"always" (always use the default mTLS endpoint), "never" (always
- use the default regular endpoint) and "auto" (auto switch to the
- default mTLS endpoint if client certificate is present, this is
- the default value). However, the ``api_endpoint`` property takes
- precedence if provided.
- (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
+ use the default regular endpoint) and "auto" (auto-switch to the
+ default mTLS endpoint if client certificate is present; this is
+ the default value).
+
+ 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
is "true", then the ``client_cert_source`` property can be used
- to provide client certificate for mutual TLS transport. If
+ to provide a client certificate for mTLS transport. If
not provided, the default SSL client certificate will be used if
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
set, no client certificate will be used.
+
+ 3. The ``universe_domain`` property can be used to override the
+ default "googleapis.com" universe. Note that the ``api_endpoint``
+ property still takes precedence; and ``universe_domain`` is
+ currently not supported for mTLS.
+
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.
@@ -368,16 +589,38 @@ def __init__(
google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport
creation failed for any reason.
"""
- if isinstance(client_options, dict):
- client_options = client_options_lib.from_dict(client_options)
- if client_options is None:
- client_options = client_options_lib.ClientOptions()
+ self._client_options = client_options
+ if isinstance(self._client_options, dict):
+ self._client_options = client_options_lib.from_dict(self._client_options)
+ if self._client_options is None:
+ self._client_options = client_options_lib.ClientOptions()
+ self._client_options = cast(
+ client_options_lib.ClientOptions, self._client_options
+ )
- api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
- client_options
+ universe_domain_opt = getattr(self._client_options, "universe_domain", None)
+
+ (
+ self._use_client_cert,
+ self._use_mtls_endpoint,
+ self._universe_domain_env,
+ ) = ErrorGroupServiceClient._read_environment_variables()
+ self._client_cert_source = ErrorGroupServiceClient._get_client_cert_source(
+ self._client_options.client_cert_source, self._use_client_cert
+ )
+ self._universe_domain = ErrorGroupServiceClient._get_universe_domain(
+ universe_domain_opt, self._universe_domain_env
)
+ self._api_endpoint = None # updated below, depending on `transport`
+
+ # Initialize the universe domain validation.
+ self._is_universe_domain_valid = False
- api_key_value = getattr(client_options, "api_key", None)
+ 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(
"client_options.api_key and credentials are mutually exclusive"
@@ -386,20 +629,33 @@ def __init__(
# Save or instantiate the transport.
# Ordinarily, we provide the transport, but allowing a custom transport
# instance provides an extensibility point for unusual situations.
- if isinstance(transport, ErrorGroupServiceTransport):
+ transport_provided = isinstance(transport, ErrorGroupServiceTransport)
+ if transport_provided:
# transport is a ErrorGroupServiceTransport instance.
- if credentials or client_options.credentials_file or api_key_value:
+ if credentials or self._client_options.credentials_file or api_key_value:
raise ValueError(
"When providing a transport instance, "
"provide its credentials directly."
)
- if client_options.scopes:
+ if self._client_options.scopes:
raise ValueError(
"When providing a transport instance, provide its scopes "
"directly."
)
- self._transport = transport
- else:
+ self._transport = cast(ErrorGroupServiceTransport, transport)
+ self._api_endpoint = self._transport.host
+
+ self._api_endpoint = (
+ self._api_endpoint
+ or ErrorGroupServiceClient._get_api_endpoint(
+ self._client_options.api_endpoint,
+ self._client_cert_source,
+ self._universe_domain,
+ self._use_mtls_endpoint,
+ )
+ )
+
+ if not transport_provided:
import google.auth._default # type: ignore
if api_key_value and hasattr(
@@ -409,31 +665,70 @@ def __init__(
api_key_value
)
- Transport = type(self).get_transport_class(transport)
- self._transport = Transport(
+ transport_init: Union[
+ Type[ErrorGroupServiceTransport],
+ Callable[..., ErrorGroupServiceTransport],
+ ] = (
+ ErrorGroupServiceClient.get_transport_class(transport)
+ if isinstance(transport, str) or transport is None
+ else cast(Callable[..., ErrorGroupServiceTransport], transport)
+ )
+ # initialize with the provided callable or the passed in class
+ self._transport = transport_init(
credentials=credentials,
- credentials_file=client_options.credentials_file,
- host=api_endpoint,
- scopes=client_options.scopes,
- client_cert_source_for_mtls=client_cert_source_func,
- quota_project_id=client_options.quota_project_id,
+ credentials_file=self._client_options.credentials_file,
+ host=self._api_endpoint,
+ scopes=self._client_options.scopes,
+ client_cert_source_for_mtls=self._client_cert_source,
+ quota_project_id=self._client_options.quota_project_id,
client_info=client_info,
always_use_jwt_access=True,
+ 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.devtools.clouderrorreporting_v1beta1.ErrorGroupServiceClient`.",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "credentialsType": None,
+ },
+ )
+
def get_group(
self,
- request: Union[error_group_service.GetGroupRequest, dict] = None,
+ request: Optional[Union[error_group_service.GetGroupRequest, dict]] = None,
*,
- group_name: str = None,
+ group_name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> common.ErrorGroup:
r"""Get the specified group.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
def sample_get_group():
@@ -456,12 +751,26 @@ def sample_get_group():
The request object. A request to return an individual
group.
group_name (str):
- Required. The group resource name. Written as
- ``projects/{projectID}/groups/{group_name}``. Call
- ```groupStats.list`` `__
+ Required. The group resource name. Written as either
+ ``projects/{projectID}/groups/{group_id}`` or
+ ``projects/{projectID}/locations/{location}/groups/{group_id}``.
+ Call [groupStats.list]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorStatsService.ListGroupStats]
to return a list of groups belonging to this project.
- Example: ``projects/my-project-123/groups/my-group``
+ Examples: ``projects/my-project-123/groups/my-group``,
+ ``projects/my-project-123/locations/global/groups/my-group``
+
+ In the group resource name, the ``group_id`` is a unique
+ identifier for a particular error group. The identifier
+ is derived from key parts of the error-log content and
+ is treated as Service Data. For information about how
+ Service Data is handled, see `Google Cloud Privacy
+ Notice `__.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
This corresponds to the ``group_name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -469,8 +778,10 @@ def sample_get_group():
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.cloud.errorreporting_v1beta1.types.ErrorGroup:
@@ -479,19 +790,20 @@ def sample_get_group():
"""
# 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([group_name])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [group_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 "
"the individual field arguments should be set."
)
- # Minor optimization to avoid making a copy if the user passes
- # in a error_group_service.GetGroupRequest.
- # There's no risk of modifying the input as we've already verified
- # there are no flattened fields.
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
if not isinstance(request, error_group_service.GetGroupRequest):
request = error_group_service.GetGroupRequest(request)
# If we have keyword arguments corresponding to fields on the
@@ -511,6 +823,9 @@ def sample_get_group():
),
)
+ # Validate the universe domain.
+ self._validate_universe_domain()
+
# Send the request.
response = rpc(
request,
@@ -524,18 +839,25 @@ def sample_get_group():
def update_group(
self,
- request: Union[error_group_service.UpdateGroupRequest, dict] = None,
+ request: Optional[Union[error_group_service.UpdateGroupRequest, dict]] = None,
*,
- group: common.ErrorGroup = None,
+ group: Optional[common.ErrorGroup] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> common.ErrorGroup:
r"""Replace the data for the specified group.
Fails if the group does not exist.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
def sample_update_group():
@@ -566,8 +888,10 @@ def sample_update_group():
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.cloud.errorreporting_v1beta1.types.ErrorGroup:
@@ -576,19 +900,20 @@ def sample_update_group():
"""
# 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([group])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [group]
+ 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 "
"the individual field arguments should be set."
)
- # Minor optimization to avoid making a copy if the user passes
- # in a error_group_service.UpdateGroupRequest.
- # There's no risk of modifying the input as we've already verified
- # there are no flattened fields.
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
if not isinstance(request, error_group_service.UpdateGroupRequest):
request = error_group_service.UpdateGroupRequest(request)
# If we have keyword arguments corresponding to fields on the
@@ -608,6 +933,9 @@ def sample_update_group():
),
)
+ # Validate the universe domain.
+ self._validate_universe_domain()
+
# Send the request.
response = rpc(
request,
@@ -619,7 +947,7 @@ def sample_update_group():
# Done; return the response.
return response
- def __enter__(self):
+ def __enter__(self) -> "ErrorGroupServiceClient":
return self
def __exit__(self, type, value, traceback):
@@ -633,14 +961,11 @@ def __exit__(self, type, value, traceback):
self.transport.close()
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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__ = ("ErrorGroupServiceClient",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/README.rst b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/README.rst
new file mode 100644
index 00000000..a0b01808
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/README.rst
@@ -0,0 +1,9 @@
+
+transport inheritance structure
+_______________________________
+
+`ErrorGroupServiceTransport` is the ABC for all transports.
+- public child `ErrorGroupServiceGrpcTransport` for sync gRPC transport (defined in `grpc.py`).
+- public child `ErrorGroupServiceGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`).
+- private child `_BaseErrorGroupServiceRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`).
+- public child `ErrorGroupServiceRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`).
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/__init__.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/__init__.py
index 873035d9..f02ba22b 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/__init__.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.
@@ -19,15 +19,20 @@
from .base import ErrorGroupServiceTransport
from .grpc import ErrorGroupServiceGrpcTransport
from .grpc_asyncio import ErrorGroupServiceGrpcAsyncIOTransport
+from .rest import ErrorGroupServiceRestTransport
+from .rest import ErrorGroupServiceRestInterceptor
# Compile a registry of transports.
_transport_registry = OrderedDict() # type: Dict[str, Type[ErrorGroupServiceTransport]]
_transport_registry["grpc"] = ErrorGroupServiceGrpcTransport
_transport_registry["grpc_asyncio"] = ErrorGroupServiceGrpcAsyncIOTransport
+_transport_registry["rest"] = ErrorGroupServiceRestTransport
__all__ = (
"ErrorGroupServiceTransport",
"ErrorGroupServiceGrpcTransport",
"ErrorGroupServiceGrpcAsyncIOTransport",
+ "ErrorGroupServiceRestTransport",
+ "ErrorGroupServiceRestInterceptor",
)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/base.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/base.py
index d7f0edd8..9938eaf4 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/base.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/base.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.
@@ -15,7 +15,8 @@
#
import abc
from typing import Awaitable, Callable, Dict, Optional, Sequence, Union
-import pkg_resources
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
import google.auth # type: ignore
import google.api_core
@@ -24,18 +25,17 @@
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.cloud.errorreporting_v1beta1.types import common
from google.cloud.errorreporting_v1beta1.types import error_group_service
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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 ErrorGroupServiceTransport(abc.ABC):
@@ -49,19 +49,20 @@ def __init__(
self,
*,
host: str = DEFAULT_HOST,
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
**kwargs,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
@@ -82,15 +83,12 @@ def __init__(
be used for service account credentials.
"""
- # Save the hostname. Default to port 443 (HTTPS) if none is specified.
- if ":" not in host:
- host += ":443"
- self._host = host
-
scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}
# Save the scopes.
self._scopes = scopes
+ if not hasattr(self, "_ignore_credentials"):
+ self._ignore_credentials: bool = False
# If no credentials are provided, then determine the appropriate
# defaults.
@@ -103,10 +101,15 @@ def __init__(
credentials, _ = google.auth.load_credentials_from_file(
credentials_file, **scopes_kwargs, quota_project_id=quota_project_id
)
- elif credentials is None:
+ elif credentials is None and not self._ignore_credentials:
credentials, _ = google.auth.default(
**scopes_kwargs, quota_project_id=quota_project_id
)
+ # Don't apply audience if the credentials file passed from user.
+ if hasattr(credentials, "with_gdch_audience"):
+ credentials = credentials.with_gdch_audience(
+ api_audience if api_audience else host
+ )
# If the credentials are service account credentials, then always try to use self signed JWT.
if (
@@ -119,6 +122,15 @@ def __init__(
# Save the credentials.
self._credentials = credentials
+ # Save the hostname. Default to port 443 (HTTPS) if none is specified.
+ if ":" not in host:
+ host += ":443"
+ self._host = host
+
+ @property
+ def host(self):
+ return self._host
+
def _prep_wrapped_messages(self, client_info):
# Precompute the wrapped methods.
self._wrapped_methods = {
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc.py
index c90e37f7..8bfab8f0 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc.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,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,13 +24,90 @@
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.cloud.errorreporting_v1beta1.types import common
from google.cloud.errorreporting_v1beta1.types import error_group_service
from .base import ErrorGroupServiceTransport, 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.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "rpcName": client_call_details.method,
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class ErrorGroupServiceGrpcTransport(ErrorGroupServiceTransport):
"""gRPC backend transport for ErrorGroupService.
@@ -48,36 +128,40 @@ def __init__(
self,
*,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
- credentials_file: str = None,
- scopes: Sequence[str] = None,
- channel: grpc.Channel = None,
- api_mtls_endpoint: str = None,
- client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
- ssl_channel_credentials: grpc.ChannelCredentials = None,
- client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
+ scopes: Optional[Sequence[str]] = None,
+ channel: Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]] = None,
+ api_mtls_endpoint: Optional[str] = None,
+ client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
- ignored if ``channel`` is provided.
- channel (Optional[grpc.Channel]): A ``Channel`` instance through
- which to make calls.
+ ignored if a ``channel`` instance is provided.
+ channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]):
+ A ``Channel`` instance through which to make calls, or a Callable
+ that constructs and returns one. If set to None, ``self.create_channel``
+ is used to create the channel. If a Callable is given, it will be called
+ with the same arguments as used in ``self.create_channel``.
api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint.
If provided, it overrides the ``host`` argument and tries to create
a mutual TLS channel with client SSL credentials from
@@ -87,11 +171,11 @@ def __init__(
private key bytes, both in PEM format. It is ignored if
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
- for the grpc channel. It is ignored if ``channel`` is provided.
+ for the grpc channel. It is ignored if a ``channel`` instance is provided.
client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
A callback to provide client certificate bytes and private key bytes,
both in PEM format. It is used to configure a mutual TLS channel. It is
- ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
+ ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -117,9 +201,10 @@ def __init__(
if client_cert_source:
warnings.warn("client_cert_source is deprecated", DeprecationWarning)
- if channel:
+ if isinstance(channel, grpc.Channel):
# Ignore credentials if a channel was passed.
- credentials = False
+ credentials = None
+ self._ignore_credentials = True
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
@@ -154,10 +239,13 @@ def __init__(
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
)
if not self._grpc_channel:
- self._grpc_channel = type(self).create_channel(
+ # initialize with the provided callable or the default channel
+ channel_init = channel or type(self).create_channel
+ self._grpc_channel = channel_init(
self._host,
# use the credentials which are saved
credentials=self._credentials,
@@ -173,15 +261,20 @@ 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
def create_channel(
cls,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
- credentials_file: str = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
**kwargs,
@@ -247,7 +340,7 @@ def get_group(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_group" not in self._stubs:
- self._stubs["get_group"] = self.grpc_channel.unary_unary(
+ self._stubs["get_group"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorGroupService/GetGroup",
request_serializer=error_group_service.GetGroupRequest.serialize,
response_deserializer=common.ErrorGroup.deserialize,
@@ -274,7 +367,7 @@ def update_group(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_group" not in self._stubs:
- self._stubs["update_group"] = self.grpc_channel.unary_unary(
+ self._stubs["update_group"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorGroupService/UpdateGroup",
request_serializer=error_group_service.UpdateGroupRequest.serialize,
response_deserializer=common.ErrorGroup.deserialize,
@@ -282,7 +375,7 @@ def update_group(
return self._stubs["update_group"]
def close(self):
- self.grpc_channel.close()
+ self._logged_channel.close()
@property
def kind(self) -> str:
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc_asyncio.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc_asyncio.py
index 718fc657..f3186e61 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc_asyncio.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/grpc_asyncio.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,15 +13,24 @@
# 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
from google.api_core import gapic_v1
from google.api_core import grpc_helpers_async
+from google.api_core import exceptions as core_exceptions
+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.cloud.errorreporting_v1beta1.types import common
@@ -29,6 +38,82 @@
from .base import ErrorGroupServiceTransport, DEFAULT_CLIENT_INFO
from .grpc import ErrorGroupServiceGrpcTransport
+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.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "rpcName": str(client_call_details.method),
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class ErrorGroupServiceGrpcAsyncIOTransport(ErrorGroupServiceTransport):
"""gRPC AsyncIO backend transport for ErrorGroupService.
@@ -50,7 +135,7 @@ class ErrorGroupServiceGrpcAsyncIOTransport(ErrorGroupServiceTransport):
def create_channel(
cls,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
@@ -66,7 +151,6 @@ def create_channel(
the credentials from the environment.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
scopes (Optional[Sequence[str]]): A 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`.
@@ -93,37 +177,41 @@ def __init__(
self,
*,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
- channel: aio.Channel = None,
- api_mtls_endpoint: str = None,
- client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
- ssl_channel_credentials: grpc.ChannelCredentials = None,
- client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
- quota_project_id=None,
+ channel: Optional[Union[aio.Channel, Callable[..., aio.Channel]]] = None,
+ api_mtls_endpoint: Optional[str] = None,
+ client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
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`.
- channel (Optional[aio.Channel]): A ``Channel`` instance through
- which to make calls.
+ channel (Optional[Union[aio.Channel, Callable[..., aio.Channel]]]):
+ A ``Channel`` instance through which to make calls, or a Callable
+ that constructs and returns one. If set to None, ``self.create_channel``
+ is used to create the channel. If a Callable is given, it will be called
+ with the same arguments as used in ``self.create_channel``.
api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint.
If provided, it overrides the ``host`` argument and tries to create
a mutual TLS channel with client SSL credentials from
@@ -133,11 +221,11 @@ def __init__(
private key bytes, both in PEM format. It is ignored if
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
- for the grpc channel. It is ignored if ``channel`` is provided.
+ for the grpc channel. It is ignored if a ``channel`` instance is provided.
client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
A callback to provide client certificate bytes and private key bytes,
both in PEM format. It is used to configure a mutual TLS channel. It is
- ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
+ ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -163,9 +251,10 @@ def __init__(
if client_cert_source:
warnings.warn("client_cert_source is deprecated", DeprecationWarning)
- if channel:
+ if isinstance(channel, aio.Channel):
# Ignore credentials if a channel was passed.
- credentials = False
+ credentials = None
+ self._ignore_credentials = True
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
@@ -199,10 +288,13 @@ def __init__(
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
)
if not self._grpc_channel:
- self._grpc_channel = type(self).create_channel(
+ # initialize with the provided callable or the default channel
+ channel_init = channel or type(self).create_channel
+ self._grpc_channel = channel_init(
self._host,
# use the credentials which are saved
credentials=self._credentials,
@@ -218,7 +310,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
@@ -250,7 +348,7 @@ def get_group(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_group" not in self._stubs:
- self._stubs["get_group"] = self.grpc_channel.unary_unary(
+ self._stubs["get_group"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorGroupService/GetGroup",
request_serializer=error_group_service.GetGroupRequest.serialize,
response_deserializer=common.ErrorGroup.deserialize,
@@ -279,15 +377,39 @@ def update_group(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "update_group" not in self._stubs:
- self._stubs["update_group"] = self.grpc_channel.unary_unary(
+ self._stubs["update_group"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorGroupService/UpdateGroup",
request_serializer=error_group_service.UpdateGroupRequest.serialize,
response_deserializer=common.ErrorGroup.deserialize,
)
return self._stubs["update_group"]
+ def _prep_wrapped_messages(self, client_info):
+ """Precompute the wrapped methods, overriding the base class method to use async wrappers."""
+ self._wrapped_methods = {
+ self.get_group: self._wrap_method(
+ self.get_group,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.update_group: self._wrap_method(
+ self.update_group,
+ 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"
__all__ = ("ErrorGroupServiceGrpcAsyncIOTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/rest.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/rest.py
new file mode 100644
index 00000000..526f1d7f
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/rest.py
@@ -0,0 +1,607 @@
+# -*- 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 logging
+import json # type: ignore
+
+from google.auth.transport.requests import AuthorizedSession # 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 gapic_v1
+import google.protobuf
+
+from google.protobuf import json_format
+
+from requests import __version__ as requests_version
+import dataclasses
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+import warnings
+
+
+from google.cloud.errorreporting_v1beta1.types import common
+from google.cloud.errorreporting_v1beta1.types import error_group_service
+
+
+from .rest_base import _BaseErrorGroupServiceRestTransport
+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
+
+ 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=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 ErrorGroupServiceRestInterceptor:
+ """Interceptor for ErrorGroupService.
+
+ Interceptors are used to manipulate requests, request metadata, and responses
+ in arbitrary ways.
+ Example use cases include:
+ * Logging
+ * Verifying requests according to service or custom semantics
+ * Stripping extraneous information from responses
+
+ These use cases and more can be enabled by injecting an
+ instance of a custom subclass when constructing the ErrorGroupServiceRestTransport.
+
+ .. code-block:: python
+ class MyCustomErrorGroupServiceInterceptor(ErrorGroupServiceRestInterceptor):
+ def pre_get_group(self, request, metadata):
+ logging.log(f"Received request: {request}")
+ return request, metadata
+
+ def post_get_group(self, response):
+ logging.log(f"Received response: {response}")
+ return response
+
+ def pre_update_group(self, request, metadata):
+ logging.log(f"Received request: {request}")
+ return request, metadata
+
+ def post_update_group(self, response):
+ logging.log(f"Received response: {response}")
+ return response
+
+ transport = ErrorGroupServiceRestTransport(interceptor=MyCustomErrorGroupServiceInterceptor())
+ client = ErrorGroupServiceClient(transport=transport)
+
+
+ """
+
+ def pre_get_group(
+ self,
+ request: error_group_service.GetGroupRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_group_service.GetGroupRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Pre-rpc interceptor for get_group
+
+ Override in a subclass to manipulate the request or metadata
+ before they are sent to the ErrorGroupService server.
+ """
+ return request, metadata
+
+ def post_get_group(self, response: common.ErrorGroup) -> common.ErrorGroup:
+ """Post-rpc interceptor for get_group
+
+ DEPRECATED. Please use the `post_get_group_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
+ after it is returned by the ErrorGroupService server but before
+ it is returned to user code. This `post_get_group` interceptor runs
+ before the `post_get_group_with_metadata` interceptor.
+ """
+ return response
+
+ def post_get_group_with_metadata(
+ self,
+ response: common.ErrorGroup,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[common.ErrorGroup, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for get_group
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the ErrorGroupService server but before it is returned to user code.
+
+ We recommend only using this `post_get_group_with_metadata`
+ interceptor in new development instead of the `post_get_group` interceptor.
+ When both interceptors are used, this `post_get_group_with_metadata` interceptor runs after the
+ `post_get_group` interceptor. The (possibly modified) response returned by
+ `post_get_group` will be passed to
+ `post_get_group_with_metadata`.
+ """
+ return response, metadata
+
+ def pre_update_group(
+ self,
+ request: error_group_service.UpdateGroupRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_group_service.UpdateGroupRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Pre-rpc interceptor for update_group
+
+ Override in a subclass to manipulate the request or metadata
+ before they are sent to the ErrorGroupService server.
+ """
+ return request, metadata
+
+ def post_update_group(self, response: common.ErrorGroup) -> common.ErrorGroup:
+ """Post-rpc interceptor for update_group
+
+ DEPRECATED. Please use the `post_update_group_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
+ after it is returned by the ErrorGroupService server but before
+ it is returned to user code. This `post_update_group` interceptor runs
+ before the `post_update_group_with_metadata` interceptor.
+ """
+ return response
+
+ def post_update_group_with_metadata(
+ self,
+ response: common.ErrorGroup,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[common.ErrorGroup, Sequence[Tuple[str, Union[str, bytes]]]]:
+ """Post-rpc interceptor for update_group
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the ErrorGroupService server but before it is returned to user code.
+
+ We recommend only using this `post_update_group_with_metadata`
+ interceptor in new development instead of the `post_update_group` interceptor.
+ When both interceptors are used, this `post_update_group_with_metadata` interceptor runs after the
+ `post_update_group` interceptor. The (possibly modified) response returned by
+ `post_update_group` will be passed to
+ `post_update_group_with_metadata`.
+ """
+ return response, metadata
+
+
+@dataclasses.dataclass
+class ErrorGroupServiceRestStub:
+ _session: AuthorizedSession
+ _host: str
+ _interceptor: ErrorGroupServiceRestInterceptor
+
+
+class ErrorGroupServiceRestTransport(_BaseErrorGroupServiceRestTransport):
+ """REST backend synchronous transport for ErrorGroupService.
+
+ Service for retrieving and updating individual error groups.
+
+ 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 = "clouderrorreporting.googleapis.com",
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
+ scopes: Optional[Sequence[str]] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ quota_project_id: Optional[str] = None,
+ client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
+ always_use_jwt_access: Optional[bool] = False,
+ url_scheme: str = "https",
+ interceptor: Optional[ErrorGroupServiceRestInterceptor] = None,
+ api_audience: Optional[str] = None,
+ ) -> None:
+ """Instantiate the transport.
+
+ Args:
+ host (Optional[str]):
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
+ credentials (Optional[google.auth.credentials.Credentials]): The
+ authorization credentials to attach to requests. These
+ credentials identify the application to the service; if none
+ are specified, the client will attempt to ascertain the
+ credentials from the environment.
+
+ credentials_file (Optional[str]): A file with credentials that can
+ be loaded with :func:`google.auth.load_credentials_from_file`.
+ This argument is ignored if ``channel`` is provided.
+ scopes (Optional(Sequence[str])): A list of scopes. This argument is
+ ignored if ``channel`` is provided.
+ client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
+ certificate to configure mutual TLS HTTP channel. It is ignored
+ if ``channel`` is provided.
+ quota_project_id (Optional[str]): An optional project to use for billing
+ and quota.
+ client_info (google.api_core.gapic_v1.client_info.ClientInfo):
+ The client info used to send a user-agent string along with
+ API requests. If ``None``, then default info will be used.
+ Generally, you only need to set this if you are developing
+ your own client library.
+ always_use_jwt_access (Optional[bool]): Whether self signed JWT should
+ be used for service account credentials.
+ url_scheme: the protocol scheme for the API endpoint. Normally
+ "https", but for testing or local servers,
+ "http" can be specified.
+ """
+ # Run the base constructor
+ # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
+ # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
+ # credentials object
+ 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(
+ self._credentials, default_host=self.DEFAULT_HOST
+ )
+ if client_cert_source_for_mtls:
+ self._session.configure_mtls_channel(client_cert_source_for_mtls)
+ self._interceptor = interceptor or ErrorGroupServiceRestInterceptor()
+ self._prep_wrapped_messages(client_info)
+
+ class _GetGroup(
+ _BaseErrorGroupServiceRestTransport._BaseGetGroup, ErrorGroupServiceRestStub
+ ):
+ def __hash__(self):
+ return hash("ErrorGroupServiceRestTransport.GetGroup")
+
+ @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: error_group_service.GetGroupRequest,
+ *,
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Optional[float] = None,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
+ ) -> common.ErrorGroup:
+ r"""Call the get group method over HTTP.
+
+ Args:
+ request (~.error_group_service.GetGroupRequest):
+ The request object. A request to return an individual
+ group.
+ 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, 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:
+ ~.common.ErrorGroup:
+ Description of a group of similar
+ error events.
+
+ """
+
+ http_options = (
+ _BaseErrorGroupServiceRestTransport._BaseGetGroup._get_http_options()
+ )
+
+ request, metadata = self._interceptor.pre_get_group(request, metadata)
+ transcoded_request = _BaseErrorGroupServiceRestTransport._BaseGetGroup._get_transcoded_request(
+ http_options, request
+ )
+
+ # Jsonify the query params
+ query_params = _BaseErrorGroupServiceRestTransport._BaseGetGroup._get_query_params_json(
+ transcoded_request
+ )
+
+ 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.devtools.clouderrorreporting_v1beta1.ErrorGroupServiceClient.GetGroup",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "rpcName": "GetGroup",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
+
+ # Send the request
+ response = ErrorGroupServiceRestTransport._GetGroup._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ )
+
+ # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
+ # subclass.
+ if response.status_code >= 400:
+ raise core_exceptions.from_http_response(response)
+
+ # Return the response
+ resp = common.ErrorGroup()
+ pb_resp = common.ErrorGroup.pb(resp)
+
+ json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
+ resp = self._interceptor.post_get_group(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_get_group_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = common.ErrorGroup.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.devtools.clouderrorreporting_v1beta1.ErrorGroupServiceClient.get_group",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "rpcName": "GetGroup",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
+ return resp
+
+ class _UpdateGroup(
+ _BaseErrorGroupServiceRestTransport._BaseUpdateGroup, ErrorGroupServiceRestStub
+ ):
+ def __hash__(self):
+ return hash("ErrorGroupServiceRestTransport.UpdateGroup")
+
+ @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: error_group_service.UpdateGroupRequest,
+ *,
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Optional[float] = None,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
+ ) -> common.ErrorGroup:
+ r"""Call the update group method over HTTP.
+
+ Args:
+ request (~.error_group_service.UpdateGroupRequest):
+ The request object. A request to replace the existing
+ data for the given group.
+ 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, 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:
+ ~.common.ErrorGroup:
+ Description of a group of similar
+ error events.
+
+ """
+
+ http_options = (
+ _BaseErrorGroupServiceRestTransport._BaseUpdateGroup._get_http_options()
+ )
+
+ request, metadata = self._interceptor.pre_update_group(request, metadata)
+ transcoded_request = _BaseErrorGroupServiceRestTransport._BaseUpdateGroup._get_transcoded_request(
+ http_options, request
+ )
+
+ body = _BaseErrorGroupServiceRestTransport._BaseUpdateGroup._get_request_body_json(
+ transcoded_request
+ )
+
+ # Jsonify the query params
+ query_params = _BaseErrorGroupServiceRestTransport._BaseUpdateGroup._get_query_params_json(
+ transcoded_request
+ )
+
+ 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.devtools.clouderrorreporting_v1beta1.ErrorGroupServiceClient.UpdateGroup",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "rpcName": "UpdateGroup",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
+
+ # Send the request
+ response = ErrorGroupServiceRestTransport._UpdateGroup._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
+ )
+
+ # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
+ # subclass.
+ if response.status_code >= 400:
+ raise core_exceptions.from_http_response(response)
+
+ # Return the response
+ resp = common.ErrorGroup()
+ pb_resp = common.ErrorGroup.pb(resp)
+
+ json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
+ resp = self._interceptor.post_update_group(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_update_group_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = common.ErrorGroup.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.devtools.clouderrorreporting_v1beta1.ErrorGroupServiceClient.update_group",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorGroupService",
+ "rpcName": "UpdateGroup",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
+ return resp
+
+ @property
+ def get_group(
+ self,
+ ) -> Callable[[error_group_service.GetGroupRequest], common.ErrorGroup]:
+ # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
+ # In C++ this would require a dynamic_cast
+ return self._GetGroup(self._session, self._host, self._interceptor) # type: ignore
+
+ @property
+ def update_group(
+ self,
+ ) -> Callable[[error_group_service.UpdateGroupRequest], common.ErrorGroup]:
+ # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
+ # In C++ this would require a dynamic_cast
+ return self._UpdateGroup(self._session, self._host, self._interceptor) # type: ignore
+
+ @property
+ def kind(self) -> str:
+ return "rest"
+
+ def close(self):
+ self._session.close()
+
+
+__all__ = ("ErrorGroupServiceRestTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/rest_base.py b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/rest_base.py
new file mode 100644
index 00000000..f4330f97
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/error_group_service/transports/rest_base.py
@@ -0,0 +1,207 @@
+# -*- 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 .base import ErrorGroupServiceTransport, DEFAULT_CLIENT_INFO
+
+import re
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+
+
+from google.cloud.errorreporting_v1beta1.types import common
+from google.cloud.errorreporting_v1beta1.types import error_group_service
+
+
+class _BaseErrorGroupServiceRestTransport(ErrorGroupServiceTransport):
+ """Base REST backend transport for ErrorGroupService.
+
+ 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 = "clouderrorreporting.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: 'clouderrorreporting.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 _BaseGetGroup:
+ 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": "/v1beta1/{group_name=projects/*/groups/*}",
+ },
+ {
+ "method": "get",
+ "uri": "/v1beta1/{group_name=projects/*/locations/*/groups/*}",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = error_group_service.GetGroupRequest.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(
+ _BaseErrorGroupServiceRestTransport._BaseGetGroup._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseUpdateGroup:
+ 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": "/v1beta1/{group.name=projects/*/groups/*}",
+ "body": "group",
+ },
+ {
+ "method": "put",
+ "uri": "/v1beta1/{group.name=projects/*/locations/*/groups/*}",
+ "body": "group",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = error_group_service.UpdateGroupRequest.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(
+ _BaseErrorGroupServiceRestTransport._BaseUpdateGroup._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+
+__all__ = ("_BaseErrorGroupServiceRestTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/__init__.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/__init__.py
index b402e01a..e04db91b 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/__init__.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.
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/async_client.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/async_client.py
index 03095875..f588025e 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/async_client.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/async_client.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,23 +13,37 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging as std_logging
from collections import OrderedDict
-import functools
import re
-from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union
-import pkg_resources
+from typing import (
+ Dict,
+ Callable,
+ Mapping,
+ MutableMapping,
+ MutableSequence,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+)
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
from google.api_core.client_options import ClientOptions
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
-from google.api_core import retry as retries
+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:
- OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault]
+ OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
- OptionalRetry = Union[retries.Retry, object] # type: ignore
+ OptionalRetry = Union[retries.AsyncRetry, object, None] # type: ignore
from google.cloud.errorreporting_v1beta1.services.error_stats_service import pagers
from google.cloud.errorreporting_v1beta1.types import common
@@ -38,6 +52,15 @@
from .transports.grpc_asyncio import ErrorStatsServiceGrpcAsyncIOTransport
from .client import ErrorStatsServiceClient
+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 ErrorStatsServiceAsyncClient:
"""An API for retrieving and managing error statistics as well
@@ -46,8 +69,12 @@ class ErrorStatsServiceAsyncClient:
_client: ErrorStatsServiceClient
+ # Copy defaults from the synchronous client for use here.
+ # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
DEFAULT_ENDPOINT = ErrorStatsServiceClient.DEFAULT_ENDPOINT
DEFAULT_MTLS_ENDPOINT = ErrorStatsServiceClient.DEFAULT_MTLS_ENDPOINT
+ _DEFAULT_ENDPOINT_TEMPLATE = ErrorStatsServiceClient._DEFAULT_ENDPOINT_TEMPLATE
+ _DEFAULT_UNIVERSE = ErrorStatsServiceClient._DEFAULT_UNIVERSE
error_group_path = staticmethod(ErrorStatsServiceClient.error_group_path)
parse_error_group_path = staticmethod(
@@ -127,7 +154,7 @@ def get_mtls_endpoint_and_cert_source(
The API endpoint is determined in the following order:
(1) if `client_options.api_endpoint` if provided, use the provided one.
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
- default mTLS endpoint; if the environment variabel is "never", use the default API
+ default mTLS endpoint; if the environment variable is "never", use the default API
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
use the default API endpoint.
@@ -156,19 +183,42 @@ def transport(self) -> ErrorStatsServiceTransport:
"""
return self._client.transport
- get_transport_class = functools.partial(
- type(ErrorStatsServiceClient).get_transport_class, type(ErrorStatsServiceClient)
- )
+ @property
+ def api_endpoint(self):
+ """Return the API endpoint used by the client instance.
+
+ Returns:
+ str: The API endpoint used by the client instance.
+ """
+ return self._client._api_endpoint
+
+ @property
+ def universe_domain(self) -> str:
+ """Return the universe domain used by the client instance.
+
+ Returns:
+ str: The universe domain used
+ by the client instance.
+ """
+ return self._client._universe_domain
+
+ get_transport_class = ErrorStatsServiceClient.get_transport_class
def __init__(
self,
*,
- credentials: ga_credentials.Credentials = None,
- transport: Union[str, ErrorStatsServiceTransport] = "grpc_asyncio",
- client_options: ClientOptions = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ transport: Optional[
+ Union[
+ str,
+ ErrorStatsServiceTransport,
+ Callable[..., ErrorStatsServiceTransport],
+ ]
+ ] = "grpc_asyncio",
+ client_options: Optional[ClientOptions] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
- """Instantiates the error stats service client.
+ """Instantiates the error stats service async client.
Args:
credentials (Optional[google.auth.credentials.Credentials]): The
@@ -176,26 +226,43 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ~.ErrorStatsServiceTransport]): The
- transport to use. If set to None, a transport is chosen
- automatically.
- client_options (ClientOptions): Custom options for the client. It
- won't take effect if a ``transport`` instance is provided.
- (1) The ``api_endpoint`` property can be used to override the
- default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
- environment variable can also be used to override the endpoint:
+ transport (Optional[Union[str,ErrorStatsServiceTransport,Callable[..., ErrorStatsServiceTransport]]]):
+ The transport to use, or a Callable that constructs and returns a new transport to use.
+ If a Callable is given, it will be called with the same set of initialization
+ arguments as used in the ErrorStatsServiceTransport constructor.
+ If set to None, a transport is chosen automatically.
+ client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]):
+ Custom options for the client.
+
+ 1. The ``api_endpoint`` property can be used to override the
+ default endpoint provided by the client when ``transport`` is
+ not explicitly provided. Only if this property is not set and
+ ``transport`` was not explicitly provided, the endpoint is
+ determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment
+ variable, which have one of the following values:
"always" (always use the default mTLS endpoint), "never" (always
- use the default regular endpoint) and "auto" (auto switch to the
- default mTLS endpoint if client certificate is present, this is
- the default value). However, the ``api_endpoint`` property takes
- precedence if provided.
- (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
+ use the default regular endpoint) and "auto" (auto-switch to the
+ default mTLS endpoint if client certificate is present; this is
+ the default value).
+
+ 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
is "true", then the ``client_cert_source`` property can be used
- to provide client certificate for mutual TLS transport. If
+ to provide a client certificate for mTLS transport. If
not provided, the default SSL client certificate will be used if
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
set, no client certificate will be used.
+ 3. The ``universe_domain`` property can be used to override the
+ default "googleapis.com" universe. Note that ``api_endpoint``
+ property still takes precedence; and ``universe_domain`` is
+ currently not supported for mTLS.
+
+ 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're developing
+ your own client library.
+
Raises:
google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
creation failed for any reason.
@@ -207,20 +274,51 @@ def __init__(
client_info=client_info,
)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceAsyncClient`.",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "credentialsType": None,
+ },
+ )
+
async def list_group_stats(
self,
- request: Union[error_stats_service.ListGroupStatsRequest, dict] = None,
+ request: Optional[
+ Union[error_stats_service.ListGroupStatsRequest, dict]
+ ] = None,
*,
- project_name: str = None,
- time_range: error_stats_service.QueryTimeRange = None,
+ project_name: Optional[str] = None,
+ time_range: Optional[error_stats_service.QueryTimeRange] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListGroupStatsAsyncPager:
r"""Lists the specified groups.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
async def sample_list_group_stats():
@@ -240,18 +338,27 @@ async def sample_list_group_stats():
print(response)
Args:
- request (Union[google.cloud.errorreporting_v1beta1.types.ListGroupStatsRequest, dict]):
- The request object. Specifies a set of `ErrorGroupStats`
- to return.
+ request (Optional[Union[google.cloud.errorreporting_v1beta1.types.ListGroupStatsRequest, dict]]):
+ The request object. Specifies a set of ``ErrorGroupStats`` to return.
project_name (:class:`str`):
Required. The resource name of the Google Cloud Platform
project. Written as ``projects/{projectID}`` or
``projects/{projectNumber}``, where ``{projectID}`` and
``{projectNumber}`` can be found in the `Google Cloud
- Console `__.
+ console `__.
+ It may also include a location, such as
+ ``projects/{projectID}/locations/{location}`` where
+ ``{location}`` is a cloud region.
Examples: ``projects/my-project-123``,
- ``projects/5551234``.
+ ``projects/5551234``,
+ ``projects/my-project-123/locations/us-central1``,
+ ``projects/5551234/locations/us-central1``.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified. Use ``-`` as
+ a wildcard to request group stats from all regions.
This corresponds to the ``project_name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -259,21 +366,31 @@ async def sample_list_group_stats():
time_range (:class:`google.cloud.errorreporting_v1beta1.types.QueryTimeRange`):
Optional. List data for the given time range. If not
set, a default time range is used. The field
- time_range_begin in the response will specify the
- beginning of this time range. Only ErrorGroupStats with
- a non-zero count in the given time range are returned,
- unless the request contains an explicit group_id list.
- If a group_id list is given, also ErrorGroupStats with
- zero occurrences are returned.
+ [time_range_begin]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsResponse.time_range_begin]
+ in the response will specify the beginning of this time
+ range. Only [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ with a non-zero count in the given time range are
+ returned, unless the request contains an explicit
+ [group_id]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.group_id]
+ list. If a [group_id]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.group_id]
+ list is given, also [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ with zero occurrences are returned.
This corresponds to the ``time_range`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ 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.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListGroupStatsAsyncPager:
@@ -285,16 +402,22 @@ async def sample_list_group_stats():
"""
# 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_name, time_range])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_name, time_range]
+ 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 "
"the individual field arguments should be set."
)
- request = error_stats_service.ListGroupStatsRequest(request)
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
+ if not isinstance(request, error_stats_service.ListGroupStatsRequest):
+ request = error_stats_service.ListGroupStatsRequest(request)
# If we have keyword arguments corresponding to fields on the
# request, apply these.
@@ -305,11 +428,9 @@ async def sample_list_group_stats():
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.list_group_stats,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._client._transport._wrapped_methods[
+ self._client._transport.list_group_stats
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -319,6 +440,9 @@ async def sample_list_group_stats():
),
)
+ # Validate the universe domain.
+ self._client._validate_universe_domain()
+
# Send the request.
response = await rpc(
request,
@@ -333,6 +457,8 @@ async def sample_list_group_stats():
method=rpc,
request=request,
response=response,
+ retry=retry,
+ timeout=timeout,
metadata=metadata,
)
@@ -341,18 +467,25 @@ async def sample_list_group_stats():
async def list_events(
self,
- request: Union[error_stats_service.ListEventsRequest, dict] = None,
+ request: Optional[Union[error_stats_service.ListEventsRequest, dict]] = None,
*,
- project_name: str = None,
- group_id: str = None,
+ project_name: Optional[str] = None,
+ group_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListEventsAsyncPager:
r"""Lists the specified events.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
async def sample_list_events():
@@ -373,32 +506,46 @@ async def sample_list_events():
print(response)
Args:
- request (Union[google.cloud.errorreporting_v1beta1.types.ListEventsRequest, dict]):
+ request (Optional[Union[google.cloud.errorreporting_v1beta1.types.ListEventsRequest, dict]]):
The request object. Specifies a set of error events to
return.
project_name (:class:`str`):
Required. The resource name of the Google Cloud Platform
- project. Written as ``projects/{projectID}``, where
+ project. Written as ``projects/{projectID}`` or
+ ``projects/{projectID}/locations/{location}``, where
``{projectID}`` is the `Google Cloud Platform project
- ID `__.
+ ID `__
+ and ``{location}`` is a Cloud region.
- Example: ``projects/my-project-123``.
+ Examples: ``projects/my-project-123``,
+ ``projects/my-project-123/locations/global``.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
This corresponds to the ``project_name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
group_id (:class:`str`):
- Required. The group for which events
- shall be returned.
+ Required. The group for which events shall be returned.
+ The ``group_id`` is a unique identifier for a particular
+ error group. The identifier is derived from key parts of
+ the error-log content and is treated as Service Data.
+ For information about how Service Data is handled, see
+ `Google Cloud Privacy
+ Notice `__.
This corresponds to the ``group_id`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ 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.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListEventsAsyncPager:
@@ -410,16 +557,22 @@ async def sample_list_events():
"""
# 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_name, group_id])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_name, group_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 "
"the individual field arguments should be set."
)
- request = error_stats_service.ListEventsRequest(request)
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
+ if not isinstance(request, error_stats_service.ListEventsRequest):
+ request = error_stats_service.ListEventsRequest(request)
# If we have keyword arguments corresponding to fields on the
# request, apply these.
@@ -430,11 +583,9 @@ async def sample_list_events():
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.list_events,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._client._transport._wrapped_methods[
+ self._client._transport.list_events
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -444,6 +595,9 @@ async def sample_list_events():
),
)
+ # Validate the universe domain.
+ self._client._validate_universe_domain()
+
# Send the request.
response = await rpc(
request,
@@ -458,6 +612,8 @@ async def sample_list_events():
method=rpc,
request=request,
response=response,
+ retry=retry,
+ timeout=timeout,
metadata=metadata,
)
@@ -466,17 +622,24 @@ async def sample_list_events():
async def delete_events(
self,
- request: Union[error_stats_service.DeleteEventsRequest, dict] = None,
+ request: Optional[Union[error_stats_service.DeleteEventsRequest, dict]] = None,
*,
- project_name: str = None,
+ project_name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> error_stats_service.DeleteEventsResponse:
r"""Deletes all error events of a given project.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
async def sample_delete_events():
@@ -495,24 +658,33 @@ async def sample_delete_events():
print(response)
Args:
- request (Union[google.cloud.errorreporting_v1beta1.types.DeleteEventsRequest, dict]):
+ request (Optional[Union[google.cloud.errorreporting_v1beta1.types.DeleteEventsRequest, dict]]):
The request object. Deletes all events in the project.
project_name (:class:`str`):
Required. The resource name of the Google Cloud Platform
- project. Written as ``projects/{projectID}``, where
+ project. Written as ``projects/{projectID}`` or
+ ``projects/{projectID}/locations/{location}``, where
``{projectID}`` is the `Google Cloud Platform project
- ID `__.
+ ID `__
+ and ``{location}`` is a Cloud region.
- Example: ``projects/my-project-123``.
+ Examples: ``projects/my-project-123``,
+ ``projects/my-project-123/locations/global``.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
This corresponds to the ``project_name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ 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.cloud.errorreporting_v1beta1.types.DeleteEventsResponse:
@@ -521,16 +693,22 @@ async def sample_delete_events():
"""
# 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_name])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_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 "
"the individual field arguments should be set."
)
- request = error_stats_service.DeleteEventsRequest(request)
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
+ if not isinstance(request, error_stats_service.DeleteEventsRequest):
+ request = error_stats_service.DeleteEventsRequest(request)
# If we have keyword arguments corresponding to fields on the
# request, apply these.
@@ -539,11 +717,9 @@ async def sample_delete_events():
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.delete_events,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._client._transport._wrapped_methods[
+ self._client._transport.delete_events
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -553,6 +729,9 @@ async def sample_delete_events():
),
)
+ # Validate the universe domain.
+ self._client._validate_universe_domain()
+
# Send the request.
response = await rpc(
request,
@@ -564,21 +743,19 @@ async def sample_delete_events():
# Done; return the response.
return response
- async def __aenter__(self):
+ async def __aenter__(self) -> "ErrorStatsServiceAsyncClient":
return self
async def __aexit__(self, exc_type, exc, tb):
await self.transport.close()
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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__ = ("ErrorStatsServiceAsyncClient",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/client.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/client.py
index f3289f77..dd22c33b 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/client.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/client.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.
@@ -14,10 +14,27 @@
# limitations under the License.
#
from collections import OrderedDict
+from http import HTTPStatus
+import json
+import logging as std_logging
import os
import re
-from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union
-import pkg_resources
+from typing import (
+ Dict,
+ Callable,
+ Mapping,
+ MutableMapping,
+ MutableSequence,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+ cast,
+)
+import warnings
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
from google.api_core import client_options as client_options_lib
from google.api_core import exceptions as core_exceptions
@@ -28,11 +45,21 @@
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]
+ OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
- OptionalRetry = Union[retries.Retry, object] # type: ignore
+ 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.cloud.errorreporting_v1beta1.services.error_stats_service import pagers
from google.cloud.errorreporting_v1beta1.types import common
@@ -40,6 +67,7 @@
from .transports.base import ErrorStatsServiceTransport, DEFAULT_CLIENT_INFO
from .transports.grpc import ErrorStatsServiceGrpcTransport
from .transports.grpc_asyncio import ErrorStatsServiceGrpcAsyncIOTransport
+from .transports.rest import ErrorStatsServiceRestTransport
class ErrorStatsServiceClientMeta(type):
@@ -55,10 +83,11 @@ class ErrorStatsServiceClientMeta(type):
) # type: Dict[str, Type[ErrorStatsServiceTransport]]
_transport_registry["grpc"] = ErrorStatsServiceGrpcTransport
_transport_registry["grpc_asyncio"] = ErrorStatsServiceGrpcAsyncIOTransport
+ _transport_registry["rest"] = ErrorStatsServiceRestTransport
def get_transport_class(
cls,
- label: str = None,
+ label: Optional[str] = None,
) -> Type[ErrorStatsServiceTransport]:
"""Returns an appropriate transport class.
@@ -113,11 +142,15 @@ def _get_default_mtls_endpoint(api_endpoint):
return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
+ # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
DEFAULT_ENDPOINT = "clouderrorreporting.googleapis.com"
DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore
DEFAULT_ENDPOINT
)
+ _DEFAULT_ENDPOINT_TEMPLATE = "clouderrorreporting.{UNIVERSE_DOMAIN}"
+ _DEFAULT_UNIVERSE = "googleapis.com"
+
@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -263,7 +296,7 @@ def parse_common_location_path(path: str) -> Dict[str, str]:
def get_mtls_endpoint_and_cert_source(
cls, client_options: Optional[client_options_lib.ClientOptions] = None
):
- """Return the API endpoint and client cert source for mutual TLS.
+ """Deprecated. Return the API endpoint and client cert source for mutual TLS.
The client cert source is determined in the following order:
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
@@ -275,7 +308,7 @@ def get_mtls_endpoint_and_cert_source(
The API endpoint is determined in the following order:
(1) if `client_options.api_endpoint` if provided, use the provided one.
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
- default mTLS endpoint; if the environment variabel is "never", use the default API
+ default mTLS endpoint; if the environment variable is "never", use the default API
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
use the default API endpoint.
@@ -293,6 +326,11 @@ def get_mtls_endpoint_and_cert_source(
Raises:
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
"""
+
+ warnings.warn(
+ "get_mtls_endpoint_and_cert_source is deprecated. Use the api_endpoint property instead.",
+ DeprecationWarning,
+ )
if client_options is None:
client_options = client_options_lib.ClientOptions()
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
@@ -326,12 +364,183 @@ def get_mtls_endpoint_and_cert_source(
return api_endpoint, client_cert_source
+ @staticmethod
+ def _read_environment_variables():
+ """Returns the environment variables used by the client.
+
+ Returns:
+ Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE,
+ GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables.
+
+ Raises:
+ ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not
+ any of ["true", "false"].
+ 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_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
+
+ @staticmethod
+ def _get_client_cert_source(provided_cert_source, use_cert_flag):
+ """Return the client cert source to be used by the client.
+
+ Args:
+ provided_cert_source (bytes): The client certificate source provided.
+ use_cert_flag (bool): A flag indicating whether to use the client certificate.
+
+ Returns:
+ bytes or None: The client cert source to be used by the client.
+ """
+ client_cert_source = None
+ if use_cert_flag:
+ if provided_cert_source:
+ client_cert_source = provided_cert_source
+ elif mtls.has_default_client_cert_source():
+ client_cert_source = mtls.default_client_cert_source()
+ return client_cert_source
+
+ @staticmethod
+ def _get_api_endpoint(
+ api_override, client_cert_source, universe_domain, use_mtls_endpoint
+ ):
+ """Return the API endpoint used by the client.
+
+ Args:
+ api_override (str): The API endpoint override. If specified, this is always
+ the return value of this function and the other arguments are not used.
+ client_cert_source (bytes): The client certificate source used by the client.
+ universe_domain (str): The universe domain used by the client.
+ use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters.
+ Possible values are "always", "auto", or "never".
+
+ Returns:
+ str: The API endpoint to be used by the client.
+ """
+ if api_override is not None:
+ api_endpoint = api_override
+ elif use_mtls_endpoint == "always" or (
+ use_mtls_endpoint == "auto" and client_cert_source
+ ):
+ _default_universe = ErrorStatsServiceClient._DEFAULT_UNIVERSE
+ if universe_domain != _default_universe:
+ raise MutualTLSChannelError(
+ f"mTLS is not supported in any universe other than {_default_universe}."
+ )
+ api_endpoint = ErrorStatsServiceClient.DEFAULT_MTLS_ENDPOINT
+ else:
+ api_endpoint = ErrorStatsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=universe_domain
+ )
+ return api_endpoint
+
+ @staticmethod
+ def _get_universe_domain(
+ client_universe_domain: Optional[str], universe_domain_env: Optional[str]
+ ) -> str:
+ """Return the universe domain used by the client.
+
+ Args:
+ client_universe_domain (Optional[str]): The universe domain configured via the client options.
+ universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable.
+
+ Returns:
+ str: The universe domain to be used by the client.
+
+ Raises:
+ ValueError: If the universe domain is an empty string.
+ """
+ universe_domain = ErrorStatsServiceClient._DEFAULT_UNIVERSE
+ if client_universe_domain is not None:
+ universe_domain = client_universe_domain
+ elif universe_domain_env is not None:
+ universe_domain = universe_domain_env
+ if len(universe_domain.strip()) == 0:
+ raise ValueError("Universe Domain cannot be an empty string.")
+ return universe_domain
+
+ def _validate_universe_domain(self):
+ """Validates client's and credentials' universe domains are consistent.
+
+ Returns:
+ bool: True iff the configured universe domain is valid.
+
+ Raises:
+ ValueError: If the configured universe domain is not valid.
+ """
+
+ # NOTE (b/349488459): universe validation is disabled until further notice.
+ return True
+
+ 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.
+
+ Args:
+ error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info.
+ """
+ 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):
+ """Return the API endpoint used by the client instance.
+
+ Returns:
+ str: The API endpoint used by the client instance.
+ """
+ return self._api_endpoint
+
+ @property
+ def universe_domain(self) -> str:
+ """Return the universe domain used by the client instance.
+
+ Returns:
+ str: The universe domain used by the client instance.
+ """
+ return self._universe_domain
+
def __init__(
self,
*,
credentials: Optional[ga_credentials.Credentials] = None,
- transport: Union[str, ErrorStatsServiceTransport, None] = None,
- client_options: Optional[client_options_lib.ClientOptions] = None,
+ transport: Optional[
+ Union[
+ str,
+ ErrorStatsServiceTransport,
+ Callable[..., ErrorStatsServiceTransport],
+ ]
+ ] = None,
+ client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
"""Instantiates the error stats service client.
@@ -342,25 +551,37 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ErrorStatsServiceTransport]): The
- transport to use. If set to None, a transport is chosen
- automatically.
- client_options (google.api_core.client_options.ClientOptions): Custom options for the
- client. It won't take effect if a ``transport`` instance is provided.
- (1) The ``api_endpoint`` property can be used to override the
- default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
- environment variable can also be used to override the endpoint:
+ transport (Optional[Union[str,ErrorStatsServiceTransport,Callable[..., ErrorStatsServiceTransport]]]):
+ The transport to use, or a Callable that constructs and returns a new transport.
+ If a Callable is given, it will be called with the same set of initialization
+ arguments as used in the ErrorStatsServiceTransport constructor.
+ If set to None, a transport is chosen automatically.
+ client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]):
+ Custom options for the client.
+
+ 1. The ``api_endpoint`` property can be used to override the
+ default endpoint provided by the client when ``transport`` is
+ not explicitly provided. Only if this property is not set and
+ ``transport`` was not explicitly provided, the endpoint is
+ determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment
+ variable, which have one of the following values:
"always" (always use the default mTLS endpoint), "never" (always
- use the default regular endpoint) and "auto" (auto switch to the
- default mTLS endpoint if client certificate is present, this is
- the default value). However, the ``api_endpoint`` property takes
- precedence if provided.
- (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
+ use the default regular endpoint) and "auto" (auto-switch to the
+ default mTLS endpoint if client certificate is present; this is
+ the default value).
+
+ 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
is "true", then the ``client_cert_source`` property can be used
- to provide client certificate for mutual TLS transport. If
+ to provide a client certificate for mTLS transport. If
not provided, the default SSL client certificate will be used if
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
set, no client certificate will be used.
+
+ 3. The ``universe_domain`` property can be used to override the
+ default "googleapis.com" universe. Note that the ``api_endpoint``
+ property still takes precedence; and ``universe_domain`` is
+ currently not supported for mTLS.
+
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.
@@ -371,16 +592,38 @@ def __init__(
google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport
creation failed for any reason.
"""
- if isinstance(client_options, dict):
- client_options = client_options_lib.from_dict(client_options)
- if client_options is None:
- client_options = client_options_lib.ClientOptions()
+ self._client_options = client_options
+ if isinstance(self._client_options, dict):
+ self._client_options = client_options_lib.from_dict(self._client_options)
+ if self._client_options is None:
+ self._client_options = client_options_lib.ClientOptions()
+ self._client_options = cast(
+ client_options_lib.ClientOptions, self._client_options
+ )
+
+ universe_domain_opt = getattr(self._client_options, "universe_domain", None)
- api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
- client_options
+ (
+ self._use_client_cert,
+ self._use_mtls_endpoint,
+ self._universe_domain_env,
+ ) = ErrorStatsServiceClient._read_environment_variables()
+ self._client_cert_source = ErrorStatsServiceClient._get_client_cert_source(
+ self._client_options.client_cert_source, self._use_client_cert
)
+ self._universe_domain = ErrorStatsServiceClient._get_universe_domain(
+ universe_domain_opt, self._universe_domain_env
+ )
+ self._api_endpoint = None # updated below, depending on `transport`
+
+ # Initialize the universe domain validation.
+ self._is_universe_domain_valid = False
- api_key_value = getattr(client_options, "api_key", None)
+ 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(
"client_options.api_key and credentials are mutually exclusive"
@@ -389,20 +632,33 @@ def __init__(
# Save or instantiate the transport.
# Ordinarily, we provide the transport, but allowing a custom transport
# instance provides an extensibility point for unusual situations.
- if isinstance(transport, ErrorStatsServiceTransport):
+ transport_provided = isinstance(transport, ErrorStatsServiceTransport)
+ if transport_provided:
# transport is a ErrorStatsServiceTransport instance.
- if credentials or client_options.credentials_file or api_key_value:
+ if credentials or self._client_options.credentials_file or api_key_value:
raise ValueError(
"When providing a transport instance, "
"provide its credentials directly."
)
- if client_options.scopes:
+ if self._client_options.scopes:
raise ValueError(
"When providing a transport instance, provide its scopes "
"directly."
)
- self._transport = transport
- else:
+ self._transport = cast(ErrorStatsServiceTransport, transport)
+ self._api_endpoint = self._transport.host
+
+ self._api_endpoint = (
+ self._api_endpoint
+ or ErrorStatsServiceClient._get_api_endpoint(
+ self._client_options.api_endpoint,
+ self._client_cert_source,
+ self._universe_domain,
+ self._use_mtls_endpoint,
+ )
+ )
+
+ if not transport_provided:
import google.auth._default # type: ignore
if api_key_value and hasattr(
@@ -412,32 +668,73 @@ def __init__(
api_key_value
)
- Transport = type(self).get_transport_class(transport)
- self._transport = Transport(
+ transport_init: Union[
+ Type[ErrorStatsServiceTransport],
+ Callable[..., ErrorStatsServiceTransport],
+ ] = (
+ ErrorStatsServiceClient.get_transport_class(transport)
+ if isinstance(transport, str) or transport is None
+ else cast(Callable[..., ErrorStatsServiceTransport], transport)
+ )
+ # initialize with the provided callable or the passed in class
+ self._transport = transport_init(
credentials=credentials,
- credentials_file=client_options.credentials_file,
- host=api_endpoint,
- scopes=client_options.scopes,
- client_cert_source_for_mtls=client_cert_source_func,
- quota_project_id=client_options.quota_project_id,
+ credentials_file=self._client_options.credentials_file,
+ host=self._api_endpoint,
+ scopes=self._client_options.scopes,
+ client_cert_source_for_mtls=self._client_cert_source,
+ quota_project_id=self._client_options.quota_project_id,
client_info=client_info,
always_use_jwt_access=True,
+ 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.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceClient`.",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "credentialsType": None,
+ },
+ )
+
def list_group_stats(
self,
- request: Union[error_stats_service.ListGroupStatsRequest, dict] = None,
+ request: Optional[
+ Union[error_stats_service.ListGroupStatsRequest, dict]
+ ] = None,
*,
- project_name: str = None,
- time_range: error_stats_service.QueryTimeRange = None,
+ project_name: Optional[str] = None,
+ time_range: Optional[error_stats_service.QueryTimeRange] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListGroupStatsPager:
r"""Lists the specified groups.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
def sample_list_group_stats():
@@ -458,17 +755,26 @@ def sample_list_group_stats():
Args:
request (Union[google.cloud.errorreporting_v1beta1.types.ListGroupStatsRequest, dict]):
- The request object. Specifies a set of `ErrorGroupStats`
- to return.
+ The request object. Specifies a set of ``ErrorGroupStats`` to return.
project_name (str):
Required. The resource name of the Google Cloud Platform
project. Written as ``projects/{projectID}`` or
``projects/{projectNumber}``, where ``{projectID}`` and
``{projectNumber}`` can be found in the `Google Cloud
- Console `__.
+ console `__.
+ It may also include a location, such as
+ ``projects/{projectID}/locations/{location}`` where
+ ``{location}`` is a cloud region.
Examples: ``projects/my-project-123``,
- ``projects/5551234``.
+ ``projects/5551234``,
+ ``projects/my-project-123/locations/us-central1``,
+ ``projects/5551234/locations/us-central1``.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified. Use ``-`` as
+ a wildcard to request group stats from all regions.
This corresponds to the ``project_name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -476,12 +782,20 @@ def sample_list_group_stats():
time_range (google.cloud.errorreporting_v1beta1.types.QueryTimeRange):
Optional. List data for the given time range. If not
set, a default time range is used. The field
- time_range_begin in the response will specify the
- beginning of this time range. Only ErrorGroupStats with
- a non-zero count in the given time range are returned,
- unless the request contains an explicit group_id list.
- If a group_id list is given, also ErrorGroupStats with
- zero occurrences are returned.
+ [time_range_begin]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsResponse.time_range_begin]
+ in the response will specify the beginning of this time
+ range. Only [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ with a non-zero count in the given time range are
+ returned, unless the request contains an explicit
+ [group_id]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.group_id]
+ list. If a [group_id]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.group_id]
+ list is given, also [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ with zero occurrences are returned.
This corresponds to the ``time_range`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -489,8 +803,10 @@ def sample_list_group_stats():
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.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListGroupStatsPager:
@@ -502,19 +818,20 @@ def sample_list_group_stats():
"""
# 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_name, time_range])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_name, time_range]
+ 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 "
"the individual field arguments should be set."
)
- # Minor optimization to avoid making a copy if the user passes
- # in a error_stats_service.ListGroupStatsRequest.
- # There's no risk of modifying the input as we've already verified
- # there are no flattened fields.
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
if not isinstance(request, error_stats_service.ListGroupStatsRequest):
request = error_stats_service.ListGroupStatsRequest(request)
# If we have keyword arguments corresponding to fields on the
@@ -536,6 +853,9 @@ def sample_list_group_stats():
),
)
+ # Validate the universe domain.
+ self._validate_universe_domain()
+
# Send the request.
response = rpc(
request,
@@ -550,6 +870,8 @@ def sample_list_group_stats():
method=rpc,
request=request,
response=response,
+ retry=retry,
+ timeout=timeout,
metadata=metadata,
)
@@ -558,18 +880,25 @@ def sample_list_group_stats():
def list_events(
self,
- request: Union[error_stats_service.ListEventsRequest, dict] = None,
+ request: Optional[Union[error_stats_service.ListEventsRequest, dict]] = None,
*,
- project_name: str = None,
- group_id: str = None,
+ project_name: Optional[str] = None,
+ group_id: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> pagers.ListEventsPager:
r"""Lists the specified events.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
def sample_list_events():
@@ -595,18 +924,30 @@ def sample_list_events():
return.
project_name (str):
Required. The resource name of the Google Cloud Platform
- project. Written as ``projects/{projectID}``, where
+ project. Written as ``projects/{projectID}`` or
+ ``projects/{projectID}/locations/{location}``, where
``{projectID}`` is the `Google Cloud Platform project
- ID `__.
+ ID `__
+ and ``{location}`` is a Cloud region.
+
+ Examples: ``projects/my-project-123``,
+ ``projects/my-project-123/locations/global``.
- Example: ``projects/my-project-123``.
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
This corresponds to the ``project_name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
group_id (str):
- Required. The group for which events
- shall be returned.
+ Required. The group for which events shall be returned.
+ The ``group_id`` is a unique identifier for a particular
+ error group. The identifier is derived from key parts of
+ the error-log content and is treated as Service Data.
+ For information about how Service Data is handled, see
+ `Google Cloud Privacy
+ Notice `__.
This corresponds to the ``group_id`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -614,8 +955,10 @@ def sample_list_events():
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.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListEventsPager:
@@ -627,19 +970,20 @@ def sample_list_events():
"""
# 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_name, group_id])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_name, group_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 "
"the individual field arguments should be set."
)
- # Minor optimization to avoid making a copy if the user passes
- # in a error_stats_service.ListEventsRequest.
- # There's no risk of modifying the input as we've already verified
- # there are no flattened fields.
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
if not isinstance(request, error_stats_service.ListEventsRequest):
request = error_stats_service.ListEventsRequest(request)
# If we have keyword arguments corresponding to fields on the
@@ -661,6 +1005,9 @@ def sample_list_events():
),
)
+ # Validate the universe domain.
+ self._validate_universe_domain()
+
# Send the request.
response = rpc(
request,
@@ -675,6 +1022,8 @@ def sample_list_events():
method=rpc,
request=request,
response=response,
+ retry=retry,
+ timeout=timeout,
metadata=metadata,
)
@@ -683,17 +1032,24 @@ def sample_list_events():
def delete_events(
self,
- request: Union[error_stats_service.DeleteEventsRequest, dict] = None,
+ request: Optional[Union[error_stats_service.DeleteEventsRequest, dict]] = None,
*,
- project_name: str = None,
+ project_name: Optional[str] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> error_stats_service.DeleteEventsResponse:
r"""Deletes all error events of a given project.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
def sample_delete_events():
@@ -716,11 +1072,18 @@ def sample_delete_events():
The request object. Deletes all events in the project.
project_name (str):
Required. The resource name of the Google Cloud Platform
- project. Written as ``projects/{projectID}``, where
+ project. Written as ``projects/{projectID}`` or
+ ``projects/{projectID}/locations/{location}``, where
``{projectID}`` is the `Google Cloud Platform project
- ID `__.
+ ID `__
+ and ``{location}`` is a Cloud region.
+
+ Examples: ``projects/my-project-123``,
+ ``projects/my-project-123/locations/global``.
- Example: ``projects/my-project-123``.
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
This corresponds to the ``project_name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -728,8 +1091,10 @@ def sample_delete_events():
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.cloud.errorreporting_v1beta1.types.DeleteEventsResponse:
@@ -738,19 +1103,20 @@ def sample_delete_events():
"""
# 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_name])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_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 "
"the individual field arguments should be set."
)
- # Minor optimization to avoid making a copy if the user passes
- # in a error_stats_service.DeleteEventsRequest.
- # There's no risk of modifying the input as we've already verified
- # there are no flattened fields.
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
if not isinstance(request, error_stats_service.DeleteEventsRequest):
request = error_stats_service.DeleteEventsRequest(request)
# If we have keyword arguments corresponding to fields on the
@@ -770,6 +1136,9 @@ def sample_delete_events():
),
)
+ # Validate the universe domain.
+ self._validate_universe_domain()
+
# Send the request.
response = rpc(
request,
@@ -781,7 +1150,7 @@ def sample_delete_events():
# Done; return the response.
return response
- def __enter__(self):
+ def __enter__(self) -> "ErrorStatsServiceClient":
return self
def __exit__(self, type, value, traceback):
@@ -795,14 +1164,11 @@ def __exit__(self, type, value, traceback):
self.transport.close()
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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__ = ("ErrorStatsServiceClient",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/pagers.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/pagers.py
index 9fe297c6..190fecd9 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/pagers.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/pagers.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,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+from google.api_core import gapic_v1
+from google.api_core import retry as retries
+from google.api_core import retry_async as retries_async
from typing import (
Any,
AsyncIterator,
@@ -22,8 +25,18 @@
Tuple,
Optional,
Iterator,
+ Union,
)
+try:
+ OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
+ OptionalAsyncRetry = Union[
+ retries_async.AsyncRetry, gapic_v1.method._MethodDefault, None
+ ]
+except AttributeError: # pragma: NO COVER
+ OptionalRetry = Union[retries.Retry, object, None] # type: ignore
+ OptionalAsyncRetry = Union[retries_async.AsyncRetry, object, None] # type: ignore
+
from google.cloud.errorreporting_v1beta1.types import common
from google.cloud.errorreporting_v1beta1.types import error_stats_service
@@ -52,7 +65,9 @@ def __init__(
request: error_stats_service.ListGroupStatsRequest,
response: error_stats_service.ListGroupStatsResponse,
*,
- metadata: Sequence[Tuple[str, str]] = ()
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -63,12 +78,19 @@ def __init__(
The initial request object.
response (google.cloud.errorreporting_v1beta1.types.ListGroupStatsResponse):
The initial response object.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ 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, 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 = error_stats_service.ListGroupStatsRequest(request)
self._response = response
+ self._retry = retry
+ self._timeout = timeout
self._metadata = metadata
def __getattr__(self, name: str) -> Any:
@@ -79,7 +101,12 @@ def pages(self) -> Iterator[error_stats_service.ListGroupStatsResponse]:
yield self._response
while self._response.next_page_token:
self._request.page_token = self._response.next_page_token
- self._response = self._method(self._request, metadata=self._metadata)
+ self._response = self._method(
+ self._request,
+ retry=self._retry,
+ timeout=self._timeout,
+ metadata=self._metadata,
+ )
yield self._response
def __iter__(self) -> Iterator[error_stats_service.ErrorGroupStats]:
@@ -114,7 +141,9 @@ def __init__(
request: error_stats_service.ListGroupStatsRequest,
response: error_stats_service.ListGroupStatsResponse,
*,
- metadata: Sequence[Tuple[str, str]] = ()
+ retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -125,12 +154,19 @@ def __init__(
The initial request object.
response (google.cloud.errorreporting_v1beta1.types.ListGroupStatsResponse):
The initial response object.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ 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, 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 = error_stats_service.ListGroupStatsRequest(request)
self._response = response
+ self._retry = retry
+ self._timeout = timeout
self._metadata = metadata
def __getattr__(self, name: str) -> Any:
@@ -141,7 +177,12 @@ async def pages(self) -> AsyncIterator[error_stats_service.ListGroupStatsRespons
yield self._response
while self._response.next_page_token:
self._request.page_token = self._response.next_page_token
- self._response = await self._method(self._request, metadata=self._metadata)
+ self._response = await self._method(
+ self._request,
+ retry=self._retry,
+ timeout=self._timeout,
+ metadata=self._metadata,
+ )
yield self._response
def __aiter__(self) -> AsyncIterator[error_stats_service.ErrorGroupStats]:
@@ -180,7 +221,9 @@ def __init__(
request: error_stats_service.ListEventsRequest,
response: error_stats_service.ListEventsResponse,
*,
- metadata: Sequence[Tuple[str, str]] = ()
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiate the pager.
@@ -191,12 +234,19 @@ def __init__(
The initial request object.
response (google.cloud.errorreporting_v1beta1.types.ListEventsResponse):
The initial response object.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ 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, 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 = error_stats_service.ListEventsRequest(request)
self._response = response
+ self._retry = retry
+ self._timeout = timeout
self._metadata = metadata
def __getattr__(self, name: str) -> Any:
@@ -207,7 +257,12 @@ def pages(self) -> Iterator[error_stats_service.ListEventsResponse]:
yield self._response
while self._response.next_page_token:
self._request.page_token = self._response.next_page_token
- self._response = self._method(self._request, metadata=self._metadata)
+ self._response = self._method(
+ self._request,
+ retry=self._retry,
+ timeout=self._timeout,
+ metadata=self._metadata,
+ )
yield self._response
def __iter__(self) -> Iterator[common.ErrorEvent]:
@@ -242,7 +297,9 @@ def __init__(
request: error_stats_service.ListEventsRequest,
response: error_stats_service.ListEventsResponse,
*,
- metadata: Sequence[Tuple[str, str]] = ()
+ retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT,
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = ()
):
"""Instantiates the pager.
@@ -253,12 +310,19 @@ def __init__(
The initial request object.
response (google.cloud.errorreporting_v1beta1.types.ListEventsResponse):
The initial response object.
- metadata (Sequence[Tuple[str, str]]): Strings which should be
- sent along with the request as metadata.
+ 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, 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 = error_stats_service.ListEventsRequest(request)
self._response = response
+ self._retry = retry
+ self._timeout = timeout
self._metadata = metadata
def __getattr__(self, name: str) -> Any:
@@ -269,7 +333,12 @@ async def pages(self) -> AsyncIterator[error_stats_service.ListEventsResponse]:
yield self._response
while self._response.next_page_token:
self._request.page_token = self._response.next_page_token
- self._response = await self._method(self._request, metadata=self._metadata)
+ self._response = await self._method(
+ self._request,
+ retry=self._retry,
+ timeout=self._timeout,
+ metadata=self._metadata,
+ )
yield self._response
def __aiter__(self) -> AsyncIterator[common.ErrorEvent]:
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/README.rst b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/README.rst
new file mode 100644
index 00000000..9fb4cf06
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/README.rst
@@ -0,0 +1,9 @@
+
+transport inheritance structure
+_______________________________
+
+`ErrorStatsServiceTransport` is the ABC for all transports.
+- public child `ErrorStatsServiceGrpcTransport` for sync gRPC transport (defined in `grpc.py`).
+- public child `ErrorStatsServiceGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`).
+- private child `_BaseErrorStatsServiceRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`).
+- public child `ErrorStatsServiceRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`).
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/__init__.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/__init__.py
index 484e788e..8f452e45 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/__init__.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.
@@ -19,15 +19,20 @@
from .base import ErrorStatsServiceTransport
from .grpc import ErrorStatsServiceGrpcTransport
from .grpc_asyncio import ErrorStatsServiceGrpcAsyncIOTransport
+from .rest import ErrorStatsServiceRestTransport
+from .rest import ErrorStatsServiceRestInterceptor
# Compile a registry of transports.
_transport_registry = OrderedDict() # type: Dict[str, Type[ErrorStatsServiceTransport]]
_transport_registry["grpc"] = ErrorStatsServiceGrpcTransport
_transport_registry["grpc_asyncio"] = ErrorStatsServiceGrpcAsyncIOTransport
+_transport_registry["rest"] = ErrorStatsServiceRestTransport
__all__ = (
"ErrorStatsServiceTransport",
"ErrorStatsServiceGrpcTransport",
"ErrorStatsServiceGrpcAsyncIOTransport",
+ "ErrorStatsServiceRestTransport",
+ "ErrorStatsServiceRestInterceptor",
)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/base.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/base.py
index 0bdbe002..12f6992a 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/base.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/base.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.
@@ -15,7 +15,8 @@
#
import abc
from typing import Awaitable, Callable, Dict, Optional, Sequence, Union
-import pkg_resources
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
import google.auth # type: ignore
import google.api_core
@@ -24,17 +25,16 @@
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.cloud.errorreporting_v1beta1.types import error_stats_service
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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 ErrorStatsServiceTransport(abc.ABC):
@@ -48,19 +48,20 @@ def __init__(
self,
*,
host: str = DEFAULT_HOST,
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
**kwargs,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
@@ -81,15 +82,12 @@ def __init__(
be used for service account credentials.
"""
- # Save the hostname. Default to port 443 (HTTPS) if none is specified.
- if ":" not in host:
- host += ":443"
- self._host = host
-
scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}
# Save the scopes.
self._scopes = scopes
+ if not hasattr(self, "_ignore_credentials"):
+ self._ignore_credentials: bool = False
# If no credentials are provided, then determine the appropriate
# defaults.
@@ -102,10 +100,15 @@ def __init__(
credentials, _ = google.auth.load_credentials_from_file(
credentials_file, **scopes_kwargs, quota_project_id=quota_project_id
)
- elif credentials is None:
+ elif credentials is None and not self._ignore_credentials:
credentials, _ = google.auth.default(
**scopes_kwargs, quota_project_id=quota_project_id
)
+ # Don't apply audience if the credentials file passed from user.
+ if hasattr(credentials, "with_gdch_audience"):
+ credentials = credentials.with_gdch_audience(
+ api_audience if api_audience else host
+ )
# If the credentials are service account credentials, then always try to use self signed JWT.
if (
@@ -118,6 +121,15 @@ def __init__(
# Save the credentials.
self._credentials = credentials
+ # Save the hostname. Default to port 443 (HTTPS) if none is specified.
+ if ":" not in host:
+ host += ":443"
+ self._host = host
+
+ @property
+ def host(self):
+ return self._host
+
def _prep_wrapped_messages(self, client_info):
# Precompute the wrapped methods.
self._wrapped_methods = {
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc.py
index 8b9510b1..9bcb9106 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc.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,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,12 +24,89 @@
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.cloud.errorreporting_v1beta1.types import error_stats_service
from .base import ErrorStatsServiceTransport, 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.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": client_call_details.method,
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class ErrorStatsServiceGrpcTransport(ErrorStatsServiceTransport):
"""gRPC backend transport for ErrorStatsService.
@@ -48,36 +128,40 @@ def __init__(
self,
*,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
- credentials_file: str = None,
- scopes: Sequence[str] = None,
- channel: grpc.Channel = None,
- api_mtls_endpoint: str = None,
- client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
- ssl_channel_credentials: grpc.ChannelCredentials = None,
- client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
+ scopes: Optional[Sequence[str]] = None,
+ channel: Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]] = None,
+ api_mtls_endpoint: Optional[str] = None,
+ client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
- ignored if ``channel`` is provided.
- channel (Optional[grpc.Channel]): A ``Channel`` instance through
- which to make calls.
+ ignored if a ``channel`` instance is provided.
+ channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]):
+ A ``Channel`` instance through which to make calls, or a Callable
+ that constructs and returns one. If set to None, ``self.create_channel``
+ is used to create the channel. If a Callable is given, it will be called
+ with the same arguments as used in ``self.create_channel``.
api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint.
If provided, it overrides the ``host`` argument and tries to create
a mutual TLS channel with client SSL credentials from
@@ -87,11 +171,11 @@ def __init__(
private key bytes, both in PEM format. It is ignored if
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
- for the grpc channel. It is ignored if ``channel`` is provided.
+ for the grpc channel. It is ignored if a ``channel`` instance is provided.
client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
A callback to provide client certificate bytes and private key bytes,
both in PEM format. It is used to configure a mutual TLS channel. It is
- ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
+ ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -117,9 +201,10 @@ def __init__(
if client_cert_source:
warnings.warn("client_cert_source is deprecated", DeprecationWarning)
- if channel:
+ if isinstance(channel, grpc.Channel):
# Ignore credentials if a channel was passed.
- credentials = False
+ credentials = None
+ self._ignore_credentials = True
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
@@ -154,10 +239,13 @@ def __init__(
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
)
if not self._grpc_channel:
- self._grpc_channel = type(self).create_channel(
+ # initialize with the provided callable or the default channel
+ channel_init = channel or type(self).create_channel
+ self._grpc_channel = channel_init(
self._host,
# use the credentials which are saved
credentials=self._credentials,
@@ -173,15 +261,20 @@ 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
def create_channel(
cls,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
- credentials_file: str = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
**kwargs,
@@ -250,7 +343,7 @@ def list_group_stats(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_group_stats" not in self._stubs:
- self._stubs["list_group_stats"] = self.grpc_channel.unary_unary(
+ self._stubs["list_group_stats"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorStatsService/ListGroupStats",
request_serializer=error_stats_service.ListGroupStatsRequest.serialize,
response_deserializer=error_stats_service.ListGroupStatsResponse.deserialize,
@@ -278,7 +371,7 @@ def list_events(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_events" not in self._stubs:
- self._stubs["list_events"] = self.grpc_channel.unary_unary(
+ self._stubs["list_events"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorStatsService/ListEvents",
request_serializer=error_stats_service.ListEventsRequest.serialize,
response_deserializer=error_stats_service.ListEventsResponse.deserialize,
@@ -307,7 +400,7 @@ def delete_events(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_events" not in self._stubs:
- self._stubs["delete_events"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_events"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorStatsService/DeleteEvents",
request_serializer=error_stats_service.DeleteEventsRequest.serialize,
response_deserializer=error_stats_service.DeleteEventsResponse.deserialize,
@@ -315,7 +408,7 @@ def delete_events(
return self._stubs["delete_events"]
def close(self):
- self.grpc_channel.close()
+ self._logged_channel.close()
@property
def kind(self) -> str:
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc_asyncio.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc_asyncio.py
index cce5c95c..a6211645 100644
--- a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc_asyncio.py
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/grpc_asyncio.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,21 +13,106 @@
# 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
from google.api_core import gapic_v1
from google.api_core import grpc_helpers_async
+from google.api_core import exceptions as core_exceptions
+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.cloud.errorreporting_v1beta1.types import error_stats_service
from .base import ErrorStatsServiceTransport, DEFAULT_CLIENT_INFO
from .grpc import ErrorStatsServiceGrpcTransport
+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.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "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.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": str(client_call_details.method),
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class ErrorStatsServiceGrpcAsyncIOTransport(ErrorStatsServiceTransport):
"""gRPC AsyncIO backend transport for ErrorStatsService.
@@ -50,7 +135,7 @@ class ErrorStatsServiceGrpcAsyncIOTransport(ErrorStatsServiceTransport):
def create_channel(
cls,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
@@ -66,7 +151,6 @@ def create_channel(
the credentials from the environment.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
scopes (Optional[Sequence[str]]): A 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`.
@@ -93,37 +177,41 @@ def __init__(
self,
*,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
- channel: aio.Channel = None,
- api_mtls_endpoint: str = None,
- client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
- ssl_channel_credentials: grpc.ChannelCredentials = None,
- client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
- quota_project_id=None,
+ channel: Optional[Union[aio.Channel, Callable[..., aio.Channel]]] = None,
+ api_mtls_endpoint: Optional[str] = None,
+ client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
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`.
- channel (Optional[aio.Channel]): A ``Channel`` instance through
- which to make calls.
+ channel (Optional[Union[aio.Channel, Callable[..., aio.Channel]]]):
+ A ``Channel`` instance through which to make calls, or a Callable
+ that constructs and returns one. If set to None, ``self.create_channel``
+ is used to create the channel. If a Callable is given, it will be called
+ with the same arguments as used in ``self.create_channel``.
api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint.
If provided, it overrides the ``host`` argument and tries to create
a mutual TLS channel with client SSL credentials from
@@ -133,11 +221,11 @@ def __init__(
private key bytes, both in PEM format. It is ignored if
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
- for the grpc channel. It is ignored if ``channel`` is provided.
+ for the grpc channel. It is ignored if a ``channel`` instance is provided.
client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
A callback to provide client certificate bytes and private key bytes,
both in PEM format. It is used to configure a mutual TLS channel. It is
- ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
+ ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -163,9 +251,10 @@ def __init__(
if client_cert_source:
warnings.warn("client_cert_source is deprecated", DeprecationWarning)
- if channel:
+ if isinstance(channel, aio.Channel):
# Ignore credentials if a channel was passed.
- credentials = False
+ credentials = None
+ self._ignore_credentials = True
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
@@ -199,10 +288,13 @@ def __init__(
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
)
if not self._grpc_channel:
- self._grpc_channel = type(self).create_channel(
+ # initialize with the provided callable or the default channel
+ channel_init = channel or type(self).create_channel
+ self._grpc_channel = channel_init(
self._host,
# use the credentials which are saved
credentials=self._credentials,
@@ -218,7 +310,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
@@ -253,7 +351,7 @@ def list_group_stats(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_group_stats" not in self._stubs:
- self._stubs["list_group_stats"] = self.grpc_channel.unary_unary(
+ self._stubs["list_group_stats"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorStatsService/ListGroupStats",
request_serializer=error_stats_service.ListGroupStatsRequest.serialize,
response_deserializer=error_stats_service.ListGroupStatsResponse.deserialize,
@@ -282,7 +380,7 @@ def list_events(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "list_events" not in self._stubs:
- self._stubs["list_events"] = self.grpc_channel.unary_unary(
+ self._stubs["list_events"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorStatsService/ListEvents",
request_serializer=error_stats_service.ListEventsRequest.serialize,
response_deserializer=error_stats_service.ListEventsResponse.deserialize,
@@ -311,15 +409,44 @@ def delete_events(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "delete_events" not in self._stubs:
- self._stubs["delete_events"] = self.grpc_channel.unary_unary(
+ self._stubs["delete_events"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ErrorStatsService/DeleteEvents",
request_serializer=error_stats_service.DeleteEventsRequest.serialize,
response_deserializer=error_stats_service.DeleteEventsResponse.deserialize,
)
return self._stubs["delete_events"]
+ def _prep_wrapped_messages(self, client_info):
+ """Precompute the wrapped methods, overriding the base class method to use async wrappers."""
+ self._wrapped_methods = {
+ self.list_group_stats: self._wrap_method(
+ self.list_group_stats,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.list_events: self._wrap_method(
+ self.list_events,
+ default_timeout=None,
+ client_info=client_info,
+ ),
+ self.delete_events: self._wrap_method(
+ self.delete_events,
+ 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"
__all__ = ("ErrorStatsServiceGrpcAsyncIOTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/rest.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/rest.py
new file mode 100644
index 00000000..1b7a4cc5
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/rest.py
@@ -0,0 +1,840 @@
+# -*- 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 logging
+import json # type: ignore
+
+from google.auth.transport.requests import AuthorizedSession # 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 gapic_v1
+import google.protobuf
+
+from google.protobuf import json_format
+
+from requests import __version__ as requests_version
+import dataclasses
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+import warnings
+
+
+from google.cloud.errorreporting_v1beta1.types import error_stats_service
+
+
+from .rest_base import _BaseErrorStatsServiceRestTransport
+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
+
+ 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=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 ErrorStatsServiceRestInterceptor:
+ """Interceptor for ErrorStatsService.
+
+ Interceptors are used to manipulate requests, request metadata, and responses
+ in arbitrary ways.
+ Example use cases include:
+ * Logging
+ * Verifying requests according to service or custom semantics
+ * Stripping extraneous information from responses
+
+ These use cases and more can be enabled by injecting an
+ instance of a custom subclass when constructing the ErrorStatsServiceRestTransport.
+
+ .. code-block:: python
+ class MyCustomErrorStatsServiceInterceptor(ErrorStatsServiceRestInterceptor):
+ def pre_delete_events(self, request, metadata):
+ logging.log(f"Received request: {request}")
+ return request, metadata
+
+ def post_delete_events(self, response):
+ logging.log(f"Received response: {response}")
+ return response
+
+ def pre_list_events(self, request, metadata):
+ logging.log(f"Received request: {request}")
+ return request, metadata
+
+ def post_list_events(self, response):
+ logging.log(f"Received response: {response}")
+ return response
+
+ def pre_list_group_stats(self, request, metadata):
+ logging.log(f"Received request: {request}")
+ return request, metadata
+
+ def post_list_group_stats(self, response):
+ logging.log(f"Received response: {response}")
+ return response
+
+ transport = ErrorStatsServiceRestTransport(interceptor=MyCustomErrorStatsServiceInterceptor())
+ client = ErrorStatsServiceClient(transport=transport)
+
+
+ """
+
+ def pre_delete_events(
+ self,
+ request: error_stats_service.DeleteEventsRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_stats_service.DeleteEventsRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Pre-rpc interceptor for delete_events
+
+ Override in a subclass to manipulate the request or metadata
+ before they are sent to the ErrorStatsService server.
+ """
+ return request, metadata
+
+ def post_delete_events(
+ self, response: error_stats_service.DeleteEventsResponse
+ ) -> error_stats_service.DeleteEventsResponse:
+ """Post-rpc interceptor for delete_events
+
+ DEPRECATED. Please use the `post_delete_events_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
+ after it is returned by the ErrorStatsService server but before
+ it is returned to user code. This `post_delete_events` interceptor runs
+ before the `post_delete_events_with_metadata` interceptor.
+ """
+ return response
+
+ def post_delete_events_with_metadata(
+ self,
+ response: error_stats_service.DeleteEventsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_stats_service.DeleteEventsResponse,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
+ """Post-rpc interceptor for delete_events
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the ErrorStatsService server but before it is returned to user code.
+
+ We recommend only using this `post_delete_events_with_metadata`
+ interceptor in new development instead of the `post_delete_events` interceptor.
+ When both interceptors are used, this `post_delete_events_with_metadata` interceptor runs after the
+ `post_delete_events` interceptor. The (possibly modified) response returned by
+ `post_delete_events` will be passed to
+ `post_delete_events_with_metadata`.
+ """
+ return response, metadata
+
+ def pre_list_events(
+ self,
+ request: error_stats_service.ListEventsRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_stats_service.ListEventsRequest, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Pre-rpc interceptor for list_events
+
+ Override in a subclass to manipulate the request or metadata
+ before they are sent to the ErrorStatsService server.
+ """
+ return request, metadata
+
+ def post_list_events(
+ self, response: error_stats_service.ListEventsResponse
+ ) -> error_stats_service.ListEventsResponse:
+ """Post-rpc interceptor for list_events
+
+ DEPRECATED. Please use the `post_list_events_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
+ after it is returned by the ErrorStatsService server but before
+ it is returned to user code. This `post_list_events` interceptor runs
+ before the `post_list_events_with_metadata` interceptor.
+ """
+ return response
+
+ def post_list_events_with_metadata(
+ self,
+ response: error_stats_service.ListEventsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_stats_service.ListEventsResponse, Sequence[Tuple[str, Union[str, bytes]]]
+ ]:
+ """Post-rpc interceptor for list_events
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the ErrorStatsService server but before it is returned to user code.
+
+ We recommend only using this `post_list_events_with_metadata`
+ interceptor in new development instead of the `post_list_events` interceptor.
+ When both interceptors are used, this `post_list_events_with_metadata` interceptor runs after the
+ `post_list_events` interceptor. The (possibly modified) response returned by
+ `post_list_events` will be passed to
+ `post_list_events_with_metadata`.
+ """
+ return response, metadata
+
+ def pre_list_group_stats(
+ self,
+ request: error_stats_service.ListGroupStatsRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_stats_service.ListGroupStatsRequest,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
+ """Pre-rpc interceptor for list_group_stats
+
+ Override in a subclass to manipulate the request or metadata
+ before they are sent to the ErrorStatsService server.
+ """
+ return request, metadata
+
+ def post_list_group_stats(
+ self, response: error_stats_service.ListGroupStatsResponse
+ ) -> error_stats_service.ListGroupStatsResponse:
+ """Post-rpc interceptor for list_group_stats
+
+ DEPRECATED. Please use the `post_list_group_stats_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
+ after it is returned by the ErrorStatsService server but before
+ it is returned to user code. This `post_list_group_stats` interceptor runs
+ before the `post_list_group_stats_with_metadata` interceptor.
+ """
+ return response
+
+ def post_list_group_stats_with_metadata(
+ self,
+ response: error_stats_service.ListGroupStatsResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ error_stats_service.ListGroupStatsResponse,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
+ """Post-rpc interceptor for list_group_stats
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the ErrorStatsService server but before it is returned to user code.
+
+ We recommend only using this `post_list_group_stats_with_metadata`
+ interceptor in new development instead of the `post_list_group_stats` interceptor.
+ When both interceptors are used, this `post_list_group_stats_with_metadata` interceptor runs after the
+ `post_list_group_stats` interceptor. The (possibly modified) response returned by
+ `post_list_group_stats` will be passed to
+ `post_list_group_stats_with_metadata`.
+ """
+ return response, metadata
+
+
+@dataclasses.dataclass
+class ErrorStatsServiceRestStub:
+ _session: AuthorizedSession
+ _host: str
+ _interceptor: ErrorStatsServiceRestInterceptor
+
+
+class ErrorStatsServiceRestTransport(_BaseErrorStatsServiceRestTransport):
+ """REST backend synchronous transport for ErrorStatsService.
+
+ An API for retrieving and managing error statistics as well
+ as data for individual events.
+
+ 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 = "clouderrorreporting.googleapis.com",
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
+ scopes: Optional[Sequence[str]] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ quota_project_id: Optional[str] = None,
+ client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
+ always_use_jwt_access: Optional[bool] = False,
+ url_scheme: str = "https",
+ interceptor: Optional[ErrorStatsServiceRestInterceptor] = None,
+ api_audience: Optional[str] = None,
+ ) -> None:
+ """Instantiate the transport.
+
+ Args:
+ host (Optional[str]):
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
+ credentials (Optional[google.auth.credentials.Credentials]): The
+ authorization credentials to attach to requests. These
+ credentials identify the application to the service; if none
+ are specified, the client will attempt to ascertain the
+ credentials from the environment.
+
+ credentials_file (Optional[str]): A file with credentials that can
+ be loaded with :func:`google.auth.load_credentials_from_file`.
+ This argument is ignored if ``channel`` is provided.
+ scopes (Optional(Sequence[str])): A list of scopes. This argument is
+ ignored if ``channel`` is provided.
+ client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
+ certificate to configure mutual TLS HTTP channel. It is ignored
+ if ``channel`` is provided.
+ quota_project_id (Optional[str]): An optional project to use for billing
+ and quota.
+ client_info (google.api_core.gapic_v1.client_info.ClientInfo):
+ The client info used to send a user-agent string along with
+ API requests. If ``None``, then default info will be used.
+ Generally, you only need to set this if you are developing
+ your own client library.
+ always_use_jwt_access (Optional[bool]): Whether self signed JWT should
+ be used for service account credentials.
+ url_scheme: the protocol scheme for the API endpoint. Normally
+ "https", but for testing or local servers,
+ "http" can be specified.
+ """
+ # Run the base constructor
+ # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
+ # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
+ # credentials object
+ 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(
+ self._credentials, default_host=self.DEFAULT_HOST
+ )
+ if client_cert_source_for_mtls:
+ self._session.configure_mtls_channel(client_cert_source_for_mtls)
+ self._interceptor = interceptor or ErrorStatsServiceRestInterceptor()
+ self._prep_wrapped_messages(client_info)
+
+ class _DeleteEvents(
+ _BaseErrorStatsServiceRestTransport._BaseDeleteEvents, ErrorStatsServiceRestStub
+ ):
+ def __hash__(self):
+ return hash("ErrorStatsServiceRestTransport.DeleteEvents")
+
+ @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: error_stats_service.DeleteEventsRequest,
+ *,
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Optional[float] = None,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
+ ) -> error_stats_service.DeleteEventsResponse:
+ r"""Call the delete events method over HTTP.
+
+ Args:
+ request (~.error_stats_service.DeleteEventsRequest):
+ The request object. Deletes all events in the project.
+ 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, 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:
+ ~.error_stats_service.DeleteEventsResponse:
+ Response message for deleting error
+ events.
+
+ """
+
+ http_options = (
+ _BaseErrorStatsServiceRestTransport._BaseDeleteEvents._get_http_options()
+ )
+
+ request, metadata = self._interceptor.pre_delete_events(request, metadata)
+ transcoded_request = _BaseErrorStatsServiceRestTransport._BaseDeleteEvents._get_transcoded_request(
+ http_options, request
+ )
+
+ # Jsonify the query params
+ query_params = _BaseErrorStatsServiceRestTransport._BaseDeleteEvents._get_query_params_json(
+ transcoded_request
+ )
+
+ 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.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceClient.DeleteEvents",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": "DeleteEvents",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
+
+ # Send the request
+ response = ErrorStatsServiceRestTransport._DeleteEvents._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ )
+
+ # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
+ # subclass.
+ if response.status_code >= 400:
+ raise core_exceptions.from_http_response(response)
+
+ # Return the response
+ resp = error_stats_service.DeleteEventsResponse()
+ pb_resp = error_stats_service.DeleteEventsResponse.pb(resp)
+
+ json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
+ resp = self._interceptor.post_delete_events(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_delete_events_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = error_stats_service.DeleteEventsResponse.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.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceClient.delete_events",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": "DeleteEvents",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
+ return resp
+
+ class _ListEvents(
+ _BaseErrorStatsServiceRestTransport._BaseListEvents, ErrorStatsServiceRestStub
+ ):
+ def __hash__(self):
+ return hash("ErrorStatsServiceRestTransport.ListEvents")
+
+ @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: error_stats_service.ListEventsRequest,
+ *,
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Optional[float] = None,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
+ ) -> error_stats_service.ListEventsResponse:
+ r"""Call the list events method over HTTP.
+
+ Args:
+ request (~.error_stats_service.ListEventsRequest):
+ The request object. Specifies a set of error events to
+ return.
+ 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, 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:
+ ~.error_stats_service.ListEventsResponse:
+ Contains a set of requested error
+ events.
+
+ """
+
+ http_options = (
+ _BaseErrorStatsServiceRestTransport._BaseListEvents._get_http_options()
+ )
+
+ request, metadata = self._interceptor.pre_list_events(request, metadata)
+ transcoded_request = _BaseErrorStatsServiceRestTransport._BaseListEvents._get_transcoded_request(
+ http_options, request
+ )
+
+ # Jsonify the query params
+ query_params = _BaseErrorStatsServiceRestTransport._BaseListEvents._get_query_params_json(
+ transcoded_request
+ )
+
+ 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.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceClient.ListEvents",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": "ListEvents",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
+
+ # Send the request
+ response = ErrorStatsServiceRestTransport._ListEvents._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ )
+
+ # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
+ # subclass.
+ if response.status_code >= 400:
+ raise core_exceptions.from_http_response(response)
+
+ # Return the response
+ resp = error_stats_service.ListEventsResponse()
+ pb_resp = error_stats_service.ListEventsResponse.pb(resp)
+
+ json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
+ resp = self._interceptor.post_list_events(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_events_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = error_stats_service.ListEventsResponse.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.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceClient.list_events",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": "ListEvents",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
+ return resp
+
+ class _ListGroupStats(
+ _BaseErrorStatsServiceRestTransport._BaseListGroupStats,
+ ErrorStatsServiceRestStub,
+ ):
+ def __hash__(self):
+ return hash("ErrorStatsServiceRestTransport.ListGroupStats")
+
+ @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: error_stats_service.ListGroupStatsRequest,
+ *,
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Optional[float] = None,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
+ ) -> error_stats_service.ListGroupStatsResponse:
+ r"""Call the list group stats method over HTTP.
+
+ Args:
+ request (~.error_stats_service.ListGroupStatsRequest):
+ The request object. Specifies a set of ``ErrorGroupStats`` to return.
+ 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, 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:
+ ~.error_stats_service.ListGroupStatsResponse:
+ Contains a set of requested error
+ group stats.
+
+ """
+
+ http_options = (
+ _BaseErrorStatsServiceRestTransport._BaseListGroupStats._get_http_options()
+ )
+
+ request, metadata = self._interceptor.pre_list_group_stats(
+ request, metadata
+ )
+ transcoded_request = _BaseErrorStatsServiceRestTransport._BaseListGroupStats._get_transcoded_request(
+ http_options, request
+ )
+
+ # Jsonify the query params
+ query_params = _BaseErrorStatsServiceRestTransport._BaseListGroupStats._get_query_params_json(
+ transcoded_request
+ )
+
+ 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.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceClient.ListGroupStats",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": "ListGroupStats",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
+
+ # Send the request
+ response = ErrorStatsServiceRestTransport._ListGroupStats._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ )
+
+ # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
+ # subclass.
+ if response.status_code >= 400:
+ raise core_exceptions.from_http_response(response)
+
+ # Return the response
+ resp = error_stats_service.ListGroupStatsResponse()
+ pb_resp = error_stats_service.ListGroupStatsResponse.pb(resp)
+
+ json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
+ resp = self._interceptor.post_list_group_stats(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_list_group_stats_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = (
+ error_stats_service.ListGroupStatsResponse.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.devtools.clouderrorreporting_v1beta1.ErrorStatsServiceClient.list_group_stats",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ErrorStatsService",
+ "rpcName": "ListGroupStats",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
+ return resp
+
+ @property
+ def delete_events(
+ self,
+ ) -> Callable[
+ [error_stats_service.DeleteEventsRequest],
+ error_stats_service.DeleteEventsResponse,
+ ]:
+ # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
+ # In C++ this would require a dynamic_cast
+ return self._DeleteEvents(self._session, self._host, self._interceptor) # type: ignore
+
+ @property
+ def list_events(
+ self,
+ ) -> Callable[
+ [error_stats_service.ListEventsRequest], error_stats_service.ListEventsResponse
+ ]:
+ # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
+ # In C++ this would require a dynamic_cast
+ return self._ListEvents(self._session, self._host, self._interceptor) # type: ignore
+
+ @property
+ def list_group_stats(
+ self,
+ ) -> Callable[
+ [error_stats_service.ListGroupStatsRequest],
+ error_stats_service.ListGroupStatsResponse,
+ ]:
+ # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
+ # In C++ this would require a dynamic_cast
+ return self._ListGroupStats(self._session, self._host, self._interceptor) # type: ignore
+
+ @property
+ def kind(self) -> str:
+ return "rest"
+
+ def close(self):
+ self._session.close()
+
+
+__all__ = ("ErrorStatsServiceRestTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/rest_base.py b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/rest_base.py
new file mode 100644
index 00000000..77269984
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/error_stats_service/transports/rest_base.py
@@ -0,0 +1,248 @@
+# -*- 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 .base import ErrorStatsServiceTransport, DEFAULT_CLIENT_INFO
+
+import re
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+
+
+from google.cloud.errorreporting_v1beta1.types import error_stats_service
+
+
+class _BaseErrorStatsServiceRestTransport(ErrorStatsServiceTransport):
+ """Base REST backend transport for ErrorStatsService.
+
+ 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 = "clouderrorreporting.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: 'clouderrorreporting.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 _BaseDeleteEvents:
+ 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": "/v1beta1/{project_name=projects/*}/events",
+ },
+ {
+ "method": "delete",
+ "uri": "/v1beta1/{project_name=projects/*/locations/*}/events",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = error_stats_service.DeleteEventsRequest.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(
+ _BaseErrorStatsServiceRestTransport._BaseDeleteEvents._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListEvents:
+ def __hash__(self): # pragma: NO COVER
+ return NotImplementedError("__hash__ must be implemented.")
+
+ __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {
+ "groupId": "",
+ }
+
+ @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": "/v1beta1/{project_name=projects/*}/events",
+ },
+ {
+ "method": "get",
+ "uri": "/v1beta1/{project_name=projects/*/locations/*}/events",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = error_stats_service.ListEventsRequest.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(
+ _BaseErrorStatsServiceRestTransport._BaseListEvents._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+ class _BaseListGroupStats:
+ 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": "/v1beta1/{project_name=projects/*}/groupStats",
+ },
+ {
+ "method": "get",
+ "uri": "/v1beta1/{project_name=projects/*/locations/*}/groupStats",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = error_stats_service.ListGroupStatsRequest.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(
+ _BaseErrorStatsServiceRestTransport._BaseListGroupStats._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+
+__all__ = ("_BaseErrorStatsServiceRestTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/__init__.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/__init__.py
index 5efbd937..2e3c1322 100644
--- a/google/cloud/errorreporting_v1beta1/services/report_errors_service/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/__init__.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.
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/async_client.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/async_client.py
index a8f85c33..33c90024 100644
--- a/google/cloud/errorreporting_v1beta1/services/report_errors_service/async_client.py
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/async_client.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,37 +13,64 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging as std_logging
from collections import OrderedDict
-import functools
import re
-from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union
-import pkg_resources
+from typing import (
+ Dict,
+ Callable,
+ Mapping,
+ MutableMapping,
+ MutableSequence,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+)
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
from google.api_core.client_options import ClientOptions
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
-from google.api_core import retry as retries
+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:
- OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault]
+ OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
- OptionalRetry = Union[retries.Retry, object] # type: ignore
+ OptionalRetry = Union[retries.AsyncRetry, object, None] # type: ignore
from google.cloud.errorreporting_v1beta1.types import report_errors_service
from .transports.base import ReportErrorsServiceTransport, DEFAULT_CLIENT_INFO
from .transports.grpc_asyncio import ReportErrorsServiceGrpcAsyncIOTransport
from .client import ReportErrorsServiceClient
+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 ReportErrorsServiceAsyncClient:
"""An API for reporting error events."""
_client: ReportErrorsServiceClient
+ # Copy defaults from the synchronous client for use here.
+ # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
DEFAULT_ENDPOINT = ReportErrorsServiceClient.DEFAULT_ENDPOINT
DEFAULT_MTLS_ENDPOINT = ReportErrorsServiceClient.DEFAULT_MTLS_ENDPOINT
+ _DEFAULT_ENDPOINT_TEMPLATE = ReportErrorsServiceClient._DEFAULT_ENDPOINT_TEMPLATE
+ _DEFAULT_UNIVERSE = ReportErrorsServiceClient._DEFAULT_UNIVERSE
common_billing_account_path = staticmethod(
ReportErrorsServiceClient.common_billing_account_path
@@ -119,7 +146,7 @@ def get_mtls_endpoint_and_cert_source(
The API endpoint is determined in the following order:
(1) if `client_options.api_endpoint` if provided, use the provided one.
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
- default mTLS endpoint; if the environment variabel is "never", use the default API
+ default mTLS endpoint; if the environment variable is "never", use the default API
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
use the default API endpoint.
@@ -148,20 +175,42 @@ def transport(self) -> ReportErrorsServiceTransport:
"""
return self._client.transport
- get_transport_class = functools.partial(
- type(ReportErrorsServiceClient).get_transport_class,
- type(ReportErrorsServiceClient),
- )
+ @property
+ def api_endpoint(self):
+ """Return the API endpoint used by the client instance.
+
+ Returns:
+ str: The API endpoint used by the client instance.
+ """
+ return self._client._api_endpoint
+
+ @property
+ def universe_domain(self) -> str:
+ """Return the universe domain used by the client instance.
+
+ Returns:
+ str: The universe domain used
+ by the client instance.
+ """
+ return self._client._universe_domain
+
+ get_transport_class = ReportErrorsServiceClient.get_transport_class
def __init__(
self,
*,
- credentials: ga_credentials.Credentials = None,
- transport: Union[str, ReportErrorsServiceTransport] = "grpc_asyncio",
- client_options: ClientOptions = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ transport: Optional[
+ Union[
+ str,
+ ReportErrorsServiceTransport,
+ Callable[..., ReportErrorsServiceTransport],
+ ]
+ ] = "grpc_asyncio",
+ client_options: Optional[ClientOptions] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
- """Instantiates the report errors service client.
+ """Instantiates the report errors service async client.
Args:
credentials (Optional[google.auth.credentials.Credentials]): The
@@ -169,26 +218,43 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ~.ReportErrorsServiceTransport]): The
- transport to use. If set to None, a transport is chosen
- automatically.
- client_options (ClientOptions): Custom options for the client. It
- won't take effect if a ``transport`` instance is provided.
- (1) The ``api_endpoint`` property can be used to override the
- default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
- environment variable can also be used to override the endpoint:
+ transport (Optional[Union[str,ReportErrorsServiceTransport,Callable[..., ReportErrorsServiceTransport]]]):
+ The transport to use, or a Callable that constructs and returns a new transport to use.
+ If a Callable is given, it will be called with the same set of initialization
+ arguments as used in the ReportErrorsServiceTransport constructor.
+ If set to None, a transport is chosen automatically.
+ client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]):
+ Custom options for the client.
+
+ 1. The ``api_endpoint`` property can be used to override the
+ default endpoint provided by the client when ``transport`` is
+ not explicitly provided. Only if this property is not set and
+ ``transport`` was not explicitly provided, the endpoint is
+ determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment
+ variable, which have one of the following values:
"always" (always use the default mTLS endpoint), "never" (always
- use the default regular endpoint) and "auto" (auto switch to the
- default mTLS endpoint if client certificate is present, this is
- the default value). However, the ``api_endpoint`` property takes
- precedence if provided.
- (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
+ use the default regular endpoint) and "auto" (auto-switch to the
+ default mTLS endpoint if client certificate is present; this is
+ the default value).
+
+ 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
is "true", then the ``client_cert_source`` property can be used
- to provide client certificate for mutual TLS transport. If
+ to provide a client certificate for mTLS transport. If
not provided, the default SSL client certificate will be used if
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
set, no client certificate will be used.
+ 3. The ``universe_domain`` property can be used to override the
+ default "googleapis.com" universe. Note that ``api_endpoint``
+ property still takes precedence; and ``universe_domain`` is
+ currently not supported for mTLS.
+
+ 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're developing
+ your own client library.
+
Raises:
google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
creation failed for any reason.
@@ -200,15 +266,39 @@ def __init__(
client_info=client_info,
)
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ std_logging.DEBUG
+ ): # pragma: NO COVER
+ _LOGGER.debug(
+ "Created client `google.devtools.clouderrorreporting_v1beta1.ReportErrorsServiceAsyncClient`.",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "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.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "credentialsType": None,
+ },
+ )
+
async def report_error_event(
self,
- request: Union[report_errors_service.ReportErrorEventRequest, dict] = None,
+ request: Optional[
+ Union[report_errors_service.ReportErrorEventRequest, dict]
+ ] = None,
*,
- project_name: str = None,
- event: report_errors_service.ReportedErrorEvent = None,
+ project_name: Optional[str] = None,
+ event: Optional[report_errors_service.ReportedErrorEvent] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> report_errors_service.ReportErrorEventResponse:
r"""Report an individual error event and record the event to a log.
@@ -219,16 +309,29 @@ async def report_error_event(
``POST https://clouderrorreporting.googleapis.com/v1beta1/{projectName}/events:report?key=123ABC456``
- **Note:** `Error Reporting `__ is a global
- service built on Cloud Logging and doesn't analyze logs stored
- in regional log buckets or logs routed to other Google Cloud
- projects.
+ **Note:** [Error Reporting]
+ (https://cloud.google.com/error-reporting) is a service built on
+ Cloud Logging and can analyze log entries when all of the
+ following are true:
- For more information, see `Using Error Reporting with
- regionalized logs `__.
+ - Customer-managed encryption keys (CMEK) are disabled on the
+ log bucket.
+ - The log bucket satisfies one of the following:
+
+ - The log bucket is stored in the same project where the
+ logs originated.
+ - The logs were routed to a project, and then that project
+ stored those logs in a log bucket that it owns.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
async def sample_report_error_event():
@@ -251,9 +354,9 @@ async def sample_report_error_event():
print(response)
Args:
- request (Union[google.cloud.errorreporting_v1beta1.types.ReportErrorEventRequest, dict]):
- The request object. A request for reporting an
- individual error event.
+ request (Optional[Union[google.cloud.errorreporting_v1beta1.types.ReportErrorEventRequest, dict]]):
+ The request object. A request for reporting an individual
+ error event.
project_name (:class:`str`):
Required. The resource name of the Google Cloud Platform
project. Written as ``projects/{projectId}``, where
@@ -272,11 +375,13 @@ async def sample_report_error_event():
This corresponds to the ``event`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- retry (google.api_core.retry.Retry): Designation of what errors, if any,
+ 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.cloud.errorreporting_v1beta1.types.ReportErrorEventResponse:
@@ -286,16 +391,22 @@ async def sample_report_error_event():
"""
# 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_name, event])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_name, event]
+ 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 "
"the individual field arguments should be set."
)
- request = report_errors_service.ReportErrorEventRequest(request)
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
+ if not isinstance(request, report_errors_service.ReportErrorEventRequest):
+ request = report_errors_service.ReportErrorEventRequest(request)
# If we have keyword arguments corresponding to fields on the
# request, apply these.
@@ -306,11 +417,9 @@ async def sample_report_error_event():
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
- rpc = gapic_v1.method_async.wrap_method(
- self._client._transport.report_error_event,
- default_timeout=None,
- client_info=DEFAULT_CLIENT_INFO,
- )
+ rpc = self._client._transport._wrapped_methods[
+ self._client._transport.report_error_event
+ ]
# Certain fields should be provided within the metadata header;
# add these here.
@@ -320,6 +429,9 @@ async def sample_report_error_event():
),
)
+ # Validate the universe domain.
+ self._client._validate_universe_domain()
+
# Send the request.
response = await rpc(
request,
@@ -331,21 +443,19 @@ async def sample_report_error_event():
# Done; return the response.
return response
- async def __aenter__(self):
+ async def __aenter__(self) -> "ReportErrorsServiceAsyncClient":
return self
async def __aexit__(self, exc_type, exc, tb):
await self.transport.close()
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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__ = ("ReportErrorsServiceAsyncClient",)
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/client.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/client.py
index 297c8d8e..9dc31d29 100644
--- a/google/cloud/errorreporting_v1beta1/services/report_errors_service/client.py
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/client.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.
@@ -14,10 +14,27 @@
# limitations under the License.
#
from collections import OrderedDict
+from http import HTTPStatus
+import json
+import logging as std_logging
import os
import re
-from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union
-import pkg_resources
+from typing import (
+ Dict,
+ Callable,
+ Mapping,
+ MutableMapping,
+ MutableSequence,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+ cast,
+)
+import warnings
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
from google.api_core import client_options as client_options_lib
from google.api_core import exceptions as core_exceptions
@@ -28,16 +45,27 @@
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]
+ OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
except AttributeError: # pragma: NO COVER
- OptionalRetry = Union[retries.Retry, object] # type: ignore
+ 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.cloud.errorreporting_v1beta1.types import report_errors_service
from .transports.base import ReportErrorsServiceTransport, DEFAULT_CLIENT_INFO
from .transports.grpc import ReportErrorsServiceGrpcTransport
from .transports.grpc_asyncio import ReportErrorsServiceGrpcAsyncIOTransport
+from .transports.rest import ReportErrorsServiceRestTransport
class ReportErrorsServiceClientMeta(type):
@@ -53,10 +81,11 @@ class ReportErrorsServiceClientMeta(type):
) # type: Dict[str, Type[ReportErrorsServiceTransport]]
_transport_registry["grpc"] = ReportErrorsServiceGrpcTransport
_transport_registry["grpc_asyncio"] = ReportErrorsServiceGrpcAsyncIOTransport
+ _transport_registry["rest"] = ReportErrorsServiceRestTransport
def get_transport_class(
cls,
- label: str = None,
+ label: Optional[str] = None,
) -> Type[ReportErrorsServiceTransport]:
"""Returns an appropriate transport class.
@@ -109,11 +138,15 @@ def _get_default_mtls_endpoint(api_endpoint):
return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
+ # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
DEFAULT_ENDPOINT = "clouderrorreporting.googleapis.com"
DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore
DEFAULT_ENDPOINT
)
+ _DEFAULT_ENDPOINT_TEMPLATE = "clouderrorreporting.{UNIVERSE_DOMAIN}"
+ _DEFAULT_UNIVERSE = "googleapis.com"
+
@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -242,7 +275,7 @@ def parse_common_location_path(path: str) -> Dict[str, str]:
def get_mtls_endpoint_and_cert_source(
cls, client_options: Optional[client_options_lib.ClientOptions] = None
):
- """Return the API endpoint and client cert source for mutual TLS.
+ """Deprecated. Return the API endpoint and client cert source for mutual TLS.
The client cert source is determined in the following order:
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
@@ -254,7 +287,7 @@ def get_mtls_endpoint_and_cert_source(
The API endpoint is determined in the following order:
(1) if `client_options.api_endpoint` if provided, use the provided one.
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
- default mTLS endpoint; if the environment variabel is "never", use the default API
+ default mTLS endpoint; if the environment variable is "never", use the default API
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
use the default API endpoint.
@@ -272,6 +305,11 @@ def get_mtls_endpoint_and_cert_source(
Raises:
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
"""
+
+ warnings.warn(
+ "get_mtls_endpoint_and_cert_source is deprecated. Use the api_endpoint property instead.",
+ DeprecationWarning,
+ )
if client_options is None:
client_options = client_options_lib.ClientOptions()
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
@@ -305,12 +343,183 @@ def get_mtls_endpoint_and_cert_source(
return api_endpoint, client_cert_source
+ @staticmethod
+ def _read_environment_variables():
+ """Returns the environment variables used by the client.
+
+ Returns:
+ Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE,
+ GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables.
+
+ Raises:
+ ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not
+ any of ["true", "false"].
+ 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_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
+
+ @staticmethod
+ def _get_client_cert_source(provided_cert_source, use_cert_flag):
+ """Return the client cert source to be used by the client.
+
+ Args:
+ provided_cert_source (bytes): The client certificate source provided.
+ use_cert_flag (bool): A flag indicating whether to use the client certificate.
+
+ Returns:
+ bytes or None: The client cert source to be used by the client.
+ """
+ client_cert_source = None
+ if use_cert_flag:
+ if provided_cert_source:
+ client_cert_source = provided_cert_source
+ elif mtls.has_default_client_cert_source():
+ client_cert_source = mtls.default_client_cert_source()
+ return client_cert_source
+
+ @staticmethod
+ def _get_api_endpoint(
+ api_override, client_cert_source, universe_domain, use_mtls_endpoint
+ ):
+ """Return the API endpoint used by the client.
+
+ Args:
+ api_override (str): The API endpoint override. If specified, this is always
+ the return value of this function and the other arguments are not used.
+ client_cert_source (bytes): The client certificate source used by the client.
+ universe_domain (str): The universe domain used by the client.
+ use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters.
+ Possible values are "always", "auto", or "never".
+
+ Returns:
+ str: The API endpoint to be used by the client.
+ """
+ if api_override is not None:
+ api_endpoint = api_override
+ elif use_mtls_endpoint == "always" or (
+ use_mtls_endpoint == "auto" and client_cert_source
+ ):
+ _default_universe = ReportErrorsServiceClient._DEFAULT_UNIVERSE
+ if universe_domain != _default_universe:
+ raise MutualTLSChannelError(
+ f"mTLS is not supported in any universe other than {_default_universe}."
+ )
+ api_endpoint = ReportErrorsServiceClient.DEFAULT_MTLS_ENDPOINT
+ else:
+ api_endpoint = ReportErrorsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=universe_domain
+ )
+ return api_endpoint
+
+ @staticmethod
+ def _get_universe_domain(
+ client_universe_domain: Optional[str], universe_domain_env: Optional[str]
+ ) -> str:
+ """Return the universe domain used by the client.
+
+ Args:
+ client_universe_domain (Optional[str]): The universe domain configured via the client options.
+ universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable.
+
+ Returns:
+ str: The universe domain to be used by the client.
+
+ Raises:
+ ValueError: If the universe domain is an empty string.
+ """
+ universe_domain = ReportErrorsServiceClient._DEFAULT_UNIVERSE
+ if client_universe_domain is not None:
+ universe_domain = client_universe_domain
+ elif universe_domain_env is not None:
+ universe_domain = universe_domain_env
+ if len(universe_domain.strip()) == 0:
+ raise ValueError("Universe Domain cannot be an empty string.")
+ return universe_domain
+
+ def _validate_universe_domain(self):
+ """Validates client's and credentials' universe domains are consistent.
+
+ Returns:
+ bool: True iff the configured universe domain is valid.
+
+ Raises:
+ ValueError: If the configured universe domain is not valid.
+ """
+
+ # NOTE (b/349488459): universe validation is disabled until further notice.
+ return True
+
+ 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.
+
+ Args:
+ error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info.
+ """
+ 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):
+ """Return the API endpoint used by the client instance.
+
+ Returns:
+ str: The API endpoint used by the client instance.
+ """
+ return self._api_endpoint
+
+ @property
+ def universe_domain(self) -> str:
+ """Return the universe domain used by the client instance.
+
+ Returns:
+ str: The universe domain used by the client instance.
+ """
+ return self._universe_domain
+
def __init__(
self,
*,
credentials: Optional[ga_credentials.Credentials] = None,
- transport: Union[str, ReportErrorsServiceTransport, None] = None,
- client_options: Optional[client_options_lib.ClientOptions] = None,
+ transport: Optional[
+ Union[
+ str,
+ ReportErrorsServiceTransport,
+ Callable[..., ReportErrorsServiceTransport],
+ ]
+ ] = None,
+ client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
"""Instantiates the report errors service client.
@@ -321,25 +530,37 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ReportErrorsServiceTransport]): The
- transport to use. If set to None, a transport is chosen
- automatically.
- client_options (google.api_core.client_options.ClientOptions): Custom options for the
- client. It won't take effect if a ``transport`` instance is provided.
- (1) The ``api_endpoint`` property can be used to override the
- default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
- environment variable can also be used to override the endpoint:
+ transport (Optional[Union[str,ReportErrorsServiceTransport,Callable[..., ReportErrorsServiceTransport]]]):
+ The transport to use, or a Callable that constructs and returns a new transport.
+ If a Callable is given, it will be called with the same set of initialization
+ arguments as used in the ReportErrorsServiceTransport constructor.
+ If set to None, a transport is chosen automatically.
+ client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]):
+ Custom options for the client.
+
+ 1. The ``api_endpoint`` property can be used to override the
+ default endpoint provided by the client when ``transport`` is
+ not explicitly provided. Only if this property is not set and
+ ``transport`` was not explicitly provided, the endpoint is
+ determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment
+ variable, which have one of the following values:
"always" (always use the default mTLS endpoint), "never" (always
- use the default regular endpoint) and "auto" (auto switch to the
- default mTLS endpoint if client certificate is present, this is
- the default value). However, the ``api_endpoint`` property takes
- precedence if provided.
- (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
+ use the default regular endpoint) and "auto" (auto-switch to the
+ default mTLS endpoint if client certificate is present; this is
+ the default value).
+
+ 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
is "true", then the ``client_cert_source`` property can be used
- to provide client certificate for mutual TLS transport. If
+ to provide a client certificate for mTLS transport. If
not provided, the default SSL client certificate will be used if
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
set, no client certificate will be used.
+
+ 3. The ``universe_domain`` property can be used to override the
+ default "googleapis.com" universe. Note that the ``api_endpoint``
+ property still takes precedence; and ``universe_domain`` is
+ currently not supported for mTLS.
+
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.
@@ -350,16 +571,38 @@ def __init__(
google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport
creation failed for any reason.
"""
- if isinstance(client_options, dict):
- client_options = client_options_lib.from_dict(client_options)
- if client_options is None:
- client_options = client_options_lib.ClientOptions()
+ self._client_options = client_options
+ if isinstance(self._client_options, dict):
+ self._client_options = client_options_lib.from_dict(self._client_options)
+ if self._client_options is None:
+ self._client_options = client_options_lib.ClientOptions()
+ self._client_options = cast(
+ client_options_lib.ClientOptions, self._client_options
+ )
+
+ universe_domain_opt = getattr(self._client_options, "universe_domain", None)
- api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
- client_options
+ (
+ self._use_client_cert,
+ self._use_mtls_endpoint,
+ self._universe_domain_env,
+ ) = ReportErrorsServiceClient._read_environment_variables()
+ self._client_cert_source = ReportErrorsServiceClient._get_client_cert_source(
+ self._client_options.client_cert_source, self._use_client_cert
)
+ self._universe_domain = ReportErrorsServiceClient._get_universe_domain(
+ universe_domain_opt, self._universe_domain_env
+ )
+ self._api_endpoint = None # updated below, depending on `transport`
+
+ # 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(client_options, "api_key", None)
+ api_key_value = getattr(self._client_options, "api_key", None)
if api_key_value and credentials:
raise ValueError(
"client_options.api_key and credentials are mutually exclusive"
@@ -368,20 +611,33 @@ def __init__(
# Save or instantiate the transport.
# Ordinarily, we provide the transport, but allowing a custom transport
# instance provides an extensibility point for unusual situations.
- if isinstance(transport, ReportErrorsServiceTransport):
+ transport_provided = isinstance(transport, ReportErrorsServiceTransport)
+ if transport_provided:
# transport is a ReportErrorsServiceTransport instance.
- if credentials or client_options.credentials_file or api_key_value:
+ if credentials or self._client_options.credentials_file or api_key_value:
raise ValueError(
"When providing a transport instance, "
"provide its credentials directly."
)
- if client_options.scopes:
+ if self._client_options.scopes:
raise ValueError(
"When providing a transport instance, provide its scopes "
"directly."
)
- self._transport = transport
- else:
+ self._transport = cast(ReportErrorsServiceTransport, transport)
+ self._api_endpoint = self._transport.host
+
+ self._api_endpoint = (
+ self._api_endpoint
+ or ReportErrorsServiceClient._get_api_endpoint(
+ self._client_options.api_endpoint,
+ self._client_cert_source,
+ self._universe_domain,
+ self._use_mtls_endpoint,
+ )
+ )
+
+ if not transport_provided:
import google.auth._default # type: ignore
if api_key_value and hasattr(
@@ -391,27 +647,61 @@ def __init__(
api_key_value
)
- Transport = type(self).get_transport_class(transport)
- self._transport = Transport(
+ transport_init: Union[
+ Type[ReportErrorsServiceTransport],
+ Callable[..., ReportErrorsServiceTransport],
+ ] = (
+ ReportErrorsServiceClient.get_transport_class(transport)
+ if isinstance(transport, str) or transport is None
+ else cast(Callable[..., ReportErrorsServiceTransport], transport)
+ )
+ # initialize with the provided callable or the passed in class
+ self._transport = transport_init(
credentials=credentials,
- credentials_file=client_options.credentials_file,
- host=api_endpoint,
- scopes=client_options.scopes,
- client_cert_source_for_mtls=client_cert_source_func,
- quota_project_id=client_options.quota_project_id,
+ credentials_file=self._client_options.credentials_file,
+ host=self._api_endpoint,
+ scopes=self._client_options.scopes,
+ client_cert_source_for_mtls=self._client_cert_source,
+ quota_project_id=self._client_options.quota_project_id,
client_info=client_info,
always_use_jwt_access=True,
+ 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.devtools.clouderrorreporting_v1beta1.ReportErrorsServiceClient`.",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "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.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "credentialsType": None,
+ },
+ )
+
def report_error_event(
self,
- request: Union[report_errors_service.ReportErrorEventRequest, dict] = None,
+ request: Optional[
+ Union[report_errors_service.ReportErrorEventRequest, dict]
+ ] = None,
*,
- project_name: str = None,
- event: report_errors_service.ReportedErrorEvent = None,
+ project_name: Optional[str] = None,
+ event: Optional[report_errors_service.ReportedErrorEvent] = None,
retry: OptionalRetry = gapic_v1.method.DEFAULT,
- timeout: float = None,
- metadata: Sequence[Tuple[str, str]] = (),
+ timeout: Union[float, object] = gapic_v1.method.DEFAULT,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
) -> report_errors_service.ReportErrorEventResponse:
r"""Report an individual error event and record the event to a log.
@@ -422,16 +712,29 @@ def report_error_event(
``POST https://clouderrorreporting.googleapis.com/v1beta1/{projectName}/events:report?key=123ABC456``
- **Note:** `Error Reporting `__ is a global
- service built on Cloud Logging and doesn't analyze logs stored
- in regional log buckets or logs routed to other Google Cloud
- projects.
+ **Note:** [Error Reporting]
+ (https://cloud.google.com/error-reporting) is a service built on
+ Cloud Logging and can analyze log entries when all of the
+ following are true:
+
+ - Customer-managed encryption keys (CMEK) are disabled on the
+ log bucket.
+ - The log bucket satisfies one of the following:
- For more information, see `Using Error Reporting with
- regionalized logs `__.
+ - The log bucket is stored in the same project where the
+ logs originated.
+ - The logs were routed to a project, and then that project
+ stored those logs in a log bucket that it owns.
.. code-block:: python
+ # This snippet has been automatically generated and should be regarded as a
+ # code template only.
+ # It will require modifications to work:
+ # - It may require correct/in-range values for request initialization.
+ # - It may require specifying regional endpoints when creating the service
+ # client as shown in:
+ # https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
def sample_report_error_event():
@@ -455,8 +758,8 @@ def sample_report_error_event():
Args:
request (Union[google.cloud.errorreporting_v1beta1.types.ReportErrorEventRequest, dict]):
- The request object. A request for reporting an
- individual error event.
+ The request object. A request for reporting an individual
+ error event.
project_name (str):
Required. The resource name of the Google Cloud Platform
project. Written as ``projects/{projectId}``, where
@@ -478,8 +781,10 @@ def sample_report_error_event():
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.cloud.errorreporting_v1beta1.types.ReportErrorEventResponse:
@@ -489,19 +794,20 @@ def sample_report_error_event():
"""
# 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_name, event])
+ # - Quick check: If we got a request object, we should *not* have
+ # gotten any keyword arguments that map to the request.
+ flattened_params = [project_name, event]
+ 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 "
"the individual field arguments should be set."
)
- # Minor optimization to avoid making a copy if the user passes
- # in a report_errors_service.ReportErrorEventRequest.
- # There's no risk of modifying the input as we've already verified
- # there are no flattened fields.
+ # - Use the request object if provided (there's no risk of modifying the input as
+ # there are no flattened fields), or create one.
if not isinstance(request, report_errors_service.ReportErrorEventRequest):
request = report_errors_service.ReportErrorEventRequest(request)
# If we have keyword arguments corresponding to fields on the
@@ -523,6 +829,9 @@ def sample_report_error_event():
),
)
+ # Validate the universe domain.
+ self._validate_universe_domain()
+
# Send the request.
response = rpc(
request,
@@ -534,7 +843,7 @@ def sample_report_error_event():
# Done; return the response.
return response
- def __enter__(self):
+ def __enter__(self) -> "ReportErrorsServiceClient":
return self
def __exit__(self, type, value, traceback):
@@ -548,14 +857,11 @@ def __exit__(self, type, value, traceback):
self.transport.close()
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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__ = ("ReportErrorsServiceClient",)
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/README.rst b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/README.rst
new file mode 100644
index 00000000..d70e9010
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/README.rst
@@ -0,0 +1,9 @@
+
+transport inheritance structure
+_______________________________
+
+`ReportErrorsServiceTransport` is the ABC for all transports.
+- public child `ReportErrorsServiceGrpcTransport` for sync gRPC transport (defined in `grpc.py`).
+- public child `ReportErrorsServiceGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`).
+- private child `_BaseReportErrorsServiceRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`).
+- public child `ReportErrorsServiceRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`).
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/__init__.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/__init__.py
index b96eb345..83aa46c6 100644
--- a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/__init__.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.
@@ -19,6 +19,8 @@
from .base import ReportErrorsServiceTransport
from .grpc import ReportErrorsServiceGrpcTransport
from .grpc_asyncio import ReportErrorsServiceGrpcAsyncIOTransport
+from .rest import ReportErrorsServiceRestTransport
+from .rest import ReportErrorsServiceRestInterceptor
# Compile a registry of transports.
@@ -27,9 +29,12 @@
) # type: Dict[str, Type[ReportErrorsServiceTransport]]
_transport_registry["grpc"] = ReportErrorsServiceGrpcTransport
_transport_registry["grpc_asyncio"] = ReportErrorsServiceGrpcAsyncIOTransport
+_transport_registry["rest"] = ReportErrorsServiceRestTransport
__all__ = (
"ReportErrorsServiceTransport",
"ReportErrorsServiceGrpcTransport",
"ReportErrorsServiceGrpcAsyncIOTransport",
+ "ReportErrorsServiceRestTransport",
+ "ReportErrorsServiceRestInterceptor",
)
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/base.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/base.py
index 5da09490..31f69093 100644
--- a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/base.py
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/base.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.
@@ -15,7 +15,8 @@
#
import abc
from typing import Awaitable, Callable, Dict, Optional, Sequence, Union
-import pkg_resources
+
+from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
import google.auth # type: ignore
import google.api_core
@@ -24,17 +25,16 @@
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.cloud.errorreporting_v1beta1.types import report_errors_service
-try:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
- gapic_version=pkg_resources.get_distribution(
- "google-cloud-errorreporting",
- ).version,
- )
-except pkg_resources.DistributionNotFound:
- DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()
+DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
+ gapic_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 ReportErrorsServiceTransport(abc.ABC):
@@ -48,19 +48,20 @@ def __init__(
self,
*,
host: str = DEFAULT_HOST,
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
**kwargs,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
@@ -81,15 +82,12 @@ def __init__(
be used for service account credentials.
"""
- # Save the hostname. Default to port 443 (HTTPS) if none is specified.
- if ":" not in host:
- host += ":443"
- self._host = host
-
scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}
# Save the scopes.
self._scopes = scopes
+ if not hasattr(self, "_ignore_credentials"):
+ self._ignore_credentials: bool = False
# If no credentials are provided, then determine the appropriate
# defaults.
@@ -102,10 +100,15 @@ def __init__(
credentials, _ = google.auth.load_credentials_from_file(
credentials_file, **scopes_kwargs, quota_project_id=quota_project_id
)
- elif credentials is None:
+ elif credentials is None and not self._ignore_credentials:
credentials, _ = google.auth.default(
**scopes_kwargs, quota_project_id=quota_project_id
)
+ # Don't apply audience if the credentials file passed from user.
+ if hasattr(credentials, "with_gdch_audience"):
+ credentials = credentials.with_gdch_audience(
+ api_audience if api_audience else host
+ )
# If the credentials are service account credentials, then always try to use self signed JWT.
if (
@@ -118,6 +121,15 @@ def __init__(
# Save the credentials.
self._credentials = credentials
+ # Save the hostname. Default to port 443 (HTTPS) if none is specified.
+ if ":" not in host:
+ host += ":443"
+ self._host = host
+
+ @property
+ def host(self):
+ return self._host
+
def _prep_wrapped_messages(self, client_info):
# Precompute the wrapped methods.
self._wrapped_methods = {
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc.py
index aab08b85..023d0f0f 100644
--- a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc.py
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc.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,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,12 +24,89 @@
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.cloud.errorreporting_v1beta1.types import report_errors_service
from .base import ReportErrorsServiceTransport, 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.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "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.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "rpcName": client_call_details.method,
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class ReportErrorsServiceGrpcTransport(ReportErrorsServiceTransport):
"""gRPC backend transport for ReportErrorsService.
@@ -47,36 +127,40 @@ def __init__(
self,
*,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
- credentials_file: str = None,
- scopes: Sequence[str] = None,
- channel: grpc.Channel = None,
- api_mtls_endpoint: str = None,
- client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
- ssl_channel_credentials: grpc.ChannelCredentials = None,
- client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
+ scopes: Optional[Sequence[str]] = None,
+ channel: Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]] = None,
+ api_mtls_endpoint: Optional[str] = None,
+ client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
scopes (Optional(Sequence[str])): A list of scopes. This argument is
- ignored if ``channel`` is provided.
- channel (Optional[grpc.Channel]): A ``Channel`` instance through
- which to make calls.
+ ignored if a ``channel`` instance is provided.
+ channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]):
+ A ``Channel`` instance through which to make calls, or a Callable
+ that constructs and returns one. If set to None, ``self.create_channel``
+ is used to create the channel. If a Callable is given, it will be called
+ with the same arguments as used in ``self.create_channel``.
api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint.
If provided, it overrides the ``host`` argument and tries to create
a mutual TLS channel with client SSL credentials from
@@ -86,11 +170,11 @@ def __init__(
private key bytes, both in PEM format. It is ignored if
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
- for the grpc channel. It is ignored if ``channel`` is provided.
+ for the grpc channel. It is ignored if a ``channel`` instance is provided.
client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
A callback to provide client certificate bytes and private key bytes,
both in PEM format. It is used to configure a mutual TLS channel. It is
- ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
+ ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -116,9 +200,10 @@ def __init__(
if client_cert_source:
warnings.warn("client_cert_source is deprecated", DeprecationWarning)
- if channel:
+ if isinstance(channel, grpc.Channel):
# Ignore credentials if a channel was passed.
- credentials = False
+ credentials = None
+ self._ignore_credentials = True
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
@@ -153,10 +238,13 @@ def __init__(
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
)
if not self._grpc_channel:
- self._grpc_channel = type(self).create_channel(
+ # initialize with the provided callable or the default channel
+ channel_init = channel or type(self).create_channel
+ self._grpc_channel = channel_init(
self._host,
# use the credentials which are saved
credentials=self._credentials,
@@ -172,15 +260,20 @@ 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
def create_channel(
cls,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
- credentials_file: str = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
**kwargs,
@@ -245,13 +338,19 @@ def report_error_event(
``POST https://clouderrorreporting.googleapis.com/v1beta1/{projectName}/events:report?key=123ABC456``
- **Note:** `Error Reporting `__ is a global
- service built on Cloud Logging and doesn't analyze logs stored
- in regional log buckets or logs routed to other Google Cloud
- projects.
+ **Note:** [Error Reporting]
+ (https://cloud.google.com/error-reporting) is a service built on
+ Cloud Logging and can analyze log entries when all of the
+ following are true:
+
+ - Customer-managed encryption keys (CMEK) are disabled on the
+ log bucket.
+ - The log bucket satisfies one of the following:
- For more information, see `Using Error Reporting with
- regionalized logs `__.
+ - The log bucket is stored in the same project where the
+ logs originated.
+ - The logs were routed to a project, and then that project
+ stored those logs in a log bucket that it owns.
Returns:
Callable[[~.ReportErrorEventRequest],
@@ -264,7 +363,7 @@ def report_error_event(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "report_error_event" not in self._stubs:
- self._stubs["report_error_event"] = self.grpc_channel.unary_unary(
+ self._stubs["report_error_event"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ReportErrorsService/ReportErrorEvent",
request_serializer=report_errors_service.ReportErrorEventRequest.serialize,
response_deserializer=report_errors_service.ReportErrorEventResponse.deserialize,
@@ -272,7 +371,7 @@ def report_error_event(
return self._stubs["report_error_event"]
def close(self):
- self.grpc_channel.close()
+ self._logged_channel.close()
@property
def kind(self) -> str:
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc_asyncio.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc_asyncio.py
index ae617404..18f942ce 100644
--- a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc_asyncio.py
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/grpc_asyncio.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,21 +13,106 @@
# 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
from google.api_core import gapic_v1
from google.api_core import grpc_helpers_async
+from google.api_core import exceptions as core_exceptions
+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.cloud.errorreporting_v1beta1.types import report_errors_service
from .base import ReportErrorsServiceTransport, DEFAULT_CLIENT_INFO
from .grpc import ReportErrorsServiceGrpcTransport
+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.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "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.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "rpcName": str(client_call_details.method),
+ "response": grpc_response,
+ "metadata": grpc_response["metadata"],
+ },
+ )
+ return response
+
class ReportErrorsServiceGrpcAsyncIOTransport(ReportErrorsServiceTransport):
"""gRPC AsyncIO backend transport for ReportErrorsService.
@@ -49,7 +134,7 @@ class ReportErrorsServiceGrpcAsyncIOTransport(ReportErrorsServiceTransport):
def create_channel(
cls,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
quota_project_id: Optional[str] = None,
@@ -65,7 +150,6 @@ def create_channel(
the credentials from the environment.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
scopes (Optional[Sequence[str]]): A 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`.
@@ -92,37 +176,41 @@ def __init__(
self,
*,
host: str = "clouderrorreporting.googleapis.com",
- credentials: ga_credentials.Credentials = None,
+ credentials: Optional[ga_credentials.Credentials] = None,
credentials_file: Optional[str] = None,
scopes: Optional[Sequence[str]] = None,
- channel: aio.Channel = None,
- api_mtls_endpoint: str = None,
- client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
- ssl_channel_credentials: grpc.ChannelCredentials = None,
- client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
- quota_project_id=None,
+ channel: Optional[Union[aio.Channel, Callable[..., aio.Channel]]] = None,
+ api_mtls_endpoint: Optional[str] = None,
+ client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
always_use_jwt_access: Optional[bool] = False,
+ api_audience: Optional[str] = None,
) -> None:
"""Instantiate the transport.
Args:
host (Optional[str]):
- The hostname to connect to.
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
credentials (Optional[google.auth.credentials.Credentials]): The
authorization credentials to attach to requests. These
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
- This argument is ignored if ``channel`` is provided.
+ This argument is ignored if a ``channel`` instance is provided.
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`.
- channel (Optional[aio.Channel]): A ``Channel`` instance through
- which to make calls.
+ channel (Optional[Union[aio.Channel, Callable[..., aio.Channel]]]):
+ A ``Channel`` instance through which to make calls, or a Callable
+ that constructs and returns one. If set to None, ``self.create_channel``
+ is used to create the channel. If a Callable is given, it will be called
+ with the same arguments as used in ``self.create_channel``.
api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint.
If provided, it overrides the ``host`` argument and tries to create
a mutual TLS channel with client SSL credentials from
@@ -132,11 +220,11 @@ def __init__(
private key bytes, both in PEM format. It is ignored if
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
- for the grpc channel. It is ignored if ``channel`` is provided.
+ for the grpc channel. It is ignored if a ``channel`` instance is provided.
client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
A callback to provide client certificate bytes and private key bytes,
both in PEM format. It is used to configure a mutual TLS channel. It is
- ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
+ ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -162,9 +250,10 @@ def __init__(
if client_cert_source:
warnings.warn("client_cert_source is deprecated", DeprecationWarning)
- if channel:
+ if isinstance(channel, aio.Channel):
# Ignore credentials if a channel was passed.
- credentials = False
+ credentials = None
+ self._ignore_credentials = True
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
@@ -198,10 +287,13 @@ def __init__(
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=always_use_jwt_access,
+ api_audience=api_audience,
)
if not self._grpc_channel:
- self._grpc_channel = type(self).create_channel(
+ # initialize with the provided callable or the default channel
+ channel_init = channel or type(self).create_channel
+ self._grpc_channel = channel_init(
self._host,
# use the credentials which are saved
credentials=self._credentials,
@@ -217,7 +309,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
@@ -248,13 +346,19 @@ def report_error_event(
``POST https://clouderrorreporting.googleapis.com/v1beta1/{projectName}/events:report?key=123ABC456``
- **Note:** `Error Reporting `__ is a global
- service built on Cloud Logging and doesn't analyze logs stored
- in regional log buckets or logs routed to other Google Cloud
- projects.
+ **Note:** [Error Reporting]
+ (https://cloud.google.com/error-reporting) is a service built on
+ Cloud Logging and can analyze log entries when all of the
+ following are true:
- For more information, see `Using Error Reporting with
- regionalized logs `__.
+ - Customer-managed encryption keys (CMEK) are disabled on the
+ log bucket.
+ - The log bucket satisfies one of the following:
+
+ - The log bucket is stored in the same project where the
+ logs originated.
+ - The logs were routed to a project, and then that project
+ stored those logs in a log bucket that it owns.
Returns:
Callable[[~.ReportErrorEventRequest],
@@ -267,15 +371,34 @@ def report_error_event(
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "report_error_event" not in self._stubs:
- self._stubs["report_error_event"] = self.grpc_channel.unary_unary(
+ self._stubs["report_error_event"] = self._logged_channel.unary_unary(
"/google.devtools.clouderrorreporting.v1beta1.ReportErrorsService/ReportErrorEvent",
request_serializer=report_errors_service.ReportErrorEventRequest.serialize,
response_deserializer=report_errors_service.ReportErrorEventResponse.deserialize,
)
return self._stubs["report_error_event"]
+ def _prep_wrapped_messages(self, client_info):
+ """Precompute the wrapped methods, overriding the base class method to use async wrappers."""
+ self._wrapped_methods = {
+ self.report_error_event: self._wrap_method(
+ self.report_error_event,
+ 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"
__all__ = ("ReportErrorsServiceGrpcAsyncIOTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/rest.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/rest.py
new file mode 100644
index 00000000..98c63125
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/rest.py
@@ -0,0 +1,412 @@
+# -*- 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 logging
+import json # type: ignore
+
+from google.auth.transport.requests import AuthorizedSession # 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 gapic_v1
+import google.protobuf
+
+from google.protobuf import json_format
+
+from requests import __version__ as requests_version
+import dataclasses
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+import warnings
+
+
+from google.cloud.errorreporting_v1beta1.types import report_errors_service
+
+
+from .rest_base import _BaseReportErrorsServiceRestTransport
+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
+
+ 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=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 ReportErrorsServiceRestInterceptor:
+ """Interceptor for ReportErrorsService.
+
+ Interceptors are used to manipulate requests, request metadata, and responses
+ in arbitrary ways.
+ Example use cases include:
+ * Logging
+ * Verifying requests according to service or custom semantics
+ * Stripping extraneous information from responses
+
+ These use cases and more can be enabled by injecting an
+ instance of a custom subclass when constructing the ReportErrorsServiceRestTransport.
+
+ .. code-block:: python
+ class MyCustomReportErrorsServiceInterceptor(ReportErrorsServiceRestInterceptor):
+ def pre_report_error_event(self, request, metadata):
+ logging.log(f"Received request: {request}")
+ return request, metadata
+
+ def post_report_error_event(self, response):
+ logging.log(f"Received response: {response}")
+ return response
+
+ transport = ReportErrorsServiceRestTransport(interceptor=MyCustomReportErrorsServiceInterceptor())
+ client = ReportErrorsServiceClient(transport=transport)
+
+
+ """
+
+ def pre_report_error_event(
+ self,
+ request: report_errors_service.ReportErrorEventRequest,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ report_errors_service.ReportErrorEventRequest,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
+ """Pre-rpc interceptor for report_error_event
+
+ Override in a subclass to manipulate the request or metadata
+ before they are sent to the ReportErrorsService server.
+ """
+ return request, metadata
+
+ def post_report_error_event(
+ self, response: report_errors_service.ReportErrorEventResponse
+ ) -> report_errors_service.ReportErrorEventResponse:
+ """Post-rpc interceptor for report_error_event
+
+ DEPRECATED. Please use the `post_report_error_event_with_metadata`
+ interceptor instead.
+
+ Override in a subclass to read or manipulate the response
+ after it is returned by the ReportErrorsService server but before
+ it is returned to user code. This `post_report_error_event` interceptor runs
+ before the `post_report_error_event_with_metadata` interceptor.
+ """
+ return response
+
+ def post_report_error_event_with_metadata(
+ self,
+ response: report_errors_service.ReportErrorEventResponse,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]],
+ ) -> Tuple[
+ report_errors_service.ReportErrorEventResponse,
+ Sequence[Tuple[str, Union[str, bytes]]],
+ ]:
+ """Post-rpc interceptor for report_error_event
+
+ Override in a subclass to read or manipulate the response or metadata after it
+ is returned by the ReportErrorsService server but before it is returned to user code.
+
+ We recommend only using this `post_report_error_event_with_metadata`
+ interceptor in new development instead of the `post_report_error_event` interceptor.
+ When both interceptors are used, this `post_report_error_event_with_metadata` interceptor runs after the
+ `post_report_error_event` interceptor. The (possibly modified) response returned by
+ `post_report_error_event` will be passed to
+ `post_report_error_event_with_metadata`.
+ """
+ return response, metadata
+
+
+@dataclasses.dataclass
+class ReportErrorsServiceRestStub:
+ _session: AuthorizedSession
+ _host: str
+ _interceptor: ReportErrorsServiceRestInterceptor
+
+
+class ReportErrorsServiceRestTransport(_BaseReportErrorsServiceRestTransport):
+ """REST backend synchronous transport for ReportErrorsService.
+
+ An API for reporting error events.
+
+ 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 = "clouderrorreporting.googleapis.com",
+ credentials: Optional[ga_credentials.Credentials] = None,
+ credentials_file: Optional[str] = None,
+ scopes: Optional[Sequence[str]] = None,
+ client_cert_source_for_mtls: Optional[Callable[[], Tuple[bytes, bytes]]] = None,
+ quota_project_id: Optional[str] = None,
+ client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
+ always_use_jwt_access: Optional[bool] = False,
+ url_scheme: str = "https",
+ interceptor: Optional[ReportErrorsServiceRestInterceptor] = None,
+ api_audience: Optional[str] = None,
+ ) -> None:
+ """Instantiate the transport.
+
+ Args:
+ host (Optional[str]):
+ The hostname to connect to (default: 'clouderrorreporting.googleapis.com').
+ credentials (Optional[google.auth.credentials.Credentials]): The
+ authorization credentials to attach to requests. These
+ credentials identify the application to the service; if none
+ are specified, the client will attempt to ascertain the
+ credentials from the environment.
+
+ credentials_file (Optional[str]): A file with credentials that can
+ be loaded with :func:`google.auth.load_credentials_from_file`.
+ This argument is ignored if ``channel`` is provided.
+ scopes (Optional(Sequence[str])): A list of scopes. This argument is
+ ignored if ``channel`` is provided.
+ client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client
+ certificate to configure mutual TLS HTTP channel. It is ignored
+ if ``channel`` is provided.
+ quota_project_id (Optional[str]): An optional project to use for billing
+ and quota.
+ client_info (google.api_core.gapic_v1.client_info.ClientInfo):
+ The client info used to send a user-agent string along with
+ API requests. If ``None``, then default info will be used.
+ Generally, you only need to set this if you are developing
+ your own client library.
+ always_use_jwt_access (Optional[bool]): Whether self signed JWT should
+ be used for service account credentials.
+ url_scheme: the protocol scheme for the API endpoint. Normally
+ "https", but for testing or local servers,
+ "http" can be specified.
+ """
+ # Run the base constructor
+ # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc.
+ # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the
+ # credentials object
+ 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(
+ self._credentials, default_host=self.DEFAULT_HOST
+ )
+ if client_cert_source_for_mtls:
+ self._session.configure_mtls_channel(client_cert_source_for_mtls)
+ self._interceptor = interceptor or ReportErrorsServiceRestInterceptor()
+ self._prep_wrapped_messages(client_info)
+
+ class _ReportErrorEvent(
+ _BaseReportErrorsServiceRestTransport._BaseReportErrorEvent,
+ ReportErrorsServiceRestStub,
+ ):
+ def __hash__(self):
+ return hash("ReportErrorsServiceRestTransport.ReportErrorEvent")
+
+ @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: report_errors_service.ReportErrorEventRequest,
+ *,
+ retry: OptionalRetry = gapic_v1.method.DEFAULT,
+ timeout: Optional[float] = None,
+ metadata: Sequence[Tuple[str, Union[str, bytes]]] = (),
+ ) -> report_errors_service.ReportErrorEventResponse:
+ r"""Call the report error event method over HTTP.
+
+ Args:
+ request (~.report_errors_service.ReportErrorEventRequest):
+ The request object. A request for reporting an individual
+ error event.
+ 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, 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:
+ ~.report_errors_service.ReportErrorEventResponse:
+ Response for reporting an individual
+ error event. Data may be added to this
+ message in the future.
+
+ """
+
+ http_options = (
+ _BaseReportErrorsServiceRestTransport._BaseReportErrorEvent._get_http_options()
+ )
+
+ request, metadata = self._interceptor.pre_report_error_event(
+ request, metadata
+ )
+ transcoded_request = _BaseReportErrorsServiceRestTransport._BaseReportErrorEvent._get_transcoded_request(
+ http_options, request
+ )
+
+ body = _BaseReportErrorsServiceRestTransport._BaseReportErrorEvent._get_request_body_json(
+ transcoded_request
+ )
+
+ # Jsonify the query params
+ query_params = _BaseReportErrorsServiceRestTransport._BaseReportErrorEvent._get_query_params_json(
+ transcoded_request
+ )
+
+ 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.devtools.clouderrorreporting_v1beta1.ReportErrorsServiceClient.ReportErrorEvent",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "rpcName": "ReportErrorEvent",
+ "httpRequest": http_request,
+ "metadata": http_request["headers"],
+ },
+ )
+
+ # Send the request
+ response = ReportErrorsServiceRestTransport._ReportErrorEvent._get_response(
+ self._host,
+ metadata,
+ query_params,
+ self._session,
+ timeout,
+ transcoded_request,
+ body,
+ )
+
+ # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception
+ # subclass.
+ if response.status_code >= 400:
+ raise core_exceptions.from_http_response(response)
+
+ # Return the response
+ resp = report_errors_service.ReportErrorEventResponse()
+ pb_resp = report_errors_service.ReportErrorEventResponse.pb(resp)
+
+ json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True)
+
+ resp = self._interceptor.post_report_error_event(resp)
+ response_metadata = [(k, str(v)) for k, v in response.headers.items()]
+ resp, _ = self._interceptor.post_report_error_event_with_metadata(
+ resp, response_metadata
+ )
+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(
+ logging.DEBUG
+ ): # pragma: NO COVER
+ try:
+ response_payload = (
+ report_errors_service.ReportErrorEventResponse.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.devtools.clouderrorreporting_v1beta1.ReportErrorsServiceClient.report_error_event",
+ extra={
+ "serviceName": "google.devtools.clouderrorreporting.v1beta1.ReportErrorsService",
+ "rpcName": "ReportErrorEvent",
+ "metadata": http_response["headers"],
+ "httpResponse": http_response,
+ },
+ )
+ return resp
+
+ @property
+ def report_error_event(
+ self,
+ ) -> Callable[
+ [report_errors_service.ReportErrorEventRequest],
+ report_errors_service.ReportErrorEventResponse,
+ ]:
+ # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here.
+ # In C++ this would require a dynamic_cast
+ return self._ReportErrorEvent(self._session, self._host, self._interceptor) # type: ignore
+
+ @property
+ def kind(self) -> str:
+ return "rest"
+
+ def close(self):
+ self._session.close()
+
+
+__all__ = ("ReportErrorsServiceRestTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/rest_base.py b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/rest_base.py
new file mode 100644
index 00000000..f6495841
--- /dev/null
+++ b/google/cloud/errorreporting_v1beta1/services/report_errors_service/transports/rest_base.py
@@ -0,0 +1,150 @@
+# -*- 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 .base import ReportErrorsServiceTransport, DEFAULT_CLIENT_INFO
+
+import re
+from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
+
+
+from google.cloud.errorreporting_v1beta1.types import report_errors_service
+
+
+class _BaseReportErrorsServiceRestTransport(ReportErrorsServiceTransport):
+ """Base REST backend transport for ReportErrorsService.
+
+ 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 = "clouderrorreporting.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: 'clouderrorreporting.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 _BaseReportErrorEvent:
+ 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": "/v1beta1/{project_name=projects/*}/events:report",
+ "body": "event",
+ },
+ ]
+ return http_options
+
+ @staticmethod
+ def _get_transcoded_request(http_options, request):
+ pb_request = report_errors_service.ReportErrorEventRequest.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(
+ _BaseReportErrorsServiceRestTransport._BaseReportErrorEvent._get_unset_required_fields(
+ query_params
+ )
+ )
+
+ query_params["$alt"] = "json;enum-encoding=int"
+ return query_params
+
+
+__all__ = ("_BaseReportErrorsServiceRestTransport",)
diff --git a/google/cloud/errorreporting_v1beta1/types/__init__.py b/google/cloud/errorreporting_v1beta1/types/__init__.py
index fed4105b..4acdb9a1 100644
--- a/google/cloud/errorreporting_v1beta1/types/__init__.py
+++ b/google/cloud/errorreporting_v1beta1/types/__init__.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.
diff --git a/google/cloud/errorreporting_v1beta1/types/common.py b/google/cloud/errorreporting_v1beta1/types/common.py
index 1fbc87c6..84320a89 100644
--- a/google/cloud/errorreporting_v1beta1/types/common.py
+++ b/google/cloud/errorreporting_v1beta1/types/common.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,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+from __future__ import annotations
+
+from typing import MutableMapping, MutableSequence
+
import proto # type: ignore
from google.protobuf import timestamp_pb2 # type: ignore
@@ -34,7 +38,26 @@
class ResolutionStatus(proto.Enum):
- r"""Resolution status of an error group."""
+ r"""Resolution status of an error group.
+
+ Values:
+ RESOLUTION_STATUS_UNSPECIFIED (0):
+ Status is unknown. When left unspecified in
+ requests, it is treated like OPEN.
+ OPEN (1):
+ The error group is not being addressed. This
+ is the default for new groups. It is also used
+ for errors re-occurring after marked RESOLVED.
+ ACKNOWLEDGED (2):
+ Error Group manually acknowledged, it can
+ have an issue link attached.
+ RESOLVED (3):
+ Error Group manually resolved, more events
+ for this group are not expected to occur.
+ MUTED (4):
+ The error group is muted and excluded by
+ default on group stats requests.
+ """
RESOLUTION_STATUS_UNSPECIFIED = 0
OPEN = 1
ACKNOWLEDGED = 2
@@ -47,36 +70,56 @@ class ErrorGroup(proto.Message):
Attributes:
name (str):
- The group resource name.
- Example:
- projects/my-project-123/groups/CNSgkpnppqKCUw
+ The group resource name. Written as
+ ``projects/{projectID}/groups/{group_id}`` or
+ ``projects/{projectID}/locations/{location}/groups/{group_id}``
+
+ Examples: ``projects/my-project-123/groups/my-group``,
+ ``projects/my-project-123/locations/us-central1/groups/my-group``
+
+ In the group resource name, the ``group_id`` is a unique
+ identifier for a particular error group. The identifier is
+ derived from key parts of the error-log content and is
+ treated as Service Data. For information about how Service
+ Data is handled, see `Google Cloud Privacy
+ Notice `__.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
group_id (str):
- Group IDs are unique for a given project. If
- the same kind of error occurs in different
- service contexts, it will receive the same group
- ID.
- tracking_issues (Sequence[google.cloud.errorreporting_v1beta1.types.TrackingIssue]):
+ An opaque identifier of the group. This field is assigned by
+ the Error Reporting system and always populated.
+
+ In the group resource name, the ``group_id`` is a unique
+ identifier for a particular error group. The identifier is
+ derived from key parts of the error-log content and is
+ treated as Service Data. For information about how Service
+ Data is handled, see `Google Cloud Privacy
+ Notice `__.
+ tracking_issues (MutableSequence[google.cloud.errorreporting_v1beta1.types.TrackingIssue]):
Associated tracking issues.
resolution_status (google.cloud.errorreporting_v1beta1.types.ResolutionStatus):
Error group's resolution status.
+
An unspecified resolution status will be
interpreted as OPEN
"""
- name = proto.Field(
+ name: str = proto.Field(
proto.STRING,
number=1,
)
- group_id = proto.Field(
+ group_id: str = proto.Field(
proto.STRING,
number=2,
)
- tracking_issues = proto.RepeatedField(
+ tracking_issues: MutableSequence["TrackingIssue"] = proto.RepeatedField(
proto.MESSAGE,
number=3,
message="TrackingIssue",
)
- resolution_status = proto.Field(
+ resolution_status: "ResolutionStatus" = proto.Field(
proto.ENUM,
number=5,
enum="ResolutionStatus",
@@ -94,7 +137,7 @@ class TrackingIssue(proto.Message):
``https://github.com/user/project/issues/4``
"""
- url = proto.Field(
+ url: str = proto.Field(
proto.STRING,
number=1,
)
@@ -120,21 +163,21 @@ class ErrorEvent(proto.Message):
occurred.
"""
- event_time = proto.Field(
+ event_time: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=1,
message=timestamp_pb2.Timestamp,
)
- service_context = proto.Field(
+ service_context: "ServiceContext" = proto.Field(
proto.MESSAGE,
number=2,
message="ServiceContext",
)
- message = proto.Field(
+ message: str = proto.Field(
proto.STRING,
number=3,
)
- context = proto.Field(
+ context: "ErrorContext" = proto.Field(
proto.MESSAGE,
number=5,
message="ErrorContext",
@@ -167,19 +210,20 @@ class ServiceContext(proto.Message):
Type of the MonitoredResource. List of
possible values:
https://cloud.google.com/monitoring/api/resources
+
Value is set automatically for incoming errors
and must not be set when reporting errors.
"""
- service = proto.Field(
+ service: str = proto.Field(
proto.STRING,
number=2,
)
- version = proto.Field(
+ version: str = proto.Field(
proto.STRING,
number=3,
)
- resource_type = proto.Field(
+ resource_type: str = proto.Field(
proto.STRING,
number=4,
)
@@ -213,16 +257,16 @@ class ErrorContext(proto.Message):
place where it was caught.
"""
- http_request = proto.Field(
+ http_request: "HttpRequestContext" = proto.Field(
proto.MESSAGE,
number=1,
message="HttpRequestContext",
)
- user = proto.Field(
+ user: str = proto.Field(
proto.STRING,
number=2,
)
- report_location = proto.Field(
+ report_location: "SourceLocation" = proto.Field(
proto.MESSAGE,
number=3,
message="SourceLocation",
@@ -258,27 +302,27 @@ class HttpRequestContext(proto.Message):
report.
"""
- method = proto.Field(
+ method: str = proto.Field(
proto.STRING,
number=1,
)
- url = proto.Field(
+ url: str = proto.Field(
proto.STRING,
number=2,
)
- user_agent = proto.Field(
+ user_agent: str = proto.Field(
proto.STRING,
number=3,
)
- referrer = proto.Field(
+ referrer: str = proto.Field(
proto.STRING,
number=4,
)
- response_status_code = proto.Field(
+ response_status_code: int = proto.Field(
proto.INT32,
number=5,
)
- remote_ip = proto.Field(
+ remote_ip: str = proto.Field(
proto.STRING,
number=6,
)
@@ -305,15 +349,15 @@ class SourceLocation(proto.Message):
example, ``my.package.MyClass.method`` in case of Java.
"""
- file_path = proto.Field(
+ file_path: str = proto.Field(
proto.STRING,
number=1,
)
- line_number = proto.Field(
+ line_number: int = proto.Field(
proto.INT32,
number=2,
)
- function_name = proto.Field(
+ function_name: str = proto.Field(
proto.STRING,
number=4,
)
diff --git a/google/cloud/errorreporting_v1beta1/types/error_group_service.py b/google/cloud/errorreporting_v1beta1/types/error_group_service.py
index f40ad9a9..be126768 100644
--- a/google/cloud/errorreporting_v1beta1/types/error_group_service.py
+++ b/google/cloud/errorreporting_v1beta1/types/error_group_service.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,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+from __future__ import annotations
+
+from typing import MutableMapping, MutableSequence
+
import proto # type: ignore
from google.cloud.errorreporting_v1beta1.types import common
@@ -32,15 +36,29 @@ class GetGroupRequest(proto.Message):
Attributes:
group_name (str):
- Required. The group resource name. Written as
- ``projects/{projectID}/groups/{group_name}``. Call
- ```groupStats.list`` `__
+ Required. The group resource name. Written as either
+ ``projects/{projectID}/groups/{group_id}`` or
+ ``projects/{projectID}/locations/{location}/groups/{group_id}``.
+ Call [groupStats.list]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorStatsService.ListGroupStats]
to return a list of groups belonging to this project.
- Example: ``projects/my-project-123/groups/my-group``
+ Examples: ``projects/my-project-123/groups/my-group``,
+ ``projects/my-project-123/locations/global/groups/my-group``
+
+ In the group resource name, the ``group_id`` is a unique
+ identifier for a particular error group. The identifier is
+ derived from key parts of the error-log content and is
+ treated as Service Data. For information about how Service
+ Data is handled, see `Google Cloud Privacy
+ Notice `__.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
"""
- group_name = proto.Field(
+ group_name: str = proto.Field(
proto.STRING,
number=1,
)
@@ -55,7 +73,7 @@ class UpdateGroupRequest(proto.Message):
resource on the server.
"""
- group = proto.Field(
+ group: common.ErrorGroup = proto.Field(
proto.MESSAGE,
number=1,
message=common.ErrorGroup,
diff --git a/google/cloud/errorreporting_v1beta1/types/error_stats_service.py b/google/cloud/errorreporting_v1beta1/types/error_stats_service.py
index 15615596..0285d9a8 100644
--- a/google/cloud/errorreporting_v1beta1/types/error_stats_service.py
+++ b/google/cloud/errorreporting_v1beta1/types/error_stats_service.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,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+from __future__ import annotations
+
+from typing import MutableMapping, MutableSequence
+
import proto # type: ignore
from google.cloud.errorreporting_v1beta1.types import common
@@ -42,6 +46,31 @@
class TimedCountAlignment(proto.Enum):
r"""Specifies how the time periods of error group counts are
aligned.
+
+ Values:
+ ERROR_COUNT_ALIGNMENT_UNSPECIFIED (0):
+ No alignment specified.
+ ALIGNMENT_EQUAL_ROUNDED (1):
+ The time periods shall be consecutive, have width equal to
+ the requested duration, and be aligned at the
+ [alignment_time]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.alignment_time]
+ provided in the request.
+
+ The [alignment_time]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.alignment_time]
+ does not have to be inside the query period but even if it
+ is outside, only time periods are returned which overlap
+ with the query period.
+
+ A rounded alignment will typically result in a different
+ size of the first or the last time period.
+ ALIGNMENT_EQUAL_AT_END (2):
+ The time periods shall be consecutive, have
+ width equal to the requested duration, and be
+ aligned at the end of the requested time period.
+ This can result in a different size of the first
+ time period.
"""
ERROR_COUNT_ALIGNMENT_UNSPECIFIED = 0
ALIGNMENT_EQUAL_ROUNDED = 1
@@ -49,7 +78,24 @@ class TimedCountAlignment(proto.Enum):
class ErrorGroupOrder(proto.Enum):
- r"""A sorting order of error groups."""
+ r"""A sorting order of error groups.
+
+ Values:
+ GROUP_ORDER_UNSPECIFIED (0):
+ No group order specified.
+ COUNT_DESC (1):
+ Total count of errors in the given time
+ window in descending order.
+ LAST_SEEN_DESC (2):
+ Timestamp when the group was last seen in the
+ given time window in descending order.
+ CREATED_DESC (3):
+ Timestamp when the group was created in
+ descending order.
+ AFFECTED_USERS_DESC (4):
+ Number of affected users in the given time
+ window in descending order.
+ """
GROUP_ORDER_UNSPECIFIED = 0
COUNT_DESC = 1
LAST_SEEN_DESC = 2
@@ -66,29 +112,54 @@ class ListGroupStatsRequest(proto.Message):
project. Written as ``projects/{projectID}`` or
``projects/{projectNumber}``, where ``{projectID}`` and
``{projectNumber}`` can be found in the `Google Cloud
- Console `__.
-
- Examples: ``projects/my-project-123``, ``projects/5551234``.
- group_id (Sequence[str]):
- Optional. List all
- ErrorGroupStats with these IDs.
+ console `__.
+ It may also include a location, such as
+ ``projects/{projectID}/locations/{location}`` where
+ ``{location}`` is a cloud region.
+
+ Examples: ``projects/my-project-123``, ``projects/5551234``,
+ ``projects/my-project-123/locations/us-central1``,
+ ``projects/5551234/locations/us-central1``.
+
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified. Use ``-`` as a
+ wildcard to request group stats from all regions.
+ group_id (MutableSequence[str]):
+ Optional. List all [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ with these IDs. The ``group_id`` is a unique identifier for
+ a particular error group. The identifier is derived from key
+ parts of the error-log content and is treated as Service
+ Data. For information about how Service Data is handled, see
+ [Google Cloud Privacy Notice]
+ (https://cloud.google.com/terms/cloud-privacy-notice).
service_filter (google.cloud.errorreporting_v1beta1.types.ServiceContextFilter):
- Optional. List only
- ErrorGroupStats which belong to a
- service context that matches the filter. Data
- for all service contexts is returned if this
- field is not specified.
+ Optional. List only [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ which belong to a service context that matches the filter.
+ Data for all service contexts is returned if this field is
+ not specified.
time_range (google.cloud.errorreporting_v1beta1.types.QueryTimeRange):
Optional. List data for the given time range. If not set, a
- default time range is used. The field time_range_begin in
- the response will specify the beginning of this time range.
- Only ErrorGroupStats with a non-zero count in the given time
- range are returned, unless the request contains an explicit
- group_id list. If a group_id list is given, also
- ErrorGroupStats with zero occurrences are returned.
+ default time range is used. The field [time_range_begin]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsResponse.time_range_begin]
+ in the response will specify the beginning of this time
+ range. Only [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ with a non-zero count in the given time range are returned,
+ unless the request contains an explicit [group_id]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.group_id]
+ list. If a [group_id]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest.group_id]
+ list is given, also [ErrorGroupStats]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorGroupStats]
+ with zero occurrences are returned.
timed_count_duration (google.protobuf.duration_pb2.Duration):
Optional. The preferred duration for a single returned
- ``TimedCount``. If not set, no timed counts are returned.
+ [TimedCount]
+ [google.devtools.clouderrorreporting.v1beta1.TimedCount]. If
+ not set, no timed counts are returned.
alignment (google.cloud.errorreporting_v1beta1.types.TimedCountAlignment):
Optional. The alignment of the timed counts to be returned.
Default is ``ALIGNMENT_EQUAL_AT_END``.
@@ -103,54 +174,56 @@ class ListGroupStatsRequest(proto.Message):
Optional. The maximum number of results to
return per response. Default is 20.
page_token (str):
- Optional. A ``next_page_token`` provided by a previous
- response. To view additional results, pass this token along
- with the identical query parameters as the first request.
+ Optional. A [next_page_token]
+ [google.devtools.clouderrorreporting.v1beta1.ListGroupStatsResponse.next_page_token]
+ provided by a previous response. To view additional results,
+ pass this token along with the identical query parameters as
+ the first request.
"""
- project_name = proto.Field(
+ project_name: str = proto.Field(
proto.STRING,
number=1,
)
- group_id = proto.RepeatedField(
+ group_id: MutableSequence[str] = proto.RepeatedField(
proto.STRING,
number=2,
)
- service_filter = proto.Field(
+ service_filter: "ServiceContextFilter" = proto.Field(
proto.MESSAGE,
number=3,
message="ServiceContextFilter",
)
- time_range = proto.Field(
+ time_range: "QueryTimeRange" = proto.Field(
proto.MESSAGE,
number=5,
message="QueryTimeRange",
)
- timed_count_duration = proto.Field(
+ timed_count_duration: duration_pb2.Duration = proto.Field(
proto.MESSAGE,
number=6,
message=duration_pb2.Duration,
)
- alignment = proto.Field(
+ alignment: "TimedCountAlignment" = proto.Field(
proto.ENUM,
number=7,
enum="TimedCountAlignment",
)
- alignment_time = proto.Field(
+ alignment_time: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=8,
message=timestamp_pb2.Timestamp,
)
- order = proto.Field(
+ order: "ErrorGroupOrder" = proto.Field(
proto.ENUM,
number=9,
enum="ErrorGroupOrder",
)
- page_size = proto.Field(
+ page_size: int = proto.Field(
proto.INT32,
number=11,
)
- page_token = proto.Field(
+ page_token: str = proto.Field(
proto.STRING,
number=12,
)
@@ -160,7 +233,7 @@ class ListGroupStatsResponse(proto.Message):
r"""Contains a set of requested error group stats.
Attributes:
- error_group_stats (Sequence[google.cloud.errorreporting_v1beta1.types.ErrorGroupStats]):
+ error_group_stats (MutableSequence[google.cloud.errorreporting_v1beta1.types.ErrorGroupStats]):
The error group stats which match the given
request.
next_page_token (str):
@@ -181,16 +254,16 @@ class ListGroupStatsResponse(proto.Message):
def raw_page(self):
return self
- error_group_stats = proto.RepeatedField(
+ error_group_stats: MutableSequence["ErrorGroupStats"] = proto.RepeatedField(
proto.MESSAGE,
number=1,
message="ErrorGroupStats",
)
- next_page_token = proto.Field(
+ next_page_token: str = proto.Field(
proto.STRING,
number=2,
)
- time_range_begin = proto.Field(
+ time_range_begin: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=4,
message=timestamp_pb2.Timestamp,
@@ -211,19 +284,21 @@ class ErrorGroupStats(proto.Message):
affected_users_count (int):
Approximate number of affected users in the given group that
match the filter criteria. Users are distinguished by data
- in the ``ErrorContext`` of the individual error events, such
- as their login name or their remote IP address in case of
- HTTP requests. The number of affected users can be zero even
- if the number of errors is non-zero if no data was provided
- from which the affected user could be deduced. Users are
- counted based on data in the request context that was
- provided in the error report. If more users are implicitly
- affected, such as due to a crash of the whole service, this
- is not reflected here.
- timed_counts (Sequence[google.cloud.errorreporting_v1beta1.types.TimedCount]):
+ in the [ErrorContext]
+ [google.devtools.clouderrorreporting.v1beta1.ErrorContext]
+ of the individual error events, such as their login name or
+ their remote IP address in case of HTTP requests. The number
+ of affected users can be zero even if the number of errors
+ is non-zero if no data was provided from which the affected
+ user could be deduced. Users are counted based on data in
+ the request context that was provided in the error report.
+ If more users are implicitly affected, such as due to a
+ crash of the whole service, this is not reflected here.
+ timed_counts (MutableSequence[google.cloud.errorreporting_v1beta1.types.TimedCount]):
Approximate number of occurrences over time.
Timed counts returned by ListGroups are
guaranteed to be:
+
- Inside the requested time interval
- Non-overlapping, and
- Ordered by ascending time.
@@ -235,7 +310,7 @@ class ErrorGroupStats(proto.Message):
Approximate last occurrence that was ever seen for this
group and which matches the given filter criteria, ignoring
the time_range that was specified in the request.
- affected_services (Sequence[google.cloud.errorreporting_v1beta1.types.ServiceContext]):
+ affected_services (MutableSequence[google.cloud.errorreporting_v1beta1.types.ServiceContext]):
Service contexts with a non-zero error count for the given
filter criteria. This list can be truncated if multiple
services are affected. Refer to ``num_affected_services``
@@ -254,44 +329,44 @@ class ErrorGroupStats(proto.Message):
characteristics of the group as a whole.
"""
- group = proto.Field(
+ group: common.ErrorGroup = proto.Field(
proto.MESSAGE,
number=1,
message=common.ErrorGroup,
)
- count = proto.Field(
+ count: int = proto.Field(
proto.INT64,
number=2,
)
- affected_users_count = proto.Field(
+ affected_users_count: int = proto.Field(
proto.INT64,
number=3,
)
- timed_counts = proto.RepeatedField(
+ timed_counts: MutableSequence["TimedCount"] = proto.RepeatedField(
proto.MESSAGE,
number=4,
message="TimedCount",
)
- first_seen_time = proto.Field(
+ first_seen_time: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=5,
message=timestamp_pb2.Timestamp,
)
- last_seen_time = proto.Field(
+ last_seen_time: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=6,
message=timestamp_pb2.Timestamp,
)
- affected_services = proto.RepeatedField(
+ affected_services: MutableSequence[common.ServiceContext] = proto.RepeatedField(
proto.MESSAGE,
number=7,
message=common.ServiceContext,
)
- num_affected_services = proto.Field(
+ num_affected_services: int = proto.Field(
proto.INT32,
number=8,
)
- representative = proto.Field(
+ representative: common.ErrorEvent = proto.Field(
proto.MESSAGE,
number=9,
message=common.ErrorEvent,
@@ -314,16 +389,16 @@ class TimedCount(proto.Message):
End of the time period to which ``count`` refers (excluded).
"""
- count = proto.Field(
+ count: int = proto.Field(
proto.INT64,
number=1,
)
- start_time = proto.Field(
+ start_time: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=2,
message=timestamp_pb2.Timestamp,
)
- end_time = proto.Field(
+ end_time: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=3,
message=timestamp_pb2.Timestamp,
@@ -336,14 +411,26 @@ class ListEventsRequest(proto.Message):
Attributes:
project_name (str):
Required. The resource name of the Google Cloud Platform
- project. Written as ``projects/{projectID}``, where
+ project. Written as ``projects/{projectID}`` or
+ ``projects/{projectID}/locations/{location}``, where
``{projectID}`` is the `Google Cloud Platform project
- ID `__.
+ ID `__ and
+ ``{location}`` is a Cloud region.
+
+ Examples: ``projects/my-project-123``,
+ ``projects/my-project-123/locations/global``.
- Example: ``projects/my-project-123``.
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
group_id (str):
- Required. The group for which events shall be
- returned.
+ Required. The group for which events shall be returned. The
+ ``group_id`` is a unique identifier for a particular error
+ group. The identifier is derived from key parts of the
+ error-log content and is treated as Service Data. For
+ information about how Service Data is handled, see `Google
+ Cloud Privacy
+ Notice `__.
service_filter (google.cloud.errorreporting_v1beta1.types.ServiceContextFilter):
Optional. List only ErrorGroups which belong
to a service context that matches the filter.
@@ -362,29 +449,29 @@ class ListEventsRequest(proto.Message):
response.
"""
- project_name = proto.Field(
+ project_name: str = proto.Field(
proto.STRING,
number=1,
)
- group_id = proto.Field(
+ group_id: str = proto.Field(
proto.STRING,
number=2,
)
- service_filter = proto.Field(
+ service_filter: "ServiceContextFilter" = proto.Field(
proto.MESSAGE,
number=3,
message="ServiceContextFilter",
)
- time_range = proto.Field(
+ time_range: "QueryTimeRange" = proto.Field(
proto.MESSAGE,
number=4,
message="QueryTimeRange",
)
- page_size = proto.Field(
+ page_size: int = proto.Field(
proto.INT32,
number=6,
)
- page_token = proto.Field(
+ page_token: str = proto.Field(
proto.STRING,
number=7,
)
@@ -394,7 +481,7 @@ class ListEventsResponse(proto.Message):
r"""Contains a set of requested error events.
Attributes:
- error_events (Sequence[google.cloud.errorreporting_v1beta1.types.ErrorEvent]):
+ error_events (MutableSequence[google.cloud.errorreporting_v1beta1.types.ErrorEvent]):
The error events which match the given
request.
next_page_token (str):
@@ -411,16 +498,16 @@ class ListEventsResponse(proto.Message):
def raw_page(self):
return self
- error_events = proto.RepeatedField(
+ error_events: MutableSequence[common.ErrorEvent] = proto.RepeatedField(
proto.MESSAGE,
number=1,
message=common.ErrorEvent,
)
- next_page_token = proto.Field(
+ next_page_token: str = proto.Field(
proto.STRING,
number=2,
)
- time_range_begin = proto.Field(
+ time_range_begin: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=4,
message=timestamp_pb2.Timestamp,
@@ -428,7 +515,13 @@ def raw_page(self):
class QueryTimeRange(proto.Message):
- r"""Requests might be rejected or the resulting timed count
+ r"""A time range for which error group data shall be displayed.
+ Query time ranges end at 'now'.
+ When longer time ranges are selected, the resolution of the data
+ decreases. The description of each time range below indicates
+ the suggested minimum timed count duration for that range.
+
+ Requests might be rejected or the resulting timed count
durations might be adjusted for lower durations.
Attributes:
@@ -438,7 +531,30 @@ class QueryTimeRange(proto.Message):
"""
class Period(proto.Enum):
- r"""The supported time ranges."""
+ r"""The supported time ranges.
+
+ Values:
+ PERIOD_UNSPECIFIED (0):
+ Do not use.
+ PERIOD_1_HOUR (1):
+ Retrieve data for the last hour.
+ Recommended minimum timed count duration: 1 min.
+ PERIOD_6_HOURS (2):
+ Retrieve data for the last 6 hours.
+ Recommended minimum timed count duration: 10
+ min.
+ PERIOD_1_DAY (3):
+ Retrieve data for the last day.
+ Recommended minimum timed count duration: 1
+ hour.
+ PERIOD_1_WEEK (4):
+ Retrieve data for the last week.
+ Recommended minimum timed count duration: 6
+ hours.
+ PERIOD_30_DAYS (5):
+ Retrieve data for the last 30 days.
+ Recommended minimum timed count duration: 1 day.
+ """
PERIOD_UNSPECIFIED = 0
PERIOD_1_HOUR = 1
PERIOD_6_HOURS = 2
@@ -446,7 +562,7 @@ class Period(proto.Enum):
PERIOD_1_WEEK = 4
PERIOD_30_DAYS = 5
- period = proto.Field(
+ period: Period = proto.Field(
proto.ENUM,
number=1,
enum=Period,
@@ -471,15 +587,15 @@ class ServiceContextFilter(proto.Message):
```ServiceContext.resource_type`` `__.
"""
- service = proto.Field(
+ service: str = proto.Field(
proto.STRING,
number=2,
)
- version = proto.Field(
+ version: str = proto.Field(
proto.STRING,
number=3,
)
- resource_type = proto.Field(
+ resource_type: str = proto.Field(
proto.STRING,
number=4,
)
@@ -491,14 +607,21 @@ class DeleteEventsRequest(proto.Message):
Attributes:
project_name (str):
Required. The resource name of the Google Cloud Platform
- project. Written as ``projects/{projectID}``, where
+ project. Written as ``projects/{projectID}`` or
+ ``projects/{projectID}/locations/{location}``, where
``{projectID}`` is the `Google Cloud Platform project
- ID `__.
+ ID `__ and
+ ``{location}`` is a Cloud region.
+
+ Examples: ``projects/my-project-123``,
+ ``projects/my-project-123/locations/global``.
- Example: ``projects/my-project-123``.
+ For a list of supported locations, see `Supported
+ Regions `__.
+ ``global`` is the default when unspecified.
"""
- project_name = proto.Field(
+ project_name: str = proto.Field(
proto.STRING,
number=1,
)
diff --git a/google/cloud/errorreporting_v1beta1/types/report_errors_service.py b/google/cloud/errorreporting_v1beta1/types/report_errors_service.py
index 22b901a4..ebd33856 100644
--- a/google/cloud/errorreporting_v1beta1/types/report_errors_service.py
+++ b/google/cloud/errorreporting_v1beta1/types/report_errors_service.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,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+from __future__ import annotations
+
+from typing import MutableMapping, MutableSequence
+
import proto # type: ignore
from google.cloud.errorreporting_v1beta1.types import common
@@ -44,11 +48,11 @@ class ReportErrorEventRequest(proto.Message):
Required. The error event to be reported.
"""
- project_name = proto.Field(
+ project_name: str = proto.Field(
proto.STRING,
number=1,
)
- event = proto.Field(
+ event: "ReportedErrorEvent" = proto.Field(
proto.MESSAGE,
number=2,
message="ReportedErrorEvent",
@@ -68,10 +72,13 @@ class ReportedErrorEvent(proto.Message):
Attributes:
event_time (google.protobuf.timestamp_pb2.Timestamp):
- Optional. Time when the event occurred.
- If not provided, the time when the event was
- received by the Error Reporting system will be
- used.
+ Optional. Time when the event occurred. If not provided, the
+ time when the event was received by the Error Reporting
+ system is used. If provided, the time must not exceed the
+ `logs retention
+ period `__
+ in the past, or be more than 24 hours in the future. If an
+ invalid time is provided, then an error is returned.
service_context (google.cloud.errorreporting_v1beta1.types.ServiceContext):
Required. The service context in which this
error has occurred.
@@ -95,10 +102,10 @@ class ReportedErrorEvent(proto.Message):
```Exception.backtrace`` `__.
- **C#**: Must be the return value of
```Exception.ToString()`` `__.
- - **PHP**: Must start with
- ``PHP (Notice|Parse error|Fatal error|Warning)`` and
+ - **PHP**: Must be prefixed with
+ ``"PHP (Notice|Parse error|Fatal error|Warning): "`` and
contain the result of
- ```(string)$exception`` `__.
+ ```(string)$exception`` `__.
- **Go**: Must be the return value of
```runtime.Stack()`` `__.
context (google.cloud.errorreporting_v1beta1.types.ErrorContext):
@@ -106,21 +113,21 @@ class ReportedErrorEvent(proto.Message):
which the error occurred.
"""
- event_time = proto.Field(
+ event_time: timestamp_pb2.Timestamp = proto.Field(
proto.MESSAGE,
number=1,
message=timestamp_pb2.Timestamp,
)
- service_context = proto.Field(
+ service_context: common.ServiceContext = proto.Field(
proto.MESSAGE,
number=2,
message=common.ServiceContext,
)
- message = proto.Field(
+ message: str = proto.Field(
proto.STRING,
number=3,
)
- context = proto.Field(
+ context: common.ErrorContext = proto.Field(
proto.MESSAGE,
number=4,
message=common.ErrorContext,
diff --git a/mypy.ini b/mypy.ini
index 4505b485..574c5aed 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,3 +1,3 @@
[mypy]
-python_version = 3.6
+python_version = 3.7
namespace_packages = True
diff --git a/noxfile.py b/noxfile.py
index 001ec73c..bc66951c 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright 2018 Google LLC
+# 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.
@@ -17,21 +17,32 @@
# 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
-BLACK_VERSION = "black==22.3.0"
-ISORT_VERSION = "isort==5.10.1"
+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"]
DEFAULT_PYTHON_VERSION = "3.8"
-UNIT_TEST_PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10"]
+UNIT_TEST_PYTHON_VERSIONS: List[str] = [
+ "3.7",
+ "3.8",
+ "3.9",
+ "3.10",
+ "3.11",
+ "3.12",
+ "3.13",
+]
UNIT_TEST_STANDARD_DEPENDENCIES = [
"mock",
"asyncmock",
@@ -39,27 +50,26 @@
"pytest-cov",
"pytest-asyncio",
]
-UNIT_TEST_EXTERNAL_DEPENDENCIES = []
-UNIT_TEST_LOCAL_DEPENDENCIES = []
-UNIT_TEST_DEPENDENCIES = []
-UNIT_TEST_EXTRAS = []
-UNIT_TEST_EXTRAS_BY_PYTHON = {}
-
-SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"]
-SYSTEM_TEST_STANDARD_DEPENDENCIES = [
+UNIT_TEST_EXTERNAL_DEPENDENCIES: List[str] = []
+UNIT_TEST_LOCAL_DEPENDENCIES: List[str] = []
+UNIT_TEST_DEPENDENCIES: List[str] = []
+UNIT_TEST_EXTRAS: List[str] = []
+UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {}
+
+SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.8"]
+SYSTEM_TEST_STANDARD_DEPENDENCIES: List[str] = [
"mock",
"pytest",
"google-cloud-testutils",
]
-SYSTEM_TEST_EXTERNAL_DEPENDENCIES = []
-SYSTEM_TEST_LOCAL_DEPENDENCIES = []
-SYSTEM_TEST_DEPENDENCIES = []
-SYSTEM_TEST_EXTRAS = []
-SYSTEM_TEST_EXTRAS_BY_PYTHON = {}
+SYSTEM_TEST_EXTERNAL_DEPENDENCIES: List[str] = []
+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()
-# 'docfx' is excluded since it only needs to run in 'docs-presubmit'
nox.options.sessions = [
"unit",
"system",
@@ -68,6 +78,8 @@
"lint_setup_py",
"blacken",
"docs",
+ "docfx",
+ "format",
]
# Error if a python version is missing
@@ -81,7 +93,7 @@ def lint(session):
Returns a failure if the linters find linting errors or sufficiently
serious code quality issues.
"""
- session.install("flake8", BLACK_VERSION)
+ session.install(FLAKE8_VERSION, BLACK_VERSION)
session.run(
"black",
"--check",
@@ -155,14 +167,28 @@ def install_unittest_dependencies(session, *constraints):
session.install("-e", ".", *constraints)
-def default(session):
+@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"):
+ 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",
@@ -176,19 +202,17 @@ def default(session):
"--cov-fail-under=0",
os.path.join("tests", "unit"),
*session.posargs,
+ env={
+ "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation,
+ },
)
-@nox.session(python=UNIT_TEST_PYTHON_VERSIONS)
-def unit(session):
- """Run the unit test suite."""
- default(session)
-
-
def install_systemtest_dependencies(session, *constraints):
-
# Use pre-release gRPC for system tests.
- session.install("--pre", "grpcio")
+ # 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)
@@ -270,12 +294,25 @@ def cover(session):
session.run("coverage", "erase")
-@nox.session(python=DEFAULT_PYTHON_VERSION)
+@nox.session(python="3.10")
def docs(session):
"""Build the docs for this library."""
session.install("-e", ".")
- session.install("sphinx==4.0.1", "alabaster", "recommonmark")
+ 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(
@@ -292,13 +329,24 @@ def docs(session):
)
-@nox.session(python=DEFAULT_PYTHON_VERSION)
+@nox.session(python="3.10")
def docfx(session):
"""Build the docfx yaml files for this library."""
session.install("-e", ".")
session.install(
- "sphinx==4.0.1", "alabaster", "recommonmark", "gcp-sphinx-docfx-yaml"
+ # 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)
@@ -327,17 +375,23 @@ def docfx(session):
)
-@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
-def prerelease_deps(session):
+@nox.session(python="3.13")
+@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"):
+ session.skip("cpp implementation is not supported in python 3.11+")
+
# Install all dependencies
session.install("-e", ".[all, tests, tracing]")
- session.install(*UNIT_TEST_STANDARD_DEPENDENCIES)
+ 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
- + SYSTEM_TEST_EXTRAS
+ SYSTEM_TEST_STANDARD_DEPENDENCIES + SYSTEM_TEST_EXTERNAL_DEPENDENCIES
)
session.install(*system_deps_all)
@@ -362,20 +416,16 @@ def prerelease_deps(session):
session.install(*constraints_deps)
- if os.path.exists("samples/snippets/requirements.txt"):
- session.install("-r", "samples/snippets/requirements.txt")
-
- if os.path.exists("samples/snippets/requirements-test.txt"):
- session.install("-r", "samples/snippets/requirements-test.txt")
-
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"
@@ -388,7 +438,6 @@ def prerelease_deps(session):
# Remaining dependencies
other_deps = [
"requests",
- "google-auth",
]
session.install(*other_deps)
@@ -397,18 +446,39 @@ def prerelease_deps(session):
"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")
+ 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) or os.path.exists(system_test_folder_path):
- session.run("py.test", "tests/system")
-
- snippets_test_path = os.path.join("samples", "snippets")
-
- # Only run samples tests if found.
- if os.path.exists(snippets_test_path):
- session.run("py.test", "samples/snippets")
+ 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/owlbot.py b/owlbot.py
index 5bc3f034..0f851388 100644
--- a/owlbot.py
+++ b/owlbot.py
@@ -1,4 +1,4 @@
-# Copyright 2018 Google LLC
+# 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.
@@ -12,36 +12,81 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""This script is used to synthesize generated parts of this library."""
+import json
+import os
+from pathlib import Path
+import shutil
+
import synthtool as s
-from synthtool import gcp
+import synthtool.gcp as gcp
from synthtool.languages import python
-common = gcp.CommonTemplates()
+# ----------------------------------------------------------------------------
+# Copy the generated client from the owl-bot staging directory
+# ----------------------------------------------------------------------------
-default_version = "v1beta1"
+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):
- s.move(library, excludes=["nox.py", "setup.py", "README.rst", "docs/index.rst", "google/cloud/errorreporting/"])
+ if clean_up_generated_samples:
+ shutil.rmtree("samples/generated_samples", ignore_errors=True)
+ clean_up_generated_samples = False
+ s.move(
+ [library],
+ excludes=[
+ "**/gapic_version.py",
+ "docs/index.rst",
+ "google/cloud/errorreporting/",
+ "setup.py",
+ "testing/constraints-3.7.txt",
+ "testing/constraints-3.8.txt",
+ ],
+ )
s.remove_staging_dirs()
# ----------------------------------------------------------------------------
# Add templated files
# ----------------------------------------------------------------------------
-templated_files = common.py_library(
- samples=True, # set to True only if there are samples
- microgenerator=True,
+
+templated_files = gcp.CommonTemplates().py_library(
cov_level=100,
+ microgenerator=True,
+ versions=gcp.common.detect_versions(path="./google", default_first=True),
+)
+s.move(
+ templated_files,
+ excludes=[
+ ".coveragerc",
+ ".github/release-please.yml",
+ ".github/auto-label.yaml",
+ "docs/index.rst",
+ "testing/constraints-3.7.txt",
+ ],
)
-s.move(templated_files, excludes=[".coveragerc", ".github/auto-label.yaml"]) # microgenerator has a good .coveragerc file
-# ----------------------------------------------------------------------------
-# Samples templates
-# ----------------------------------------------------------------------------
python.py_samples(skip_readmes=True)
-python.configure_previous_major_version_branches()
+# 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)
-s.shell.run(["nox", "-s", "blacken"], hide_output=False)
+# --------------------------------------------------------------------------
+# Modify test configs
+# --------------------------------------------------------------------------
+# add shared environment variables to test configs
+tracked_subdirs = ["continuous", "presubmit", "samples"]
+for subdir in tracked_subdirs:
+ for path, subdirs, files in os.walk(f".kokoro/{subdir}"):
+ for name in files:
+ if name == "common.cfg":
+ file_path = os.path.join(path, name)
+ s.move(
+ ".kokoro/common_env_vars.cfg",
+ file_path,
+ merge=lambda src, dst, _,: f"{dst}\n{src}",
+ )
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 00000000..4166e729
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,15 @@
+[pytest]
+filterwarnings =
+ # treat all warnings as errors
+ error
+ # Remove once https://github.com/protocolbuffers/protobuf/issues/12186 is fixed
+ ignore:.*custom tp_new.*in Python 3.14:DeprecationWarning
+ # Remove warning once https://github.com/grpc/grpc/issues/35974 is fixed
+ ignore:unclosed:ResourceWarning
+ # Remove once https://github.com/googleapis/python-api-common-protos/pull/187/files is merged
+ ignore:.*pkg_resources.declare_namespace:DeprecationWarning
+ ignore:.*pkg_resources is deprecated as an API:DeprecationWarning
+ # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1939 is fixed
+ ignore:get_mtls_endpoint_and_cert_source is deprecated.:DeprecationWarning
+ # Remove warning once the minium supported version of google-cloud-logging is 2.x.x
+ ignore:.*Create unlinked descriptors is going to go away. Please use get/find descriptors from generated code or query the descriptor_pool.*:DeprecationWarning
diff --git a/release-please-config.json b/release-please-config.json
new file mode 100644
index 00000000..27b697b9
--- /dev/null
+++ b/release-please-config.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
+ "packages": {
+ ".": {
+ "release-type": "python",
+ "extra-files": [
+ "google/cloud/error_reporting/gapic_version.py",
+ "google/cloud/errorreporting_v1beta1/gapic_version.py",
+ {
+ "type": "json",
+ "path": "samples/generated_samples/snippet_metadata_google.devtools.clouderrorreporting.v1beta1.json",
+ "jsonpath": "$.clientLibrary.version"
+ }
+ ]
+ }
+ },
+ "release-type": "python",
+ "plugins": [
+ {
+ "type": "sentence-case"
+ }
+ ],
+ "initial-version": "0.1.0"
+}
diff --git a/renovate.json b/renovate.json
index c21036d3..c7875c46 100644
--- a/renovate.json
+++ b/renovate.json
@@ -5,7 +5,7 @@
":preserveSemverRanges",
":disableDependencyDashboard"
],
- "ignorePaths": [".pre-commit-config.yaml"],
+ "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/clouderrorreporting_v1beta1_generated_error_group_service_get_group_async.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_get_group_async.py
index 7a6e06fc..4a0dabf5 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_get_group_async.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_get_group_async.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorGroupService_GetGroup_async]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_get_group_sync.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_get_group_sync.py
index 9c8ac42b..79c76d18 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_get_group_sync.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_get_group_sync.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorGroupService_GetGroup_sync]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_async.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_async.py
index 4581e274..58a7d5b3 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_async.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_async.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorGroupService_UpdateGroup_async]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_sync.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_sync.py
index 67cb2a40..9fa53da6 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_sync.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_group_service_update_group_sync.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorGroupService_UpdateGroup_sync]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_async.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_async.py
index e8f462be..6479d386 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_async.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_async.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorStatsService_DeleteEvents_async]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_sync.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_sync.py
index 75da055a..60dcebe9 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_sync.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_delete_events_sync.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorStatsService_DeleteEvents_sync]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_async.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_async.py
index 8e3bd85f..3b143dca 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_async.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_async.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorStatsService_ListEvents_async]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_sync.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_sync.py
index 9bd122f5..273d4a44 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_sync.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_events_sync.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorStatsService_ListEvents_sync]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_async.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_async.py
index d6176174..92ef3ed1 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_async.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_async.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorStatsService_ListGroupStats_async]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_sync.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_sync.py
index 02053bde..ed6b7f5a 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_sync.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_error_stats_service_list_group_stats_sync.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ErrorStatsService_ListGroupStats_sync]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_async.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_async.py
index 333e710b..b2c9d42e 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_async.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_async.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ReportErrorsService_ReportErrorEvent_async]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_sync.py b/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_sync.py
index 4ffd5a4f..b413c1f6 100644
--- a/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_sync.py
+++ b/samples/generated_samples/clouderrorreporting_v1beta1_generated_report_errors_service_report_error_event_sync.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.
@@ -20,10 +20,17 @@
# It may require modifications to work in your environment.
# To install the latest published package dependency, execute the following:
-# python3 -m pip install google-cloud-errorreporting
+# python3 -m pip install google-cloud-error-reporting
# [START clouderrorreporting_v1beta1_generated_ReportErrorsService_ReportErrorEvent_sync]
+# This snippet has been automatically generated and should be regarded as a
+# code template only.
+# It will require modifications to work:
+# - It may require correct/in-range values for request initialization.
+# - It may require specifying regional endpoints when creating the service
+# client as shown in:
+# https://googleapis.dev/python/google-api-core/latest/client_options.html
from google.cloud import errorreporting_v1beta1
diff --git a/samples/generated_samples/snippet_metadata_errorreporting_v1beta1.json b/samples/generated_samples/snippet_metadata_google.devtools.clouderrorreporting.v1beta1.json
similarity index 90%
rename from samples/generated_samples/snippet_metadata_errorreporting_v1beta1.json
rename to samples/generated_samples/snippet_metadata_google.devtools.clouderrorreporting.v1beta1.json
index 6b5f5926..226f3879 100644
--- a/samples/generated_samples/snippet_metadata_errorreporting_v1beta1.json
+++ b/samples/generated_samples/snippet_metadata_google.devtools.clouderrorreporting.v1beta1.json
@@ -7,7 +7,8 @@
}
],
"language": "PYTHON",
- "name": "google-cloud-errorreporting"
+ "name": "google-cloud-error-reporting",
+ "version": "1.12.0"
},
"snippets": [
{
@@ -46,7 +47,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.ErrorGroup",
@@ -59,33 +60,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorGroupService_GetGroup_async",
"segments": [
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "FULL"
},
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 38,
- "start": 34,
+ "end": 45,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 41,
- "start": 39,
+ "end": 48,
+ "start": 46,
"type": "REQUEST_EXECUTION"
},
{
- "end": 45,
- "start": 42,
+ "end": 52,
+ "start": 49,
"type": "RESPONSE_HANDLING"
}
],
@@ -126,7 +127,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.ErrorGroup",
@@ -139,33 +140,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorGroupService_GetGroup_sync",
"segments": [
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "FULL"
},
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 38,
- "start": 34,
+ "end": 45,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 41,
- "start": 39,
+ "end": 48,
+ "start": 46,
"type": "REQUEST_EXECUTION"
},
{
- "end": 45,
- "start": 42,
+ "end": 52,
+ "start": 49,
"type": "RESPONSE_HANDLING"
}
],
@@ -207,7 +208,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.ErrorGroup",
@@ -220,33 +221,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorGroupService_UpdateGroup_async",
"segments": [
{
- "end": 43,
+ "end": 50,
"start": 27,
"type": "FULL"
},
{
- "end": 43,
+ "end": 50,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 37,
- "start": 34,
+ "end": 44,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 40,
- "start": 38,
+ "end": 47,
+ "start": 45,
"type": "REQUEST_EXECUTION"
},
{
- "end": 44,
- "start": 41,
+ "end": 51,
+ "start": 48,
"type": "RESPONSE_HANDLING"
}
],
@@ -287,7 +288,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.ErrorGroup",
@@ -300,33 +301,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorGroupService_UpdateGroup_sync",
"segments": [
{
- "end": 43,
+ "end": 50,
"start": 27,
"type": "FULL"
},
{
- "end": 43,
+ "end": 50,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 37,
- "start": 34,
+ "end": 44,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 40,
- "start": 38,
+ "end": 47,
+ "start": 45,
"type": "REQUEST_EXECUTION"
},
{
- "end": 44,
- "start": 41,
+ "end": 51,
+ "start": 48,
"type": "RESPONSE_HANDLING"
}
],
@@ -368,7 +369,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.DeleteEventsResponse",
@@ -381,33 +382,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorStatsService_DeleteEvents_async",
"segments": [
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "FULL"
},
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 38,
- "start": 34,
+ "end": 45,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 41,
- "start": 39,
+ "end": 48,
+ "start": 46,
"type": "REQUEST_EXECUTION"
},
{
- "end": 45,
- "start": 42,
+ "end": 52,
+ "start": 49,
"type": "RESPONSE_HANDLING"
}
],
@@ -448,7 +449,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.DeleteEventsResponse",
@@ -461,33 +462,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorStatsService_DeleteEvents_sync",
"segments": [
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "FULL"
},
{
- "end": 44,
+ "end": 51,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 38,
- "start": 34,
+ "end": 45,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 41,
- "start": 39,
+ "end": 48,
+ "start": 46,
"type": "REQUEST_EXECUTION"
},
{
- "end": 45,
- "start": 42,
+ "end": 52,
+ "start": 49,
"type": "RESPONSE_HANDLING"
}
],
@@ -533,7 +534,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListEventsAsyncPager",
@@ -546,33 +547,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorStatsService_ListEvents_async",
"segments": [
{
- "end": 46,
+ "end": 53,
"start": 27,
"type": "FULL"
},
{
- "end": 46,
+ "end": 53,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 39,
- "start": 34,
+ "end": 46,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 42,
- "start": 40,
+ "end": 49,
+ "start": 47,
"type": "REQUEST_EXECUTION"
},
{
- "end": 47,
- "start": 43,
+ "end": 54,
+ "start": 50,
"type": "RESPONSE_HANDLING"
}
],
@@ -617,7 +618,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListEventsPager",
@@ -630,33 +631,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorStatsService_ListEvents_sync",
"segments": [
{
- "end": 46,
+ "end": 53,
"start": 27,
"type": "FULL"
},
{
- "end": 46,
+ "end": 53,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 39,
- "start": 34,
+ "end": 46,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 42,
- "start": 40,
+ "end": 49,
+ "start": 47,
"type": "REQUEST_EXECUTION"
},
{
- "end": 47,
- "start": 43,
+ "end": 54,
+ "start": 50,
"type": "RESPONSE_HANDLING"
}
],
@@ -702,7 +703,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListGroupStatsAsyncPager",
@@ -715,33 +716,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorStatsService_ListGroupStats_async",
"segments": [
{
- "end": 45,
+ "end": 52,
"start": 27,
"type": "FULL"
},
{
- "end": 45,
+ "end": 52,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 38,
- "start": 34,
+ "end": 45,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 41,
- "start": 39,
+ "end": 48,
+ "start": 46,
"type": "REQUEST_EXECUTION"
},
{
- "end": 46,
- "start": 42,
+ "end": 53,
+ "start": 49,
"type": "RESPONSE_HANDLING"
}
],
@@ -786,7 +787,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.services.error_stats_service.pagers.ListGroupStatsPager",
@@ -799,33 +800,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ErrorStatsService_ListGroupStats_sync",
"segments": [
{
- "end": 45,
+ "end": 52,
"start": 27,
"type": "FULL"
},
{
- "end": 45,
+ "end": 52,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 38,
- "start": 34,
+ "end": 45,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 41,
- "start": 39,
+ "end": 48,
+ "start": 46,
"type": "REQUEST_EXECUTION"
},
{
- "end": 46,
- "start": 42,
+ "end": 53,
+ "start": 49,
"type": "RESPONSE_HANDLING"
}
],
@@ -871,7 +872,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.ReportErrorEventResponse",
@@ -884,33 +885,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ReportErrorsService_ReportErrorEvent_async",
"segments": [
{
- "end": 48,
+ "end": 55,
"start": 27,
"type": "FULL"
},
{
- "end": 48,
+ "end": 55,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 42,
- "start": 34,
+ "end": 49,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 45,
- "start": 43,
+ "end": 52,
+ "start": 50,
"type": "REQUEST_EXECUTION"
},
{
- "end": 49,
- "start": 46,
+ "end": 56,
+ "start": 53,
"type": "RESPONSE_HANDLING"
}
],
@@ -955,7 +956,7 @@
},
{
"name": "metadata",
- "type": "Sequence[Tuple[str, str]"
+ "type": "Sequence[Tuple[str, Union[str, bytes]]]"
}
],
"resultType": "google.cloud.errorreporting_v1beta1.types.ReportErrorEventResponse",
@@ -968,33 +969,33 @@
"regionTag": "clouderrorreporting_v1beta1_generated_ReportErrorsService_ReportErrorEvent_sync",
"segments": [
{
- "end": 48,
+ "end": 55,
"start": 27,
"type": "FULL"
},
{
- "end": 48,
+ "end": 55,
"start": 27,
"type": "SHORT"
},
{
- "end": 33,
- "start": 31,
+ "end": 40,
+ "start": 38,
"type": "CLIENT_INITIALIZATION"
},
{
- "end": 42,
- "start": 34,
+ "end": 49,
+ "start": 41,
"type": "REQUEST_INITIALIZATION"
},
{
- "end": 45,
- "start": 43,
+ "end": 52,
+ "start": 50,
"type": "REQUEST_EXECUTION"
},
{
- "end": 49,
- "start": 46,
+ "end": 56,
+ "start": 53,
"type": "RESPONSE_HANDLING"
}
],
diff --git a/samples/snippets/README.md b/samples/snippets/README.md
new file mode 100644
index 00000000..b8c6e222
--- /dev/null
+++ b/samples/snippets/README.md
@@ -0,0 +1,4 @@
+Samples migrated
+================
+
+New location: https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/error_reporting
diff --git a/samples/snippets/api/README.rst b/samples/snippets/api/README.rst
deleted file mode 100644
index fe98a482..00000000
--- a/samples/snippets/api/README.rst
+++ /dev/null
@@ -1,98 +0,0 @@
-.. This file is automatically generated. Do not edit this file directly.
-
-Error Reporting Python Samples
-===============================================================================
-
-.. image:: https://gstatic.com/cloudssh/images/open-btn.png
- :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=error_reporting/api/README.rst
-
-
-This directory contains samples for Error Reporting. `Error Reporting`_ aggregates and displays errors produced in
- your running cloud services.
-
-
-
-
-.. _Error Reporting: https://cloud.google.com/error-reporting/docs/
-
-Setup
--------------------------------------------------------------------------------
-
-
-Authentication
-++++++++++++++
-
-This sample requires you to have authentication setup. Refer to the
-`Authentication Getting Started Guide`_ for instructions on setting up
-credentials for applications.
-
-.. _Authentication Getting Started Guide:
- https://cloud.google.com/docs/authentication/getting-started
-
-Install Dependencies
-++++++++++++++++++++
-
-#. Clone python-docs-samples and change directory to the sample directory you want to use.
-
- .. code-block:: bash
-
- $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
-
-#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions.
-
- .. _Python Development Environment Setup Guide:
- https://cloud.google.com/python/setup
-
-#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+.
-
- .. code-block:: bash
-
- $ virtualenv env
- $ source env/bin/activate
-
-#. Install the dependencies needed to run the samples.
-
- .. code-block:: bash
-
- $ pip install -r requirements.txt
-
-.. _pip: https://pip.pypa.io/
-.. _virtualenv: https://virtualenv.pypa.io/
-
-Samples
--------------------------------------------------------------------------------
-
-Report Exception
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-.. image:: https://gstatic.com/cloudssh/images/open-btn.png
- :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=error_reporting/api/report_exception.py,error_reporting/api/README.rst
-
-
-
-
-To run this sample:
-
-.. code-block:: bash
-
- $ python report_exception.py
-
-
-
-
-The client library
--------------------------------------------------------------------------------
-
-This sample uses the `Google Cloud Client Library for Python`_.
-You can read the documentation for more details on API usage and use GitHub
-to `browse the source`_ and `report issues`_.
-
-.. _Google Cloud Client Library for Python:
- https://googlecloudplatform.github.io/google-cloud-python/
-.. _browse the source:
- https://github.com/GoogleCloudPlatform/google-cloud-python
-.. _report issues:
- https://github.com/GoogleCloudPlatform/google-cloud-python/issues
-
-
-.. _Google Cloud SDK: https://cloud.google.com/sdk/
\ No newline at end of file
diff --git a/samples/snippets/api/README.rst.in b/samples/snippets/api/README.rst.in
deleted file mode 100644
index 56f4080f..00000000
--- a/samples/snippets/api/README.rst.in
+++ /dev/null
@@ -1,21 +0,0 @@
-# This file is used to generate README.rst
-
-product:
- name: Error Reporting
- short_name: Error Reporting
- url: https://cloud.google.com/error-reporting/docs/
- description: >
- `Error Reporting`_ aggregates and displays errors produced in
- your running cloud services.
-
-setup:
-- auth
-- install_deps
-
-samples:
-- name: Report Exception
- file: report_exception.py
-
-cloud_client_library: true
-
-folder: error_reporting/api
\ No newline at end of file
diff --git a/samples/snippets/api/noxfile.py b/samples/snippets/api/noxfile.py
deleted file mode 100644
index 5fcb9d74..00000000
--- a/samples/snippets/api/noxfile.py
+++ /dev/null
@@ -1,312 +0,0 @@
-# Copyright 2019 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 __future__ import print_function
-
-import glob
-import os
-from pathlib import Path
-import sys
-from typing import Callable, Dict, List, Optional
-
-import nox
-
-
-# WARNING - WARNING - WARNING - WARNING - WARNING
-# WARNING - WARNING - WARNING - WARNING - WARNING
-# DO NOT EDIT THIS FILE EVER!
-# WARNING - WARNING - WARNING - WARNING - WARNING
-# WARNING - WARNING - WARNING - WARNING - WARNING
-
-BLACK_VERSION = "black==22.3.0"
-ISORT_VERSION = "isort==5.10.1"
-
-# Copy `noxfile_config.py` to your directory and modify it instead.
-
-# `TEST_CONFIG` dict is a configuration hook that allows users to
-# modify the test configurations. The values here should be in sync
-# with `noxfile_config.py`. Users will copy `noxfile_config.py` into
-# their directory and modify it.
-
-TEST_CONFIG = {
- # You can opt out from the test for specific Python versions.
- "ignored_versions": [],
- # Old samples are opted out of enforcing Python type hints
- # All new samples should feature them
- "enforce_type_hints": False,
- # An envvar key for determining the project id to use. Change it
- # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
- # build specific Cloud project. You can also use your own string
- # to use your own Cloud project.
- "gcloud_project_env": "GOOGLE_CLOUD_PROJECT",
- # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT',
- # If you need to use a specific version of pip,
- # change pip_version_override to the string representation
- # of the version number, for example, "20.2.4"
- "pip_version_override": None,
- # A dictionary you want to inject into your test. Don't put any
- # secrets here. These values will override predefined values.
- "envs": {},
-}
-
-
-try:
- # Ensure we can import noxfile_config in the project's directory.
- sys.path.append(".")
- from noxfile_config import TEST_CONFIG_OVERRIDE
-except ImportError as e:
- print("No user noxfile_config found: detail: {}".format(e))
- TEST_CONFIG_OVERRIDE = {}
-
-# Update the TEST_CONFIG with the user supplied values.
-TEST_CONFIG.update(TEST_CONFIG_OVERRIDE)
-
-
-def get_pytest_env_vars() -> Dict[str, str]:
- """Returns a dict for pytest invocation."""
- ret = {}
-
- # Override the GCLOUD_PROJECT and the alias.
- env_key = TEST_CONFIG["gcloud_project_env"]
- # This should error out if not set.
- ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key]
-
- # Apply user supplied envs.
- ret.update(TEST_CONFIG["envs"])
- return ret
-
-
-# DO NOT EDIT - automatically generated.
-# All versions used to test samples.
-ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"]
-
-# Any default versions that should be ignored.
-IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"]
-
-TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS])
-
-INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in (
- "True",
- "true",
-)
-
-# Error if a python version is missing
-nox.options.error_on_missing_interpreters = True
-
-#
-# Style Checks
-#
-
-
-def _determine_local_import_names(start_dir: str) -> List[str]:
- """Determines all import names that should be considered "local".
-
- This is used when running the linter to insure that import order is
- properly checked.
- """
- file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)]
- return [
- basename
- for basename, extension in file_ext_pairs
- if extension == ".py"
- or os.path.isdir(os.path.join(start_dir, basename))
- and basename not in ("__pycache__")
- ]
-
-
-# Linting with flake8.
-#
-# We ignore the following rules:
-# E203: whitespace before ‘:’
-# E266: too many leading ‘#’ for block comment
-# E501: line too long
-# I202: Additional newline in a section of imports
-#
-# We also need to specify the rules which are ignored by default:
-# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121']
-FLAKE8_COMMON_ARGS = [
- "--show-source",
- "--builtin=gettext",
- "--max-complexity=20",
- "--import-order-style=google",
- "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py",
- "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202",
- "--max-line-length=88",
-]
-
-
-@nox.session
-def lint(session: nox.sessions.Session) -> None:
- if not TEST_CONFIG["enforce_type_hints"]:
- session.install("flake8", "flake8-import-order")
- else:
- session.install("flake8", "flake8-import-order", "flake8-annotations")
-
- local_names = _determine_local_import_names(".")
- args = FLAKE8_COMMON_ARGS + [
- "--application-import-names",
- ",".join(local_names),
- ".",
- ]
- session.run("flake8", *args)
-
-
-#
-# Black
-#
-
-
-@nox.session
-def blacken(session: nox.sessions.Session) -> None:
- """Run black. Format code to uniform standard."""
- session.install(BLACK_VERSION)
- python_files = [path for path in os.listdir(".") if path.endswith(".py")]
-
- session.run("black", *python_files)
-
-
-#
-# format = isort + black
-#
-
-@nox.session
-def format(session: nox.sessions.Session) -> None:
- """
- Run isort to sort imports. Then run black
- to format code to uniform standard.
- """
- session.install(BLACK_VERSION, ISORT_VERSION)
- python_files = [path for path in os.listdir(".") if path.endswith(".py")]
-
- # 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", *python_files)
- session.run("black", *python_files)
-
-
-#
-# Sample Tests
-#
-
-
-PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"]
-
-
-def _session_tests(
- session: nox.sessions.Session, post_install: Callable = None
-) -> None:
- # check for presence of tests
- test_list = glob.glob("*_test.py") + glob.glob("test_*.py")
- test_list.extend(glob.glob("tests"))
-
- if len(test_list) == 0:
- print("No tests found, skipping directory.")
- return
-
- if TEST_CONFIG["pip_version_override"]:
- pip_version = TEST_CONFIG["pip_version_override"]
- session.install(f"pip=={pip_version}")
- """Runs py.test for a particular project."""
- concurrent_args = []
- if os.path.exists("requirements.txt"):
- if os.path.exists("constraints.txt"):
- session.install("-r", "requirements.txt", "-c", "constraints.txt")
- else:
- session.install("-r", "requirements.txt")
- with open("requirements.txt") as rfile:
- packages = rfile.read()
-
- if os.path.exists("requirements-test.txt"):
- if os.path.exists("constraints-test.txt"):
- session.install(
- "-r", "requirements-test.txt", "-c", "constraints-test.txt"
- )
- else:
- session.install("-r", "requirements-test.txt")
- with open("requirements-test.txt") as rtfile:
- packages += rtfile.read()
-
- if INSTALL_LIBRARY_FROM_SOURCE:
- session.install("-e", _get_repo_root())
-
- if post_install:
- post_install(session)
-
- if "pytest-parallel" in packages:
- concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto'])
- elif "pytest-xdist" in packages:
- concurrent_args.extend(['-n', 'auto'])
-
- session.run(
- "pytest",
- *(PYTEST_COMMON_ARGS + session.posargs + concurrent_args),
- # Pytest will return 5 when no tests are collected. This can happen
- # on travis where slow and flaky tests are excluded.
- # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html
- success_codes=[0, 5],
- env=get_pytest_env_vars(),
- )
-
-
-@nox.session(python=ALL_VERSIONS)
-def py(session: nox.sessions.Session) -> None:
- """Runs py.test for a sample using the specified version of Python."""
- if session.python in TESTED_VERSIONS:
- _session_tests(session)
- else:
- session.skip(
- "SKIPPED: {} tests are disabled for this sample.".format(session.python)
- )
-
-
-#
-# Readmegen
-#
-
-
-def _get_repo_root() -> Optional[str]:
- """ Returns the root folder of the project. """
- # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
- p = Path(os.getcwd())
- for i in range(10):
- if p is None:
- break
- if Path(p / ".git").exists():
- return str(p)
- # .git is not available in repos cloned via Cloud Build
- # setup.py is always in the library's root, so use that instead
- # https://github.com/googleapis/synthtool/issues/792
- if Path(p / "setup.py").exists():
- return str(p)
- p = p.parent
- raise Exception("Unable to detect repository root.")
-
-
-GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")])
-
-
-@nox.session
-@nox.parametrize("path", GENERATED_READMES)
-def readmegen(session: nox.sessions.Session, path: str) -> None:
- """(Re-)generates the readme for a sample."""
- session.install("jinja2", "pyyaml")
- dir_ = os.path.dirname(path)
-
- if os.path.exists(os.path.join(dir_, "requirements.txt")):
- session.install("-r", os.path.join(dir_, "requirements.txt"))
-
- in_file = os.path.join(dir_, "README.rst.in")
- session.run(
- "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file
- )
diff --git a/samples/snippets/api/report_exception.py b/samples/snippets/api/report_exception.py
deleted file mode 100644
index 2b7e8f06..00000000
--- a/samples/snippets/api/report_exception.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-
-
-# [START error_reporting]
-# [START error_reporting_quickstart]
-# [START error_reporting_setup_python]
-def simulate_error():
- from google.cloud import error_reporting
-
- client = error_reporting.Client()
- try:
- # simulate calling a method that's not defined
- raise NameError
- except Exception:
- client.report_exception()
-# [END error_reporting_setup_python]
-# [END error_reporting_quickstart]
-# [END error_reporting]
-
-
-# [START error_reporting_manual]
-# [START error_reporting_setup_python_manual]
-def report_manual_error():
- from google.cloud import error_reporting
-
- client = error_reporting.Client()
- client.report("An error has occurred.")
-# [START error_reporting_setup_python_manual]
-# [END error_reporting_manual]
-
-
-if __name__ == '__main__':
- simulate_error()
- report_manual_error()
diff --git a/samples/snippets/api/requirements-test.txt b/samples/snippets/api/requirements-test.txt
deleted file mode 100644
index d00689e0..00000000
--- a/samples/snippets/api/requirements-test.txt
+++ /dev/null
@@ -1 +0,0 @@
-pytest==7.1.2
diff --git a/samples/snippets/api/requirements.txt b/samples/snippets/api/requirements.txt
deleted file mode 100644
index d44c4105..00000000
--- a/samples/snippets/api/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-google-cloud-error-reporting==1.5.2
diff --git a/samples/snippets/fluent_on_compute/README.md b/samples/snippets/fluent_on_compute/README.md
deleted file mode 100644
index d3a58c16..00000000
--- a/samples/snippets/fluent_on_compute/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Google Error Reorting Samples Samples
-
-[![Open in Cloud Shell][shell_img]][shell_link]
-
-[shell_img]: http://gstatic.com/cloudssh/images/open-btn.png
-[shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=error_reporting/fluent_on_compute/README.md
-
-This section contains samples for [Google Cloud Error Reporting](https://cloud.google.com/error-reporting).
-
-A startup script has been provided to demonstrated how to properly provision a GCE
-instance with fluentd configured. Note the intallation of fluentd, the addition of the config file,
- and the restarting of the fluetnd service. You can start an instance using
-it like this:
-
- gcloud compute instances create example-instance --metadata-from-file startup-script=startup_script.sh
-
-or simply use it as reference when creating your own instance.
-
-After fluentd is configured, main.py could be used to simulate an error:
-
- gcloud compute copy-files main.py example-instance:~/main.py
-
-Then,
-
- gcloud compute ssh example-instance
- python ~/main.py
-
-And you will see the message in the Errors Console.
-
-
-These samples are used on the following documentation page:
-
-> https://cloud.google.com/error-reporting/docs/setting-up-on-compute-engine
-
-
diff --git a/samples/snippets/fluent_on_compute/main.py b/samples/snippets/fluent_on_compute/main.py
deleted file mode 100644
index 45208c91..00000000
--- a/samples/snippets/fluent_on_compute/main.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-
-# [START error_reporting]
-import traceback
-
-import fluent.event
-import fluent.sender
-
-
-def simulate_error():
- fluent.sender.setup('myapp', host='localhost', port=24224)
-
- def report(ex):
- data = {}
- data['message'] = '{0}'.format(ex)
- data['serviceContext'] = {'service': 'myapp'}
- # ... add more metadata
- fluent.event.Event('errors', data)
-
- # report exception data using:
- try:
- # simulate calling a method that's not defined
- raise NameError
- except Exception:
- report(traceback.format_exc())
-# [END error_reporting]
-
-
-if __name__ == '__main__':
- simulate_error()
diff --git a/samples/snippets/fluent_on_compute/noxfile.py b/samples/snippets/fluent_on_compute/noxfile.py
deleted file mode 100644
index 5fcb9d74..00000000
--- a/samples/snippets/fluent_on_compute/noxfile.py
+++ /dev/null
@@ -1,312 +0,0 @@
-# Copyright 2019 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 __future__ import print_function
-
-import glob
-import os
-from pathlib import Path
-import sys
-from typing import Callable, Dict, List, Optional
-
-import nox
-
-
-# WARNING - WARNING - WARNING - WARNING - WARNING
-# WARNING - WARNING - WARNING - WARNING - WARNING
-# DO NOT EDIT THIS FILE EVER!
-# WARNING - WARNING - WARNING - WARNING - WARNING
-# WARNING - WARNING - WARNING - WARNING - WARNING
-
-BLACK_VERSION = "black==22.3.0"
-ISORT_VERSION = "isort==5.10.1"
-
-# Copy `noxfile_config.py` to your directory and modify it instead.
-
-# `TEST_CONFIG` dict is a configuration hook that allows users to
-# modify the test configurations. The values here should be in sync
-# with `noxfile_config.py`. Users will copy `noxfile_config.py` into
-# their directory and modify it.
-
-TEST_CONFIG = {
- # You can opt out from the test for specific Python versions.
- "ignored_versions": [],
- # Old samples are opted out of enforcing Python type hints
- # All new samples should feature them
- "enforce_type_hints": False,
- # An envvar key for determining the project id to use. Change it
- # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
- # build specific Cloud project. You can also use your own string
- # to use your own Cloud project.
- "gcloud_project_env": "GOOGLE_CLOUD_PROJECT",
- # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT',
- # If you need to use a specific version of pip,
- # change pip_version_override to the string representation
- # of the version number, for example, "20.2.4"
- "pip_version_override": None,
- # A dictionary you want to inject into your test. Don't put any
- # secrets here. These values will override predefined values.
- "envs": {},
-}
-
-
-try:
- # Ensure we can import noxfile_config in the project's directory.
- sys.path.append(".")
- from noxfile_config import TEST_CONFIG_OVERRIDE
-except ImportError as e:
- print("No user noxfile_config found: detail: {}".format(e))
- TEST_CONFIG_OVERRIDE = {}
-
-# Update the TEST_CONFIG with the user supplied values.
-TEST_CONFIG.update(TEST_CONFIG_OVERRIDE)
-
-
-def get_pytest_env_vars() -> Dict[str, str]:
- """Returns a dict for pytest invocation."""
- ret = {}
-
- # Override the GCLOUD_PROJECT and the alias.
- env_key = TEST_CONFIG["gcloud_project_env"]
- # This should error out if not set.
- ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key]
-
- # Apply user supplied envs.
- ret.update(TEST_CONFIG["envs"])
- return ret
-
-
-# DO NOT EDIT - automatically generated.
-# All versions used to test samples.
-ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"]
-
-# Any default versions that should be ignored.
-IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"]
-
-TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS])
-
-INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in (
- "True",
- "true",
-)
-
-# Error if a python version is missing
-nox.options.error_on_missing_interpreters = True
-
-#
-# Style Checks
-#
-
-
-def _determine_local_import_names(start_dir: str) -> List[str]:
- """Determines all import names that should be considered "local".
-
- This is used when running the linter to insure that import order is
- properly checked.
- """
- file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)]
- return [
- basename
- for basename, extension in file_ext_pairs
- if extension == ".py"
- or os.path.isdir(os.path.join(start_dir, basename))
- and basename not in ("__pycache__")
- ]
-
-
-# Linting with flake8.
-#
-# We ignore the following rules:
-# E203: whitespace before ‘:’
-# E266: too many leading ‘#’ for block comment
-# E501: line too long
-# I202: Additional newline in a section of imports
-#
-# We also need to specify the rules which are ignored by default:
-# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121']
-FLAKE8_COMMON_ARGS = [
- "--show-source",
- "--builtin=gettext",
- "--max-complexity=20",
- "--import-order-style=google",
- "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py",
- "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202",
- "--max-line-length=88",
-]
-
-
-@nox.session
-def lint(session: nox.sessions.Session) -> None:
- if not TEST_CONFIG["enforce_type_hints"]:
- session.install("flake8", "flake8-import-order")
- else:
- session.install("flake8", "flake8-import-order", "flake8-annotations")
-
- local_names = _determine_local_import_names(".")
- args = FLAKE8_COMMON_ARGS + [
- "--application-import-names",
- ",".join(local_names),
- ".",
- ]
- session.run("flake8", *args)
-
-
-#
-# Black
-#
-
-
-@nox.session
-def blacken(session: nox.sessions.Session) -> None:
- """Run black. Format code to uniform standard."""
- session.install(BLACK_VERSION)
- python_files = [path for path in os.listdir(".") if path.endswith(".py")]
-
- session.run("black", *python_files)
-
-
-#
-# format = isort + black
-#
-
-@nox.session
-def format(session: nox.sessions.Session) -> None:
- """
- Run isort to sort imports. Then run black
- to format code to uniform standard.
- """
- session.install(BLACK_VERSION, ISORT_VERSION)
- python_files = [path for path in os.listdir(".") if path.endswith(".py")]
-
- # 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", *python_files)
- session.run("black", *python_files)
-
-
-#
-# Sample Tests
-#
-
-
-PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"]
-
-
-def _session_tests(
- session: nox.sessions.Session, post_install: Callable = None
-) -> None:
- # check for presence of tests
- test_list = glob.glob("*_test.py") + glob.glob("test_*.py")
- test_list.extend(glob.glob("tests"))
-
- if len(test_list) == 0:
- print("No tests found, skipping directory.")
- return
-
- if TEST_CONFIG["pip_version_override"]:
- pip_version = TEST_CONFIG["pip_version_override"]
- session.install(f"pip=={pip_version}")
- """Runs py.test for a particular project."""
- concurrent_args = []
- if os.path.exists("requirements.txt"):
- if os.path.exists("constraints.txt"):
- session.install("-r", "requirements.txt", "-c", "constraints.txt")
- else:
- session.install("-r", "requirements.txt")
- with open("requirements.txt") as rfile:
- packages = rfile.read()
-
- if os.path.exists("requirements-test.txt"):
- if os.path.exists("constraints-test.txt"):
- session.install(
- "-r", "requirements-test.txt", "-c", "constraints-test.txt"
- )
- else:
- session.install("-r", "requirements-test.txt")
- with open("requirements-test.txt") as rtfile:
- packages += rtfile.read()
-
- if INSTALL_LIBRARY_FROM_SOURCE:
- session.install("-e", _get_repo_root())
-
- if post_install:
- post_install(session)
-
- if "pytest-parallel" in packages:
- concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto'])
- elif "pytest-xdist" in packages:
- concurrent_args.extend(['-n', 'auto'])
-
- session.run(
- "pytest",
- *(PYTEST_COMMON_ARGS + session.posargs + concurrent_args),
- # Pytest will return 5 when no tests are collected. This can happen
- # on travis where slow and flaky tests are excluded.
- # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html
- success_codes=[0, 5],
- env=get_pytest_env_vars(),
- )
-
-
-@nox.session(python=ALL_VERSIONS)
-def py(session: nox.sessions.Session) -> None:
- """Runs py.test for a sample using the specified version of Python."""
- if session.python in TESTED_VERSIONS:
- _session_tests(session)
- else:
- session.skip(
- "SKIPPED: {} tests are disabled for this sample.".format(session.python)
- )
-
-
-#
-# Readmegen
-#
-
-
-def _get_repo_root() -> Optional[str]:
- """ Returns the root folder of the project. """
- # Get root of this repository. Assume we don't have directories nested deeper than 10 items.
- p = Path(os.getcwd())
- for i in range(10):
- if p is None:
- break
- if Path(p / ".git").exists():
- return str(p)
- # .git is not available in repos cloned via Cloud Build
- # setup.py is always in the library's root, so use that instead
- # https://github.com/googleapis/synthtool/issues/792
- if Path(p / "setup.py").exists():
- return str(p)
- p = p.parent
- raise Exception("Unable to detect repository root.")
-
-
-GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")])
-
-
-@nox.session
-@nox.parametrize("path", GENERATED_READMES)
-def readmegen(session: nox.sessions.Session, path: str) -> None:
- """(Re-)generates the readme for a sample."""
- session.install("jinja2", "pyyaml")
- dir_ = os.path.dirname(path)
-
- if os.path.exists(os.path.join(dir_, "requirements.txt")):
- session.install("-r", os.path.join(dir_, "requirements.txt"))
-
- in_file = os.path.join(dir_, "README.rst.in")
- session.run(
- "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file
- )
diff --git a/samples/snippets/fluent_on_compute/requirements-test.txt b/samples/snippets/fluent_on_compute/requirements-test.txt
deleted file mode 100644
index fb466e50..00000000
--- a/samples/snippets/fluent_on_compute/requirements-test.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-pytest==7.1.2
-mock==4.0.3
diff --git a/samples/snippets/fluent_on_compute/requirements.txt b/samples/snippets/fluent_on_compute/requirements.txt
deleted file mode 100644
index 693841f6..00000000
--- a/samples/snippets/fluent_on_compute/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-fluent-logger==0.10.0
diff --git a/samples/snippets/fluent_on_compute/startup_script.sh b/samples/snippets/fluent_on_compute/startup_script.sh
deleted file mode 100644
index f2ef895d..00000000
--- a/samples/snippets/fluent_on_compute/startup_script.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2016 Google Inc. 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.
-
-set -v
-
-curl -sSO "https://dl.google.com/cloudagents/install-logging-agent.sh"
-chmod +x install-logging-agent.sh
-./install-logging-agent.sh
-mkdir -p /etc/google-fluentd/config.d/
-cat < /etc/google-fluentd/config.d/forward.conf
-
- type forward
- port 24224
-
-EOF
-service google-fluentd restart
-
-apt-get update
-apt-get install -yq \
- git build-essential supervisor python python-dev python-pip libffi-dev \
- libssl-dev
-pip install fluent-logger
-
diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh
index 21f6d2a2..120b0ddc 100755
--- a/scripts/decrypt-secrets.sh
+++ b/scripts/decrypt-secrets.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-# Copyright 2015 Google Inc. All rights reserved.
+# 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.
diff --git a/scripts/fixup_errorreporting_v1beta1_keywords.py b/scripts/fixup_errorreporting_v1beta1_keywords.py
index db88785c..60f94ffc 100644
--- a/scripts/fixup_errorreporting_v1beta1_keywords.py
+++ b/scripts/fixup_errorreporting_v1beta1_keywords.py
@@ -1,6 +1,6 @@
#! /usr/bin/env python3
# -*- 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.
diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py
index 91b59676..8f5e248a 100644
--- a/scripts/readme-gen/readme_gen.py
+++ b/scripts/readme-gen/readme_gen.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright 2016 Google Inc
+# 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.
@@ -33,17 +33,17 @@
autoescape=True,
)
-README_TMPL = jinja_env.get_template('README.tmpl.rst')
+README_TMPL = jinja_env.get_template("README.tmpl.rst")
def get_help(file):
- return subprocess.check_output(['python', file, '--help']).decode()
+ return subprocess.check_output(["python", file, "--help"]).decode()
def main():
parser = argparse.ArgumentParser()
- parser.add_argument('source')
- parser.add_argument('--destination', default='README.rst')
+ parser.add_argument("source")
+ parser.add_argument("--destination", default="README.rst")
args = parser.parse_args()
@@ -51,9 +51,9 @@ def main():
root = os.path.dirname(source)
destination = os.path.join(root, args.destination)
- jinja_env.globals['get_help'] = get_help
+ jinja_env.globals["get_help"] = get_help
- with io.open(source, 'r') as f:
+ with io.open(source, "r") as f:
config = yaml.load(f)
# This allows get_help to execute in the right directory.
@@ -61,9 +61,9 @@ def main():
output = README_TMPL.render(config)
- with io.open(destination, 'w') as f:
+ with io.open(destination, "w") as f:
f.write(output)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/setup.py b/setup.py
index 6035154f..530e3c7f 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,5 @@
-# Copyright 2018 Google LLC
+# -*- 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.
@@ -11,36 +12,43 @@
# 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
+import setuptools # type: ignore
-
-# Package metadata.
+package_root = os.path.abspath(os.path.dirname(__file__))
name = "google-cloud-error-reporting"
-description = "Error Reporting API client library"
-version = "1.5.3"
-# Should be one of:
-# 'Development Status :: 3 - Alpha'
-# 'Development Status :: 4 - Beta'
-# 'Development Status :: 5 - Production/Stable'
-release_status = "Development Status :: 4 - Beta"
-dependencies = [
- "google-cloud-logging>=1.14.0, <4.0.0dev",
- # NOTE: Maintainers, please do not require google-api-core>=2.x.x
- # Until this issue is closed
- # https://github.com/googleapis/google-cloud-python/issues/10566
- "google-api-core[grpc] >= 1.31.5, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0",
- "proto-plus >= 1.15.0, <2.0.0dev",
- "protobuf >= 3.19.0, <4.0.0dev",
-]
-extras = {}
-# Setup boilerplate below this line.
+description = "Google Cloud Error Reporting API client library"
+
+version = {}
+with open(
+ os.path.join(package_root, "google/cloud/error_reporting/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 = [
+ "google-cloud-logging>=1.14.0, <4.0.0",
+ # Exclude incompatible versions of `google-auth`
+ # See https://github.com/googleapis/google-cloud-python/issues/12364
+ "google-auth >= 2.14.1, <3.0.0,!=2.24.0,!=2.25.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.0dev; 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",
+]
+url = "https://github.com/googleapis/python-error-reporting"
package_root = os.path.abspath(os.path.dirname(__file__))
@@ -48,20 +56,12 @@
with io.open(readme_filename, encoding="utf-8") as readme_file:
readme = readme_file.read()
-# Only include packages under the 'google' namespace. Do not include tests,
-# benchmarks, etc.
packages = [
package
- for package in setuptools.PEP420PackageFinder.find()
+ for package in setuptools.find_namespace_packages()
if package.startswith("google")
]
-# Determine which namespaces are needed.
-namespaces = ["google"]
-if "google.cloud" in packages:
- namespaces.append("google.cloud")
-
-
setuptools.setup(
name=name,
version=version,
@@ -70,7 +70,7 @@
author="Google LLC",
author_email="googleapis-packages@google.com",
license="Apache 2.0",
- url="https://github.com/googleapis/python-error-reporting",
+ url=url,
classifiers=[
release_status,
"Intended Audience :: Developers",
@@ -81,16 +81,16 @@
"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",
"Operating System :: OS Independent",
"Topic :: Internet",
],
platforms="Posix; MacOS X; Windows",
packages=packages,
- namespace_packages=namespaces,
- install_requires=dependencies,
- extras_require=extras,
python_requires=">=3.7",
- scripts=["scripts/fixup_errorreporting_v1beta1_keywords.py"],
+ install_requires=dependencies,
include_package_data=True,
zip_safe=False,
)
diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt
index e69de29b..ed7f9aed 100644
--- a/testing/constraints-3.10.txt
+++ b/testing/constraints-3.10.txt
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# This constraints file is required for unit tests.
+# List all library dependencies and extras in this file.
+google-api-core
+proto-plus
+protobuf
diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt
index e69de29b..ed7f9aed 100644
--- a/testing/constraints-3.11.txt
+++ b/testing/constraints-3.11.txt
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# This constraints file is required for unit tests.
+# List all library dependencies and extras in this file.
+google-api-core
+proto-plus
+protobuf
diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt
new file mode 100644
index 00000000..ed7f9aed
--- /dev/null
+++ b/testing/constraints-3.12.txt
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# This constraints file is required for unit tests.
+# List all library dependencies and extras in this file.
+google-api-core
+proto-plus
+protobuf
diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt
new file mode 100644
index 00000000..c20a7781
--- /dev/null
+++ b/testing/constraints-3.13.txt
@@ -0,0 +1,11 @@
+# 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
+proto-plus>=1
+protobuf>=6
diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt
deleted file mode 100644
index cf48a7f8..00000000
--- a/testing/constraints-3.6.txt
+++ /dev/null
@@ -1,11 +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 "foo >= 1.14.0, < 2.0.0dev",
-# Then this file should have foo==1.14.0
-google-cloud-logging==1.14.0
-google-api-core==1.31.5
-proto-plus==1.15.0
-protobuf==3.19.0
diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt
index cf48a7f8..76b09648 100644
--- a/testing/constraints-3.7.txt
+++ b/testing/constraints-3.7.txt
@@ -1,11 +1,11 @@
# This constraints file is used to check that lower bounds
# are correct in setup.py
-# List *all* library dependencies and extras in this file.
+# List all library dependencies and extras in this file.
# Pin the version to the lower bound.
-#
-# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev",
-# Then this file should have foo==1.14.0
+# 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
google-cloud-logging==1.14.0
-google-api-core==1.31.5
-proto-plus==1.15.0
-protobuf==3.19.0
diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt
index e69de29b..3f307898 100644
--- a/testing/constraints-3.8.txt
+++ b/testing/constraints-3.8.txt
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# This constraints file is required for unit tests.
+# List all library dependencies and extras in this file.
+google-api-core==2.14.0
+proto-plus
+protobuf
diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt
index e69de29b..ed7f9aed 100644
--- a/testing/constraints-3.9.txt
+++ b/testing/constraints-3.9.txt
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# This constraints file is required for unit tests.
+# List all library dependencies and extras in this file.
+google-api-core
+proto-plus
+protobuf
diff --git a/tests/__init__.py b/tests/__init__.py
index e8e1c384..cbf94b28 100644
--- a/tests/__init__.py
+++ b/tests/__init__.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.
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
index e8e1c384..cbf94b28 100644
--- a/tests/unit/__init__.py
+++ b/tests/unit/__init__.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.
diff --git a/tests/unit/gapic/__init__.py b/tests/unit/gapic/__init__.py
index e8e1c384..cbf94b28 100644
--- a/tests/unit/gapic/__init__.py
+++ b/tests/unit/gapic/__init__.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.
diff --git a/tests/unit/gapic/errorreporting_v1beta1/__init__.py b/tests/unit/gapic/errorreporting_v1beta1/__init__.py
index e8e1c384..cbf94b28 100644
--- a/tests/unit/gapic/errorreporting_v1beta1/__init__.py
+++ b/tests/unit/gapic/errorreporting_v1beta1/__init__.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.
diff --git a/tests/unit/gapic/errorreporting_v1beta1/test_error_group_service.py b/tests/unit/gapic/errorreporting_v1beta1/test_error_group_service.py
index 96f3472f..67adff43 100644
--- a/tests/unit/gapic/errorreporting_v1beta1/test_error_group_service.py
+++ b/tests/unit/gapic/errorreporting_v1beta1/test_error_group_service.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.
@@ -18,16 +18,31 @@
# try/except added for compatibility with python < 3.8
try:
from unittest import mock
- from unittest.mock import AsyncMock
-except ImportError:
+ from unittest.mock import AsyncMock # pragma: NO COVER
+except ImportError: # pragma: NO COVER
import mock
import grpc
from grpc.experimental import aio
+from collections.abc import Iterable, AsyncIterable
+from google.protobuf import json_format
+import json
import math
import pytest
+from google.api_core import api_core_version
from proto.marshal.rules.dates import DurationRule, TimestampRule
+from proto.marshal.rules import wrappers
+from requests import Response
+from requests import Request, PreparedRequest
+from requests.sessions import Session
+from google.protobuf import json_format
+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
@@ -35,6 +50,7 @@
from google.api_core import grpc_helpers
from google.api_core import grpc_helpers_async
from google.api_core import path_template
+from google.api_core import retry as retries
from google.auth import credentials as ga_credentials
from google.auth.exceptions import MutualTLSChannelError
from google.cloud.errorreporting_v1beta1.services.error_group_service import (
@@ -50,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.
@@ -65,6 +103,17 @@ def modify_default_endpoint(client):
)
+# If default endpoint template is localhost, then default mtls endpoint will be the same.
+# This method modifies the default endpoint template so the client can produce a different
+# mtls endpoint for endpoint testing purposes.
+def modify_default_endpoint_template(client):
+ return (
+ "test.{UNIVERSE_DOMAIN}"
+ if ("localhost" in client._DEFAULT_ENDPOINT_TEMPLATE)
+ else client._DEFAULT_ENDPOINT_TEMPLATE
+ )
+
+
def test__get_default_mtls_endpoint():
api_endpoint = "example.googleapis.com"
api_mtls_endpoint = "example.mtls.googleapis.com"
@@ -95,11 +144,251 @@ def test__get_default_mtls_endpoint():
)
+def test__read_environment_variables():
+ assert ErrorGroupServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ assert ErrorGroupServiceClient._read_environment_variables() == (
+ True,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
+ assert ErrorGroupServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
+ ):
+ with pytest.raises(ValueError) as excinfo:
+ ErrorGroupServiceClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ assert ErrorGroupServiceClient._read_environment_variables() == (
+ False,
+ "never",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
+ assert ErrorGroupServiceClient._read_environment_variables() == (
+ False,
+ "always",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}):
+ assert ErrorGroupServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ ErrorGroupServiceClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_CLOUD_UNIVERSE_DOMAIN": "foo.com"}):
+ assert ErrorGroupServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ "foo.com",
+ )
+
+
+def test__get_client_cert_source():
+ mock_provided_cert_source = mock.Mock()
+ mock_default_cert_source = mock.Mock()
+
+ assert ErrorGroupServiceClient._get_client_cert_source(None, False) is None
+ assert (
+ ErrorGroupServiceClient._get_client_cert_source(
+ mock_provided_cert_source, False
+ )
+ is None
+ )
+ assert (
+ ErrorGroupServiceClient._get_client_cert_source(mock_provided_cert_source, True)
+ == mock_provided_cert_source
+ )
+
+ with mock.patch(
+ "google.auth.transport.mtls.has_default_client_cert_source", return_value=True
+ ):
+ with mock.patch(
+ "google.auth.transport.mtls.default_client_cert_source",
+ return_value=mock_default_cert_source,
+ ):
+ assert (
+ ErrorGroupServiceClient._get_client_cert_source(None, True)
+ is mock_default_cert_source
+ )
+ assert (
+ ErrorGroupServiceClient._get_client_cert_source(
+ mock_provided_cert_source, "true"
+ )
+ is mock_provided_cert_source
+ )
+
+
+@mock.patch.object(
+ ErrorGroupServiceClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceClient),
+)
+@mock.patch.object(
+ ErrorGroupServiceAsyncClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceAsyncClient),
+)
+def test__get_api_endpoint():
+ api_override = "foo.com"
+ mock_client_cert_source = mock.Mock()
+ default_universe = ErrorGroupServiceClient._DEFAULT_UNIVERSE
+ default_endpoint = ErrorGroupServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=default_universe
+ )
+ mock_universe = "bar.com"
+ mock_endpoint = ErrorGroupServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=mock_universe
+ )
+
+ assert (
+ ErrorGroupServiceClient._get_api_endpoint(
+ api_override, mock_client_cert_source, default_universe, "always"
+ )
+ == api_override
+ )
+ assert (
+ ErrorGroupServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, default_universe, "auto"
+ )
+ == ErrorGroupServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ErrorGroupServiceClient._get_api_endpoint(None, None, default_universe, "auto")
+ == default_endpoint
+ )
+ assert (
+ ErrorGroupServiceClient._get_api_endpoint(
+ None, None, default_universe, "always"
+ )
+ == ErrorGroupServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ErrorGroupServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, default_universe, "always"
+ )
+ == ErrorGroupServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ErrorGroupServiceClient._get_api_endpoint(None, None, mock_universe, "never")
+ == mock_endpoint
+ )
+ assert (
+ ErrorGroupServiceClient._get_api_endpoint(None, None, default_universe, "never")
+ == default_endpoint
+ )
+
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ ErrorGroupServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, mock_universe, "auto"
+ )
+ assert (
+ str(excinfo.value)
+ == "mTLS is not supported in any universe other than googleapis.com."
+ )
+
+
+def test__get_universe_domain():
+ client_universe_domain = "foo.com"
+ universe_domain_env = "bar.com"
+
+ assert (
+ ErrorGroupServiceClient._get_universe_domain(
+ client_universe_domain, universe_domain_env
+ )
+ == client_universe_domain
+ )
+ assert (
+ ErrorGroupServiceClient._get_universe_domain(None, universe_domain_env)
+ == universe_domain_env
+ )
+ assert (
+ ErrorGroupServiceClient._get_universe_domain(None, None)
+ == ErrorGroupServiceClient._DEFAULT_UNIVERSE
+ )
+
+ with pytest.raises(ValueError) as excinfo:
+ ErrorGroupServiceClient._get_universe_domain("", None)
+ assert str(excinfo.value) == "Universe Domain cannot be an empty string."
+
+
+@pytest.mark.parametrize(
+ "error_code,cred_info_json,show_cred_info",
+ [
+ (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__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 = ErrorGroupServiceClient(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"]
+
+
+@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 = ErrorGroupServiceClient(credentials=cred)
+ client._transport._credentials = cred
+
+ error = core_exceptions.GoogleAPICallError("message", details=[])
+ error.code = error_code
+
+ client._add_cred_info_for_auth_errors(error)
+ assert error.details == []
+
+
@pytest.mark.parametrize(
"client_class,transport_name",
[
(ErrorGroupServiceClient, "grpc"),
(ErrorGroupServiceAsyncClient, "grpc_asyncio"),
+ (ErrorGroupServiceClient, "rest"),
],
)
def test_error_group_service_client_from_service_account_info(
@@ -115,7 +404,11 @@ def test_error_group_service_client_from_service_account_info(
assert client.transport._credentials == creds
assert isinstance(client, client_class)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
@pytest.mark.parametrize(
@@ -123,6 +416,7 @@ def test_error_group_service_client_from_service_account_info(
[
(transports.ErrorGroupServiceGrpcTransport, "grpc"),
(transports.ErrorGroupServiceGrpcAsyncIOTransport, "grpc_asyncio"),
+ (transports.ErrorGroupServiceRestTransport, "rest"),
],
)
def test_error_group_service_client_service_account_always_use_jwt(
@@ -148,6 +442,7 @@ def test_error_group_service_client_service_account_always_use_jwt(
[
(ErrorGroupServiceClient, "grpc"),
(ErrorGroupServiceAsyncClient, "grpc_asyncio"),
+ (ErrorGroupServiceClient, "rest"),
],
)
def test_error_group_service_client_from_service_account_file(
@@ -170,13 +465,18 @@ def test_error_group_service_client_from_service_account_file(
assert client.transport._credentials == creds
assert isinstance(client, client_class)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
def test_error_group_service_client_get_transport_class():
transport = ErrorGroupServiceClient.get_transport_class()
available_transports = [
transports.ErrorGroupServiceGrpcTransport,
+ transports.ErrorGroupServiceRestTransport,
]
assert transport in available_transports
@@ -193,17 +493,18 @@ def test_error_group_service_client_get_transport_class():
transports.ErrorGroupServiceGrpcAsyncIOTransport,
"grpc_asyncio",
),
+ (ErrorGroupServiceClient, transports.ErrorGroupServiceRestTransport, "rest"),
],
)
@mock.patch.object(
ErrorGroupServiceClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorGroupServiceClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceClient),
)
@mock.patch.object(
ErrorGroupServiceAsyncClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorGroupServiceAsyncClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceAsyncClient),
)
def test_error_group_service_client_client_options(
client_class, transport_class, transport_name
@@ -233,6 +534,7 @@ def test_error_group_service_client_client_options(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
@@ -244,12 +546,15 @@ def test_error_group_service_client_client_options(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
@@ -267,20 +572,29 @@ def test_error_group_service_client_client_options(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has
# unsupported value.
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
- with pytest.raises(MutualTLSChannelError):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
client = client_class(transport=transport_name)
+ assert (
+ str(excinfo.value)
+ == "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):
+ 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")
@@ -290,12 +604,35 @@ def test_error_group_service_client_client_options(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id="octopus",
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
+ )
+ # Check the case api_endpoint is provided
+ options = client_options.ClientOptions(
+ api_audience="https://language.googleapis.com"
+ )
+ with mock.patch.object(transport_class, "__init__") as patched:
+ patched.return_value = None
+ client = client_class(client_options=options, transport=transport_name)
+ patched.assert_called_once_with(
+ credentials=None,
+ credentials_file=None,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
+ scopes=None,
+ client_cert_source_for_mtls=None,
+ quota_project_id=None,
+ client_info=transports.base.DEFAULT_CLIENT_INFO,
+ always_use_jwt_access=True,
+ api_audience="https://language.googleapis.com",
)
@@ -326,17 +663,29 @@ def test_error_group_service_client_client_options(
"grpc_asyncio",
"false",
),
+ (
+ ErrorGroupServiceClient,
+ transports.ErrorGroupServiceRestTransport,
+ "rest",
+ "true",
+ ),
+ (
+ ErrorGroupServiceClient,
+ transports.ErrorGroupServiceRestTransport,
+ "rest",
+ "false",
+ ),
],
)
@mock.patch.object(
ErrorGroupServiceClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorGroupServiceClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceClient),
)
@mock.patch.object(
ErrorGroupServiceAsyncClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorGroupServiceAsyncClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceAsyncClient),
)
@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"})
def test_error_group_service_client_mtls_env_auto(
@@ -359,7 +708,9 @@ def test_error_group_service_client_mtls_env_auto(
if use_client_cert_env == "false":
expected_client_cert_source = None
- expected_host = client.DEFAULT_ENDPOINT
+ expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ )
else:
expected_client_cert_source = client_cert_source_callback
expected_host = client.DEFAULT_MTLS_ENDPOINT
@@ -373,6 +724,7 @@ def test_error_group_service_client_mtls_env_auto(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case ADC client cert is provided. Whether client cert is used depends on
@@ -390,7 +742,9 @@ def test_error_group_service_client_mtls_env_auto(
return_value=client_cert_source_callback,
):
if use_client_cert_env == "false":
- expected_host = client.DEFAULT_ENDPOINT
+ expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ )
expected_client_cert_source = None
else:
expected_host = client.DEFAULT_MTLS_ENDPOINT
@@ -407,6 +761,7 @@ def test_error_group_service_client_mtls_env_auto(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case client_cert_source and ADC client cert are not provided.
@@ -423,12 +778,15 @@ def test_error_group_service_client_mtls_env_auto(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -512,6 +870,115 @@ def test_error_group_service_client_get_mtls_endpoint_and_cert_source(client_cla
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
assert cert_source == mock_client_cert_source
+ # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has
+ # unsupported value.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ client_class.get_mtls_endpoint_and_cert_source()
+
+ assert (
+ str(excinfo.value)
+ == "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", [ErrorGroupServiceClient, ErrorGroupServiceAsyncClient]
+)
+@mock.patch.object(
+ ErrorGroupServiceClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceClient),
+)
+@mock.patch.object(
+ ErrorGroupServiceAsyncClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorGroupServiceAsyncClient),
+)
+def test_error_group_service_client_client_api_endpoint(client_class):
+ mock_client_cert_source = client_cert_source_callback
+ api_override = "foo.com"
+ default_universe = ErrorGroupServiceClient._DEFAULT_UNIVERSE
+ default_endpoint = ErrorGroupServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=default_universe
+ )
+ mock_universe = "bar.com"
+ mock_endpoint = ErrorGroupServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=mock_universe
+ )
+
+ # If ClientOptions.api_endpoint is set and GOOGLE_API_USE_CLIENT_CERTIFICATE="true",
+ # use ClientOptions.api_endpoint as the api endpoint regardless.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ with mock.patch(
+ "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel"
+ ):
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source, api_endpoint=api_override
+ )
+ client = client_class(
+ client_options=options,
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ assert client.api_endpoint == api_override
+
+ # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="never",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ client = client_class(credentials=ga_credentials.AnonymousCredentials())
+ assert client.api_endpoint == default_endpoint
+
+ # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="always",
+ # use the DEFAULT_MTLS_ENDPOINT as the api endpoint.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
+ client = client_class(credentials=ga_credentials.AnonymousCredentials())
+ assert client.api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
+
+ # If ClientOptions.api_endpoint is not set, GOOGLE_API_USE_MTLS_ENDPOINT="auto" (default),
+ # GOOGLE_API_USE_CLIENT_CERTIFICATE="false" (default), default cert source doesn't exist,
+ # and ClientOptions.universe_domain="bar.com",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with universe domain as the api endpoint.
+ options = client_options.ClientOptions()
+ universe_exists = hasattr(options, "universe_domain")
+ if universe_exists:
+ options = client_options.ClientOptions(universe_domain=mock_universe)
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ else:
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert client.api_endpoint == (
+ mock_endpoint if universe_exists else default_endpoint
+ )
+ assert client.universe_domain == (
+ mock_universe if universe_exists else default_universe
+ )
+
+ # If ClientOptions does not have a universe domain attribute and GOOGLE_API_USE_MTLS_ENDPOINT="never",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint.
+ options = client_options.ClientOptions()
+ if hasattr(options, "universe_domain"):
+ delattr(options, "universe_domain")
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert client.api_endpoint == default_endpoint
+
@pytest.mark.parametrize(
"client_class,transport_class,transport_name",
@@ -522,6 +989,7 @@ def test_error_group_service_client_get_mtls_endpoint_and_cert_source(client_cla
transports.ErrorGroupServiceGrpcAsyncIOTransport,
"grpc_asyncio",
),
+ (ErrorGroupServiceClient, transports.ErrorGroupServiceRestTransport, "rest"),
],
)
def test_error_group_service_client_client_options_scopes(
@@ -537,12 +1005,15 @@ def test_error_group_service_client_client_options_scopes(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=["1", "2"],
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -561,6 +1032,12 @@ def test_error_group_service_client_client_options_scopes(
"grpc_asyncio",
grpc_helpers_async,
),
+ (
+ ErrorGroupServiceClient,
+ transports.ErrorGroupServiceRestTransport,
+ "rest",
+ None,
+ ),
],
)
def test_error_group_service_client_client_options_credentials_file(
@@ -575,12 +1052,15 @@ def test_error_group_service_client_client_options_credentials_file(
patched.assert_called_once_with(
credentials=None,
credentials_file="credentials.json",
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -601,6 +1081,7 @@ def test_error_group_service_client_client_options_from_dict():
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -633,12 +1114,15 @@ def test_error_group_service_client_create_channel_credentials_file(
patched.assert_called_once_with(
credentials=None,
credentials_file="credentials.json",
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# test that the credentials from file are saved and used as the credentials.
@@ -700,7 +1184,8 @@ def test_get_group(request_type, transport: str = "grpc"):
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls) == 1
_, args, _ = call.mock_calls[0]
- assert args[0] == error_group_service.GetGroupRequest()
+ request = error_group_service.GetGroupRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, common.ErrorGroup)
@@ -709,20 +1194,107 @@ def test_get_group(request_type, transport: str = "grpc"):
assert response.resolution_status == common.ResolutionStatus.OPEN
-def test_get_group_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_get_group_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 = ErrorGroupServiceClient(
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 = error_group_service.GetGroupRequest(
+ group_name="group_name_value",
+ )
+
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.get_group), "__call__") as call:
- client.get_group()
+ call.return_value.name = (
+ "foo" # operation_request.operation in compute client(s) expect a string.
+ )
+ client.get_group(request=request)
call.assert_called()
_, args, _ = call.mock_calls[0]
- assert args[0] == error_group_service.GetGroupRequest()
+ assert args[0] == error_group_service.GetGroupRequest(
+ group_name="group_name_value",
+ )
+
+
+def test_get_group_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 = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # 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.get_group 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.get_group] = mock_rpc
+ request = {}
+ client.get_group(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.get_group(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_get_group_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 = ErrorGroupServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport=transport,
+ )
+
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
+
+ # Ensure method has been cached
+ assert (
+ client._client._transport.get_group
+ in client._client._transport._wrapped_methods
+ )
+
+ # Replace cached wrapped function with mock
+ mock_rpc = mock.AsyncMock()
+ mock_rpc.return_value = mock.Mock()
+ client._client._transport._wrapped_methods[
+ client._client._transport.get_group
+ ] = mock_rpc
+
+ request = {}
+ await client.get_group(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ await client.get_group(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
@pytest.mark.asyncio
@@ -730,7 +1302,7 @@ async def test_get_group_async(
transport: str = "grpc_asyncio", request_type=error_group_service.GetGroupRequest
):
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -753,7 +1325,8 @@ async def test_get_group_async(
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls)
_, args, _ = call.mock_calls[0]
- assert args[0] == error_group_service.GetGroupRequest()
+ request = error_group_service.GetGroupRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, common.ErrorGroup)
@@ -799,7 +1372,7 @@ def test_get_group_field_headers():
@pytest.mark.asyncio
async def test_get_group_field_headers_async():
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -867,7 +1440,7 @@ def test_get_group_flattened_error():
@pytest.mark.asyncio
async def test_get_group_flattened_async():
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -894,7 +1467,7 @@ async def test_get_group_flattened_async():
@pytest.mark.asyncio
async def test_get_group_flattened_error_async():
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -936,7 +1509,8 @@ def test_update_group(request_type, transport: str = "grpc"):
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls) == 1
_, args, _ = call.mock_calls[0]
- assert args[0] == error_group_service.UpdateGroupRequest()
+ request = error_group_service.UpdateGroupRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, common.ErrorGroup)
@@ -945,28 +1519,113 @@ def test_update_group(request_type, transport: str = "grpc"):
assert response.resolution_status == common.ResolutionStatus.OPEN
-def test_update_group_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_update_group_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 = ErrorGroupServiceClient(
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 = error_group_service.UpdateGroupRequest()
+
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.update_group), "__call__") as call:
- client.update_group()
+ call.return_value.name = (
+ "foo" # operation_request.operation in compute client(s) expect a string.
+ )
+ client.update_group(request=request)
call.assert_called()
_, args, _ = call.mock_calls[0]
assert args[0] == error_group_service.UpdateGroupRequest()
-@pytest.mark.asyncio
-async def test_update_group_async(
- transport: str = "grpc_asyncio", request_type=error_group_service.UpdateGroupRequest
-):
- client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+def test_update_group_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 = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # 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_group 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_group] = mock_rpc
+ request = {}
+ client.update_group(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.update_group(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_update_group_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 = ErrorGroupServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport=transport,
+ )
+
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
+
+ # Ensure method has been cached
+ assert (
+ client._client._transport.update_group
+ in client._client._transport._wrapped_methods
+ )
+
+ # Replace cached wrapped function with mock
+ mock_rpc = mock.AsyncMock()
+ mock_rpc.return_value = mock.Mock()
+ client._client._transport._wrapped_methods[
+ client._client._transport.update_group
+ ] = mock_rpc
+
+ request = {}
+ await client.update_group(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ await client.update_group(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_update_group_async(
+ transport: str = "grpc_asyncio", request_type=error_group_service.UpdateGroupRequest
+):
+ client = ErrorGroupServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -989,7 +1648,8 @@ async def test_update_group_async(
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls)
_, args, _ = call.mock_calls[0]
- assert args[0] == error_group_service.UpdateGroupRequest()
+ request = error_group_service.UpdateGroupRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, common.ErrorGroup)
@@ -1035,7 +1695,7 @@ def test_update_group_field_headers():
@pytest.mark.asyncio
async def test_update_group_field_headers_async():
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1103,7 +1763,7 @@ def test_update_group_flattened_error():
@pytest.mark.asyncio
async def test_update_group_flattened_async():
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1130,7 +1790,7 @@ async def test_update_group_flattened_async():
@pytest.mark.asyncio
async def test_update_group_flattened_error_async():
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1142,6 +1802,356 @@ async def test_update_group_flattened_error_async():
)
+def test_get_group_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 = ErrorGroupServiceClient(
+ 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.get_group 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.get_group] = mock_rpc
+
+ request = {}
+ client.get_group(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.get_group(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+def test_get_group_rest_required_fields(
+ request_type=error_group_service.GetGroupRequest,
+):
+ transport_class = transports.ErrorGroupServiceRestTransport
+
+ request_init = {}
+ request_init["group_name"] = ""
+ request = request_type(**request_init)
+ pb_request = request_type.pb(request)
+ jsonified_request = json.loads(
+ json_format.MessageToJson(pb_request, use_integers_for_enums=False)
+ )
+
+ # verify fields with default values are dropped
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).get_group._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with default values are now present
+
+ jsonified_request["groupName"] = "group_name_value"
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).get_group._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with non-default values are left alone
+ assert "groupName" in jsonified_request
+ assert jsonified_request["groupName"] == "group_name_value"
+
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type(**request_init)
+
+ # Designate an appropriate value for the returned response.
+ return_value = common.ErrorGroup()
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # We need to mock transcode() because providing default values
+ # for required fields will fail the real version if the http_options
+ # expect actual values for those fields.
+ with mock.patch.object(path_template, "transcode") as transcode:
+ # A uri without fields and an empty body will force all the
+ # request fields to show up in the query_params.
+ pb_request = request_type.pb(request)
+ transcode_result = {
+ "uri": "v1/sample_method",
+ "method": "get",
+ "query_params": pb_request,
+ }
+ transcode.return_value = transcode_result
+
+ response_value = Response()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = common.ErrorGroup.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_group(request)
+
+ expected_params = [("$alt", "json;enum-encoding=int")]
+ actual_params = req.call_args.kwargs["params"]
+ assert expected_params == actual_params
+
+
+def test_get_group_rest_unset_required_fields():
+ transport = transports.ErrorGroupServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials
+ )
+
+ unset_fields = transport.get_group._get_unset_required_fields({})
+ assert set(unset_fields) == (set(()) & set(("groupName",)))
+
+
+def test_get_group_rest_flattened():
+ client = ErrorGroupServiceClient(
+ 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 = common.ErrorGroup()
+
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"group_name": "projects/sample1/groups/sample2"}
+
+ # get truthy value for each flattened field
+ mock_args = dict(
+ group_name="group_name_value",
+ )
+ mock_args.update(sample_request)
+
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ response_value.status_code = 200
+ # Convert return value to protobuf type
+ return_value = common.ErrorGroup.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"}
+
+ client.get_group(**mock_args)
+
+ # Establish that the underlying call was made with the expected
+ # request object values.
+ assert len(req.mock_calls) == 1
+ _, args, _ = req.mock_calls[0]
+ assert path_template.validate(
+ "%s/v1beta1/{group_name=projects/*/groups/*}" % client.transport._host,
+ args[1],
+ )
+
+
+def test_get_group_rest_flattened_error(transport: str = "rest"):
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Attempting to call a method with both a request object and flattened
+ # fields is an error.
+ with pytest.raises(ValueError):
+ client.get_group(
+ error_group_service.GetGroupRequest(),
+ group_name="group_name_value",
+ )
+
+
+def test_update_group_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 = ErrorGroupServiceClient(
+ 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_group 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_group] = mock_rpc
+
+ request = {}
+ client.update_group(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.update_group(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+def test_update_group_rest_required_fields(
+ request_type=error_group_service.UpdateGroupRequest,
+):
+ transport_class = transports.ErrorGroupServiceRestTransport
+
+ request_init = {}
+ request = request_type(**request_init)
+ pb_request = request_type.pb(request)
+ jsonified_request = json.loads(
+ json_format.MessageToJson(pb_request, use_integers_for_enums=False)
+ )
+
+ # verify fields with default values are dropped
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).update_group._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with default values are now present
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).update_group._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with non-default values are left alone
+
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type(**request_init)
+
+ # Designate an appropriate value for the returned response.
+ return_value = common.ErrorGroup()
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # We need to mock transcode() because providing default values
+ # for required fields will fail the real version if the http_options
+ # expect actual values for those fields.
+ with mock.patch.object(path_template, "transcode") as transcode:
+ # A uri without fields and an empty body will force all the
+ # request fields to show up in the query_params.
+ pb_request = request_type.pb(request)
+ transcode_result = {
+ "uri": "v1/sample_method",
+ "method": "put",
+ "query_params": pb_request,
+ }
+ transcode_result["body"] = pb_request
+ transcode.return_value = transcode_result
+
+ response_value = Response()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = common.ErrorGroup.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_group(request)
+
+ expected_params = [("$alt", "json;enum-encoding=int")]
+ actual_params = req.call_args.kwargs["params"]
+ assert expected_params == actual_params
+
+
+def test_update_group_rest_unset_required_fields():
+ transport = transports.ErrorGroupServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials
+ )
+
+ unset_fields = transport.update_group._get_unset_required_fields({})
+ assert set(unset_fields) == (set(()) & set(("group",)))
+
+
+def test_update_group_rest_flattened():
+ client = ErrorGroupServiceClient(
+ 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 = common.ErrorGroup()
+
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"group": {"name": "projects/sample1/groups/sample2"}}
+
+ # get truthy value for each flattened field
+ mock_args = dict(
+ group=common.ErrorGroup(name="name_value"),
+ )
+ mock_args.update(sample_request)
+
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ response_value.status_code = 200
+ # Convert return value to protobuf type
+ return_value = common.ErrorGroup.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"}
+
+ client.update_group(**mock_args)
+
+ # Establish that the underlying call was made with the expected
+ # request object values.
+ assert len(req.mock_calls) == 1
+ _, args, _ = req.mock_calls[0]
+ assert path_template.validate(
+ "%s/v1beta1/{group.name=projects/*/groups/*}" % client.transport._host,
+ args[1],
+ )
+
+
+def test_update_group_rest_flattened_error(transport: str = "rest"):
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Attempting to call a method with both a request object and flattened
+ # fields is an error.
+ with pytest.raises(ValueError):
+ client.update_group(
+ error_group_service.UpdateGroupRequest(),
+ group=common.ErrorGroup(name="name_value"),
+ )
+
+
def test_credentials_transport_error():
# It is an error to provide credentials and a transport instance.
transport = transports.ErrorGroupServiceGrpcTransport(
@@ -1176,7 +2186,7 @@ def test_credentials_transport_error():
)
# It is an error to provide an api_key and a credential.
- options = mock.Mock()
+ options = client_options.ClientOptions()
options.api_key = "api_key"
with pytest.raises(ValueError):
client = ErrorGroupServiceClient(
@@ -1223,6 +2233,7 @@ def test_transport_get_channel():
[
transports.ErrorGroupServiceGrpcTransport,
transports.ErrorGroupServiceGrpcAsyncIOTransport,
+ transports.ErrorGroupServiceRestTransport,
],
)
def test_transport_adc(transport_class):
@@ -1233,17 +2244,519 @@ def test_transport_adc(transport_class):
adc.assert_called_once()
+def test_transport_kind_grpc():
+ transport = ErrorGroupServiceClient.get_transport_class("grpc")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "grpc"
+
+
+def test_initialize_client_w_grpc():
+ client = ErrorGroupServiceClient(
+ 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_get_group_empty_call_grpc():
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_group), "__call__") as call:
+ call.return_value = common.ErrorGroup()
+ client.get_group(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_group_service.GetGroupRequest()
+
+ 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_group_empty_call_grpc():
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_group), "__call__") as call:
+ call.return_value = common.ErrorGroup()
+ client.update_group(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_group_service.UpdateGroupRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_grpc_asyncio():
+ transport = ErrorGroupServiceAsyncClient.get_transport_class("grpc_asyncio")(
+ credentials=async_anonymous_credentials()
+ )
+ assert transport.kind == "grpc_asyncio"
+
+
+def test_initialize_client_w_grpc_asyncio():
+ client = ErrorGroupServiceAsyncClient(
+ 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_get_group_empty_call_grpc_asyncio():
+ client = ErrorGroupServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_group), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ common.ErrorGroup(
+ name="name_value",
+ group_id="group_id_value",
+ resolution_status=common.ResolutionStatus.OPEN,
+ )
+ )
+ await client.get_group(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_group_service.GetGroupRequest()
+
+ 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_group_empty_call_grpc_asyncio():
+ client = ErrorGroupServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_group), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ common.ErrorGroup(
+ name="name_value",
+ group_id="group_id_value",
+ resolution_status=common.ResolutionStatus.OPEN,
+ )
+ )
+ await client.update_group(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_group_service.UpdateGroupRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_rest():
+ transport = ErrorGroupServiceClient.get_transport_class("rest")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "rest"
+
+
+def test_get_group_rest_bad_request(request_type=error_group_service.GetGroupRequest):
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"group_name": "projects/sample1/groups/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_group(request)
+
+
@pytest.mark.parametrize(
- "transport_name",
+ "request_type",
[
- "grpc",
+ error_group_service.GetGroupRequest,
+ dict,
],
)
-def test_transport_kind(transport_name):
- transport = ErrorGroupServiceClient.get_transport_class(transport_name)(
+def test_get_group_rest_call_success(request_type):
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"group_name": "projects/sample1/groups/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 = common.ErrorGroup(
+ name="name_value",
+ group_id="group_id_value",
+ resolution_status=common.ResolutionStatus.OPEN,
+ )
+
+ # 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 = common.ErrorGroup.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_group(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, common.ErrorGroup)
+ assert response.name == "name_value"
+ assert response.group_id == "group_id_value"
+ assert response.resolution_status == common.ResolutionStatus.OPEN
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_get_group_rest_interceptors(null_interceptor):
+ transport = transports.ErrorGroupServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.ErrorGroupServiceRestInterceptor(),
+ )
+ client = ErrorGroupServiceClient(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.ErrorGroupServiceRestInterceptor, "post_get_group"
+ ) as post, mock.patch.object(
+ transports.ErrorGroupServiceRestInterceptor, "post_get_group_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.ErrorGroupServiceRestInterceptor, "pre_get_group"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = error_group_service.GetGroupRequest.pb(
+ error_group_service.GetGroupRequest()
+ )
+ 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 = common.ErrorGroup.to_json(common.ErrorGroup())
+ req.return_value.content = return_value
+
+ request = error_group_service.GetGroupRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = common.ErrorGroup()
+ post_with_metadata.return_value = common.ErrorGroup(), metadata
+
+ client.get_group(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_update_group_rest_bad_request(
+ request_type=error_group_service.UpdateGroupRequest,
+):
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"group": {"name": "projects/sample1/groups/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_group(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ error_group_service.UpdateGroupRequest,
+ dict,
+ ],
+)
+def test_update_group_rest_call_success(request_type):
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"group": {"name": "projects/sample1/groups/sample2"}}
+ request_init["group"] = {
+ "name": "projects/sample1/groups/sample2",
+ "group_id": "group_id_value",
+ "tracking_issues": [{"url": "url_value"}],
+ "resolution_status": 1,
+ }
+ # 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 = error_group_service.UpdateGroupRequest.meta.fields["group"]
+
+ 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["group"].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["group"][field])):
+ del request_init["group"][field][i][subfield]
+ else:
+ del request_init["group"][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 = common.ErrorGroup(
+ name="name_value",
+ group_id="group_id_value",
+ resolution_status=common.ResolutionStatus.OPEN,
+ )
+
+ # 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 = common.ErrorGroup.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_group(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, common.ErrorGroup)
+ assert response.name == "name_value"
+ assert response.group_id == "group_id_value"
+ assert response.resolution_status == common.ResolutionStatus.OPEN
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_update_group_rest_interceptors(null_interceptor):
+ transport = transports.ErrorGroupServiceRestTransport(
credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.ErrorGroupServiceRestInterceptor(),
)
- assert transport.kind == transport_name
+ client = ErrorGroupServiceClient(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.ErrorGroupServiceRestInterceptor, "post_update_group"
+ ) as post, mock.patch.object(
+ transports.ErrorGroupServiceRestInterceptor, "post_update_group_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.ErrorGroupServiceRestInterceptor, "pre_update_group"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = error_group_service.UpdateGroupRequest.pb(
+ error_group_service.UpdateGroupRequest()
+ )
+ 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 = common.ErrorGroup.to_json(common.ErrorGroup())
+ req.return_value.content = return_value
+
+ request = error_group_service.UpdateGroupRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = common.ErrorGroup()
+ post_with_metadata.return_value = common.ErrorGroup(), metadata
+
+ client.update_group(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_initialize_client_w_rest():
+ client = ErrorGroupServiceClient(
+ 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_get_group_empty_call_rest():
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.get_group), "__call__") as call:
+ client.get_group(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_group_service.GetGroupRequest()
+
+ 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_group_empty_call_rest():
+ client = ErrorGroupServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.update_group), "__call__") as call:
+ client.update_group(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_group_service.UpdateGroupRequest()
+
+ assert args[0] == request_msg
def test_transport_grpc_default():
@@ -1362,6 +2875,29 @@ def test_error_group_service_transport_auth_adc(transport_class):
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.ErrorGroupServiceGrpcTransport,
+ transports.ErrorGroupServiceGrpcAsyncIOTransport,
+ transports.ErrorGroupServiceRestTransport,
+ ],
+)
+def test_error_group_service_transport_auth_gdch_credentials(transport_class):
+ host = "https://language.com"
+ api_audience_tests = [None, "https://language2.com"]
+ api_audience_expect = [host, "https://language2.com"]
+ for t, e in zip(api_audience_tests, api_audience_expect):
+ with mock.patch.object(google.auth, "default", autospec=True) as adc:
+ gdch_mock = mock.MagicMock()
+ type(gdch_mock).with_gdch_audience = mock.PropertyMock(
+ return_value=gdch_mock
+ )
+ adc.return_value = (gdch_mock, None)
+ transport_class(host=host, api_audience=t)
+ gdch_mock.with_gdch_audience.assert_called_once_with(e)
+
+
@pytest.mark.parametrize(
"transport_class,grpc_helpers",
[
@@ -1444,11 +2980,23 @@ def test_error_group_service_grpc_transport_client_cert_source_for_mtls(
)
+def test_error_group_service_http_transport_client_cert_source_for_mtls():
+ cred = ga_credentials.AnonymousCredentials()
+ with mock.patch(
+ "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel"
+ ) as mock_configure_mtls_channel:
+ transports.ErrorGroupServiceRestTransport(
+ credentials=cred, client_cert_source_for_mtls=client_cert_source_callback
+ )
+ mock_configure_mtls_channel.assert_called_once_with(client_cert_source_callback)
+
+
@pytest.mark.parametrize(
"transport_name",
[
"grpc",
"grpc_asyncio",
+ "rest",
],
)
def test_error_group_service_host_no_port(transport_name):
@@ -1459,7 +3007,11 @@ def test_error_group_service_host_no_port(transport_name):
),
transport=transport_name,
)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
@pytest.mark.parametrize(
@@ -1467,6 +3019,7 @@ def test_error_group_service_host_no_port(transport_name):
[
"grpc",
"grpc_asyncio",
+ "rest",
],
)
def test_error_group_service_host_with_port(transport_name):
@@ -1477,7 +3030,36 @@ def test_error_group_service_host_with_port(transport_name):
),
transport=transport_name,
)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:8000")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:8000"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com:8000"
+ )
+
+
+@pytest.mark.parametrize(
+ "transport_name",
+ [
+ "rest",
+ ],
+)
+def test_error_group_service_client_transport_session_collision(transport_name):
+ creds1 = ga_credentials.AnonymousCredentials()
+ creds2 = ga_credentials.AnonymousCredentials()
+ client1 = ErrorGroupServiceClient(
+ credentials=creds1,
+ transport=transport_name,
+ )
+ client2 = ErrorGroupServiceClient(
+ credentials=creds2,
+ transport=transport_name,
+ )
+ session1 = client1.transport.get_group._session
+ session2 = client2.transport.get_group._session
+ assert session1 != session2
+ session1 = client1.transport.update_group._session
+ session2 = client2.transport.update_group._session
+ assert session1 != session2
def test_error_group_service_grpc_transport_channel():
@@ -1755,39 +3337,46 @@ def test_client_with_default_client_info():
prep.assert_called_once_with(client_info)
+def test_transport_close_grpc():
+ client = ErrorGroupServiceClient(
+ 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()
+
+
@pytest.mark.asyncio
-async def test_transport_close_async():
+async def test_transport_close_grpc_asyncio():
client = ErrorGroupServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
)
with mock.patch.object(
- type(getattr(client.transport, "grpc_channel")), "close"
+ type(getattr(client.transport, "_grpc_channel")), "close"
) as close:
async with client:
close.assert_not_called()
close.assert_called_once()
-def test_transport_close():
- transports = {
- "grpc": "_grpc_channel",
- }
-
- for transport, close_name in transports.items():
- client = ErrorGroupServiceClient(
- 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()
+def test_transport_close_rest():
+ client = ErrorGroupServiceClient(
+ 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():
transports = [
+ "rest",
"grpc",
]
for transport in transports:
@@ -1826,10 +3415,13 @@ def test_api_key_credentials(client_class, transport_class):
patched.assert_called_once_with(
credentials=mock_cred,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
diff --git a/tests/unit/gapic/errorreporting_v1beta1/test_error_stats_service.py b/tests/unit/gapic/errorreporting_v1beta1/test_error_stats_service.py
index d3d0634b..0b1bdc25 100644
--- a/tests/unit/gapic/errorreporting_v1beta1/test_error_stats_service.py
+++ b/tests/unit/gapic/errorreporting_v1beta1/test_error_stats_service.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.
@@ -18,16 +18,31 @@
# try/except added for compatibility with python < 3.8
try:
from unittest import mock
- from unittest.mock import AsyncMock
-except ImportError:
+ from unittest.mock import AsyncMock # pragma: NO COVER
+except ImportError: # pragma: NO COVER
import mock
import grpc
from grpc.experimental import aio
+from collections.abc import Iterable, AsyncIterable
+from google.protobuf import json_format
+import json
import math
import pytest
+from google.api_core import api_core_version
from proto.marshal.rules.dates import DurationRule, TimestampRule
+from proto.marshal.rules import wrappers
+from requests import Response
+from requests import Request, PreparedRequest
+from requests.sessions import Session
+from google.protobuf import json_format
+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
@@ -35,6 +50,7 @@
from google.api_core import grpc_helpers
from google.api_core import grpc_helpers_async
from google.api_core import path_template
+from google.api_core import retry as retries
from google.auth import credentials as ga_credentials
from google.auth.exceptions import MutualTLSChannelError
from google.cloud.errorreporting_v1beta1.services.error_stats_service import (
@@ -53,10 +69,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.
@@ -68,6 +106,17 @@ def modify_default_endpoint(client):
)
+# If default endpoint template is localhost, then default mtls endpoint will be the same.
+# This method modifies the default endpoint template so the client can produce a different
+# mtls endpoint for endpoint testing purposes.
+def modify_default_endpoint_template(client):
+ return (
+ "test.{UNIVERSE_DOMAIN}"
+ if ("localhost" in client._DEFAULT_ENDPOINT_TEMPLATE)
+ else client._DEFAULT_ENDPOINT_TEMPLATE
+ )
+
+
def test__get_default_mtls_endpoint():
api_endpoint = "example.googleapis.com"
api_mtls_endpoint = "example.mtls.googleapis.com"
@@ -98,11 +147,251 @@ def test__get_default_mtls_endpoint():
)
+def test__read_environment_variables():
+ assert ErrorStatsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ assert ErrorStatsServiceClient._read_environment_variables() == (
+ True,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
+ assert ErrorStatsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
+ ):
+ with pytest.raises(ValueError) as excinfo:
+ ErrorStatsServiceClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ assert ErrorStatsServiceClient._read_environment_variables() == (
+ False,
+ "never",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
+ assert ErrorStatsServiceClient._read_environment_variables() == (
+ False,
+ "always",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}):
+ assert ErrorStatsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ ErrorStatsServiceClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_CLOUD_UNIVERSE_DOMAIN": "foo.com"}):
+ assert ErrorStatsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ "foo.com",
+ )
+
+
+def test__get_client_cert_source():
+ mock_provided_cert_source = mock.Mock()
+ mock_default_cert_source = mock.Mock()
+
+ assert ErrorStatsServiceClient._get_client_cert_source(None, False) is None
+ assert (
+ ErrorStatsServiceClient._get_client_cert_source(
+ mock_provided_cert_source, False
+ )
+ is None
+ )
+ assert (
+ ErrorStatsServiceClient._get_client_cert_source(mock_provided_cert_source, True)
+ == mock_provided_cert_source
+ )
+
+ with mock.patch(
+ "google.auth.transport.mtls.has_default_client_cert_source", return_value=True
+ ):
+ with mock.patch(
+ "google.auth.transport.mtls.default_client_cert_source",
+ return_value=mock_default_cert_source,
+ ):
+ assert (
+ ErrorStatsServiceClient._get_client_cert_source(None, True)
+ is mock_default_cert_source
+ )
+ assert (
+ ErrorStatsServiceClient._get_client_cert_source(
+ mock_provided_cert_source, "true"
+ )
+ is mock_provided_cert_source
+ )
+
+
+@mock.patch.object(
+ ErrorStatsServiceClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceClient),
+)
+@mock.patch.object(
+ ErrorStatsServiceAsyncClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceAsyncClient),
+)
+def test__get_api_endpoint():
+ api_override = "foo.com"
+ mock_client_cert_source = mock.Mock()
+ default_universe = ErrorStatsServiceClient._DEFAULT_UNIVERSE
+ default_endpoint = ErrorStatsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=default_universe
+ )
+ mock_universe = "bar.com"
+ mock_endpoint = ErrorStatsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=mock_universe
+ )
+
+ assert (
+ ErrorStatsServiceClient._get_api_endpoint(
+ api_override, mock_client_cert_source, default_universe, "always"
+ )
+ == api_override
+ )
+ assert (
+ ErrorStatsServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, default_universe, "auto"
+ )
+ == ErrorStatsServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ErrorStatsServiceClient._get_api_endpoint(None, None, default_universe, "auto")
+ == default_endpoint
+ )
+ assert (
+ ErrorStatsServiceClient._get_api_endpoint(
+ None, None, default_universe, "always"
+ )
+ == ErrorStatsServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ErrorStatsServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, default_universe, "always"
+ )
+ == ErrorStatsServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ErrorStatsServiceClient._get_api_endpoint(None, None, mock_universe, "never")
+ == mock_endpoint
+ )
+ assert (
+ ErrorStatsServiceClient._get_api_endpoint(None, None, default_universe, "never")
+ == default_endpoint
+ )
+
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ ErrorStatsServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, mock_universe, "auto"
+ )
+ assert (
+ str(excinfo.value)
+ == "mTLS is not supported in any universe other than googleapis.com."
+ )
+
+
+def test__get_universe_domain():
+ client_universe_domain = "foo.com"
+ universe_domain_env = "bar.com"
+
+ assert (
+ ErrorStatsServiceClient._get_universe_domain(
+ client_universe_domain, universe_domain_env
+ )
+ == client_universe_domain
+ )
+ assert (
+ ErrorStatsServiceClient._get_universe_domain(None, universe_domain_env)
+ == universe_domain_env
+ )
+ assert (
+ ErrorStatsServiceClient._get_universe_domain(None, None)
+ == ErrorStatsServiceClient._DEFAULT_UNIVERSE
+ )
+
+ with pytest.raises(ValueError) as excinfo:
+ ErrorStatsServiceClient._get_universe_domain("", None)
+ assert str(excinfo.value) == "Universe Domain cannot be an empty string."
+
+
+@pytest.mark.parametrize(
+ "error_code,cred_info_json,show_cred_info",
+ [
+ (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__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 = ErrorStatsServiceClient(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"]
+
+
+@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 = ErrorStatsServiceClient(credentials=cred)
+ client._transport._credentials = cred
+
+ error = core_exceptions.GoogleAPICallError("message", details=[])
+ error.code = error_code
+
+ client._add_cred_info_for_auth_errors(error)
+ assert error.details == []
+
+
@pytest.mark.parametrize(
"client_class,transport_name",
[
(ErrorStatsServiceClient, "grpc"),
(ErrorStatsServiceAsyncClient, "grpc_asyncio"),
+ (ErrorStatsServiceClient, "rest"),
],
)
def test_error_stats_service_client_from_service_account_info(
@@ -118,7 +407,11 @@ def test_error_stats_service_client_from_service_account_info(
assert client.transport._credentials == creds
assert isinstance(client, client_class)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
@pytest.mark.parametrize(
@@ -126,6 +419,7 @@ def test_error_stats_service_client_from_service_account_info(
[
(transports.ErrorStatsServiceGrpcTransport, "grpc"),
(transports.ErrorStatsServiceGrpcAsyncIOTransport, "grpc_asyncio"),
+ (transports.ErrorStatsServiceRestTransport, "rest"),
],
)
def test_error_stats_service_client_service_account_always_use_jwt(
@@ -151,6 +445,7 @@ def test_error_stats_service_client_service_account_always_use_jwt(
[
(ErrorStatsServiceClient, "grpc"),
(ErrorStatsServiceAsyncClient, "grpc_asyncio"),
+ (ErrorStatsServiceClient, "rest"),
],
)
def test_error_stats_service_client_from_service_account_file(
@@ -173,13 +468,18 @@ def test_error_stats_service_client_from_service_account_file(
assert client.transport._credentials == creds
assert isinstance(client, client_class)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
def test_error_stats_service_client_get_transport_class():
transport = ErrorStatsServiceClient.get_transport_class()
available_transports = [
transports.ErrorStatsServiceGrpcTransport,
+ transports.ErrorStatsServiceRestTransport,
]
assert transport in available_transports
@@ -196,17 +496,18 @@ def test_error_stats_service_client_get_transport_class():
transports.ErrorStatsServiceGrpcAsyncIOTransport,
"grpc_asyncio",
),
+ (ErrorStatsServiceClient, transports.ErrorStatsServiceRestTransport, "rest"),
],
)
@mock.patch.object(
ErrorStatsServiceClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorStatsServiceClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceClient),
)
@mock.patch.object(
ErrorStatsServiceAsyncClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorStatsServiceAsyncClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceAsyncClient),
)
def test_error_stats_service_client_client_options(
client_class, transport_class, transport_name
@@ -236,6 +537,7 @@ def test_error_stats_service_client_client_options(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
@@ -247,12 +549,15 @@ def test_error_stats_service_client_client_options(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
@@ -270,20 +575,29 @@ def test_error_stats_service_client_client_options(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has
# unsupported value.
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
- with pytest.raises(MutualTLSChannelError):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
client = client_class(transport=transport_name)
+ assert (
+ str(excinfo.value)
+ == "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):
+ 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")
@@ -293,12 +607,35 @@ def test_error_stats_service_client_client_options(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id="octopus",
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
+ )
+ # Check the case api_endpoint is provided
+ options = client_options.ClientOptions(
+ api_audience="https://language.googleapis.com"
+ )
+ with mock.patch.object(transport_class, "__init__") as patched:
+ patched.return_value = None
+ client = client_class(client_options=options, transport=transport_name)
+ patched.assert_called_once_with(
+ credentials=None,
+ credentials_file=None,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
+ scopes=None,
+ client_cert_source_for_mtls=None,
+ quota_project_id=None,
+ client_info=transports.base.DEFAULT_CLIENT_INFO,
+ always_use_jwt_access=True,
+ api_audience="https://language.googleapis.com",
)
@@ -329,17 +666,29 @@ def test_error_stats_service_client_client_options(
"grpc_asyncio",
"false",
),
+ (
+ ErrorStatsServiceClient,
+ transports.ErrorStatsServiceRestTransport,
+ "rest",
+ "true",
+ ),
+ (
+ ErrorStatsServiceClient,
+ transports.ErrorStatsServiceRestTransport,
+ "rest",
+ "false",
+ ),
],
)
@mock.patch.object(
ErrorStatsServiceClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorStatsServiceClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceClient),
)
@mock.patch.object(
ErrorStatsServiceAsyncClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ErrorStatsServiceAsyncClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceAsyncClient),
)
@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"})
def test_error_stats_service_client_mtls_env_auto(
@@ -362,7 +711,9 @@ def test_error_stats_service_client_mtls_env_auto(
if use_client_cert_env == "false":
expected_client_cert_source = None
- expected_host = client.DEFAULT_ENDPOINT
+ expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ )
else:
expected_client_cert_source = client_cert_source_callback
expected_host = client.DEFAULT_MTLS_ENDPOINT
@@ -376,6 +727,7 @@ def test_error_stats_service_client_mtls_env_auto(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case ADC client cert is provided. Whether client cert is used depends on
@@ -393,7 +745,9 @@ def test_error_stats_service_client_mtls_env_auto(
return_value=client_cert_source_callback,
):
if use_client_cert_env == "false":
- expected_host = client.DEFAULT_ENDPOINT
+ expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ )
expected_client_cert_source = None
else:
expected_host = client.DEFAULT_MTLS_ENDPOINT
@@ -410,6 +764,7 @@ def test_error_stats_service_client_mtls_env_auto(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case client_cert_source and ADC client cert are not provided.
@@ -426,12 +781,15 @@ def test_error_stats_service_client_mtls_env_auto(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -515,6 +873,115 @@ def test_error_stats_service_client_get_mtls_endpoint_and_cert_source(client_cla
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
assert cert_source == mock_client_cert_source
+ # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has
+ # unsupported value.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ client_class.get_mtls_endpoint_and_cert_source()
+
+ assert (
+ str(excinfo.value)
+ == "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", [ErrorStatsServiceClient, ErrorStatsServiceAsyncClient]
+)
+@mock.patch.object(
+ ErrorStatsServiceClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceClient),
+)
+@mock.patch.object(
+ ErrorStatsServiceAsyncClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ErrorStatsServiceAsyncClient),
+)
+def test_error_stats_service_client_client_api_endpoint(client_class):
+ mock_client_cert_source = client_cert_source_callback
+ api_override = "foo.com"
+ default_universe = ErrorStatsServiceClient._DEFAULT_UNIVERSE
+ default_endpoint = ErrorStatsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=default_universe
+ )
+ mock_universe = "bar.com"
+ mock_endpoint = ErrorStatsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=mock_universe
+ )
+
+ # If ClientOptions.api_endpoint is set and GOOGLE_API_USE_CLIENT_CERTIFICATE="true",
+ # use ClientOptions.api_endpoint as the api endpoint regardless.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ with mock.patch(
+ "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel"
+ ):
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source, api_endpoint=api_override
+ )
+ client = client_class(
+ client_options=options,
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ assert client.api_endpoint == api_override
+
+ # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="never",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ client = client_class(credentials=ga_credentials.AnonymousCredentials())
+ assert client.api_endpoint == default_endpoint
+
+ # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="always",
+ # use the DEFAULT_MTLS_ENDPOINT as the api endpoint.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
+ client = client_class(credentials=ga_credentials.AnonymousCredentials())
+ assert client.api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
+
+ # If ClientOptions.api_endpoint is not set, GOOGLE_API_USE_MTLS_ENDPOINT="auto" (default),
+ # GOOGLE_API_USE_CLIENT_CERTIFICATE="false" (default), default cert source doesn't exist,
+ # and ClientOptions.universe_domain="bar.com",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with universe domain as the api endpoint.
+ options = client_options.ClientOptions()
+ universe_exists = hasattr(options, "universe_domain")
+ if universe_exists:
+ options = client_options.ClientOptions(universe_domain=mock_universe)
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ else:
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert client.api_endpoint == (
+ mock_endpoint if universe_exists else default_endpoint
+ )
+ assert client.universe_domain == (
+ mock_universe if universe_exists else default_universe
+ )
+
+ # If ClientOptions does not have a universe domain attribute and GOOGLE_API_USE_MTLS_ENDPOINT="never",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint.
+ options = client_options.ClientOptions()
+ if hasattr(options, "universe_domain"):
+ delattr(options, "universe_domain")
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert client.api_endpoint == default_endpoint
+
@pytest.mark.parametrize(
"client_class,transport_class,transport_name",
@@ -525,6 +992,7 @@ def test_error_stats_service_client_get_mtls_endpoint_and_cert_source(client_cla
transports.ErrorStatsServiceGrpcAsyncIOTransport,
"grpc_asyncio",
),
+ (ErrorStatsServiceClient, transports.ErrorStatsServiceRestTransport, "rest"),
],
)
def test_error_stats_service_client_client_options_scopes(
@@ -540,12 +1008,15 @@ def test_error_stats_service_client_client_options_scopes(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=["1", "2"],
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -564,6 +1035,12 @@ def test_error_stats_service_client_client_options_scopes(
"grpc_asyncio",
grpc_helpers_async,
),
+ (
+ ErrorStatsServiceClient,
+ transports.ErrorStatsServiceRestTransport,
+ "rest",
+ None,
+ ),
],
)
def test_error_stats_service_client_client_options_credentials_file(
@@ -578,12 +1055,15 @@ def test_error_stats_service_client_client_options_credentials_file(
patched.assert_called_once_with(
credentials=None,
credentials_file="credentials.json",
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -604,6 +1084,7 @@ def test_error_stats_service_client_client_options_from_dict():
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -636,12 +1117,15 @@ def test_error_stats_service_client_create_channel_credentials_file(
patched.assert_called_once_with(
credentials=None,
credentials_file="credentials.json",
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# test that the credentials from file are saved and used as the credentials.
@@ -701,27 +1185,121 @@ def test_list_group_stats(request_type, transport: str = "grpc"):
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls) == 1
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.ListGroupStatsRequest()
+ request = error_stats_service.ListGroupStatsRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, pagers.ListGroupStatsPager)
assert response.next_page_token == "next_page_token_value"
-def test_list_group_stats_empty_call():
- # This test is a coverage failsafe to make sure that totally empty calls,
- # i.e. request == None and no flattened fields passed, work.
+def test_list_group_stats_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 = ErrorStatsServiceClient(
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 = error_stats_service.ListGroupStatsRequest(
+ project_name="project_name_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_group_stats), "__call__") as call:
- client.list_group_stats()
+ call.return_value.name = (
+ "foo" # operation_request.operation in compute client(s) expect a string.
+ )
+ client.list_group_stats(request=request)
call.assert_called()
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.ListGroupStatsRequest()
+ assert args[0] == error_stats_service.ListGroupStatsRequest(
+ project_name="project_name_value",
+ page_token="page_token_value",
+ )
+
+
+def test_list_group_stats_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 = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # 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.list_group_stats 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.list_group_stats
+ ] = mock_rpc
+ request = {}
+ client.list_group_stats(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.list_group_stats(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_list_group_stats_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 = ErrorStatsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport=transport,
+ )
+
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
+
+ # Ensure method has been cached
+ assert (
+ client._client._transport.list_group_stats
+ in client._client._transport._wrapped_methods
+ )
+
+ # Replace cached wrapped function with mock
+ mock_rpc = mock.AsyncMock()
+ mock_rpc.return_value = mock.Mock()
+ client._client._transport._wrapped_methods[
+ client._client._transport.list_group_stats
+ ] = mock_rpc
+
+ request = {}
+ await client.list_group_stats(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ await client.list_group_stats(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
@pytest.mark.asyncio
@@ -730,7 +1308,7 @@ async def test_list_group_stats_async(
request_type=error_stats_service.ListGroupStatsRequest,
):
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -751,7 +1329,8 @@ async def test_list_group_stats_async(
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls)
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.ListGroupStatsRequest()
+ request = error_stats_service.ListGroupStatsRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, pagers.ListGroupStatsAsyncPager)
@@ -795,7 +1374,7 @@ def test_list_group_stats_field_headers():
@pytest.mark.asyncio
async def test_list_group_stats_field_headers_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -876,7 +1455,7 @@ def test_list_group_stats_flattened_error():
@pytest.mark.asyncio
async def test_list_group_stats_flattened_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -913,7 +1492,7 @@ async def test_list_group_stats_flattened_async():
@pytest.mark.asyncio
async def test_list_group_stats_flattened_error_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -930,7 +1509,7 @@ async def test_list_group_stats_flattened_error_async():
def test_list_group_stats_pager(transport_name: str = "grpc"):
client = ErrorStatsServiceClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=ga_credentials.AnonymousCredentials(),
transport=transport_name,
)
@@ -965,13 +1544,17 @@ def test_list_group_stats_pager(transport_name: str = "grpc"):
RuntimeError,
)
- metadata = ()
- metadata = tuple(metadata) + (
+ expected_metadata = ()
+ retry = retries.Retry()
+ timeout = 5
+ expected_metadata = tuple(expected_metadata) + (
gapic_v1.routing_header.to_grpc_metadata((("project_name", ""),)),
)
- pager = client.list_group_stats(request={})
+ pager = client.list_group_stats(request={}, retry=retry, timeout=timeout)
- assert pager._metadata == metadata
+ assert pager._metadata == expected_metadata
+ assert pager._retry == retry
+ assert pager._timeout == timeout
results = list(pager)
assert len(results) == 6
@@ -980,7 +1563,7 @@ def test_list_group_stats_pager(transport_name: str = "grpc"):
def test_list_group_stats_pages(transport_name: str = "grpc"):
client = ErrorStatsServiceClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=ga_credentials.AnonymousCredentials(),
transport=transport_name,
)
@@ -1022,7 +1605,7 @@ def test_list_group_stats_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_group_stats_async_pager():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1074,7 +1657,7 @@ async def test_list_group_stats_async_pager():
@pytest.mark.asyncio
async def test_list_group_stats_async_pages():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1110,9 +1693,11 @@ async def test_list_group_stats_async_pages():
RuntimeError,
)
pages = []
- async for page_ in (
+ # Workaround issue in python 3.9 related to code coverage by adding `# pragma: no branch`
+ # See https://github.com/googleapis/gapic-generator-python/pull/1174#issuecomment-1025132372
+ async for page_ in ( # pragma: no branch
await client.list_group_stats(request={})
- ).pages: # pragma: no branch
+ ).pages:
pages.append(page_)
for page_, token in zip(pages, ["abc", "def", "ghi", ""]):
assert page_.raw_page.next_page_token == token
@@ -1146,27 +1731,121 @@ def test_list_events(request_type, transport: str = "grpc"):
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls) == 1
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.ListEventsRequest()
+ request = error_stats_service.ListEventsRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, pagers.ListEventsPager)
assert response.next_page_token == "next_page_token_value"
-def test_list_events_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_events_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 = ErrorStatsServiceClient(
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 = error_stats_service.ListEventsRequest(
+ project_name="project_name_value",
+ group_id="group_id_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_events), "__call__") as call:
- client.list_events()
+ call.return_value.name = (
+ "foo" # operation_request.operation in compute client(s) expect a string.
+ )
+ client.list_events(request=request)
call.assert_called()
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.ListEventsRequest()
+ assert args[0] == error_stats_service.ListEventsRequest(
+ project_name="project_name_value",
+ group_id="group_id_value",
+ page_token="page_token_value",
+ )
+
+
+def test_list_events_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 = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # 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.list_events 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.list_events] = mock_rpc
+ request = {}
+ client.list_events(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.list_events(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_list_events_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 = ErrorStatsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport=transport,
+ )
+
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
+
+ # Ensure method has been cached
+ assert (
+ client._client._transport.list_events
+ in client._client._transport._wrapped_methods
+ )
+
+ # Replace cached wrapped function with mock
+ mock_rpc = mock.AsyncMock()
+ mock_rpc.return_value = mock.Mock()
+ client._client._transport._wrapped_methods[
+ client._client._transport.list_events
+ ] = mock_rpc
+
+ request = {}
+ await client.list_events(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ await client.list_events(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
@pytest.mark.asyncio
@@ -1174,7 +1853,7 @@ async def test_list_events_async(
transport: str = "grpc_asyncio", request_type=error_stats_service.ListEventsRequest
):
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1195,7 +1874,8 @@ async def test_list_events_async(
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls)
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.ListEventsRequest()
+ request = error_stats_service.ListEventsRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, pagers.ListEventsAsyncPager)
@@ -1239,7 +1919,7 @@ def test_list_events_field_headers():
@pytest.mark.asyncio
async def test_list_events_field_headers_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1314,7 +1994,7 @@ def test_list_events_flattened_error():
@pytest.mark.asyncio
async def test_list_events_flattened_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1347,7 +2027,7 @@ async def test_list_events_flattened_async():
@pytest.mark.asyncio
async def test_list_events_flattened_error_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1362,7 +2042,7 @@ async def test_list_events_flattened_error_async():
def test_list_events_pager(transport_name: str = "grpc"):
client = ErrorStatsServiceClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=ga_credentials.AnonymousCredentials(),
transport=transport_name,
)
@@ -1397,13 +2077,17 @@ def test_list_events_pager(transport_name: str = "grpc"):
RuntimeError,
)
- metadata = ()
- metadata = tuple(metadata) + (
+ expected_metadata = ()
+ retry = retries.Retry()
+ timeout = 5
+ expected_metadata = tuple(expected_metadata) + (
gapic_v1.routing_header.to_grpc_metadata((("project_name", ""),)),
)
- pager = client.list_events(request={})
+ pager = client.list_events(request={}, retry=retry, timeout=timeout)
- assert pager._metadata == metadata
+ assert pager._metadata == expected_metadata
+ assert pager._retry == retry
+ assert pager._timeout == timeout
results = list(pager)
assert len(results) == 6
@@ -1412,7 +2096,7 @@ def test_list_events_pager(transport_name: str = "grpc"):
def test_list_events_pages(transport_name: str = "grpc"):
client = ErrorStatsServiceClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=ga_credentials.AnonymousCredentials(),
transport=transport_name,
)
@@ -1454,7 +2138,7 @@ def test_list_events_pages(transport_name: str = "grpc"):
@pytest.mark.asyncio
async def test_list_events_async_pager():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1504,7 +2188,7 @@ async def test_list_events_async_pager():
@pytest.mark.asyncio
async def test_list_events_async_pages():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials,
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1540,9 +2224,11 @@ async def test_list_events_async_pages():
RuntimeError,
)
pages = []
- async for page_ in (
+ # Workaround issue in python 3.9 related to code coverage by adding `# pragma: no branch`
+ # See https://github.com/googleapis/gapic-generator-python/pull/1174#issuecomment-1025132372
+ async for page_ in ( # pragma: no branch
await client.list_events(request={})
- ).pages: # pragma: no branch
+ ).pages:
pages.append(page_)
for page_, token in zip(pages, ["abc", "def", "ghi", ""]):
assert page_.raw_page.next_page_token == token
@@ -1574,26 +2260,116 @@ def test_delete_events(request_type, transport: str = "grpc"):
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls) == 1
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.DeleteEventsRequest()
+ request = error_stats_service.DeleteEventsRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, error_stats_service.DeleteEventsResponse)
-def test_delete_events_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_delete_events_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 = ErrorStatsServiceClient(
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 = error_stats_service.DeleteEventsRequest(
+ project_name="project_name_value",
+ )
+
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(type(client.transport.delete_events), "__call__") as call:
- client.delete_events()
+ call.return_value.name = (
+ "foo" # operation_request.operation in compute client(s) expect a string.
+ )
+ client.delete_events(request=request)
call.assert_called()
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.DeleteEventsRequest()
+ assert args[0] == error_stats_service.DeleteEventsRequest(
+ project_name="project_name_value",
+ )
+
+
+def test_delete_events_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 = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # 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.delete_events 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.delete_events] = mock_rpc
+ request = {}
+ client.delete_events(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.delete_events(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_delete_events_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 = ErrorStatsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport=transport,
+ )
+
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
+
+ # Ensure method has been cached
+ assert (
+ client._client._transport.delete_events
+ in client._client._transport._wrapped_methods
+ )
+
+ # Replace cached wrapped function with mock
+ mock_rpc = mock.AsyncMock()
+ mock_rpc.return_value = mock.Mock()
+ client._client._transport._wrapped_methods[
+ client._client._transport.delete_events
+ ] = mock_rpc
+
+ request = {}
+ await client.delete_events(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ await client.delete_events(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
@pytest.mark.asyncio
@@ -1602,7 +2378,7 @@ async def test_delete_events_async(
request_type=error_stats_service.DeleteEventsRequest,
):
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -1621,7 +2397,8 @@ async def test_delete_events_async(
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls)
_, args, _ = call.mock_calls[0]
- assert args[0] == error_stats_service.DeleteEventsRequest()
+ request = error_stats_service.DeleteEventsRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, error_stats_service.DeleteEventsResponse)
@@ -1664,7 +2441,7 @@ def test_delete_events_field_headers():
@pytest.mark.asyncio
async def test_delete_events_field_headers_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -1734,7 +2511,7 @@ def test_delete_events_flattened_error():
@pytest.mark.asyncio
async def test_delete_events_flattened_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -1763,7 +2540,7 @@ async def test_delete_events_flattened_async():
@pytest.mark.asyncio
async def test_delete_events_flattened_error_async():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -1775,193 +2552,1554 @@ async def test_delete_events_flattened_error_async():
)
-def test_credentials_transport_error():
- # It is an error to provide credentials and a transport instance.
- transport = transports.ErrorStatsServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- with pytest.raises(ValueError):
+def test_list_group_stats_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 = ErrorStatsServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
- transport=transport,
+ transport="rest",
)
- # It is an error to provide a credentials file and a transport instance.
- transport = transports.ErrorStatsServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- with pytest.raises(ValueError):
- client = ErrorStatsServiceClient(
- client_options={"credentials_file": "credentials.json"},
- transport=transport,
- )
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
- # It is an error to provide an api_key and a transport instance.
- transport = transports.ErrorStatsServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- options = client_options.ClientOptions()
- options.api_key = "api_key"
- with pytest.raises(ValueError):
- client = ErrorStatsServiceClient(
- client_options=options,
- transport=transport,
- )
+ # Ensure method has been cached
+ assert client._transport.list_group_stats in client._transport._wrapped_methods
- # It is an error to provide an api_key and a credential.
- options = mock.Mock()
- options.api_key = "api_key"
- with pytest.raises(ValueError):
- client = ErrorStatsServiceClient(
- client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ # 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.list_group_stats
+ ] = mock_rpc
- # It is an error to provide scopes and a transport instance.
- transport = transports.ErrorStatsServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- with pytest.raises(ValueError):
- client = ErrorStatsServiceClient(
- client_options={"scopes": ["1", "2"]},
- transport=transport,
- )
+ request = {}
+ client.list_group_stats(request)
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
-def test_transport_instance():
- # A client may be instantiated with a custom transport instance.
- transport = transports.ErrorStatsServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- client = ErrorStatsServiceClient(transport=transport)
- assert client.transport is transport
+ client.list_group_stats(request)
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
-def test_transport_get_channel():
- # A client may be instantiated with a custom transport instance.
- transport = transports.ErrorStatsServiceGrpcTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
- channel = transport.grpc_channel
- assert channel
- transport = transports.ErrorStatsServiceGrpcAsyncIOTransport(
- credentials=ga_credentials.AnonymousCredentials(),
+def test_list_group_stats_rest_required_fields(
+ request_type=error_stats_service.ListGroupStatsRequest,
+):
+ transport_class = transports.ErrorStatsServiceRestTransport
+
+ request_init = {}
+ request_init["project_name"] = ""
+ request = request_type(**request_init)
+ pb_request = request_type.pb(request)
+ jsonified_request = json.loads(
+ json_format.MessageToJson(pb_request, use_integers_for_enums=False)
)
- channel = transport.grpc_channel
- assert channel
+ # verify fields with default values are dropped
-@pytest.mark.parametrize(
- "transport_class",
- [
- transports.ErrorStatsServiceGrpcTransport,
- transports.ErrorStatsServiceGrpcAsyncIOTransport,
- ],
-)
-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()
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).list_group_stats._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+ # verify required fields with default values are now present
-@pytest.mark.parametrize(
- "transport_name",
- [
- "grpc",
- ],
-)
-def test_transport_kind(transport_name):
- transport = ErrorStatsServiceClient.get_transport_class(transport_name)(
- credentials=ga_credentials.AnonymousCredentials(),
+ jsonified_request["projectName"] = "project_name_value"
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).list_group_stats._get_unset_required_fields(jsonified_request)
+ # Check that path parameters and body parameters are not mixing in.
+ assert not set(unset_fields) - set(
+ (
+ "alignment",
+ "alignment_time",
+ "group_id",
+ "order",
+ "page_size",
+ "page_token",
+ "service_filter",
+ "time_range",
+ "timed_count_duration",
+ )
)
- assert transport.kind == transport_name
+ jsonified_request.update(unset_fields)
+ # verify required fields with non-default values are left alone
+ assert "projectName" in jsonified_request
+ assert jsonified_request["projectName"] == "project_name_value"
-def test_transport_grpc_default():
- # A client should use the gRPC transport by default.
client = ErrorStatsServiceClient(
credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
)
- assert isinstance(
- client.transport,
- transports.ErrorStatsServiceGrpcTransport,
+ request = request_type(**request_init)
+
+ # Designate an appropriate value for the returned response.
+ return_value = error_stats_service.ListGroupStatsResponse()
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # We need to mock transcode() because providing default values
+ # for required fields will fail the real version if the http_options
+ # expect actual values for those fields.
+ with mock.patch.object(path_template, "transcode") as transcode:
+ # A uri without fields and an empty body will force all the
+ # request fields to show up in the query_params.
+ pb_request = request_type.pb(request)
+ transcode_result = {
+ "uri": "v1/sample_method",
+ "method": "get",
+ "query_params": pb_request,
+ }
+ transcode.return_value = transcode_result
+
+ response_value = Response()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = error_stats_service.ListGroupStatsResponse.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_group_stats(request)
+
+ expected_params = [("$alt", "json;enum-encoding=int")]
+ actual_params = req.call_args.kwargs["params"]
+ assert expected_params == actual_params
+
+
+def test_list_group_stats_rest_unset_required_fields():
+ transport = transports.ErrorStatsServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials
)
-
-def test_error_stats_service_base_transport_error():
- # Passing both a credentials object and credentials_file should raise an error
- with pytest.raises(core_exceptions.DuplicateCredentialArgs):
- transport = transports.ErrorStatsServiceTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- credentials_file="credentials.json",
+ unset_fields = transport.list_group_stats._get_unset_required_fields({})
+ assert set(unset_fields) == (
+ set(
+ (
+ "alignment",
+ "alignmentTime",
+ "groupId",
+ "order",
+ "pageSize",
+ "pageToken",
+ "serviceFilter",
+ "timeRange",
+ "timedCountDuration",
+ )
)
+ & set(("projectName",))
+ )
-def test_error_stats_service_base_transport():
- # Instantiate the base transport.
- with mock.patch(
- "google.cloud.errorreporting_v1beta1.services.error_stats_service.transports.ErrorStatsServiceTransport.__init__"
- ) as Transport:
- Transport.return_value = None
- transport = transports.ErrorStatsServiceTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- )
-
- # Every method on the transport should just blindly
- # raise NotImplementedError.
- methods = (
- "list_group_stats",
- "list_events",
- "delete_events",
+def test_list_group_stats_rest_flattened():
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
)
- for method in methods:
- with pytest.raises(NotImplementedError):
- getattr(transport, method)(request=object())
- with pytest.raises(NotImplementedError):
- transport.close()
-
- # Catch all for all remaining methods and properties
- remainder = [
- "kind",
- ]
- for r in remainder:
- with pytest.raises(NotImplementedError):
- getattr(transport, r)()
+ # 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 = error_stats_service.ListGroupStatsResponse()
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"project_name": "projects/sample1"}
-def test_error_stats_service_base_transport_with_credentials_file():
- # Instantiate the base transport with a credentials file
- with mock.patch.object(
- google.auth, "load_credentials_from_file", autospec=True
- ) as load_creds, mock.patch(
- "google.cloud.errorreporting_v1beta1.services.error_stats_service.transports.ErrorStatsServiceTransport._prep_wrapped_messages"
- ) as Transport:
- Transport.return_value = None
- load_creds.return_value = (ga_credentials.AnonymousCredentials(), None)
- transport = transports.ErrorStatsServiceTransport(
- credentials_file="credentials.json",
- quota_project_id="octopus",
- )
- load_creds.assert_called_once_with(
- "credentials.json",
- scopes=None,
- default_scopes=("https://www.googleapis.com/auth/cloud-platform",),
- quota_project_id="octopus",
+ # get truthy value for each flattened field
+ mock_args = dict(
+ project_name="project_name_value",
+ time_range=error_stats_service.QueryTimeRange(
+ period=error_stats_service.QueryTimeRange.Period.PERIOD_1_HOUR
+ ),
)
+ mock_args.update(sample_request)
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ response_value.status_code = 200
+ # Convert return value to protobuf type
+ return_value = error_stats_service.ListGroupStatsResponse.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"}
-def test_error_stats_service_base_transport_with_adc():
- # Test the default credentials are used if credentials and credentials_file are None.
- with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch(
- "google.cloud.errorreporting_v1beta1.services.error_stats_service.transports.ErrorStatsServiceTransport._prep_wrapped_messages"
- ) as Transport:
- Transport.return_value = None
- adc.return_value = (ga_credentials.AnonymousCredentials(), None)
- transport = transports.ErrorStatsServiceTransport()
- adc.assert_called_once()
+ client.list_group_stats(**mock_args)
+
+ # Establish that the underlying call was made with the expected
+ # request object values.
+ assert len(req.mock_calls) == 1
+ _, args, _ = req.mock_calls[0]
+ assert path_template.validate(
+ "%s/v1beta1/{project_name=projects/*}/groupStats" % client.transport._host,
+ args[1],
+ )
+
+
+def test_list_group_stats_rest_flattened_error(transport: str = "rest"):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Attempting to call a method with both a request object and flattened
+ # fields is an error.
+ with pytest.raises(ValueError):
+ client.list_group_stats(
+ error_stats_service.ListGroupStatsRequest(),
+ project_name="project_name_value",
+ time_range=error_stats_service.QueryTimeRange(
+ period=error_stats_service.QueryTimeRange.Period.PERIOD_1_HOUR
+ ),
+ )
+
+
+def test_list_group_stats_rest_pager(transport: str = "rest"):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # TODO(kbandes): remove this mock unless there's a good reason for it.
+ # with mock.patch.object(path_template, 'transcode') as transcode:
+ # Set the response as a series of pages
+ response = (
+ error_stats_service.ListGroupStatsResponse(
+ error_group_stats=[
+ error_stats_service.ErrorGroupStats(),
+ error_stats_service.ErrorGroupStats(),
+ error_stats_service.ErrorGroupStats(),
+ ],
+ next_page_token="abc",
+ ),
+ error_stats_service.ListGroupStatsResponse(
+ error_group_stats=[],
+ next_page_token="def",
+ ),
+ error_stats_service.ListGroupStatsResponse(
+ error_group_stats=[
+ error_stats_service.ErrorGroupStats(),
+ ],
+ next_page_token="ghi",
+ ),
+ error_stats_service.ListGroupStatsResponse(
+ error_group_stats=[
+ error_stats_service.ErrorGroupStats(),
+ error_stats_service.ErrorGroupStats(),
+ ],
+ ),
+ )
+ # Two responses for two calls
+ response = response + response
+
+ # Wrap the values into proper Response objs
+ response = tuple(
+ error_stats_service.ListGroupStatsResponse.to_json(x) for x in response
+ )
+ return_values = tuple(Response() for i in response)
+ for return_val, response_val in zip(return_values, response):
+ return_val._content = response_val.encode("UTF-8")
+ return_val.status_code = 200
+ req.side_effect = return_values
+
+ sample_request = {"project_name": "projects/sample1"}
+
+ pager = client.list_group_stats(request=sample_request)
+
+ results = list(pager)
+ assert len(results) == 6
+ assert all(isinstance(i, error_stats_service.ErrorGroupStats) for i in results)
+
+ pages = list(client.list_group_stats(request=sample_request).pages)
+ for page_, token in zip(pages, ["abc", "def", "ghi", ""]):
+ assert page_.raw_page.next_page_token == token
+
+
+def test_list_events_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 = ErrorStatsServiceClient(
+ 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.list_events 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.list_events] = mock_rpc
+
+ request = {}
+ client.list_events(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.list_events(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+def test_list_events_rest_required_fields(
+ request_type=error_stats_service.ListEventsRequest,
+):
+ transport_class = transports.ErrorStatsServiceRestTransport
+
+ request_init = {}
+ request_init["project_name"] = ""
+ request_init["group_id"] = ""
+ request = request_type(**request_init)
+ pb_request = request_type.pb(request)
+ jsonified_request = json.loads(
+ json_format.MessageToJson(pb_request, use_integers_for_enums=False)
+ )
+
+ # verify fields with default values are dropped
+ assert "groupId" not in jsonified_request
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).list_events._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with default values are now present
+ assert "groupId" in jsonified_request
+ assert jsonified_request["groupId"] == request_init["group_id"]
+
+ jsonified_request["projectName"] = "project_name_value"
+ jsonified_request["groupId"] = "group_id_value"
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).list_events._get_unset_required_fields(jsonified_request)
+ # Check that path parameters and body parameters are not mixing in.
+ assert not set(unset_fields) - set(
+ (
+ "group_id",
+ "page_size",
+ "page_token",
+ "service_filter",
+ "time_range",
+ )
+ )
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with non-default values are left alone
+ assert "projectName" in jsonified_request
+ assert jsonified_request["projectName"] == "project_name_value"
+ assert "groupId" in jsonified_request
+ assert jsonified_request["groupId"] == "group_id_value"
+
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type(**request_init)
+
+ # Designate an appropriate value for the returned response.
+ return_value = error_stats_service.ListEventsResponse()
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # We need to mock transcode() because providing default values
+ # for required fields will fail the real version if the http_options
+ # expect actual values for those fields.
+ with mock.patch.object(path_template, "transcode") as transcode:
+ # A uri without fields and an empty body will force all the
+ # request fields to show up in the query_params.
+ pb_request = request_type.pb(request)
+ transcode_result = {
+ "uri": "v1/sample_method",
+ "method": "get",
+ "query_params": pb_request,
+ }
+ transcode.return_value = transcode_result
+
+ response_value = Response()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = error_stats_service.ListEventsResponse.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_events(request)
+
+ expected_params = [
+ (
+ "groupId",
+ "",
+ ),
+ ("$alt", "json;enum-encoding=int"),
+ ]
+ actual_params = req.call_args.kwargs["params"]
+ assert expected_params == actual_params
+
+
+def test_list_events_rest_unset_required_fields():
+ transport = transports.ErrorStatsServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials
+ )
+
+ unset_fields = transport.list_events._get_unset_required_fields({})
+ assert set(unset_fields) == (
+ set(
+ (
+ "groupId",
+ "pageSize",
+ "pageToken",
+ "serviceFilter",
+ "timeRange",
+ )
+ )
+ & set(
+ (
+ "projectName",
+ "groupId",
+ )
+ )
+ )
+
+
+def test_list_events_rest_flattened():
+ client = ErrorStatsServiceClient(
+ 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 = error_stats_service.ListEventsResponse()
+
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"project_name": "projects/sample1"}
+
+ # get truthy value for each flattened field
+ mock_args = dict(
+ project_name="project_name_value",
+ group_id="group_id_value",
+ )
+ mock_args.update(sample_request)
+
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ response_value.status_code = 200
+ # Convert return value to protobuf type
+ return_value = error_stats_service.ListEventsResponse.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"}
+
+ client.list_events(**mock_args)
+
+ # Establish that the underlying call was made with the expected
+ # request object values.
+ assert len(req.mock_calls) == 1
+ _, args, _ = req.mock_calls[0]
+ assert path_template.validate(
+ "%s/v1beta1/{project_name=projects/*}/events" % client.transport._host,
+ args[1],
+ )
+
+
+def test_list_events_rest_flattened_error(transport: str = "rest"):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Attempting to call a method with both a request object and flattened
+ # fields is an error.
+ with pytest.raises(ValueError):
+ client.list_events(
+ error_stats_service.ListEventsRequest(),
+ project_name="project_name_value",
+ group_id="group_id_value",
+ )
+
+
+def test_list_events_rest_pager(transport: str = "rest"):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # TODO(kbandes): remove this mock unless there's a good reason for it.
+ # with mock.patch.object(path_template, 'transcode') as transcode:
+ # Set the response as a series of pages
+ response = (
+ error_stats_service.ListEventsResponse(
+ error_events=[
+ common.ErrorEvent(),
+ common.ErrorEvent(),
+ common.ErrorEvent(),
+ ],
+ next_page_token="abc",
+ ),
+ error_stats_service.ListEventsResponse(
+ error_events=[],
+ next_page_token="def",
+ ),
+ error_stats_service.ListEventsResponse(
+ error_events=[
+ common.ErrorEvent(),
+ ],
+ next_page_token="ghi",
+ ),
+ error_stats_service.ListEventsResponse(
+ error_events=[
+ common.ErrorEvent(),
+ common.ErrorEvent(),
+ ],
+ ),
+ )
+ # Two responses for two calls
+ response = response + response
+
+ # Wrap the values into proper Response objs
+ response = tuple(
+ error_stats_service.ListEventsResponse.to_json(x) for x in response
+ )
+ return_values = tuple(Response() for i in response)
+ for return_val, response_val in zip(return_values, response):
+ return_val._content = response_val.encode("UTF-8")
+ return_val.status_code = 200
+ req.side_effect = return_values
+
+ sample_request = {"project_name": "projects/sample1"}
+
+ pager = client.list_events(request=sample_request)
+
+ results = list(pager)
+ assert len(results) == 6
+ assert all(isinstance(i, common.ErrorEvent) for i in results)
+
+ pages = list(client.list_events(request=sample_request).pages)
+ for page_, token in zip(pages, ["abc", "def", "ghi", ""]):
+ assert page_.raw_page.next_page_token == token
+
+
+def test_delete_events_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 = ErrorStatsServiceClient(
+ 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.delete_events 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.delete_events] = mock_rpc
+
+ request = {}
+ client.delete_events(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.delete_events(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+def test_delete_events_rest_required_fields(
+ request_type=error_stats_service.DeleteEventsRequest,
+):
+ transport_class = transports.ErrorStatsServiceRestTransport
+
+ request_init = {}
+ request_init["project_name"] = ""
+ request = request_type(**request_init)
+ pb_request = request_type.pb(request)
+ jsonified_request = json.loads(
+ json_format.MessageToJson(pb_request, use_integers_for_enums=False)
+ )
+
+ # verify fields with default values are dropped
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).delete_events._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with default values are now present
+
+ jsonified_request["projectName"] = "project_name_value"
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).delete_events._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with non-default values are left alone
+ assert "projectName" in jsonified_request
+ assert jsonified_request["projectName"] == "project_name_value"
+
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type(**request_init)
+
+ # Designate an appropriate value for the returned response.
+ return_value = error_stats_service.DeleteEventsResponse()
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # We need to mock transcode() because providing default values
+ # for required fields will fail the real version if the http_options
+ # expect actual values for those fields.
+ with mock.patch.object(path_template, "transcode") as transcode:
+ # A uri without fields and an empty body will force all the
+ # request fields to show up in the query_params.
+ pb_request = request_type.pb(request)
+ transcode_result = {
+ "uri": "v1/sample_method",
+ "method": "delete",
+ "query_params": pb_request,
+ }
+ transcode.return_value = transcode_result
+
+ response_value = Response()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = error_stats_service.DeleteEventsResponse.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_events(request)
+
+ expected_params = [("$alt", "json;enum-encoding=int")]
+ actual_params = req.call_args.kwargs["params"]
+ assert expected_params == actual_params
+
+
+def test_delete_events_rest_unset_required_fields():
+ transport = transports.ErrorStatsServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials
+ )
+
+ unset_fields = transport.delete_events._get_unset_required_fields({})
+ assert set(unset_fields) == (set(()) & set(("projectName",)))
+
+
+def test_delete_events_rest_flattened():
+ client = ErrorStatsServiceClient(
+ 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 = error_stats_service.DeleteEventsResponse()
+
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"project_name": "projects/sample1"}
+
+ # get truthy value for each flattened field
+ mock_args = dict(
+ project_name="project_name_value",
+ )
+ mock_args.update(sample_request)
+
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ response_value.status_code = 200
+ # Convert return value to protobuf type
+ return_value = error_stats_service.DeleteEventsResponse.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"}
+
+ client.delete_events(**mock_args)
+
+ # Establish that the underlying call was made with the expected
+ # request object values.
+ assert len(req.mock_calls) == 1
+ _, args, _ = req.mock_calls[0]
+ assert path_template.validate(
+ "%s/v1beta1/{project_name=projects/*}/events" % client.transport._host,
+ args[1],
+ )
+
+
+def test_delete_events_rest_flattened_error(transport: str = "rest"):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Attempting to call a method with both a request object and flattened
+ # fields is an error.
+ with pytest.raises(ValueError):
+ client.delete_events(
+ error_stats_service.DeleteEventsRequest(),
+ project_name="project_name_value",
+ )
+
+
+def test_credentials_transport_error():
+ # It is an error to provide credentials and a transport instance.
+ transport = transports.ErrorStatsServiceGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ with pytest.raises(ValueError):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # It is an error to provide a credentials file and a transport instance.
+ transport = transports.ErrorStatsServiceGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ with pytest.raises(ValueError):
+ client = ErrorStatsServiceClient(
+ client_options={"credentials_file": "credentials.json"},
+ transport=transport,
+ )
+
+ # It is an error to provide an api_key and a transport instance.
+ transport = transports.ErrorStatsServiceGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ options = client_options.ClientOptions()
+ options.api_key = "api_key"
+ with pytest.raises(ValueError):
+ client = ErrorStatsServiceClient(
+ client_options=options,
+ transport=transport,
+ )
+
+ # It is an error to provide an api_key and a credential.
+ options = client_options.ClientOptions()
+ options.api_key = "api_key"
+ with pytest.raises(ValueError):
+ client = ErrorStatsServiceClient(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+
+ # It is an error to provide scopes and a transport instance.
+ transport = transports.ErrorStatsServiceGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ with pytest.raises(ValueError):
+ client = ErrorStatsServiceClient(
+ client_options={"scopes": ["1", "2"]},
+ transport=transport,
+ )
+
+
+def test_transport_instance():
+ # A client may be instantiated with a custom transport instance.
+ transport = transports.ErrorStatsServiceGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ client = ErrorStatsServiceClient(transport=transport)
+ assert client.transport is transport
+
+
+def test_transport_get_channel():
+ # A client may be instantiated with a custom transport instance.
+ transport = transports.ErrorStatsServiceGrpcTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ channel = transport.grpc_channel
+ assert channel
+
+ transport = transports.ErrorStatsServiceGrpcAsyncIOTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ channel = transport.grpc_channel
+ assert channel
+
+
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.ErrorStatsServiceGrpcTransport,
+ transports.ErrorStatsServiceGrpcAsyncIOTransport,
+ transports.ErrorStatsServiceRestTransport,
+ ],
+)
+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 = ErrorStatsServiceClient.get_transport_class("grpc")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "grpc"
+
+
+def test_initialize_client_w_grpc():
+ client = ErrorStatsServiceClient(
+ 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_list_group_stats_empty_call_grpc():
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_group_stats), "__call__") as call:
+ call.return_value = error_stats_service.ListGroupStatsResponse()
+ client.list_group_stats(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.ListGroupStatsRequest()
+
+ 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_events_empty_call_grpc():
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_events), "__call__") as call:
+ call.return_value = error_stats_service.ListEventsResponse()
+ client.list_events(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.ListEventsRequest()
+
+ 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_events_empty_call_grpc():
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_events), "__call__") as call:
+ call.return_value = error_stats_service.DeleteEventsResponse()
+ client.delete_events(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.DeleteEventsRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_grpc_asyncio():
+ transport = ErrorStatsServiceAsyncClient.get_transport_class("grpc_asyncio")(
+ credentials=async_anonymous_credentials()
+ )
+ assert transport.kind == "grpc_asyncio"
+
+
+def test_initialize_client_w_grpc_asyncio():
+ client = ErrorStatsServiceAsyncClient(
+ 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_list_group_stats_empty_call_grpc_asyncio():
+ client = ErrorStatsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_group_stats), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ error_stats_service.ListGroupStatsResponse(
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_group_stats(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.ListGroupStatsRequest()
+
+ 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_events_empty_call_grpc_asyncio():
+ client = ErrorStatsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_events), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ error_stats_service.ListEventsResponse(
+ next_page_token="next_page_token_value",
+ )
+ )
+ await client.list_events(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.ListEventsRequest()
+
+ 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_events_empty_call_grpc_asyncio():
+ client = ErrorStatsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_events), "__call__") as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ error_stats_service.DeleteEventsResponse()
+ )
+ await client.delete_events(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.DeleteEventsRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_rest():
+ transport = ErrorStatsServiceClient.get_transport_class("rest")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "rest"
+
+
+def test_list_group_stats_rest_bad_request(
+ request_type=error_stats_service.ListGroupStatsRequest,
+):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "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_group_stats(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ error_stats_service.ListGroupStatsRequest,
+ dict,
+ ],
+)
+def test_list_group_stats_rest_call_success(request_type):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "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 = error_stats_service.ListGroupStatsResponse(
+ 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 = error_stats_service.ListGroupStatsResponse.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_group_stats(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListGroupStatsPager)
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_group_stats_rest_interceptors(null_interceptor):
+ transport = transports.ErrorStatsServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.ErrorStatsServiceRestInterceptor(),
+ )
+ client = ErrorStatsServiceClient(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.ErrorStatsServiceRestInterceptor, "post_list_group_stats"
+ ) as post, mock.patch.object(
+ transports.ErrorStatsServiceRestInterceptor,
+ "post_list_group_stats_with_metadata",
+ ) as post_with_metadata, mock.patch.object(
+ transports.ErrorStatsServiceRestInterceptor, "pre_list_group_stats"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = error_stats_service.ListGroupStatsRequest.pb(
+ error_stats_service.ListGroupStatsRequest()
+ )
+ 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 = error_stats_service.ListGroupStatsResponse.to_json(
+ error_stats_service.ListGroupStatsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = error_stats_service.ListGroupStatsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = error_stats_service.ListGroupStatsResponse()
+ post_with_metadata.return_value = (
+ error_stats_service.ListGroupStatsResponse(),
+ metadata,
+ )
+
+ client.list_group_stats(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_list_events_rest_bad_request(
+ request_type=error_stats_service.ListEventsRequest,
+):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "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_events(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ error_stats_service.ListEventsRequest,
+ dict,
+ ],
+)
+def test_list_events_rest_call_success(request_type):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "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 = error_stats_service.ListEventsResponse(
+ 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 = error_stats_service.ListEventsResponse.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_events(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, pagers.ListEventsPager)
+ assert response.next_page_token == "next_page_token_value"
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_list_events_rest_interceptors(null_interceptor):
+ transport = transports.ErrorStatsServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.ErrorStatsServiceRestInterceptor(),
+ )
+ client = ErrorStatsServiceClient(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.ErrorStatsServiceRestInterceptor, "post_list_events"
+ ) as post, mock.patch.object(
+ transports.ErrorStatsServiceRestInterceptor, "post_list_events_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.ErrorStatsServiceRestInterceptor, "pre_list_events"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = error_stats_service.ListEventsRequest.pb(
+ error_stats_service.ListEventsRequest()
+ )
+ 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 = error_stats_service.ListEventsResponse.to_json(
+ error_stats_service.ListEventsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = error_stats_service.ListEventsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = error_stats_service.ListEventsResponse()
+ post_with_metadata.return_value = (
+ error_stats_service.ListEventsResponse(),
+ metadata,
+ )
+
+ client.list_events(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_delete_events_rest_bad_request(
+ request_type=error_stats_service.DeleteEventsRequest,
+):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "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.delete_events(request)
+
+
+@pytest.mark.parametrize(
+ "request_type",
+ [
+ error_stats_service.DeleteEventsRequest,
+ dict,
+ ],
+)
+def test_delete_events_rest_call_success(request_type):
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "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 = error_stats_service.DeleteEventsResponse()
+
+ # 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 = error_stats_service.DeleteEventsResponse.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_events(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, error_stats_service.DeleteEventsResponse)
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_delete_events_rest_interceptors(null_interceptor):
+ transport = transports.ErrorStatsServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.ErrorStatsServiceRestInterceptor(),
+ )
+ client = ErrorStatsServiceClient(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.ErrorStatsServiceRestInterceptor, "post_delete_events"
+ ) as post, mock.patch.object(
+ transports.ErrorStatsServiceRestInterceptor, "post_delete_events_with_metadata"
+ ) as post_with_metadata, mock.patch.object(
+ transports.ErrorStatsServiceRestInterceptor, "pre_delete_events"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = error_stats_service.DeleteEventsRequest.pb(
+ error_stats_service.DeleteEventsRequest()
+ )
+ 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 = error_stats_service.DeleteEventsResponse.to_json(
+ error_stats_service.DeleteEventsResponse()
+ )
+ req.return_value.content = return_value
+
+ request = error_stats_service.DeleteEventsRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = error_stats_service.DeleteEventsResponse()
+ post_with_metadata.return_value = (
+ error_stats_service.DeleteEventsResponse(),
+ metadata,
+ )
+
+ client.delete_events(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_initialize_client_w_rest():
+ client = ErrorStatsServiceClient(
+ 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_list_group_stats_empty_call_rest():
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_group_stats), "__call__") as call:
+ client.list_group_stats(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.ListGroupStatsRequest()
+
+ 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_events_empty_call_rest():
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.list_events), "__call__") as call:
+ client.list_events(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.ListEventsRequest()
+
+ 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_events_empty_call_rest():
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(type(client.transport.delete_events), "__call__") as call:
+ client.delete_events(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = error_stats_service.DeleteEventsRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_grpc_default():
+ # A client should use the gRPC transport by default.
+ client = ErrorStatsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ assert isinstance(
+ client.transport,
+ transports.ErrorStatsServiceGrpcTransport,
+ )
+
+
+def test_error_stats_service_base_transport_error():
+ # Passing both a credentials object and credentials_file should raise an error
+ with pytest.raises(core_exceptions.DuplicateCredentialArgs):
+ transport = transports.ErrorStatsServiceTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ credentials_file="credentials.json",
+ )
+
+
+def test_error_stats_service_base_transport():
+ # Instantiate the base transport.
+ with mock.patch(
+ "google.cloud.errorreporting_v1beta1.services.error_stats_service.transports.ErrorStatsServiceTransport.__init__"
+ ) as Transport:
+ Transport.return_value = None
+ transport = transports.ErrorStatsServiceTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+
+ # Every method on the transport should just blindly
+ # raise NotImplementedError.
+ methods = (
+ "list_group_stats",
+ "list_events",
+ "delete_events",
+ )
+ for method in methods:
+ with pytest.raises(NotImplementedError):
+ getattr(transport, method)(request=object())
+
+ with pytest.raises(NotImplementedError):
+ transport.close()
+
+ # Catch all for all remaining methods and properties
+ remainder = [
+ "kind",
+ ]
+ for r in remainder:
+ with pytest.raises(NotImplementedError):
+ getattr(transport, r)()
+
+
+def test_error_stats_service_base_transport_with_credentials_file():
+ # Instantiate the base transport with a credentials file
+ with mock.patch.object(
+ google.auth, "load_credentials_from_file", autospec=True
+ ) as load_creds, mock.patch(
+ "google.cloud.errorreporting_v1beta1.services.error_stats_service.transports.ErrorStatsServiceTransport._prep_wrapped_messages"
+ ) as Transport:
+ Transport.return_value = None
+ load_creds.return_value = (ga_credentials.AnonymousCredentials(), None)
+ transport = transports.ErrorStatsServiceTransport(
+ credentials_file="credentials.json",
+ quota_project_id="octopus",
+ )
+ load_creds.assert_called_once_with(
+ "credentials.json",
+ scopes=None,
+ default_scopes=("https://www.googleapis.com/auth/cloud-platform",),
+ quota_project_id="octopus",
+ )
+
+
+def test_error_stats_service_base_transport_with_adc():
+ # Test the default credentials are used if credentials and credentials_file are None.
+ with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch(
+ "google.cloud.errorreporting_v1beta1.services.error_stats_service.transports.ErrorStatsServiceTransport._prep_wrapped_messages"
+ ) as Transport:
+ Transport.return_value = None
+ adc.return_value = (ga_credentials.AnonymousCredentials(), None)
+ transport = transports.ErrorStatsServiceTransport()
+ adc.assert_called_once()
def test_error_stats_service_auth_adc():
@@ -1996,6 +4134,29 @@ def test_error_stats_service_transport_auth_adc(transport_class):
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.ErrorStatsServiceGrpcTransport,
+ transports.ErrorStatsServiceGrpcAsyncIOTransport,
+ transports.ErrorStatsServiceRestTransport,
+ ],
+)
+def test_error_stats_service_transport_auth_gdch_credentials(transport_class):
+ host = "https://language.com"
+ api_audience_tests = [None, "https://language2.com"]
+ api_audience_expect = [host, "https://language2.com"]
+ for t, e in zip(api_audience_tests, api_audience_expect):
+ with mock.patch.object(google.auth, "default", autospec=True) as adc:
+ gdch_mock = mock.MagicMock()
+ type(gdch_mock).with_gdch_audience = mock.PropertyMock(
+ return_value=gdch_mock
+ )
+ adc.return_value = (gdch_mock, None)
+ transport_class(host=host, api_audience=t)
+ gdch_mock.with_gdch_audience.assert_called_once_with(e)
+
+
@pytest.mark.parametrize(
"transport_class,grpc_helpers",
[
@@ -2078,11 +4239,23 @@ def test_error_stats_service_grpc_transport_client_cert_source_for_mtls(
)
+def test_error_stats_service_http_transport_client_cert_source_for_mtls():
+ cred = ga_credentials.AnonymousCredentials()
+ with mock.patch(
+ "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel"
+ ) as mock_configure_mtls_channel:
+ transports.ErrorStatsServiceRestTransport(
+ credentials=cred, client_cert_source_for_mtls=client_cert_source_callback
+ )
+ mock_configure_mtls_channel.assert_called_once_with(client_cert_source_callback)
+
+
@pytest.mark.parametrize(
"transport_name",
[
"grpc",
"grpc_asyncio",
+ "rest",
],
)
def test_error_stats_service_host_no_port(transport_name):
@@ -2093,7 +4266,11 @@ def test_error_stats_service_host_no_port(transport_name):
),
transport=transport_name,
)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
@pytest.mark.parametrize(
@@ -2101,6 +4278,7 @@ def test_error_stats_service_host_no_port(transport_name):
[
"grpc",
"grpc_asyncio",
+ "rest",
],
)
def test_error_stats_service_host_with_port(transport_name):
@@ -2111,7 +4289,39 @@ def test_error_stats_service_host_with_port(transport_name):
),
transport=transport_name,
)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:8000")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:8000"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com:8000"
+ )
+
+
+@pytest.mark.parametrize(
+ "transport_name",
+ [
+ "rest",
+ ],
+)
+def test_error_stats_service_client_transport_session_collision(transport_name):
+ creds1 = ga_credentials.AnonymousCredentials()
+ creds2 = ga_credentials.AnonymousCredentials()
+ client1 = ErrorStatsServiceClient(
+ credentials=creds1,
+ transport=transport_name,
+ )
+ client2 = ErrorStatsServiceClient(
+ credentials=creds2,
+ transport=transport_name,
+ )
+ session1 = client1.transport.list_group_stats._session
+ session2 = client2.transport.list_group_stats._session
+ assert session1 != session2
+ session1 = client1.transport.list_events._session
+ session2 = client2.transport.list_events._session
+ assert session1 != session2
+ session1 = client1.transport.delete_events._session
+ session2 = client2.transport.delete_events._session
+ assert session1 != session2
def test_error_stats_service_grpc_transport_channel():
@@ -2389,39 +4599,46 @@ def test_client_with_default_client_info():
prep.assert_called_once_with(client_info)
+def test_transport_close_grpc():
+ client = ErrorStatsServiceClient(
+ 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()
+
+
@pytest.mark.asyncio
-async def test_transport_close_async():
+async def test_transport_close_grpc_asyncio():
client = ErrorStatsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
)
with mock.patch.object(
- type(getattr(client.transport, "grpc_channel")), "close"
+ type(getattr(client.transport, "_grpc_channel")), "close"
) as close:
async with client:
close.assert_not_called()
close.assert_called_once()
-def test_transport_close():
- transports = {
- "grpc": "_grpc_channel",
- }
-
- for transport, close_name in transports.items():
- client = ErrorStatsServiceClient(
- 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()
+def test_transport_close_rest():
+ client = ErrorStatsServiceClient(
+ 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():
transports = [
+ "rest",
"grpc",
]
for transport in transports:
@@ -2460,10 +4677,13 @@ def test_api_key_credentials(client_class, transport_class):
patched.assert_called_once_with(
credentials=mock_cred,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
diff --git a/tests/unit/gapic/errorreporting_v1beta1/test_report_errors_service.py b/tests/unit/gapic/errorreporting_v1beta1/test_report_errors_service.py
index 18494673..9ac7c004 100644
--- a/tests/unit/gapic/errorreporting_v1beta1/test_report_errors_service.py
+++ b/tests/unit/gapic/errorreporting_v1beta1/test_report_errors_service.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.
@@ -18,16 +18,31 @@
# try/except added for compatibility with python < 3.8
try:
from unittest import mock
- from unittest.mock import AsyncMock
-except ImportError:
+ from unittest.mock import AsyncMock # pragma: NO COVER
+except ImportError: # pragma: NO COVER
import mock
import grpc
from grpc.experimental import aio
+from collections.abc import Iterable, AsyncIterable
+from google.protobuf import json_format
+import json
import math
import pytest
+from google.api_core import api_core_version
from proto.marshal.rules.dates import DurationRule, TimestampRule
+from proto.marshal.rules import wrappers
+from requests import Response
+from requests import Request, PreparedRequest
+from requests.sessions import Session
+from google.protobuf import json_format
+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
@@ -35,6 +50,7 @@
from google.api_core import grpc_helpers
from google.api_core import grpc_helpers_async
from google.api_core import path_template
+from google.api_core import retry as retries
from google.auth import credentials as ga_credentials
from google.auth.exceptions import MutualTLSChannelError
from google.cloud.errorreporting_v1beta1.services.report_errors_service import (
@@ -53,10 +69,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.
@@ -68,6 +106,17 @@ def modify_default_endpoint(client):
)
+# If default endpoint template is localhost, then default mtls endpoint will be the same.
+# This method modifies the default endpoint template so the client can produce a different
+# mtls endpoint for endpoint testing purposes.
+def modify_default_endpoint_template(client):
+ return (
+ "test.{UNIVERSE_DOMAIN}"
+ if ("localhost" in client._DEFAULT_ENDPOINT_TEMPLATE)
+ else client._DEFAULT_ENDPOINT_TEMPLATE
+ )
+
+
def test__get_default_mtls_endpoint():
api_endpoint = "example.googleapis.com"
api_mtls_endpoint = "example.mtls.googleapis.com"
@@ -98,11 +147,257 @@ def test__get_default_mtls_endpoint():
)
+def test__read_environment_variables():
+ assert ReportErrorsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ assert ReportErrorsServiceClient._read_environment_variables() == (
+ True,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
+ assert ReportErrorsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
+ ):
+ with pytest.raises(ValueError) as excinfo:
+ ReportErrorsServiceClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ assert ReportErrorsServiceClient._read_environment_variables() == (
+ False,
+ "never",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
+ assert ReportErrorsServiceClient._read_environment_variables() == (
+ False,
+ "always",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}):
+ assert ReportErrorsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ None,
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ ReportErrorsServiceClient._read_environment_variables()
+ assert (
+ str(excinfo.value)
+ == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
+ )
+
+ with mock.patch.dict(os.environ, {"GOOGLE_CLOUD_UNIVERSE_DOMAIN": "foo.com"}):
+ assert ReportErrorsServiceClient._read_environment_variables() == (
+ False,
+ "auto",
+ "foo.com",
+ )
+
+
+def test__get_client_cert_source():
+ mock_provided_cert_source = mock.Mock()
+ mock_default_cert_source = mock.Mock()
+
+ assert ReportErrorsServiceClient._get_client_cert_source(None, False) is None
+ assert (
+ ReportErrorsServiceClient._get_client_cert_source(
+ mock_provided_cert_source, False
+ )
+ is None
+ )
+ assert (
+ ReportErrorsServiceClient._get_client_cert_source(
+ mock_provided_cert_source, True
+ )
+ == mock_provided_cert_source
+ )
+
+ with mock.patch(
+ "google.auth.transport.mtls.has_default_client_cert_source", return_value=True
+ ):
+ with mock.patch(
+ "google.auth.transport.mtls.default_client_cert_source",
+ return_value=mock_default_cert_source,
+ ):
+ assert (
+ ReportErrorsServiceClient._get_client_cert_source(None, True)
+ is mock_default_cert_source
+ )
+ assert (
+ ReportErrorsServiceClient._get_client_cert_source(
+ mock_provided_cert_source, "true"
+ )
+ is mock_provided_cert_source
+ )
+
+
+@mock.patch.object(
+ ReportErrorsServiceClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceClient),
+)
+@mock.patch.object(
+ ReportErrorsServiceAsyncClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceAsyncClient),
+)
+def test__get_api_endpoint():
+ api_override = "foo.com"
+ mock_client_cert_source = mock.Mock()
+ default_universe = ReportErrorsServiceClient._DEFAULT_UNIVERSE
+ default_endpoint = ReportErrorsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=default_universe
+ )
+ mock_universe = "bar.com"
+ mock_endpoint = ReportErrorsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=mock_universe
+ )
+
+ assert (
+ ReportErrorsServiceClient._get_api_endpoint(
+ api_override, mock_client_cert_source, default_universe, "always"
+ )
+ == api_override
+ )
+ assert (
+ ReportErrorsServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, default_universe, "auto"
+ )
+ == ReportErrorsServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ReportErrorsServiceClient._get_api_endpoint(
+ None, None, default_universe, "auto"
+ )
+ == default_endpoint
+ )
+ assert (
+ ReportErrorsServiceClient._get_api_endpoint(
+ None, None, default_universe, "always"
+ )
+ == ReportErrorsServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ReportErrorsServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, default_universe, "always"
+ )
+ == ReportErrorsServiceClient.DEFAULT_MTLS_ENDPOINT
+ )
+ assert (
+ ReportErrorsServiceClient._get_api_endpoint(None, None, mock_universe, "never")
+ == mock_endpoint
+ )
+ assert (
+ ReportErrorsServiceClient._get_api_endpoint(
+ None, None, default_universe, "never"
+ )
+ == default_endpoint
+ )
+
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ ReportErrorsServiceClient._get_api_endpoint(
+ None, mock_client_cert_source, mock_universe, "auto"
+ )
+ assert (
+ str(excinfo.value)
+ == "mTLS is not supported in any universe other than googleapis.com."
+ )
+
+
+def test__get_universe_domain():
+ client_universe_domain = "foo.com"
+ universe_domain_env = "bar.com"
+
+ assert (
+ ReportErrorsServiceClient._get_universe_domain(
+ client_universe_domain, universe_domain_env
+ )
+ == client_universe_domain
+ )
+ assert (
+ ReportErrorsServiceClient._get_universe_domain(None, universe_domain_env)
+ == universe_domain_env
+ )
+ assert (
+ ReportErrorsServiceClient._get_universe_domain(None, None)
+ == ReportErrorsServiceClient._DEFAULT_UNIVERSE
+ )
+
+ with pytest.raises(ValueError) as excinfo:
+ ReportErrorsServiceClient._get_universe_domain("", None)
+ assert str(excinfo.value) == "Universe Domain cannot be an empty string."
+
+
+@pytest.mark.parametrize(
+ "error_code,cred_info_json,show_cred_info",
+ [
+ (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__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 = ReportErrorsServiceClient(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"]
+
+
+@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 = ReportErrorsServiceClient(credentials=cred)
+ client._transport._credentials = cred
+
+ error = core_exceptions.GoogleAPICallError("message", details=[])
+ error.code = error_code
+
+ client._add_cred_info_for_auth_errors(error)
+ assert error.details == []
+
+
@pytest.mark.parametrize(
"client_class,transport_name",
[
(ReportErrorsServiceClient, "grpc"),
(ReportErrorsServiceAsyncClient, "grpc_asyncio"),
+ (ReportErrorsServiceClient, "rest"),
],
)
def test_report_errors_service_client_from_service_account_info(
@@ -118,7 +413,11 @@ def test_report_errors_service_client_from_service_account_info(
assert client.transport._credentials == creds
assert isinstance(client, client_class)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
@pytest.mark.parametrize(
@@ -126,6 +425,7 @@ def test_report_errors_service_client_from_service_account_info(
[
(transports.ReportErrorsServiceGrpcTransport, "grpc"),
(transports.ReportErrorsServiceGrpcAsyncIOTransport, "grpc_asyncio"),
+ (transports.ReportErrorsServiceRestTransport, "rest"),
],
)
def test_report_errors_service_client_service_account_always_use_jwt(
@@ -151,6 +451,7 @@ def test_report_errors_service_client_service_account_always_use_jwt(
[
(ReportErrorsServiceClient, "grpc"),
(ReportErrorsServiceAsyncClient, "grpc_asyncio"),
+ (ReportErrorsServiceClient, "rest"),
],
)
def test_report_errors_service_client_from_service_account_file(
@@ -173,13 +474,18 @@ def test_report_errors_service_client_from_service_account_file(
assert client.transport._credentials == creds
assert isinstance(client, client_class)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
def test_report_errors_service_client_get_transport_class():
transport = ReportErrorsServiceClient.get_transport_class()
available_transports = [
transports.ReportErrorsServiceGrpcTransport,
+ transports.ReportErrorsServiceRestTransport,
]
assert transport in available_transports
@@ -200,17 +506,22 @@ def test_report_errors_service_client_get_transport_class():
transports.ReportErrorsServiceGrpcAsyncIOTransport,
"grpc_asyncio",
),
+ (
+ ReportErrorsServiceClient,
+ transports.ReportErrorsServiceRestTransport,
+ "rest",
+ ),
],
)
@mock.patch.object(
ReportErrorsServiceClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ReportErrorsServiceClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceClient),
)
@mock.patch.object(
ReportErrorsServiceAsyncClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ReportErrorsServiceAsyncClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceAsyncClient),
)
def test_report_errors_service_client_client_options(
client_class, transport_class, transport_name
@@ -240,6 +551,7 @@ def test_report_errors_service_client_client_options(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
@@ -251,12 +563,15 @@ def test_report_errors_service_client_client_options(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
@@ -274,20 +589,29 @@ def test_report_errors_service_client_client_options(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has
# unsupported value.
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
- with pytest.raises(MutualTLSChannelError):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
client = client_class(transport=transport_name)
+ assert (
+ str(excinfo.value)
+ == "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):
+ 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")
@@ -297,12 +621,35 @@ def test_report_errors_service_client_client_options(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id="octopus",
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
+ )
+ # Check the case api_endpoint is provided
+ options = client_options.ClientOptions(
+ api_audience="https://language.googleapis.com"
+ )
+ with mock.patch.object(transport_class, "__init__") as patched:
+ patched.return_value = None
+ client = client_class(client_options=options, transport=transport_name)
+ patched.assert_called_once_with(
+ credentials=None,
+ credentials_file=None,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
+ scopes=None,
+ client_cert_source_for_mtls=None,
+ quota_project_id=None,
+ client_info=transports.base.DEFAULT_CLIENT_INFO,
+ always_use_jwt_access=True,
+ api_audience="https://language.googleapis.com",
)
@@ -333,17 +680,29 @@ def test_report_errors_service_client_client_options(
"grpc_asyncio",
"false",
),
+ (
+ ReportErrorsServiceClient,
+ transports.ReportErrorsServiceRestTransport,
+ "rest",
+ "true",
+ ),
+ (
+ ReportErrorsServiceClient,
+ transports.ReportErrorsServiceRestTransport,
+ "rest",
+ "false",
+ ),
],
)
@mock.patch.object(
ReportErrorsServiceClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ReportErrorsServiceClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceClient),
)
@mock.patch.object(
ReportErrorsServiceAsyncClient,
- "DEFAULT_ENDPOINT",
- modify_default_endpoint(ReportErrorsServiceAsyncClient),
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceAsyncClient),
)
@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"})
def test_report_errors_service_client_mtls_env_auto(
@@ -366,7 +725,9 @@ def test_report_errors_service_client_mtls_env_auto(
if use_client_cert_env == "false":
expected_client_cert_source = None
- expected_host = client.DEFAULT_ENDPOINT
+ expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ )
else:
expected_client_cert_source = client_cert_source_callback
expected_host = client.DEFAULT_MTLS_ENDPOINT
@@ -380,6 +741,7 @@ def test_report_errors_service_client_mtls_env_auto(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case ADC client cert is provided. Whether client cert is used depends on
@@ -397,7 +759,9 @@ def test_report_errors_service_client_mtls_env_auto(
return_value=client_cert_source_callback,
):
if use_client_cert_env == "false":
- expected_host = client.DEFAULT_ENDPOINT
+ expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ )
expected_client_cert_source = None
else:
expected_host = client.DEFAULT_MTLS_ENDPOINT
@@ -414,6 +778,7 @@ def test_report_errors_service_client_mtls_env_auto(
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# Check the case client_cert_source and ADC client cert are not provided.
@@ -430,12 +795,15 @@ def test_report_errors_service_client_mtls_env_auto(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -519,6 +887,115 @@ def test_report_errors_service_client_get_mtls_endpoint_and_cert_source(client_c
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
assert cert_source == mock_client_cert_source
+ # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has
+ # unsupported value.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
+ with pytest.raises(MutualTLSChannelError) as excinfo:
+ client_class.get_mtls_endpoint_and_cert_source()
+
+ assert (
+ str(excinfo.value)
+ == "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", [ReportErrorsServiceClient, ReportErrorsServiceAsyncClient]
+)
+@mock.patch.object(
+ ReportErrorsServiceClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceClient),
+)
+@mock.patch.object(
+ ReportErrorsServiceAsyncClient,
+ "_DEFAULT_ENDPOINT_TEMPLATE",
+ modify_default_endpoint_template(ReportErrorsServiceAsyncClient),
+)
+def test_report_errors_service_client_client_api_endpoint(client_class):
+ mock_client_cert_source = client_cert_source_callback
+ api_override = "foo.com"
+ default_universe = ReportErrorsServiceClient._DEFAULT_UNIVERSE
+ default_endpoint = ReportErrorsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=default_universe
+ )
+ mock_universe = "bar.com"
+ mock_endpoint = ReportErrorsServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=mock_universe
+ )
+
+ # If ClientOptions.api_endpoint is set and GOOGLE_API_USE_CLIENT_CERTIFICATE="true",
+ # use ClientOptions.api_endpoint as the api endpoint regardless.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
+ with mock.patch(
+ "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel"
+ ):
+ options = client_options.ClientOptions(
+ client_cert_source=mock_client_cert_source, api_endpoint=api_override
+ )
+ client = client_class(
+ client_options=options,
+ credentials=ga_credentials.AnonymousCredentials(),
+ )
+ assert client.api_endpoint == api_override
+
+ # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="never",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ client = client_class(credentials=ga_credentials.AnonymousCredentials())
+ assert client.api_endpoint == default_endpoint
+
+ # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="always",
+ # use the DEFAULT_MTLS_ENDPOINT as the api endpoint.
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
+ client = client_class(credentials=ga_credentials.AnonymousCredentials())
+ assert client.api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
+
+ # If ClientOptions.api_endpoint is not set, GOOGLE_API_USE_MTLS_ENDPOINT="auto" (default),
+ # GOOGLE_API_USE_CLIENT_CERTIFICATE="false" (default), default cert source doesn't exist,
+ # and ClientOptions.universe_domain="bar.com",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with universe domain as the api endpoint.
+ options = client_options.ClientOptions()
+ universe_exists = hasattr(options, "universe_domain")
+ if universe_exists:
+ options = client_options.ClientOptions(universe_domain=mock_universe)
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ else:
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert client.api_endpoint == (
+ mock_endpoint if universe_exists else default_endpoint
+ )
+ assert client.universe_domain == (
+ mock_universe if universe_exists else default_universe
+ )
+
+ # If ClientOptions does not have a universe domain attribute and GOOGLE_API_USE_MTLS_ENDPOINT="never",
+ # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint.
+ options = client_options.ClientOptions()
+ if hasattr(options, "universe_domain"):
+ delattr(options, "universe_domain")
+ with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
+ client = client_class(
+ client_options=options, credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert client.api_endpoint == default_endpoint
+
@pytest.mark.parametrize(
"client_class,transport_class,transport_name",
@@ -533,6 +1010,11 @@ def test_report_errors_service_client_get_mtls_endpoint_and_cert_source(client_c
transports.ReportErrorsServiceGrpcAsyncIOTransport,
"grpc_asyncio",
),
+ (
+ ReportErrorsServiceClient,
+ transports.ReportErrorsServiceRestTransport,
+ "rest",
+ ),
],
)
def test_report_errors_service_client_client_options_scopes(
@@ -548,12 +1030,15 @@ def test_report_errors_service_client_client_options_scopes(
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=["1", "2"],
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -572,6 +1057,12 @@ def test_report_errors_service_client_client_options_scopes(
"grpc_asyncio",
grpc_helpers_async,
),
+ (
+ ReportErrorsServiceClient,
+ transports.ReportErrorsServiceRestTransport,
+ "rest",
+ None,
+ ),
],
)
def test_report_errors_service_client_client_options_credentials_file(
@@ -586,12 +1077,15 @@ def test_report_errors_service_client_client_options_credentials_file(
patched.assert_called_once_with(
credentials=None,
credentials_file="credentials.json",
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -612,6 +1106,7 @@ def test_report_errors_service_client_client_options_from_dict():
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
@@ -644,12 +1139,15 @@ def test_report_errors_service_client_create_channel_credentials_file(
patched.assert_called_once_with(
credentials=None,
credentials_file="credentials.json",
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
# test that the credentials from file are saved and used as the credentials.
@@ -709,28 +1207,122 @@ def test_report_error_event(request_type, transport: str = "grpc"):
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls) == 1
_, args, _ = call.mock_calls[0]
- assert args[0] == report_errors_service.ReportErrorEventRequest()
+ request = report_errors_service.ReportErrorEventRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, report_errors_service.ReportErrorEventResponse)
-def test_report_error_event_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_report_error_event_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 = ReportErrorsServiceClient(
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 = report_errors_service.ReportErrorEventRequest(
+ project_name="project_name_value",
+ )
+
# Mock the actual call within the gRPC stub, and fake the request.
with mock.patch.object(
type(client.transport.report_error_event), "__call__"
) as call:
- client.report_error_event()
+ call.return_value.name = (
+ "foo" # operation_request.operation in compute client(s) expect a string.
+ )
+ client.report_error_event(request=request)
call.assert_called()
_, args, _ = call.mock_calls[0]
- assert args[0] == report_errors_service.ReportErrorEventRequest()
+ assert args[0] == report_errors_service.ReportErrorEventRequest(
+ project_name="project_name_value",
+ )
+
+
+def test_report_error_event_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 = ReportErrorsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # 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.report_error_event 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.report_error_event
+ ] = mock_rpc
+ request = {}
+ client.report_error_event(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.report_error_event(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_report_error_event_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 = ReportErrorsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport=transport,
+ )
+
+ # Should wrap all calls on client creation
+ assert wrapper_fn.call_count > 0
+ wrapper_fn.reset_mock()
+
+ # Ensure method has been cached
+ assert (
+ client._client._transport.report_error_event
+ in client._client._transport._wrapped_methods
+ )
+
+ # Replace cached wrapped function with mock
+ mock_rpc = mock.AsyncMock()
+ mock_rpc.return_value = mock.Mock()
+ client._client._transport._wrapped_methods[
+ client._client._transport.report_error_event
+ ] = mock_rpc
+
+ request = {}
+ await client.report_error_event(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ await client.report_error_event(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
@pytest.mark.asyncio
@@ -739,7 +1331,7 @@ async def test_report_error_event_async(
request_type=report_errors_service.ReportErrorEventRequest,
):
client = ReportErrorsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
transport=transport,
)
@@ -760,7 +1352,8 @@ async def test_report_error_event_async(
# Establish that the underlying gRPC stub method was called.
assert len(call.mock_calls)
_, args, _ = call.mock_calls[0]
- assert args[0] == report_errors_service.ReportErrorEventRequest()
+ request = report_errors_service.ReportErrorEventRequest()
+ assert args[0] == request
# Establish that the response is the type that we expect.
assert isinstance(response, report_errors_service.ReportErrorEventResponse)
@@ -805,7 +1398,7 @@ def test_report_error_event_field_headers():
@pytest.mark.asyncio
async def test_report_error_event_field_headers_async():
client = ReportErrorsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Any value that is part of the HTTP/1.1 URI should be sent as
@@ -890,7 +1483,7 @@ def test_report_error_event_flattened_error():
@pytest.mark.asyncio
async def test_report_error_event_flattened_async():
client = ReportErrorsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Mock the actual call within the gRPC stub, and fake the request.
@@ -929,7 +1522,7 @@ async def test_report_error_event_flattened_async():
@pytest.mark.asyncio
async def test_report_error_event_flattened_error_async():
client = ReportErrorsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
+ credentials=async_anonymous_credentials(),
)
# Attempting to call a method with both a request object and flattened
@@ -944,6 +1537,205 @@ async def test_report_error_event_flattened_error_async():
)
+def test_report_error_event_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 = ReportErrorsServiceClient(
+ 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.report_error_event 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.report_error_event
+ ] = mock_rpc
+
+ request = {}
+ client.report_error_event(request)
+
+ # Establish that the underlying gRPC stub method was called.
+ assert mock_rpc.call_count == 1
+
+ client.report_error_event(request)
+
+ # Establish that a new wrapper was not created for this call
+ assert wrapper_fn.call_count == 0
+ assert mock_rpc.call_count == 2
+
+
+def test_report_error_event_rest_required_fields(
+ request_type=report_errors_service.ReportErrorEventRequest,
+):
+ transport_class = transports.ReportErrorsServiceRestTransport
+
+ request_init = {}
+ request_init["project_name"] = ""
+ request = request_type(**request_init)
+ pb_request = request_type.pb(request)
+ jsonified_request = json.loads(
+ json_format.MessageToJson(pb_request, use_integers_for_enums=False)
+ )
+
+ # verify fields with default values are dropped
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).report_error_event._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with default values are now present
+
+ jsonified_request["projectName"] = "project_name_value"
+
+ unset_fields = transport_class(
+ credentials=ga_credentials.AnonymousCredentials()
+ ).report_error_event._get_unset_required_fields(jsonified_request)
+ jsonified_request.update(unset_fields)
+
+ # verify required fields with non-default values are left alone
+ assert "projectName" in jsonified_request
+ assert jsonified_request["projectName"] == "project_name_value"
+
+ client = ReportErrorsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+ request = request_type(**request_init)
+
+ # Designate an appropriate value for the returned response.
+ return_value = report_errors_service.ReportErrorEventResponse()
+ # Mock the http request call within the method and fake a response.
+ with mock.patch.object(Session, "request") as req:
+ # We need to mock transcode() because providing default values
+ # for required fields will fail the real version if the http_options
+ # expect actual values for those fields.
+ with mock.patch.object(path_template, "transcode") as transcode:
+ # A uri without fields and an empty body will force all the
+ # request fields to show up in the query_params.
+ pb_request = request_type.pb(request)
+ transcode_result = {
+ "uri": "v1/sample_method",
+ "method": "post",
+ "query_params": pb_request,
+ }
+ transcode_result["body"] = pb_request
+ transcode.return_value = transcode_result
+
+ response_value = Response()
+ response_value.status_code = 200
+
+ # Convert return value to protobuf type
+ return_value = report_errors_service.ReportErrorEventResponse.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.report_error_event(request)
+
+ expected_params = [("$alt", "json;enum-encoding=int")]
+ actual_params = req.call_args.kwargs["params"]
+ assert expected_params == actual_params
+
+
+def test_report_error_event_rest_unset_required_fields():
+ transport = transports.ReportErrorsServiceRestTransport(
+ credentials=ga_credentials.AnonymousCredentials
+ )
+
+ unset_fields = transport.report_error_event._get_unset_required_fields({})
+ assert set(unset_fields) == (
+ set(())
+ & set(
+ (
+ "projectName",
+ "event",
+ )
+ )
+ )
+
+
+def test_report_error_event_rest_flattened():
+ client = ReportErrorsServiceClient(
+ 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 = report_errors_service.ReportErrorEventResponse()
+
+ # get arguments that satisfy an http rule for this method
+ sample_request = {"project_name": "projects/sample1"}
+
+ # get truthy value for each flattened field
+ mock_args = dict(
+ project_name="project_name_value",
+ event=report_errors_service.ReportedErrorEvent(
+ event_time=timestamp_pb2.Timestamp(seconds=751)
+ ),
+ )
+ mock_args.update(sample_request)
+
+ # Wrap the value into a proper Response obj
+ response_value = Response()
+ response_value.status_code = 200
+ # Convert return value to protobuf type
+ return_value = report_errors_service.ReportErrorEventResponse.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"}
+
+ client.report_error_event(**mock_args)
+
+ # Establish that the underlying call was made with the expected
+ # request object values.
+ assert len(req.mock_calls) == 1
+ _, args, _ = req.mock_calls[0]
+ assert path_template.validate(
+ "%s/v1beta1/{project_name=projects/*}/events:report"
+ % client.transport._host,
+ args[1],
+ )
+
+
+def test_report_error_event_rest_flattened_error(transport: str = "rest"):
+ client = ReportErrorsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport,
+ )
+
+ # Attempting to call a method with both a request object and flattened
+ # fields is an error.
+ with pytest.raises(ValueError):
+ client.report_error_event(
+ report_errors_service.ReportErrorEventRequest(),
+ project_name="project_name_value",
+ event=report_errors_service.ReportedErrorEvent(
+ event_time=timestamp_pb2.Timestamp(seconds=751)
+ ),
+ )
+
+
def test_credentials_transport_error():
# It is an error to provide credentials and a transport instance.
transport = transports.ReportErrorsServiceGrpcTransport(
@@ -978,7 +1770,7 @@ def test_credentials_transport_error():
)
# It is an error to provide an api_key and a credential.
- options = mock.Mock()
+ options = client_options.ClientOptions()
options.api_key = "api_key"
with pytest.raises(ValueError):
client = ReportErrorsServiceClient(
@@ -1025,6 +1817,7 @@ def test_transport_get_channel():
[
transports.ReportErrorsServiceGrpcTransport,
transports.ReportErrorsServiceGrpcAsyncIOTransport,
+ transports.ReportErrorsServiceRestTransport,
],
)
def test_transport_adc(transport_class):
@@ -1035,17 +1828,340 @@ def test_transport_adc(transport_class):
adc.assert_called_once()
+def test_transport_kind_grpc():
+ transport = ReportErrorsServiceClient.get_transport_class("grpc")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "grpc"
+
+
+def test_initialize_client_w_grpc():
+ client = ReportErrorsServiceClient(
+ 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_report_error_event_empty_call_grpc():
+ client = ReportErrorsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="grpc",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.report_error_event), "__call__"
+ ) as call:
+ call.return_value = report_errors_service.ReportErrorEventResponse()
+ client.report_error_event(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = report_errors_service.ReportErrorEventRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_grpc_asyncio():
+ transport = ReportErrorsServiceAsyncClient.get_transport_class("grpc_asyncio")(
+ credentials=async_anonymous_credentials()
+ )
+ assert transport.kind == "grpc_asyncio"
+
+
+def test_initialize_client_w_grpc_asyncio():
+ client = ReportErrorsServiceAsyncClient(
+ 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_report_error_event_empty_call_grpc_asyncio():
+ client = ReportErrorsServiceAsyncClient(
+ credentials=async_anonymous_credentials(),
+ transport="grpc_asyncio",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.report_error_event), "__call__"
+ ) as call:
+ # Designate an appropriate return value for the call.
+ call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(
+ report_errors_service.ReportErrorEventResponse()
+ )
+ await client.report_error_event(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = report_errors_service.ReportErrorEventRequest()
+
+ assert args[0] == request_msg
+
+
+def test_transport_kind_rest():
+ transport = ReportErrorsServiceClient.get_transport_class("rest")(
+ credentials=ga_credentials.AnonymousCredentials()
+ )
+ assert transport.kind == "rest"
+
+
+def test_report_error_event_rest_bad_request(
+ request_type=report_errors_service.ReportErrorEventRequest,
+):
+ client = ReportErrorsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "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.report_error_event(request)
+
+
@pytest.mark.parametrize(
- "transport_name",
+ "request_type",
[
- "grpc",
+ report_errors_service.ReportErrorEventRequest,
+ dict,
],
)
-def test_transport_kind(transport_name):
- transport = ReportErrorsServiceClient.get_transport_class(transport_name)(
+def test_report_error_event_rest_call_success(request_type):
+ client = ReportErrorsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(), transport="rest"
+ )
+
+ # send a request that will satisfy transcoding
+ request_init = {"project_name": "projects/sample1"}
+ request_init["event"] = {
+ "event_time": {"seconds": 751, "nanos": 543},
+ "service_context": {
+ "service": "service_value",
+ "version": "version_value",
+ "resource_type": "resource_type_value",
+ },
+ "message": "message_value",
+ "context": {
+ "http_request": {
+ "method": "method_value",
+ "url": "url_value",
+ "user_agent": "user_agent_value",
+ "referrer": "referrer_value",
+ "response_status_code": 2156,
+ "remote_ip": "remote_ip_value",
+ },
+ "user": "user_value",
+ "report_location": {
+ "file_path": "file_path_value",
+ "line_number": 1168,
+ "function_name": "function_name_value",
+ },
+ },
+ }
+ # 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 = report_errors_service.ReportErrorEventRequest.meta.fields["event"]
+
+ 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["event"].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["event"][field])):
+ del request_init["event"][field][i][subfield]
+ else:
+ del request_init["event"][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 = report_errors_service.ReportErrorEventResponse()
+
+ # 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 = report_errors_service.ReportErrorEventResponse.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.report_error_event(request)
+
+ # Establish that the response is the type that we expect.
+ assert isinstance(response, report_errors_service.ReportErrorEventResponse)
+
+
+@pytest.mark.parametrize("null_interceptor", [True, False])
+def test_report_error_event_rest_interceptors(null_interceptor):
+ transport = transports.ReportErrorsServiceRestTransport(
credentials=ga_credentials.AnonymousCredentials(),
+ interceptor=None
+ if null_interceptor
+ else transports.ReportErrorsServiceRestInterceptor(),
)
- assert transport.kind == transport_name
+ client = ReportErrorsServiceClient(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.ReportErrorsServiceRestInterceptor, "post_report_error_event"
+ ) as post, mock.patch.object(
+ transports.ReportErrorsServiceRestInterceptor,
+ "post_report_error_event_with_metadata",
+ ) as post_with_metadata, mock.patch.object(
+ transports.ReportErrorsServiceRestInterceptor, "pre_report_error_event"
+ ) as pre:
+ pre.assert_not_called()
+ post.assert_not_called()
+ post_with_metadata.assert_not_called()
+ pb_message = report_errors_service.ReportErrorEventRequest.pb(
+ report_errors_service.ReportErrorEventRequest()
+ )
+ 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 = report_errors_service.ReportErrorEventResponse.to_json(
+ report_errors_service.ReportErrorEventResponse()
+ )
+ req.return_value.content = return_value
+
+ request = report_errors_service.ReportErrorEventRequest()
+ metadata = [
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ]
+ pre.return_value = request, metadata
+ post.return_value = report_errors_service.ReportErrorEventResponse()
+ post_with_metadata.return_value = (
+ report_errors_service.ReportErrorEventResponse(),
+ metadata,
+ )
+
+ client.report_error_event(
+ request,
+ metadata=[
+ ("key", "val"),
+ ("cephalopod", "squid"),
+ ],
+ )
+
+ pre.assert_called_once()
+ post.assert_called_once()
+ post_with_metadata.assert_called_once()
+
+
+def test_initialize_client_w_rest():
+ client = ReportErrorsServiceClient(
+ 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_report_error_event_empty_call_rest():
+ client = ReportErrorsServiceClient(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport="rest",
+ )
+
+ # Mock the actual call, and fake the request.
+ with mock.patch.object(
+ type(client.transport.report_error_event), "__call__"
+ ) as call:
+ client.report_error_event(request=None)
+
+ # Establish that the underlying stub method was called.
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+ request_msg = report_errors_service.ReportErrorEventRequest()
+
+ assert args[0] == request_msg
def test_transport_grpc_default():
@@ -1161,6 +2277,29 @@ def test_report_errors_service_transport_auth_adc(transport_class):
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [
+ transports.ReportErrorsServiceGrpcTransport,
+ transports.ReportErrorsServiceGrpcAsyncIOTransport,
+ transports.ReportErrorsServiceRestTransport,
+ ],
+)
+def test_report_errors_service_transport_auth_gdch_credentials(transport_class):
+ host = "https://language.com"
+ api_audience_tests = [None, "https://language2.com"]
+ api_audience_expect = [host, "https://language2.com"]
+ for t, e in zip(api_audience_tests, api_audience_expect):
+ with mock.patch.object(google.auth, "default", autospec=True) as adc:
+ gdch_mock = mock.MagicMock()
+ type(gdch_mock).with_gdch_audience = mock.PropertyMock(
+ return_value=gdch_mock
+ )
+ adc.return_value = (gdch_mock, None)
+ transport_class(host=host, api_audience=t)
+ gdch_mock.with_gdch_audience.assert_called_once_with(e)
+
+
@pytest.mark.parametrize(
"transport_class,grpc_helpers",
[
@@ -1243,11 +2382,23 @@ def test_report_errors_service_grpc_transport_client_cert_source_for_mtls(
)
+def test_report_errors_service_http_transport_client_cert_source_for_mtls():
+ cred = ga_credentials.AnonymousCredentials()
+ with mock.patch(
+ "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel"
+ ) as mock_configure_mtls_channel:
+ transports.ReportErrorsServiceRestTransport(
+ credentials=cred, client_cert_source_for_mtls=client_cert_source_callback
+ )
+ mock_configure_mtls_channel.assert_called_once_with(client_cert_source_callback)
+
+
@pytest.mark.parametrize(
"transport_name",
[
"grpc",
"grpc_asyncio",
+ "rest",
],
)
def test_report_errors_service_host_no_port(transport_name):
@@ -1258,7 +2409,11 @@ def test_report_errors_service_host_no_port(transport_name):
),
transport=transport_name,
)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:443")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:443"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com"
+ )
@pytest.mark.parametrize(
@@ -1266,6 +2421,7 @@ def test_report_errors_service_host_no_port(transport_name):
[
"grpc",
"grpc_asyncio",
+ "rest",
],
)
def test_report_errors_service_host_with_port(transport_name):
@@ -1276,7 +2432,33 @@ def test_report_errors_service_host_with_port(transport_name):
),
transport=transport_name,
)
- assert client.transport._host == ("clouderrorreporting.googleapis.com:8000")
+ assert client.transport._host == (
+ "clouderrorreporting.googleapis.com:8000"
+ if transport_name in ["grpc", "grpc_asyncio"]
+ else "https://clouderrorreporting.googleapis.com:8000"
+ )
+
+
+@pytest.mark.parametrize(
+ "transport_name",
+ [
+ "rest",
+ ],
+)
+def test_report_errors_service_client_transport_session_collision(transport_name):
+ creds1 = ga_credentials.AnonymousCredentials()
+ creds2 = ga_credentials.AnonymousCredentials()
+ client1 = ReportErrorsServiceClient(
+ credentials=creds1,
+ transport=transport_name,
+ )
+ client2 = ReportErrorsServiceClient(
+ credentials=creds2,
+ transport=transport_name,
+ )
+ session1 = client1.transport.report_error_event._session
+ session2 = client2.transport.report_error_event._session
+ assert session1 != session2
def test_report_errors_service_grpc_transport_channel():
@@ -1531,39 +2713,46 @@ def test_client_with_default_client_info():
prep.assert_called_once_with(client_info)
+def test_transport_close_grpc():
+ client = ReportErrorsServiceClient(
+ 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()
+
+
@pytest.mark.asyncio
-async def test_transport_close_async():
+async def test_transport_close_grpc_asyncio():
client = ReportErrorsServiceAsyncClient(
- credentials=ga_credentials.AnonymousCredentials(),
- transport="grpc_asyncio",
+ credentials=async_anonymous_credentials(), transport="grpc_asyncio"
)
with mock.patch.object(
- type(getattr(client.transport, "grpc_channel")), "close"
+ type(getattr(client.transport, "_grpc_channel")), "close"
) as close:
async with client:
close.assert_not_called()
close.assert_called_once()
-def test_transport_close():
- transports = {
- "grpc": "_grpc_channel",
- }
-
- for transport, close_name in transports.items():
- client = ReportErrorsServiceClient(
- 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()
+def test_transport_close_rest():
+ client = ReportErrorsServiceClient(
+ 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():
transports = [
+ "rest",
"grpc",
]
for transport in transports:
@@ -1602,10 +2791,13 @@ def test_api_key_credentials(client_class, transport_class):
patched.assert_called_once_with(
credentials=mock_cred,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=client._DEFAULT_ENDPOINT_TEMPLATE.format(
+ UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE
+ ),
scopes=None,
client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
always_use_jwt_access=True,
+ api_audience=None,
)
diff --git a/tests/unit/test__gapic.py b/tests/unit/test__gapic.py
index 365eee27..52e525e3 100644
--- a/tests/unit/test__gapic.py
+++ b/tests/unit/test__gapic.py
@@ -49,7 +49,6 @@ def test_make_report_error_api(self):
class Test_ErrorReportingGapicApi(unittest.TestCase):
-
PROJECT = "PROJECT"
def _make_one(self, gapic_api, project):
diff --git a/tests/unit/test__logging.py b/tests/unit/test__logging.py
index c5b1cc32..433c9192 100644
--- a/tests/unit/test__logging.py
+++ b/tests/unit/test__logging.py
@@ -24,7 +24,6 @@ def _make_credentials():
class Test_ErrorReportingLoggingAPI(unittest.TestCase):
-
PROJECT = "PROJECT"
def _make_one(self, project, credentials, **kw):
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
index 3a7290e8..e53b1545 100644
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -25,7 +25,6 @@ def _make_credentials():
class TestClient(unittest.TestCase):
-
PROJECT = "PROJECT"
SERVICE = "SERVICE"
VERSION = "myversion"
diff --git a/tests/unit/test_packaging.py b/tests/unit/test_packaging.py
new file mode 100644
index 00000000..01ab6a79
--- /dev/null
+++ b/tests/unit/test_packaging.py
@@ -0,0 +1,57 @@
+# Copyright 2023 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 os
+import subprocess
+import sys
+
+
+def test_namespace_package_compat(tmp_path):
+ # The ``google`` namespace package should not be masked
+ # by the presence of ``google-cloud-error-reporting``.
+
+ google = tmp_path / "google"
+ google.mkdir()
+ google.joinpath("othermod.py").write_text("")
+
+ google_otherpkg = tmp_path / "google" / "otherpkg"
+ google_otherpkg.mkdir()
+ google_otherpkg.joinpath("__init__.py").write_text("")
+
+ # The ``google.cloud`` namespace package should not be masked
+ # by the presence of ``google-cloud-error-reporting``.
+ google_cloud = tmp_path / "google" / "cloud"
+ google_cloud.mkdir()
+ google_cloud.joinpath("othermod.py").write_text("")
+
+ google_cloud_otherpkg = tmp_path / "google" / "cloud" / "otherpkg"
+ google_cloud_otherpkg.mkdir()
+ google_cloud_otherpkg.joinpath("__init__.py").write_text("")
+
+ env = dict(os.environ, PYTHONPATH=str(tmp_path))
+
+ for pkg in [
+ "google.othermod",
+ "google.cloud.othermod",
+ "google.otherpkg",
+ "google.cloud.otherpkg",
+ "google.cloud.error_reporting",
+ "google.cloud.errorreporting_v1beta1",
+ ]:
+ cmd = [sys.executable, "-c", f"import {pkg}"]
+ subprocess.check_output(cmd, env=env)
+
+ for module in ["google.othermod", "google.cloud.othermod"]:
+ cmd = [sys.executable, "-m", module]
+ subprocess.check_output(cmd, env=env)