diff --git a/.coveragerc b/.coveragerc
index e1eed34..50fa7f7 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,27 +1,11 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Generated by synthtool. DO NOT EDIT!
[run]
branch = True
[report]
fail_under = 100
show_missing = True
-omit = google/cloud/recommender/__init__.py
+omit =
+ google/cloud/recommender/__init__.py
exclude_lines =
# Re-enable the standard pragma
pragma: NO COVER
diff --git a/.github/header-checker-lint.yml b/.github/header-checker-lint.yml
new file mode 100644
index 0000000..fc281c0
--- /dev/null
+++ b/.github/header-checker-lint.yml
@@ -0,0 +1,15 @@
+{"allowedCopyrightHolders": ["Google LLC"],
+ "allowedLicenses": ["Apache-2.0", "MIT", "BSD-3"],
+ "ignoreFiles": ["**/requirements.txt", "**/requirements-test.txt"],
+ "sourceFileExtensions": [
+ "ts",
+ "js",
+ "java",
+ "sh",
+ "Dockerfile",
+ "yaml",
+ "py",
+ "html",
+ "txt"
+ ]
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index b9daa52..b4243ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,8 +50,10 @@ docs.metadata
# Virtual environment
env/
+
+# Test logs
coverage.xml
-sponge_log.xml
+*sponge_log.xml
# System test environment variables.
system_tests/local_test_setup
diff --git a/.kokoro/build.sh b/.kokoro/build.sh
index 2957bf6..7c14027 100755
--- a/.kokoro/build.sh
+++ b/.kokoro/build.sh
@@ -15,7 +15,11 @@
set -eo pipefail
-cd github/python-recommender
+if [[ -z "${PROJECT_ROOT:-}" ]]; then
+ PROJECT_ROOT="github/python-recommender"
+fi
+
+cd "${PROJECT_ROOT}"
# Disable buffering, so that the logs stream through.
export PYTHONUNBUFFERED=1
@@ -30,16 +34,26 @@ export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json
export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json")
# Remove old nox
-python3.6 -m pip uninstall --yes --quiet nox-automation
+python3 -m pip uninstall --yes --quiet nox-automation
# Install nox
-python3.6 -m pip install --upgrade --quiet nox
-python3.6 -m nox --version
+python3 -m pip install --upgrade --quiet nox
+python3 -m nox --version
+
+# If this is a continuous build, send the test log to the FlakyBot.
+# See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot.
+if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]]; then
+ cleanup() {
+ chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot
+ $KOKORO_GFILE_DIR/linux_amd64/flakybot
+ }
+ trap cleanup EXIT HUP
+fi
# If NOX_SESSION is set, it only runs the specified session,
# otherwise run all the sessions.
if [[ -n "${NOX_SESSION:-}" ]]; then
- python3.6 -m nox -s "${NOX_SESSION:-}"
+ python3 -m nox -s ${NOX_SESSION:-}
else
- python3.6 -m nox
+ python3 -m nox
fi
diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg
index 1118107..7083e4b 100644
--- a/.kokoro/docs/docs-presubmit.cfg
+++ b/.kokoro/docs/docs-presubmit.cfg
@@ -15,3 +15,14 @@ env_vars: {
key: "TRAMPOLINE_IMAGE_UPLOAD"
value: "false"
}
+
+env_vars: {
+ key: "TRAMPOLINE_BUILD_FILE"
+ value: "github/python-recommender/.kokoro/build.sh"
+}
+
+# Only run this nox session.
+env_vars: {
+ key: "NOX_SESSION"
+ value: "docs docfx"
+}
diff --git a/.kokoro/samples/python3.6/periodic-head.cfg b/.kokoro/samples/python3.6/periodic-head.cfg
new file mode 100644
index 0000000..f9cfcd3
--- /dev/null
+++ b/.kokoro/samples/python3.6/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-pubsub/.kokoro/test-samples-against-head.sh"
+}
diff --git a/.kokoro/samples/python3.7/periodic-head.cfg b/.kokoro/samples/python3.7/periodic-head.cfg
new file mode 100644
index 0000000..f9cfcd3
--- /dev/null
+++ b/.kokoro/samples/python3.7/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-pubsub/.kokoro/test-samples-against-head.sh"
+}
diff --git a/.kokoro/samples/python3.8/periodic-head.cfg b/.kokoro/samples/python3.8/periodic-head.cfg
new file mode 100644
index 0000000..f9cfcd3
--- /dev/null
+++ b/.kokoro/samples/python3.8/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-pubsub/.kokoro/test-samples-against-head.sh"
+}
diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh
new file mode 100755
index 0000000..befb5b5
--- /dev/null
+++ b/.kokoro/test-samples-against-head.sh
@@ -0,0 +1,28 @@
+#!/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.
+
+# A customized test runner for samples.
+#
+# For periodic builds, you can specify this file for testing against head.
+
+# `-e` enables the script to automatically fail when a command fails
+# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero
+set -eo pipefail
+# Enables `**` to include files nested inside sub-folders
+shopt -s globstar
+
+cd github/python-recommender
+
+exec .kokoro/test-samples-impl.sh
diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh
new file mode 100755
index 0000000..cf5de74
--- /dev/null
+++ b/.kokoro/test-samples-impl.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# 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.
+
+
+# `-e` enables the script to automatically fail when a command fails
+# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero
+set -eo pipefail
+# Enables `**` to include files nested inside sub-folders
+shopt -s globstar
+
+# Exit early if samples directory doesn't exist
+if [ ! -d "./samples" ]; then
+ echo "No tests run. `./samples` not found"
+ exit 0
+fi
+
+# Disable buffering, so that the logs stream through.
+export PYTHONUNBUFFERED=1
+
+# Debug: show build environment
+env | grep KOKORO
+
+# Install nox
+python3.6 -m pip install --upgrade --quiet nox
+
+# Use secrets acessor service account to get secrets
+if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then
+ gcloud auth activate-service-account \
+ --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \
+ --project="cloud-devrel-kokoro-resources"
+fi
+
+# This script will create 3 files:
+# - testing/test-env.sh
+# - testing/service-account.json
+# - testing/client-secrets.json
+./scripts/decrypt-secrets.sh
+
+source ./testing/test-env.sh
+export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json
+
+# For cloud-run session, we activate the service account for gcloud sdk.
+gcloud auth activate-service-account \
+ --key-file "${GOOGLE_APPLICATION_CREDENTIALS}"
+
+export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json
+
+echo -e "\n******************** TESTING PROJECTS ********************"
+
+# Switch to 'fail at end' to allow all tests to complete before exiting.
+set +e
+# Use RTN to return a non-zero value if the test fails.
+RTN=0
+ROOT=$(pwd)
+# Find all requirements.txt in the samples directory (may break on whitespace).
+for file in samples/**/requirements.txt; do
+ cd "$ROOT"
+ # Navigate to the project folder.
+ file=$(dirname "$file")
+ cd "$file"
+
+ echo "------------------------------------------------------------"
+ echo "- testing $file"
+ echo "------------------------------------------------------------"
+
+ # Use nox to execute the tests for the project.
+ python3.6 -m nox -s "$RUN_TESTS_SESSION"
+ EXIT=$?
+
+ # If this is a periodic build, send the test log to the FlakyBot.
+ # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot.
+ if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then
+ chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot
+ $KOKORO_GFILE_DIR/linux_amd64/flakybot
+ fi
+
+ if [[ $EXIT -ne 0 ]]; then
+ RTN=1
+ echo -e "\n Testing failed: Nox returned a non-zero exit code. \n"
+ else
+ echo -e "\n Testing completed.\n"
+ fi
+
+done
+cd "$ROOT"
+
+# Workaround for Kokoro permissions issue: delete secrets
+rm testing/{test-env.sh,client-secrets.json,service-account.json}
+
+exit "$RTN"
diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh
index 3bf4255..6187ab1 100755
--- a/.kokoro/test-samples.sh
+++ b/.kokoro/test-samples.sh
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# The default test runner for samples.
+#
+# For periodic builds, we rewinds the repo to the latest release, and
+# run test-samples-impl.sh.
# `-e` enables the script to automatically fail when a command fails
# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero
@@ -24,87 +28,19 @@ cd github/python-recommender
# Run periodic samples tests at latest release
if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then
+ # preserving the test runner implementation.
+ cp .kokoro/test-samples-impl.sh "${TMPDIR}/test-samples-impl.sh"
+ echo "--- IMPORTANT IMPORTANT IMPORTANT ---"
+ echo "Now we rewind the repo back to the latest release..."
LATEST_RELEASE=$(git describe --abbrev=0 --tags)
git checkout $LATEST_RELEASE
-fi
-
-# Exit early if samples directory doesn't exist
-if [ ! -d "./samples" ]; then
- echo "No tests run. `./samples` not found"
- exit 0
-fi
-
-# Disable buffering, so that the logs stream through.
-export PYTHONUNBUFFERED=1
-
-# Debug: show build environment
-env | grep KOKORO
-
-# Install nox
-python3.6 -m pip install --upgrade --quiet nox
-
-# Use secrets acessor service account to get secrets
-if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then
- gcloud auth activate-service-account \
- --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \
- --project="cloud-devrel-kokoro-resources"
-fi
-
-# This script will create 3 files:
-# - testing/test-env.sh
-# - testing/service-account.json
-# - testing/client-secrets.json
-./scripts/decrypt-secrets.sh
-
-source ./testing/test-env.sh
-export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json
-
-# For cloud-run session, we activate the service account for gcloud sdk.
-gcloud auth activate-service-account \
- --key-file "${GOOGLE_APPLICATION_CREDENTIALS}"
-
-export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json
-
-echo -e "\n******************** TESTING PROJECTS ********************"
-
-# Switch to 'fail at end' to allow all tests to complete before exiting.
-set +e
-# Use RTN to return a non-zero value if the test fails.
-RTN=0
-ROOT=$(pwd)
-# Find all requirements.txt in the samples directory (may break on whitespace).
-for file in samples/**/requirements.txt; do
- cd "$ROOT"
- # Navigate to the project folder.
- file=$(dirname "$file")
- cd "$file"
-
- echo "------------------------------------------------------------"
- echo "- testing $file"
- echo "------------------------------------------------------------"
-
- # Use nox to execute the tests for the project.
- python3.6 -m nox -s "$RUN_TESTS_SESSION"
- EXIT=$?
-
- # If this is a periodic build, send the test log to the FlakyBot.
- # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot.
- if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then
- chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot
- $KOKORO_GFILE_DIR/linux_amd64/flakybot
+ echo "The current head is: "
+ echo $(git rev-parse --verify HEAD)
+ echo "--- IMPORTANT IMPORTANT IMPORTANT ---"
+ # move back the test runner implementation if there's no file.
+ if [ ! -f .kokoro/test-samples-impl.sh ]; then
+ cp "${TMPDIR}/test-samples-impl.sh" .kokoro/test-samples-impl.sh
fi
+fi
- if [[ $EXIT -ne 0 ]]; then
- RTN=1
- echo -e "\n Testing failed: Nox returned a non-zero exit code. \n"
- else
- echo -e "\n Testing completed.\n"
- fi
-
-done
-cd "$ROOT"
-
-# Workaround for Kokoro permissions issue: delete secrets
-rm testing/{test-env.sh,client-secrets.json,service-account.json}
-
-exit "$RTN"
+exec .kokoro/test-samples-impl.sh
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a9024b1..32302e4 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,6 +12,6 @@ repos:
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
- rev: 3.8.4
+ rev: 3.9.0
hooks:
- id: flake8
diff --git a/.trampolinerc b/.trampolinerc
index 995ee29..383b6ec 100644
--- a/.trampolinerc
+++ b/.trampolinerc
@@ -24,6 +24,7 @@ required_envvars+=(
pass_down_envvars+=(
"STAGING_BUCKET"
"V2_STAGING_BUCKET"
+ "NOX_SESSION"
)
# Prevent unintentional override on the default image.
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 576bfde..f7310d8 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -70,9 +70,14 @@ We use `nox `__ to instrument our tests.
- To test your changes, run unit tests with ``nox``::
$ nox -s unit-2.7
- $ nox -s unit-3.7
+ $ nox -s unit-3.8
$ ...
+- Args to pytest can be passed through the nox command separated by a `--`. For
+ example, to run a single test::
+
+ $ nox -s unit-3.8 -- -k
+
.. note::
The unit tests and system tests are described in the
@@ -93,8 +98,12 @@ On Debian/Ubuntu::
************
Coding Style
************
+- We use the automatic code formatter ``black``. You can run it using
+ the nox session ``blacken``. This will eliminate many lint errors. Run via::
+
+ $ nox -s blacken
-- PEP8 compliance, with exceptions defined in the linter configuration.
+- PEP8 compliance is required, with exceptions defined in the linter configuration.
If you have ``nox`` installed, you can test that you have not introduced
any non-compliant code via::
@@ -133,13 +142,18 @@ Running System Tests
- To run system tests, you can execute::
- $ nox -s system-3.7
+ # Run all system tests
+ $ nox -s system-3.8
$ nox -s system-2.7
+ # Run a single system test
+ $ nox -s system-3.8 -- -k
+
+
.. note::
System tests are only configured to run under Python 2.7 and
- Python 3.7. For expediency, we do not run them in older versions
+ Python 3.8. For expediency, we do not run them in older versions
of Python 3.
This alone will not run the tests. You'll need to change some local
diff --git a/LICENSE b/LICENSE
index a8ee855..d645695 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
- Apache License
+
+ Apache License
Version 2.0, January 2004
- https://www.apache.org/licenses/
+ http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@@ -192,7 +193,7 @@
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- https://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/MANIFEST.in b/MANIFEST.in
index e9e29d1..e783f4c 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -16,10 +16,10 @@
# Generated by synthtool. DO NOT EDIT!
include README.rst LICENSE
-recursive-include google *.json *.proto
+recursive-include google *.json *.proto py.typed
recursive-include tests *
global-exclude *.py[co]
global-exclude __pycache__
# Exclude scripts for samples readmegen
-prune scripts/readme-gen
\ No newline at end of file
+prune scripts/readme-gen
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
index 0abaf22..bcd37bb 100644
--- a/docs/_static/custom.css
+++ b/docs/_static/custom.css
@@ -1,4 +1,9 @@
div#python2-eol {
border-color: red;
border-width: medium;
-}
\ No newline at end of file
+}
+
+/* Ensure minimum width for 'Parameters' / 'Returns' column */
+dl.field-list > dt {
+ min-width: 100px
+}
diff --git a/docs/recommender_v1/recommender.rst b/docs/recommender_v1/recommender.rst
new file mode 100644
index 0000000..b05f944
--- /dev/null
+++ b/docs/recommender_v1/recommender.rst
@@ -0,0 +1,11 @@
+Recommender
+-----------------------------
+
+.. automodule:: google.cloud.recommender_v1.services.recommender
+ :members:
+ :inherited-members:
+
+
+.. automodule:: google.cloud.recommender_v1.services.recommender.pagers
+ :members:
+ :inherited-members:
diff --git a/docs/recommender_v1/services.rst b/docs/recommender_v1/services.rst
index 64345c5..d23296a 100644
--- a/docs/recommender_v1/services.rst
+++ b/docs/recommender_v1/services.rst
@@ -1,6 +1,6 @@
Services for Google Cloud Recommender v1 API
============================================
+.. toctree::
+ :maxdepth: 2
-.. automodule:: google.cloud.recommender_v1.services.recommender
- :members:
- :inherited-members:
+ recommender
diff --git a/docs/recommender_v1/types.rst b/docs/recommender_v1/types.rst
index 99c254c..dd13661 100644
--- a/docs/recommender_v1/types.rst
+++ b/docs/recommender_v1/types.rst
@@ -3,4 +3,5 @@ Types for Google Cloud Recommender v1 API
.. automodule:: google.cloud.recommender_v1.types
:members:
+ :undoc-members:
:show-inheritance:
diff --git a/docs/recommender_v1beta1/recommender.rst b/docs/recommender_v1beta1/recommender.rst
new file mode 100644
index 0000000..416d238
--- /dev/null
+++ b/docs/recommender_v1beta1/recommender.rst
@@ -0,0 +1,11 @@
+Recommender
+-----------------------------
+
+.. automodule:: google.cloud.recommender_v1beta1.services.recommender
+ :members:
+ :inherited-members:
+
+
+.. automodule:: google.cloud.recommender_v1beta1.services.recommender.pagers
+ :members:
+ :inherited-members:
diff --git a/docs/recommender_v1beta1/services.rst b/docs/recommender_v1beta1/services.rst
index 5b4764d..5341142 100644
--- a/docs/recommender_v1beta1/services.rst
+++ b/docs/recommender_v1beta1/services.rst
@@ -1,6 +1,6 @@
Services for Google Cloud Recommender v1beta1 API
=================================================
+.. toctree::
+ :maxdepth: 2
-.. automodule:: google.cloud.recommender_v1beta1.services.recommender
- :members:
- :inherited-members:
+ recommender
diff --git a/docs/recommender_v1beta1/types.rst b/docs/recommender_v1beta1/types.rst
index c16ac11..16ab2a8 100644
--- a/docs/recommender_v1beta1/types.rst
+++ b/docs/recommender_v1beta1/types.rst
@@ -3,4 +3,5 @@ Types for Google Cloud Recommender v1beta1 API
.. automodule:: google.cloud.recommender_v1beta1.types
:members:
+ :undoc-members:
:show-inheritance:
diff --git a/google/cloud/recommender_v1/services/recommender/async_client.py b/google/cloud/recommender_v1/services/recommender/async_client.py
index 2302ff9..071901c 100644
--- a/google/cloud/recommender_v1/services/recommender/async_client.py
+++ b/google/cloud/recommender_v1/services/recommender/async_client.py
@@ -90,7 +90,36 @@ class RecommenderAsyncClient:
RecommenderClient.parse_common_location_path
)
- from_service_account_file = RecommenderClient.from_service_account_file
+ @classmethod
+ def from_service_account_info(cls, info: dict, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials info.
+
+ Args:
+ info (dict): The service account private key info.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ RecommenderAsyncClient: The constructed client.
+ """
+ return RecommenderClient.from_service_account_info.__func__(RecommenderAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ RecommenderAsyncClient: The constructed client.
+ """
+ return RecommenderClient.from_service_account_file.__func__(RecommenderAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -168,7 +197,7 @@ async def list_insights(
type.
Args:
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (:class:`google.cloud.recommender_v1.types.ListInsightsRequest`):
The request object. Request for the `ListInsights`
method.
parent (:class:`str`):
@@ -183,6 +212,7 @@ async def list_insights(
https://cloud.google.com/about/locations/
INSIGHT_TYPE_ID refers to supported insight types:
https://cloud.google.com/recommender/docs/insights/insight-types.)
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -194,8 +224,8 @@ async def list_insights(
sent along with the request as metadata.
Returns:
- ~.pagers.ListInsightsAsyncPager:
- Response to the ``ListInsights`` method.
+ google.cloud.recommender_v1.services.recommender.pagers.ListInsightsAsyncPager:
+ Response to the ListInsights method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -230,6 +260,7 @@ async def list_insights(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -266,7 +297,7 @@ async def get_insight(
permission for the specified insight type.
Args:
- request (:class:`~.recommender_service.GetInsightRequest`):
+ request (:class:`google.cloud.recommender_v1.types.GetInsightRequest`):
The request object. Request to the `GetInsight` method.
name (:class:`str`):
Required. Name of the insight.
@@ -281,7 +312,7 @@ async def get_insight(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -317,6 +348,7 @@ async def get_insight(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -357,7 +389,7 @@ async def mark_insight_accepted(
specified insight.
Args:
- request (:class:`~.recommender_service.MarkInsightAcceptedRequest`):
+ request (:class:`google.cloud.recommender_v1.types.MarkInsightAcceptedRequest`):
The request object. Request for the
`MarkInsightAccepted` method.
name (:class:`str`):
@@ -365,15 +397,17 @@ async def mark_insight_accepted(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkInsightAcceptedRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1.types.MarkInsightAcceptedRequest.StateMetadataEntry]`):
Optional. State properties user wish to include with
this state. Full replace of the current state_metadata.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
etag (:class:`str`):
Required. Fingerprint of the Insight.
Provides optimistic locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -385,7 +419,7 @@ async def mark_insight_accepted(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -449,7 +483,7 @@ async def list_recommendations(
recommender.*.list IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (:class:`google.cloud.recommender_v1.types.ListRecommendationsRequest`):
The request object. Request for the
`ListRecommendations` method.
parent (:class:`str`):
@@ -464,6 +498,7 @@ async def list_recommendations(
https://cloud.google.com/about/locations/ RECOMMENDER_ID
refers to supported recommenders:
https://cloud.google.com/recommender/docs/recommenders.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -471,7 +506,8 @@ async def list_recommendations(
Filter expression to restrict the recommendations
returned. Supported filter fields: state_info.state Eg:
\`state_info.state:"DISMISSED" or
- state_info.state:"FAILED".
+ state_info.state:"FAILED"
+
This corresponds to the ``filter`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -483,8 +519,8 @@ async def list_recommendations(
sent along with the request as metadata.
Returns:
- ~.pagers.ListRecommendationsAsyncPager:
- Response to the ``ListRecommendations`` method.
+ google.cloud.recommender_v1.services.recommender.pagers.ListRecommendationsAsyncPager:
+ Response to the ListRecommendations method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -521,6 +557,7 @@ async def list_recommendations(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -557,7 +594,7 @@ async def get_recommendation(
recommender.*.get IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.GetRecommendationRequest`):
+ request (:class:`google.cloud.recommender_v1.types.GetRecommendationRequest`):
The request object. Request to the `GetRecommendation`
method.
name (:class:`str`):
@@ -573,7 +610,7 @@ async def get_recommendation(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -609,6 +646,7 @@ async def get_recommendation(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -652,7 +690,7 @@ async def mark_recommendation_claimed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationClaimedRequest`):
+ request (:class:`google.cloud.recommender_v1.types.MarkRecommendationClaimedRequest`):
The request object. Request for the
`MarkRecommendationClaimed` Method.
name (:class:`str`):
@@ -660,11 +698,12 @@ async def mark_recommendation_claimed(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationClaimedRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1.types.MarkRecommendationClaimedRequest.StateMetadataEntry]`):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -672,6 +711,7 @@ async def mark_recommendation_claimed(
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -683,7 +723,7 @@ async def mark_recommendation_claimed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -759,7 +799,7 @@ async def mark_recommendation_succeeded(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationSucceededRequest`):
+ request (:class:`google.cloud.recommender_v1.types.MarkRecommendationSucceededRequest`):
The request object. Request for the
`MarkRecommendationSucceeded` Method.
name (:class:`str`):
@@ -767,11 +807,12 @@ async def mark_recommendation_succeeded(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationSucceededRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1.types.MarkRecommendationSucceededRequest.StateMetadataEntry]`):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -779,6 +820,7 @@ async def mark_recommendation_succeeded(
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -790,7 +832,7 @@ async def mark_recommendation_succeeded(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -866,7 +908,7 @@ async def mark_recommendation_failed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationFailedRequest`):
+ request (:class:`google.cloud.recommender_v1.types.MarkRecommendationFailedRequest`):
The request object. Request for the
`MarkRecommendationFailed` Method.
name (:class:`str`):
@@ -874,11 +916,12 @@ async def mark_recommendation_failed(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationFailedRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1.types.MarkRecommendationFailedRequest.StateMetadataEntry]`):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -886,6 +929,7 @@ async def mark_recommendation_failed(
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -897,7 +941,7 @@ async def mark_recommendation_failed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
diff --git a/google/cloud/recommender_v1/services/recommender/client.py b/google/cloud/recommender_v1/services/recommender/client.py
index 7139bd5..4310b24 100644
--- a/google/cloud/recommender_v1/services/recommender/client.py
+++ b/google/cloud/recommender_v1/services/recommender/client.py
@@ -118,6 +118,22 @@ def _get_default_mtls_endpoint(api_endpoint):
DEFAULT_ENDPOINT
)
+ @classmethod
+ def from_service_account_info(cls, info: dict, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials info.
+
+ Args:
+ info (dict): The service account private key info.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ RecommenderClient: The constructed client.
+ """
+ credentials = service_account.Credentials.from_service_account_info(info)
+ kwargs["credentials"] = credentials
+ return cls(*args, **kwargs)
+
@classmethod
def from_service_account_file(cls, filename: str, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -130,7 +146,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
kwargs: Additional arguments to pass to the constructor.
Returns:
- {@api.name}: The constructed client.
+ RecommenderClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -296,10 +312,10 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ~.RecommenderTransport]): The
+ transport (Union[str, RecommenderTransport]): The
transport to use. If set to None, a transport is chosen
automatically.
- client_options (client_options_lib.ClientOptions): Custom options for the
+ client_options (google.api_core.client_options.ClientOptions): Custom options for the
client. It won't take effect if a ``transport`` instance is provided.
(1) The ``api_endpoint`` property can be used to override the
default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
@@ -335,21 +351,17 @@ def __init__(
util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"))
)
- ssl_credentials = None
+ client_cert_source_func = None
is_mtls = False
if use_client_cert:
if client_options.client_cert_source:
- import grpc # type: ignore
-
- cert, key = client_options.client_cert_source()
- ssl_credentials = grpc.ssl_channel_credentials(
- certificate_chain=cert, private_key=key
- )
is_mtls = True
+ client_cert_source_func = client_options.client_cert_source
else:
- creds = SslCredentials()
- is_mtls = creds.is_mtls
- ssl_credentials = creds.ssl_credentials if is_mtls else None
+ is_mtls = mtls.has_default_client_cert_source()
+ client_cert_source_func = (
+ mtls.default_client_cert_source() if is_mtls else None
+ )
# Figure out which api endpoint to use.
if client_options.api_endpoint is not None:
@@ -392,7 +404,7 @@ def __init__(
credentials_file=client_options.credentials_file,
host=api_endpoint,
scopes=client_options.scopes,
- ssl_channel_credentials=ssl_credentials,
+ client_cert_source_for_mtls=client_cert_source_func,
quota_project_id=client_options.quota_project_id,
client_info=client_info,
)
@@ -411,10 +423,10 @@ def list_insights(
type.
Args:
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (google.cloud.recommender_v1.types.ListInsightsRequest):
The request object. Request for the `ListInsights`
method.
- parent (:class:`str`):
+ parent (str):
Required. The container resource on which to execute the
request. Acceptable formats:
@@ -426,6 +438,7 @@ def list_insights(
https://cloud.google.com/about/locations/
INSIGHT_TYPE_ID refers to supported insight types:
https://cloud.google.com/recommender/docs/insights/insight-types.)
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -437,8 +450,8 @@ def list_insights(
sent along with the request as metadata.
Returns:
- ~.pagers.ListInsightsPager:
- Response to the ``ListInsights`` method.
+ google.cloud.recommender_v1.services.recommender.pagers.ListInsightsPager:
+ Response to the ListInsights method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -502,9 +515,9 @@ def get_insight(
permission for the specified insight type.
Args:
- request (:class:`~.recommender_service.GetInsightRequest`):
+ request (google.cloud.recommender_v1.types.GetInsightRequest):
The request object. Request to the `GetInsight` method.
- name (:class:`str`):
+ name (str):
Required. Name of the insight.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -517,7 +530,7 @@ def get_insight(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -586,23 +599,25 @@ def mark_insight_accepted(
specified insight.
Args:
- request (:class:`~.recommender_service.MarkInsightAcceptedRequest`):
+ request (google.cloud.recommender_v1.types.MarkInsightAcceptedRequest):
The request object. Request for the
`MarkInsightAccepted` method.
- name (:class:`str`):
+ name (str):
Required. Name of the insight.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkInsightAcceptedRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkInsightAcceptedRequest.StateMetadataEntry]):
Optional. State properties user wish to include with
this state. Full replace of the current state_metadata.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the Insight.
Provides optimistic locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -614,7 +629,7 @@ def mark_insight_accepted(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -643,12 +658,11 @@ def mark_insight_accepted(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[self._transport.mark_insight_accepted]
@@ -679,10 +693,10 @@ def list_recommendations(
recommender.*.list IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (google.cloud.recommender_v1.types.ListRecommendationsRequest):
The request object. Request for the
`ListRecommendations` method.
- parent (:class:`str`):
+ parent (str):
Required. The container resource on which to execute the
request. Acceptable formats:
@@ -694,14 +708,16 @@ def list_recommendations(
https://cloud.google.com/about/locations/ RECOMMENDER_ID
refers to supported recommenders:
https://cloud.google.com/recommender/docs/recommenders.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- filter (:class:`str`):
+ filter (str):
Filter expression to restrict the recommendations
returned. Supported filter fields: state_info.state Eg:
\`state_info.state:"DISMISSED" or
- state_info.state:"FAILED".
+ state_info.state:"FAILED"
+
This corresponds to the ``filter`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -713,8 +729,8 @@ def list_recommendations(
sent along with the request as metadata.
Returns:
- ~.pagers.ListRecommendationsPager:
- Response to the ``ListRecommendations`` method.
+ google.cloud.recommender_v1.services.recommender.pagers.ListRecommendationsPager:
+ Response to the ListRecommendations method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -780,10 +796,10 @@ def get_recommendation(
recommender.*.get IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.GetRecommendationRequest`):
+ request (google.cloud.recommender_v1.types.GetRecommendationRequest):
The request object. Request to the `GetRecommendation`
method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -796,7 +812,7 @@ def get_recommendation(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -868,26 +884,28 @@ def mark_recommendation_claimed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationClaimedRequest`):
+ request (google.cloud.recommender_v1.types.MarkRecommendationClaimedRequest):
The request object. Request for the
`MarkRecommendationClaimed` Method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationClaimedRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkRecommendationClaimedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -899,7 +917,7 @@ def mark_recommendation_claimed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -930,12 +948,11 @@ def mark_recommendation_claimed(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[
@@ -980,26 +997,28 @@ def mark_recommendation_succeeded(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationSucceededRequest`):
+ request (google.cloud.recommender_v1.types.MarkRecommendationSucceededRequest):
The request object. Request for the
`MarkRecommendationSucceeded` Method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationSucceededRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkRecommendationSucceededRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -1011,7 +1030,7 @@ def mark_recommendation_succeeded(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -1042,12 +1061,11 @@ def mark_recommendation_succeeded(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[
@@ -1092,26 +1110,28 @@ def mark_recommendation_failed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationFailedRequest`):
+ request (google.cloud.recommender_v1.types.MarkRecommendationFailedRequest):
The request object. Request for the
`MarkRecommendationFailed` Method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationFailedRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkRecommendationFailedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -1123,7 +1143,7 @@ def mark_recommendation_failed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -1152,12 +1172,11 @@ def mark_recommendation_failed(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[
diff --git a/google/cloud/recommender_v1/services/recommender/pagers.py b/google/cloud/recommender_v1/services/recommender/pagers.py
index 86f9944..e51870d 100644
--- a/google/cloud/recommender_v1/services/recommender/pagers.py
+++ b/google/cloud/recommender_v1/services/recommender/pagers.py
@@ -15,7 +15,16 @@
# limitations under the License.
#
-from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple
+from typing import (
+ Any,
+ AsyncIterable,
+ Awaitable,
+ Callable,
+ Iterable,
+ Sequence,
+ Tuple,
+ Optional,
+)
from google.cloud.recommender_v1.types import insight
from google.cloud.recommender_v1.types import recommendation
@@ -26,7 +35,7 @@ class ListInsightsPager:
"""A pager for iterating through ``list_insights`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListInsightsResponse` object, and
+ :class:`google.cloud.recommender_v1.types.ListInsightsResponse` object, and
provides an ``__iter__`` method to iterate through its
``insights`` field.
@@ -35,7 +44,7 @@ class ListInsightsPager:
through the ``insights`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListInsightsResponse`
+ All the usual :class:`google.cloud.recommender_v1.types.ListInsightsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -53,9 +62,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (google.cloud.recommender_v1.types.ListInsightsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListInsightsResponse`):
+ response (google.cloud.recommender_v1.types.ListInsightsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -88,7 +97,7 @@ class ListInsightsAsyncPager:
"""A pager for iterating through ``list_insights`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListInsightsResponse` object, and
+ :class:`google.cloud.recommender_v1.types.ListInsightsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``insights`` field.
@@ -97,7 +106,7 @@ class ListInsightsAsyncPager:
through the ``insights`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListInsightsResponse`
+ All the usual :class:`google.cloud.recommender_v1.types.ListInsightsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -115,9 +124,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (google.cloud.recommender_v1.types.ListInsightsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListInsightsResponse`):
+ response (google.cloud.recommender_v1.types.ListInsightsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -154,7 +163,7 @@ class ListRecommendationsPager:
"""A pager for iterating through ``list_recommendations`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListRecommendationsResponse` object, and
+ :class:`google.cloud.recommender_v1.types.ListRecommendationsResponse` object, and
provides an ``__iter__`` method to iterate through its
``recommendations`` field.
@@ -163,7 +172,7 @@ class ListRecommendationsPager:
through the ``recommendations`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListRecommendationsResponse`
+ All the usual :class:`google.cloud.recommender_v1.types.ListRecommendationsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -181,9 +190,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (google.cloud.recommender_v1.types.ListRecommendationsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListRecommendationsResponse`):
+ response (google.cloud.recommender_v1.types.ListRecommendationsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -216,7 +225,7 @@ class ListRecommendationsAsyncPager:
"""A pager for iterating through ``list_recommendations`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListRecommendationsResponse` object, and
+ :class:`google.cloud.recommender_v1.types.ListRecommendationsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``recommendations`` field.
@@ -225,7 +234,7 @@ class ListRecommendationsAsyncPager:
through the ``recommendations`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListRecommendationsResponse`
+ All the usual :class:`google.cloud.recommender_v1.types.ListRecommendationsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -245,9 +254,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (google.cloud.recommender_v1.types.ListRecommendationsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListRecommendationsResponse`):
+ response (google.cloud.recommender_v1.types.ListRecommendationsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
diff --git a/google/cloud/recommender_v1/services/recommender/transports/base.py b/google/cloud/recommender_v1/services/recommender/transports/base.py
index a8a8541..9148199 100644
--- a/google/cloud/recommender_v1/services/recommender/transports/base.py
+++ b/google/cloud/recommender_v1/services/recommender/transports/base.py
@@ -71,10 +71,10 @@ def __init__(
scope (Optional[Sequence[str]]): A list of scopes.
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're developing
+ 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.
"""
# Save the hostname. Default to port 443 (HTTPS) if none is specified.
@@ -82,6 +82,9 @@ def __init__(
host += ":443"
self._host = host
+ # Save the scopes.
+ self._scopes = scopes or self.AUTH_SCOPES
+
# If no credentials are provided, then determine the appropriate
# defaults.
if credentials and credentials_file:
@@ -91,20 +94,17 @@ def __init__(
if credentials_file is not None:
credentials, _ = auth.load_credentials_from_file(
- credentials_file, scopes=scopes, quota_project_id=quota_project_id
+ credentials_file, scopes=self._scopes, quota_project_id=quota_project_id
)
elif credentials is None:
credentials, _ = auth.default(
- scopes=scopes, quota_project_id=quota_project_id
+ scopes=self._scopes, quota_project_id=quota_project_id
)
# Save the credentials.
self._credentials = credentials
- # Lifted into its own function so it can be stubbed out during tests.
- self._prep_wrapped_messages(client_info)
-
def _prep_wrapped_messages(self, client_info):
# Precompute the wrapped methods.
self._wrapped_methods = {
@@ -117,6 +117,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
@@ -130,6 +131,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
@@ -148,6 +150,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
@@ -161,6 +164,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
diff --git a/google/cloud/recommender_v1/services/recommender/transports/grpc.py b/google/cloud/recommender_v1/services/recommender/transports/grpc.py
index 1f1bd87..0dc6294 100644
--- a/google/cloud/recommender_v1/services/recommender/transports/grpc.py
+++ b/google/cloud/recommender_v1/services/recommender/transports/grpc.py
@@ -63,6 +63,7 @@ def __init__(
api_mtls_endpoint: str = None,
client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
ssl_channel_credentials: grpc.ChannelCredentials = None,
+ client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
@@ -93,6 +94,10 @@ def __init__(
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
for grpc channel. It is ignored if ``channel`` is provided.
+ client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
+ A callback to provide client certificate bytes and private key bytes,
+ both in PEM format. It is used to configure mutual TLS channel. It is
+ ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -107,72 +112,60 @@ def __init__(
google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials``
and ``credentials_file`` are passed.
"""
+ self._grpc_channel = None
self._ssl_channel_credentials = ssl_channel_credentials
+ self._stubs: Dict[str, Callable] = {}
+
+ if api_mtls_endpoint:
+ warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning)
+ if client_cert_source:
+ warnings.warn("client_cert_source is deprecated", DeprecationWarning)
if channel:
- # Sanity check: Ensure that channel and credentials are not both
- # provided.
+ # Ignore credentials if a channel was passed.
credentials = False
-
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
- elif api_mtls_endpoint:
- warnings.warn(
- "api_mtls_endpoint and client_cert_source are deprecated",
- DeprecationWarning,
- )
- host = (
- api_mtls_endpoint
- if ":" in api_mtls_endpoint
- else api_mtls_endpoint + ":443"
- )
+ else:
+ if api_mtls_endpoint:
+ host = api_mtls_endpoint
+
+ # Create SSL credentials with client_cert_source or application
+ # default SSL credentials.
+ if client_cert_source:
+ cert, key = client_cert_source()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
+ else:
+ self._ssl_channel_credentials = SslCredentials().ssl_credentials
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
-
- # Create SSL credentials with client_cert_source or application
- # default SSL credentials.
- if client_cert_source:
- cert, key = client_cert_source()
- ssl_credentials = grpc.ssl_channel_credentials(
- certificate_chain=cert, private_key=key
- )
else:
- ssl_credentials = SslCredentials().ssl_credentials
-
- # create a new channel. The provided one is ignored.
- self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
- credentials_file=credentials_file,
- ssl_credentials=ssl_credentials,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- options=[
- ("grpc.max_send_message_length", -1),
- ("grpc.max_receive_message_length", -1),
- ],
- )
- self._ssl_channel_credentials = ssl_credentials
- else:
- host = host if ":" in host else host + ":443"
+ if client_cert_source_for_mtls and not ssl_channel_credentials:
+ cert, key = client_cert_source_for_mtls()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
+ # The base transport sets the host, credentials and scopes
+ super().__init__(
+ host=host,
+ credentials=credentials,
+ credentials_file=credentials_file,
+ scopes=scopes,
+ quota_project_id=quota_project_id,
+ client_info=client_info,
+ )
- # create a new channel. The provided one is ignored.
+ if not self._grpc_channel:
self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
+ self._host,
+ credentials=self._credentials,
credentials_file=credentials_file,
- ssl_credentials=ssl_channel_credentials,
- scopes=scopes or self.AUTH_SCOPES,
+ scopes=self._scopes,
+ ssl_credentials=self._ssl_channel_credentials,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
@@ -180,17 +173,8 @@ def __init__(
],
)
- self._stubs = {} # type: Dict[str, Callable]
-
- # Run the base constructor.
- super().__init__(
- host=host,
- credentials=credentials,
- credentials_file=credentials_file,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- client_info=client_info,
- )
+ # Wrap messages. This must be done after self._grpc_channel exists
+ self._prep_wrapped_messages(client_info)
@classmethod
def create_channel(
@@ -204,7 +188,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optional[str]): The host for the channel to use.
+ host (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
diff --git a/google/cloud/recommender_v1/services/recommender/transports/grpc_asyncio.py b/google/cloud/recommender_v1/services/recommender/transports/grpc_asyncio.py
index 4098244..d7b2804 100644
--- a/google/cloud/recommender_v1/services/recommender/transports/grpc_asyncio.py
+++ b/google/cloud/recommender_v1/services/recommender/transports/grpc_asyncio.py
@@ -67,7 +67,7 @@ def create_channel(
) -> aio.Channel:
"""Create and return a gRPC AsyncIO channel object.
Args:
- address (Optional[str]): The host for the channel to use.
+ host (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
@@ -107,6 +107,7 @@ def __init__(
api_mtls_endpoint: str = None,
client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
ssl_channel_credentials: grpc.ChannelCredentials = None,
+ client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
quota_project_id=None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
@@ -138,12 +139,16 @@ def __init__(
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
for grpc channel. It is ignored if ``channel`` is provided.
+ client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
+ A callback to provide client certificate bytes and private key bytes,
+ both in PEM format. It is used to configure mutual TLS channel. It is
+ ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
- client_info (google.api_core.gapic_v1.client_info.ClientInfo):
- 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
+ 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:
@@ -152,72 +157,60 @@ def __init__(
google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials``
and ``credentials_file`` are passed.
"""
+ self._grpc_channel = None
self._ssl_channel_credentials = ssl_channel_credentials
+ self._stubs: Dict[str, Callable] = {}
+
+ if api_mtls_endpoint:
+ warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning)
+ if client_cert_source:
+ warnings.warn("client_cert_source is deprecated", DeprecationWarning)
if channel:
- # Sanity check: Ensure that channel and credentials are not both
- # provided.
+ # Ignore credentials if a channel was passed.
credentials = False
-
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
- elif api_mtls_endpoint:
- warnings.warn(
- "api_mtls_endpoint and client_cert_source are deprecated",
- DeprecationWarning,
- )
- host = (
- api_mtls_endpoint
- if ":" in api_mtls_endpoint
- else api_mtls_endpoint + ":443"
- )
+ else:
+ if api_mtls_endpoint:
+ host = api_mtls_endpoint
+
+ # Create SSL credentials with client_cert_source or application
+ # default SSL credentials.
+ if client_cert_source:
+ cert, key = client_cert_source()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
+ else:
+ self._ssl_channel_credentials = SslCredentials().ssl_credentials
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
-
- # Create SSL credentials with client_cert_source or application
- # default SSL credentials.
- if client_cert_source:
- cert, key = client_cert_source()
- ssl_credentials = grpc.ssl_channel_credentials(
- certificate_chain=cert, private_key=key
- )
else:
- ssl_credentials = SslCredentials().ssl_credentials
-
- # create a new channel. The provided one is ignored.
- self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
- credentials_file=credentials_file,
- ssl_credentials=ssl_credentials,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- options=[
- ("grpc.max_send_message_length", -1),
- ("grpc.max_receive_message_length", -1),
- ],
- )
- self._ssl_channel_credentials = ssl_credentials
- else:
- host = host if ":" in host else host + ":443"
+ if client_cert_source_for_mtls and not ssl_channel_credentials:
+ cert, key = client_cert_source_for_mtls()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
+ # The base transport sets the host, credentials and scopes
+ super().__init__(
+ host=host,
+ credentials=credentials,
+ credentials_file=credentials_file,
+ scopes=scopes,
+ quota_project_id=quota_project_id,
+ client_info=client_info,
+ )
- # create a new channel. The provided one is ignored.
+ if not self._grpc_channel:
self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
+ self._host,
+ credentials=self._credentials,
credentials_file=credentials_file,
- ssl_credentials=ssl_channel_credentials,
- scopes=scopes or self.AUTH_SCOPES,
+ scopes=self._scopes,
+ ssl_credentials=self._ssl_channel_credentials,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
@@ -225,17 +218,8 @@ def __init__(
],
)
- # Run the base constructor.
- super().__init__(
- host=host,
- credentials=credentials,
- credentials_file=credentials_file,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- client_info=client_info,
- )
-
- self._stubs = {}
+ # Wrap messages. This must be done after self._grpc_channel exists
+ self._prep_wrapped_messages(client_info)
@property
def grpc_channel(self) -> aio.Channel:
diff --git a/google/cloud/recommender_v1/types/__init__.py b/google/cloud/recommender_v1/types/__init__.py
index f75c4f2..395fe1a 100644
--- a/google/cloud/recommender_v1/types/__init__.py
+++ b/google/cloud/recommender_v1/types/__init__.py
@@ -20,47 +20,47 @@
InsightStateInfo,
)
from .recommendation import (
- Recommendation,
- RecommendationContent,
- OperationGroup,
- Operation,
- ValueMatcher,
CostProjection,
Impact,
+ Operation,
+ OperationGroup,
+ Recommendation,
+ RecommendationContent,
RecommendationStateInfo,
+ ValueMatcher,
)
from .recommender_service import (
+ GetInsightRequest,
+ GetRecommendationRequest,
ListInsightsRequest,
ListInsightsResponse,
- GetInsightRequest,
- MarkInsightAcceptedRequest,
ListRecommendationsRequest,
ListRecommendationsResponse,
- GetRecommendationRequest,
+ MarkInsightAcceptedRequest,
MarkRecommendationClaimedRequest,
- MarkRecommendationSucceededRequest,
MarkRecommendationFailedRequest,
+ MarkRecommendationSucceededRequest,
)
__all__ = (
"Insight",
"InsightStateInfo",
- "Recommendation",
- "RecommendationContent",
- "OperationGroup",
- "Operation",
- "ValueMatcher",
"CostProjection",
"Impact",
+ "Operation",
+ "OperationGroup",
+ "Recommendation",
+ "RecommendationContent",
"RecommendationStateInfo",
+ "ValueMatcher",
+ "GetInsightRequest",
+ "GetRecommendationRequest",
"ListInsightsRequest",
"ListInsightsResponse",
- "GetInsightRequest",
- "MarkInsightAcceptedRequest",
"ListRecommendationsRequest",
"ListRecommendationsResponse",
- "GetRecommendationRequest",
+ "MarkInsightAcceptedRequest",
"MarkRecommendationClaimedRequest",
- "MarkRecommendationSucceededRequest",
"MarkRecommendationFailedRequest",
+ "MarkRecommendationSucceededRequest",
)
diff --git a/google/cloud/recommender_v1/types/insight.py b/google/cloud/recommender_v1/types/insight.py
index fb351cd..65ebc99 100644
--- a/google/cloud/recommender_v1/types/insight.py
+++ b/google/cloud/recommender_v1/types/insight.py
@@ -44,25 +44,25 @@ class Insight(proto.Message):
insight_subtype (str):
Insight subtype. Insight content schema will
be stable for a given subtype.
- content (~.struct.Struct):
+ content (google.protobuf.struct_pb2.Struct):
A struct of custom fields to explain the
insight. Example: "grantedPermissionsCount":
"1000".
- last_refresh_time (~.timestamp.Timestamp):
+ last_refresh_time (google.protobuf.timestamp_pb2.Timestamp):
Timestamp of the latest data used to generate
the insight.
- observation_period (~.duration.Duration):
+ observation_period (google.protobuf.duration_pb2.Duration):
Observation period that led to the insight. The source data
used to generate the insight ends at last_refresh_time and
begins at (last_refresh_time - observation_period).
- state_info (~.insight.InsightStateInfo):
+ state_info (google.cloud.recommender_v1.types.InsightStateInfo):
Information state and metadata.
- category (~.insight.Insight.Category):
+ category (google.cloud.recommender_v1.types.Insight.Category):
Category being targeted by the insight.
etag (str):
Fingerprint of the Insight. Provides
optimistic locking when updating states.
- associated_recommendations (Sequence[~.insight.Insight.RecommendationReference]):
+ associated_recommendations (Sequence[google.cloud.recommender_v1.types.Insight.RecommendationReference]):
Recommendations derived from this insight.
"""
@@ -118,9 +118,9 @@ class InsightStateInfo(proto.Message):
r"""Information related to insight state.
Attributes:
- state (~.insight.InsightStateInfo.State):
+ state (google.cloud.recommender_v1.types.InsightStateInfo.State):
Insight state.
- state_metadata (Sequence[~.insight.InsightStateInfo.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.InsightStateInfo.StateMetadataEntry]):
A map of metadata for the state, provided by
user or automations systems.
"""
diff --git a/google/cloud/recommender_v1/types/recommendation.py b/google/cloud/recommender_v1/types/recommendation.py
index e1c4de9..ee4ad3f 100644
--- a/google/cloud/recommender_v1/types/recommendation.py
+++ b/google/cloud/recommender_v1/types/recommendation.py
@@ -62,29 +62,29 @@ class Recommendation(proto.Message):
Examples: For recommender = "google.iam.policy.Recommender",
recommender_subtype can be one of
"REMOVE_ROLE"/"REPLACE_ROLE".
- last_refresh_time (~.timestamp.Timestamp):
+ last_refresh_time (google.protobuf.timestamp_pb2.Timestamp):
Last time this recommendation was refreshed
by the system that created it in the first
place.
- primary_impact (~.recommendation.Impact):
+ primary_impact (google.cloud.recommender_v1.types.Impact):
The primary impact that this recommendation
can have while trying to optimize for one
category.
- additional_impact (Sequence[~.recommendation.Impact]):
+ additional_impact (Sequence[google.cloud.recommender_v1.types.Impact]):
Optional set of additional impact that this
recommendation may have when trying to optimize
for the primary category. These may be positive
or negative.
- content (~.recommendation.RecommendationContent):
+ content (google.cloud.recommender_v1.types.RecommendationContent):
Content of the recommendation describing
recommended changes to resources.
- state_info (~.recommendation.RecommendationStateInfo):
+ state_info (google.cloud.recommender_v1.types.RecommendationStateInfo):
Information for state. Contains state and
metadata.
etag (str):
Fingerprint of the Recommendation. Provides
optimistic locking when updating states.
- associated_insights (Sequence[~.recommendation.Recommendation.InsightReference]):
+ associated_insights (Sequence[google.cloud.recommender_v1.types.Recommendation.InsightReference]):
Insights that led to this recommendation.
"""
@@ -131,7 +131,7 @@ class RecommendationContent(proto.Message):
changing.
Attributes:
- operation_groups (Sequence[~.recommendation.OperationGroup]):
+ operation_groups (Sequence[google.cloud.recommender_v1.types.OperationGroup]):
Operations to one or more Google Cloud
resources grouped in such a way that, all
operations within one group are expected to be
@@ -147,7 +147,7 @@ class OperationGroup(proto.Message):
r"""Group of operations that need to be performed atomically.
Attributes:
- operations (Sequence[~.recommendation.Operation]):
+ operations (Sequence[google.cloud.recommender_v1.types.Operation]):
List of operations across one or more
resources that belong to this group. Loosely
based on RFC6902 and should be performed in the
@@ -199,16 +199,16 @@ class Operation(proto.Message):
Can be set with action 'copy' or 'move' to indicate the
source field within resource or source_resource, ignored if
provided for other operation types.
- value (~.struct.Value):
+ value (google.protobuf.struct_pb2.Value):
Value for the ``path`` field. Will be set for
actions:'add'/'replace'. Maybe set for action: 'test'.
Either this or ``value_matcher`` will be set for 'test'
operation. An exact match must be performed.
- value_matcher (~.recommendation.ValueMatcher):
+ value_matcher (google.cloud.recommender_v1.types.ValueMatcher):
Can be set for action 'test' for advanced matching for the
value of 'path' field. Either this or ``value`` will be set
for 'test' operation.
- path_filters (Sequence[~.recommendation.Operation.PathFiltersEntry]):
+ path_filters (Sequence[google.cloud.recommender_v1.types.Operation.PathFiltersEntry]):
Set of filters to apply if ``path`` refers to array elements
or nested array elements in order to narrow down to a single
unique element that is being tested/modified. This is
@@ -224,7 +224,7 @@ class Operation(proto.Message):
"y@example.com"] }`` When both path_filters and
path_value_matchers are set, an implicit AND must be
performed.
- path_value_matchers (Sequence[~.recommendation.Operation.PathValueMatchersEntry]):
+ path_value_matchers (Sequence[google.cloud.recommender_v1.types.Operation.PathValueMatchersEntry]):
Similar to path_filters, this contains set of filters to
apply if ``path`` field referes to array elements. This is
meant to support value matching beyond exact match. To
@@ -283,13 +283,13 @@ class CostProjection(proto.Message):
save or incur.
Attributes:
- cost (~.money.Money):
+ cost (google.type.money_pb2.Money):
An approximate projection on amount saved or
amount incurred. Negative cost units indicate
cost savings and positive cost units indicate
increase. See google.type.Money documentation
for positive/negative units.
- duration (~.gp_duration.Duration):
+ duration (google.protobuf.duration_pb2.Duration):
Duration for which this cost applies.
"""
@@ -303,9 +303,9 @@ class Impact(proto.Message):
category.
Attributes:
- category (~.recommendation.Impact.Category):
+ category (google.cloud.recommender_v1.types.Impact.Category):
Category that is being targeted.
- cost_projection (~.recommendation.CostProjection):
+ cost_projection (google.cloud.recommender_v1.types.CostProjection):
Use with CategoryType.COST
"""
@@ -328,10 +328,10 @@ class RecommendationStateInfo(proto.Message):
r"""Information for state. Contains state and metadata.
Attributes:
- state (~.recommendation.RecommendationStateInfo.State):
+ state (google.cloud.recommender_v1.types.RecommendationStateInfo.State):
The state of the recommendation, Eg ACTIVE,
SUCCEEDED, FAILED.
- state_metadata (Sequence[~.recommendation.RecommendationStateInfo.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.RecommendationStateInfo.StateMetadataEntry]):
A map of metadata for the state, provided by
user or automations systems.
"""
diff --git a/google/cloud/recommender_v1/types/recommender_service.py b/google/cloud/recommender_v1/types/recommender_service.py
index c366b02..44c8d21 100644
--- a/google/cloud/recommender_v1/types/recommender_service.py
+++ b/google/cloud/recommender_v1/types/recommender_service.py
@@ -85,7 +85,7 @@ class ListInsightsResponse(proto.Message):
r"""Response to the ``ListInsights`` method.
Attributes:
- insights (Sequence[~.insight.Insight]):
+ insights (Sequence[google.cloud.recommender_v1.types.Insight]):
The set of insights for the ``parent`` resource.
next_page_token (str):
A token that can be used to request the next
@@ -119,7 +119,7 @@ class MarkInsightAcceptedRequest(proto.Message):
Attributes:
name (str):
Required. Name of the insight.
- state_metadata (Sequence[~.recommender_service.MarkInsightAcceptedRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkInsightAcceptedRequest.StateMetadataEntry]):
Optional. State properties user wish to include with this
state. Full replace of the current state_metadata.
etag (str):
@@ -180,7 +180,7 @@ class ListRecommendationsResponse(proto.Message):
r"""Response to the ``ListRecommendations`` method.
Attributes:
- recommendations (Sequence[~.recommendation.Recommendation]):
+ recommendations (Sequence[google.cloud.recommender_v1.types.Recommendation]):
The set of recommendations for the ``parent`` resource.
next_page_token (str):
A token that can be used to request the next
@@ -216,7 +216,7 @@ class MarkRecommendationClaimedRequest(proto.Message):
Attributes:
name (str):
Required. Name of the recommendation.
- state_metadata (Sequence[~.recommender_service.MarkRecommendationClaimedRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkRecommendationClaimedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites any
existing ``state_metadata``. Keys must match the regex
``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match the regex
@@ -239,7 +239,7 @@ class MarkRecommendationSucceededRequest(proto.Message):
Attributes:
name (str):
Required. Name of the recommendation.
- state_metadata (Sequence[~.recommender_service.MarkRecommendationSucceededRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkRecommendationSucceededRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites any
existing ``state_metadata``. Keys must match the regex
``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match the regex
@@ -262,7 +262,7 @@ class MarkRecommendationFailedRequest(proto.Message):
Attributes:
name (str):
Required. Name of the recommendation.
- state_metadata (Sequence[~.recommender_service.MarkRecommendationFailedRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1.types.MarkRecommendationFailedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites any
existing ``state_metadata``. Keys must match the regex
``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match the regex
diff --git a/google/cloud/recommender_v1beta1/services/recommender/async_client.py b/google/cloud/recommender_v1beta1/services/recommender/async_client.py
index 6a93552..edcfe33 100644
--- a/google/cloud/recommender_v1beta1/services/recommender/async_client.py
+++ b/google/cloud/recommender_v1beta1/services/recommender/async_client.py
@@ -90,7 +90,36 @@ class RecommenderAsyncClient:
RecommenderClient.parse_common_location_path
)
- from_service_account_file = RecommenderClient.from_service_account_file
+ @classmethod
+ def from_service_account_info(cls, info: dict, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials info.
+
+ Args:
+ info (dict): The service account private key info.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ RecommenderAsyncClient: The constructed client.
+ """
+ return RecommenderClient.from_service_account_info.__func__(RecommenderAsyncClient, info, *args, **kwargs) # type: ignore
+
+ @classmethod
+ def from_service_account_file(cls, filename: str, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials
+ file.
+
+ Args:
+ filename (str): The path to the service account private key json
+ file.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ RecommenderAsyncClient: The constructed client.
+ """
+ return RecommenderClient.from_service_account_file.__func__(RecommenderAsyncClient, filename, *args, **kwargs) # type: ignore
+
from_service_account_json = from_service_account_file
@property
@@ -168,7 +197,7 @@ async def list_insights(
type.
Args:
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.ListInsightsRequest`):
The request object. Request for the `ListInsights`
method.
parent (:class:`str`):
@@ -183,6 +212,7 @@ async def list_insights(
https://cloud.google.com/about/locations/
INSIGHT_TYPE_ID refers to supported insight types:
https://cloud.google.com/recommender/docs/insights/insight-types.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -194,8 +224,8 @@ async def list_insights(
sent along with the request as metadata.
Returns:
- ~.pagers.ListInsightsAsyncPager:
- Response to the ``ListInsights`` method.
+ google.cloud.recommender_v1beta1.services.recommender.pagers.ListInsightsAsyncPager:
+ Response to the ListInsights method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -230,6 +260,7 @@ async def list_insights(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -266,7 +297,7 @@ async def get_insight(
permission for the specified insight type.
Args:
- request (:class:`~.recommender_service.GetInsightRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.GetInsightRequest`):
The request object. Request to the `GetInsight` method.
name (:class:`str`):
Required. Name of the insight.
@@ -281,7 +312,7 @@ async def get_insight(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1beta1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -317,6 +348,7 @@ async def get_insight(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -357,7 +389,7 @@ async def mark_insight_accepted(
specified insight.
Args:
- request (:class:`~.recommender_service.MarkInsightAcceptedRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.MarkInsightAcceptedRequest`):
The request object. Request for the
`MarkInsightAccepted` method.
name (:class:`str`):
@@ -365,15 +397,17 @@ async def mark_insight_accepted(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkInsightAcceptedRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1beta1.types.MarkInsightAcceptedRequest.StateMetadataEntry]`):
Optional. State properties user wish to include with
this state. Full replace of the current state_metadata.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
etag (:class:`str`):
Required. Fingerprint of the Insight.
Provides optimistic locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -385,7 +419,7 @@ async def mark_insight_accepted(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1beta1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -449,7 +483,7 @@ async def list_recommendations(
recommender.*.list IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.ListRecommendationsRequest`):
The request object. Request for the
`ListRecommendations` method.
parent (:class:`str`):
@@ -464,6 +498,7 @@ async def list_recommendations(
https://cloud.google.com/about/locations/ RECOMMENDER_ID
refers to supported recommenders:
https://cloud.google.com/recommender/docs/recommenders.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -471,7 +506,8 @@ async def list_recommendations(
Filter expression to restrict the recommendations
returned. Supported filter fields: state_info.state Eg:
\`state_info.state:"DISMISSED" or
- state_info.state:"FAILED".
+ state_info.state:"FAILED"
+
This corresponds to the ``filter`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -483,8 +519,8 @@ async def list_recommendations(
sent along with the request as metadata.
Returns:
- ~.pagers.ListRecommendationsAsyncPager:
- Response to the ``ListRecommendations`` method.
+ google.cloud.recommender_v1beta1.services.recommender.pagers.ListRecommendationsAsyncPager:
+ Response to the ListRecommendations method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -521,6 +557,7 @@ async def list_recommendations(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -557,7 +594,7 @@ async def get_recommendation(
recommender.*.get IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.GetRecommendationRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.GetRecommendationRequest`):
The request object. Request to the `GetRecommendation`
method.
name (:class:`str`):
@@ -573,7 +610,7 @@ async def get_recommendation(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -609,6 +646,7 @@ async def get_recommendation(
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=DEFAULT_CLIENT_INFO,
@@ -652,7 +690,7 @@ async def mark_recommendation_claimed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationClaimedRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.MarkRecommendationClaimedRequest`):
The request object. Request for the
`MarkRecommendationClaimed` Method.
name (:class:`str`):
@@ -660,11 +698,12 @@ async def mark_recommendation_claimed(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationClaimedRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationClaimedRequest.StateMetadataEntry]`):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -672,6 +711,7 @@ async def mark_recommendation_claimed(
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -683,7 +723,7 @@ async def mark_recommendation_claimed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -759,7 +799,7 @@ async def mark_recommendation_succeeded(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationSucceededRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.MarkRecommendationSucceededRequest`):
The request object. Request for the
`MarkRecommendationSucceeded` Method.
name (:class:`str`):
@@ -767,11 +807,12 @@ async def mark_recommendation_succeeded(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationSucceededRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationSucceededRequest.StateMetadataEntry]`):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -779,6 +820,7 @@ async def mark_recommendation_succeeded(
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -790,7 +832,7 @@ async def mark_recommendation_succeeded(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -866,7 +908,7 @@ async def mark_recommendation_failed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationFailedRequest`):
+ request (:class:`google.cloud.recommender_v1beta1.types.MarkRecommendationFailedRequest`):
The request object. Request for the
`MarkRecommendationFailed` Method.
name (:class:`str`):
@@ -874,11 +916,12 @@ async def mark_recommendation_failed(
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationFailedRequest.StateMetadataEntry]`):
+ state_metadata (:class:`Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationFailedRequest.StateMetadataEntry]`):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -886,6 +929,7 @@ async def mark_recommendation_failed(
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -897,7 +941,7 @@ async def mark_recommendation_failed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
diff --git a/google/cloud/recommender_v1beta1/services/recommender/client.py b/google/cloud/recommender_v1beta1/services/recommender/client.py
index e3ea7d3..901efec 100644
--- a/google/cloud/recommender_v1beta1/services/recommender/client.py
+++ b/google/cloud/recommender_v1beta1/services/recommender/client.py
@@ -118,6 +118,22 @@ def _get_default_mtls_endpoint(api_endpoint):
DEFAULT_ENDPOINT
)
+ @classmethod
+ def from_service_account_info(cls, info: dict, *args, **kwargs):
+ """Creates an instance of this client using the provided credentials info.
+
+ Args:
+ info (dict): The service account private key info.
+ args: Additional arguments to pass to the constructor.
+ kwargs: Additional arguments to pass to the constructor.
+
+ Returns:
+ RecommenderClient: The constructed client.
+ """
+ credentials = service_account.Credentials.from_service_account_info(info)
+ kwargs["credentials"] = credentials
+ return cls(*args, **kwargs)
+
@classmethod
def from_service_account_file(cls, filename: str, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
@@ -130,7 +146,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
kwargs: Additional arguments to pass to the constructor.
Returns:
- {@api.name}: The constructed client.
+ RecommenderClient: The constructed client.
"""
credentials = service_account.Credentials.from_service_account_file(filename)
kwargs["credentials"] = credentials
@@ -296,10 +312,10 @@ def __init__(
credentials identify the application to the service; if none
are specified, the client will attempt to ascertain the
credentials from the environment.
- transport (Union[str, ~.RecommenderTransport]): The
+ transport (Union[str, RecommenderTransport]): The
transport to use. If set to None, a transport is chosen
automatically.
- client_options (client_options_lib.ClientOptions): Custom options for the
+ client_options (google.api_core.client_options.ClientOptions): Custom options for the
client. It won't take effect if a ``transport`` instance is provided.
(1) The ``api_endpoint`` property can be used to override the
default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
@@ -335,21 +351,17 @@ def __init__(
util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"))
)
- ssl_credentials = None
+ client_cert_source_func = None
is_mtls = False
if use_client_cert:
if client_options.client_cert_source:
- import grpc # type: ignore
-
- cert, key = client_options.client_cert_source()
- ssl_credentials = grpc.ssl_channel_credentials(
- certificate_chain=cert, private_key=key
- )
is_mtls = True
+ client_cert_source_func = client_options.client_cert_source
else:
- creds = SslCredentials()
- is_mtls = creds.is_mtls
- ssl_credentials = creds.ssl_credentials if is_mtls else None
+ is_mtls = mtls.has_default_client_cert_source()
+ client_cert_source_func = (
+ mtls.default_client_cert_source() if is_mtls else None
+ )
# Figure out which api endpoint to use.
if client_options.api_endpoint is not None:
@@ -392,7 +404,7 @@ def __init__(
credentials_file=client_options.credentials_file,
host=api_endpoint,
scopes=client_options.scopes,
- ssl_channel_credentials=ssl_credentials,
+ client_cert_source_for_mtls=client_cert_source_func,
quota_project_id=client_options.quota_project_id,
client_info=client_info,
)
@@ -411,10 +423,10 @@ def list_insights(
type.
Args:
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (google.cloud.recommender_v1beta1.types.ListInsightsRequest):
The request object. Request for the `ListInsights`
method.
- parent (:class:`str`):
+ parent (str):
Required. The container resource on which to execute the
request. Acceptable formats:
@@ -426,6 +438,7 @@ def list_insights(
https://cloud.google.com/about/locations/
INSIGHT_TYPE_ID refers to supported insight types:
https://cloud.google.com/recommender/docs/insights/insight-types.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -437,8 +450,8 @@ def list_insights(
sent along with the request as metadata.
Returns:
- ~.pagers.ListInsightsPager:
- Response to the ``ListInsights`` method.
+ google.cloud.recommender_v1beta1.services.recommender.pagers.ListInsightsPager:
+ Response to the ListInsights method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -502,9 +515,9 @@ def get_insight(
permission for the specified insight type.
Args:
- request (:class:`~.recommender_service.GetInsightRequest`):
+ request (google.cloud.recommender_v1beta1.types.GetInsightRequest):
The request object. Request to the `GetInsight` method.
- name (:class:`str`):
+ name (str):
Required. Name of the insight.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -517,7 +530,7 @@ def get_insight(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1beta1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -586,23 +599,25 @@ def mark_insight_accepted(
specified insight.
Args:
- request (:class:`~.recommender_service.MarkInsightAcceptedRequest`):
+ request (google.cloud.recommender_v1beta1.types.MarkInsightAcceptedRequest):
The request object. Request for the
`MarkInsightAccepted` method.
- name (:class:`str`):
+ name (str):
Required. Name of the insight.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkInsightAcceptedRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkInsightAcceptedRequest.StateMetadataEntry]):
Optional. State properties user wish to include with
this state. Full replace of the current state_metadata.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the Insight.
Provides optimistic locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -614,7 +629,7 @@ def mark_insight_accepted(
sent along with the request as metadata.
Returns:
- ~.insight.Insight:
+ google.cloud.recommender_v1beta1.types.Insight:
An insight along with the information
used to derive the insight. The insight
may have associated recomendations as
@@ -643,12 +658,11 @@ def mark_insight_accepted(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[self._transport.mark_insight_accepted]
@@ -679,10 +693,10 @@ def list_recommendations(
recommender.*.list IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (google.cloud.recommender_v1beta1.types.ListRecommendationsRequest):
The request object. Request for the
`ListRecommendations` method.
- parent (:class:`str`):
+ parent (str):
Required. The container resource on which to execute the
request. Acceptable formats:
@@ -694,14 +708,16 @@ def list_recommendations(
https://cloud.google.com/about/locations/ RECOMMENDER_ID
refers to supported recommenders:
https://cloud.google.com/recommender/docs/recommenders.
+
This corresponds to the ``parent`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- filter (:class:`str`):
+ filter (str):
Filter expression to restrict the recommendations
returned. Supported filter fields: state_info.state Eg:
\`state_info.state:"DISMISSED" or
- state_info.state:"FAILED".
+ state_info.state:"FAILED"
+
This corresponds to the ``filter`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -713,8 +729,8 @@ def list_recommendations(
sent along with the request as metadata.
Returns:
- ~.pagers.ListRecommendationsPager:
- Response to the ``ListRecommendations`` method.
+ google.cloud.recommender_v1beta1.services.recommender.pagers.ListRecommendationsPager:
+ Response to the ListRecommendations method.
Iterating over this object will yield results and
resolve additional pages automatically.
@@ -780,10 +796,10 @@ def get_recommendation(
recommender.*.get IAM permission for the specified recommender.
Args:
- request (:class:`~.recommender_service.GetRecommendationRequest`):
+ request (google.cloud.recommender_v1beta1.types.GetRecommendationRequest):
The request object. Request to the `GetRecommendation`
method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
@@ -796,7 +812,7 @@ def get_recommendation(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -868,26 +884,28 @@ def mark_recommendation_claimed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationClaimedRequest`):
+ request (google.cloud.recommender_v1beta1.types.MarkRecommendationClaimedRequest):
The request object. Request for the
`MarkRecommendationClaimed` Method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationClaimedRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationClaimedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -899,7 +917,7 @@ def mark_recommendation_claimed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -930,12 +948,11 @@ def mark_recommendation_claimed(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[
@@ -980,26 +997,28 @@ def mark_recommendation_succeeded(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationSucceededRequest`):
+ request (google.cloud.recommender_v1beta1.types.MarkRecommendationSucceededRequest):
The request object. Request for the
`MarkRecommendationSucceeded` Method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationSucceededRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationSucceededRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -1011,7 +1030,7 @@ def mark_recommendation_succeeded(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -1042,12 +1061,11 @@ def mark_recommendation_succeeded(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[
@@ -1092,26 +1110,28 @@ def mark_recommendation_failed(
specified recommender.
Args:
- request (:class:`~.recommender_service.MarkRecommendationFailedRequest`):
+ request (google.cloud.recommender_v1beta1.types.MarkRecommendationFailedRequest):
The request object. Request for the
`MarkRecommendationFailed` Method.
- name (:class:`str`):
+ name (str):
Required. Name of the recommendation.
This corresponds to the ``name`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- state_metadata (:class:`Sequence[~.recommender_service.MarkRecommendationFailedRequest.StateMetadataEntry]`):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationFailedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites
any existing ``state_metadata``. Keys must match the
regex ``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match
the regex ``/^[a-zA-Z0-9_./-]{0,255}$/``.
+
This corresponds to the ``state_metadata`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
- etag (:class:`str`):
+ etag (str):
Required. Fingerprint of the
Recommendation. Provides optimistic
locking.
+
This corresponds to the ``etag`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
@@ -1123,7 +1143,7 @@ def mark_recommendation_failed(
sent along with the request as metadata.
Returns:
- ~.recommendation.Recommendation:
+ google.cloud.recommender_v1beta1.types.Recommendation:
A recommendation along with a
suggested action. E.g., a rightsizing
recommendation for an underutilized VM,
@@ -1152,12 +1172,11 @@ def mark_recommendation_failed(
if name is not None:
request.name = name
+ if state_metadata is not None:
+ request.state_metadata = state_metadata
if etag is not None:
request.etag = etag
- if state_metadata:
- request.state_metadata.update(state_metadata)
-
# Wrap the RPC method; this adds retry and timeout information,
# and friendly error handling.
rpc = self._transport._wrapped_methods[
diff --git a/google/cloud/recommender_v1beta1/services/recommender/pagers.py b/google/cloud/recommender_v1beta1/services/recommender/pagers.py
index cbcf8d9..57ced80 100644
--- a/google/cloud/recommender_v1beta1/services/recommender/pagers.py
+++ b/google/cloud/recommender_v1beta1/services/recommender/pagers.py
@@ -15,7 +15,16 @@
# limitations under the License.
#
-from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple
+from typing import (
+ Any,
+ AsyncIterable,
+ Awaitable,
+ Callable,
+ Iterable,
+ Sequence,
+ Tuple,
+ Optional,
+)
from google.cloud.recommender_v1beta1.types import insight
from google.cloud.recommender_v1beta1.types import recommendation
@@ -26,7 +35,7 @@ class ListInsightsPager:
"""A pager for iterating through ``list_insights`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListInsightsResponse` object, and
+ :class:`google.cloud.recommender_v1beta1.types.ListInsightsResponse` object, and
provides an ``__iter__`` method to iterate through its
``insights`` field.
@@ -35,7 +44,7 @@ class ListInsightsPager:
through the ``insights`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListInsightsResponse`
+ All the usual :class:`google.cloud.recommender_v1beta1.types.ListInsightsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -53,9 +62,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (google.cloud.recommender_v1beta1.types.ListInsightsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListInsightsResponse`):
+ response (google.cloud.recommender_v1beta1.types.ListInsightsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -88,7 +97,7 @@ class ListInsightsAsyncPager:
"""A pager for iterating through ``list_insights`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListInsightsResponse` object, and
+ :class:`google.cloud.recommender_v1beta1.types.ListInsightsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``insights`` field.
@@ -97,7 +106,7 @@ class ListInsightsAsyncPager:
through the ``insights`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListInsightsResponse`
+ All the usual :class:`google.cloud.recommender_v1beta1.types.ListInsightsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -115,9 +124,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListInsightsRequest`):
+ request (google.cloud.recommender_v1beta1.types.ListInsightsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListInsightsResponse`):
+ response (google.cloud.recommender_v1beta1.types.ListInsightsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -154,7 +163,7 @@ class ListRecommendationsPager:
"""A pager for iterating through ``list_recommendations`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListRecommendationsResponse` object, and
+ :class:`google.cloud.recommender_v1beta1.types.ListRecommendationsResponse` object, and
provides an ``__iter__`` method to iterate through its
``recommendations`` field.
@@ -163,7 +172,7 @@ class ListRecommendationsPager:
through the ``recommendations`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListRecommendationsResponse`
+ All the usual :class:`google.cloud.recommender_v1beta1.types.ListRecommendationsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -181,9 +190,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (google.cloud.recommender_v1beta1.types.ListRecommendationsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListRecommendationsResponse`):
+ response (google.cloud.recommender_v1beta1.types.ListRecommendationsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
@@ -216,7 +225,7 @@ class ListRecommendationsAsyncPager:
"""A pager for iterating through ``list_recommendations`` requests.
This class thinly wraps an initial
- :class:`~.recommender_service.ListRecommendationsResponse` object, and
+ :class:`google.cloud.recommender_v1beta1.types.ListRecommendationsResponse` object, and
provides an ``__aiter__`` method to iterate through its
``recommendations`` field.
@@ -225,7 +234,7 @@ class ListRecommendationsAsyncPager:
through the ``recommendations`` field on the
corresponding responses.
- All the usual :class:`~.recommender_service.ListRecommendationsResponse`
+ All the usual :class:`google.cloud.recommender_v1beta1.types.ListRecommendationsResponse`
attributes are available on the pager. If multiple requests are made, only
the most recent response is retained, and thus used for attribute lookup.
"""
@@ -245,9 +254,9 @@ def __init__(
Args:
method (Callable): The method that was originally called, and
which instantiated this pager.
- request (:class:`~.recommender_service.ListRecommendationsRequest`):
+ request (google.cloud.recommender_v1beta1.types.ListRecommendationsRequest):
The initial request object.
- response (:class:`~.recommender_service.ListRecommendationsResponse`):
+ response (google.cloud.recommender_v1beta1.types.ListRecommendationsResponse):
The initial response object.
metadata (Sequence[Tuple[str, str]]): Strings which should be
sent along with the request as metadata.
diff --git a/google/cloud/recommender_v1beta1/services/recommender/transports/base.py b/google/cloud/recommender_v1beta1/services/recommender/transports/base.py
index 00e2b76..28e9cc2 100644
--- a/google/cloud/recommender_v1beta1/services/recommender/transports/base.py
+++ b/google/cloud/recommender_v1beta1/services/recommender/transports/base.py
@@ -71,10 +71,10 @@ def __init__(
scope (Optional[Sequence[str]]): A list of scopes.
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're developing
+ 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.
"""
# Save the hostname. Default to port 443 (HTTPS) if none is specified.
@@ -82,6 +82,9 @@ def __init__(
host += ":443"
self._host = host
+ # Save the scopes.
+ self._scopes = scopes or self.AUTH_SCOPES
+
# If no credentials are provided, then determine the appropriate
# defaults.
if credentials and credentials_file:
@@ -91,20 +94,17 @@ def __init__(
if credentials_file is not None:
credentials, _ = auth.load_credentials_from_file(
- credentials_file, scopes=scopes, quota_project_id=quota_project_id
+ credentials_file, scopes=self._scopes, quota_project_id=quota_project_id
)
elif credentials is None:
credentials, _ = auth.default(
- scopes=scopes, quota_project_id=quota_project_id
+ scopes=self._scopes, quota_project_id=quota_project_id
)
# Save the credentials.
self._credentials = credentials
- # Lifted into its own function so it can be stubbed out during tests.
- self._prep_wrapped_messages(client_info)
-
def _prep_wrapped_messages(self, client_info):
# Precompute the wrapped methods.
self._wrapped_methods = {
@@ -117,6 +117,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
@@ -130,6 +131,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
@@ -148,6 +150,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
@@ -161,6 +164,7 @@ def _prep_wrapped_messages(self, client_info):
predicate=retries.if_exception_type(
exceptions.DeadlineExceeded, exceptions.ServiceUnavailable,
),
+ deadline=60.0,
),
default_timeout=60.0,
client_info=client_info,
diff --git a/google/cloud/recommender_v1beta1/services/recommender/transports/grpc.py b/google/cloud/recommender_v1beta1/services/recommender/transports/grpc.py
index 10c3583..e8d4502 100644
--- a/google/cloud/recommender_v1beta1/services/recommender/transports/grpc.py
+++ b/google/cloud/recommender_v1beta1/services/recommender/transports/grpc.py
@@ -63,6 +63,7 @@ def __init__(
api_mtls_endpoint: str = None,
client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
ssl_channel_credentials: grpc.ChannelCredentials = None,
+ client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
quota_project_id: Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
@@ -93,6 +94,10 @@ def __init__(
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
for grpc channel. It is ignored if ``channel`` is provided.
+ client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
+ A callback to provide client certificate bytes and private key bytes,
+ both in PEM format. It is used to configure mutual TLS channel. It is
+ ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
@@ -107,72 +112,60 @@ def __init__(
google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials``
and ``credentials_file`` are passed.
"""
+ self._grpc_channel = None
self._ssl_channel_credentials = ssl_channel_credentials
+ self._stubs: Dict[str, Callable] = {}
+
+ if api_mtls_endpoint:
+ warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning)
+ if client_cert_source:
+ warnings.warn("client_cert_source is deprecated", DeprecationWarning)
if channel:
- # Sanity check: Ensure that channel and credentials are not both
- # provided.
+ # Ignore credentials if a channel was passed.
credentials = False
-
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
- elif api_mtls_endpoint:
- warnings.warn(
- "api_mtls_endpoint and client_cert_source are deprecated",
- DeprecationWarning,
- )
- host = (
- api_mtls_endpoint
- if ":" in api_mtls_endpoint
- else api_mtls_endpoint + ":443"
- )
+ else:
+ if api_mtls_endpoint:
+ host = api_mtls_endpoint
+
+ # Create SSL credentials with client_cert_source or application
+ # default SSL credentials.
+ if client_cert_source:
+ cert, key = client_cert_source()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
+ else:
+ self._ssl_channel_credentials = SslCredentials().ssl_credentials
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
-
- # Create SSL credentials with client_cert_source or application
- # default SSL credentials.
- if client_cert_source:
- cert, key = client_cert_source()
- ssl_credentials = grpc.ssl_channel_credentials(
- certificate_chain=cert, private_key=key
- )
else:
- ssl_credentials = SslCredentials().ssl_credentials
-
- # create a new channel. The provided one is ignored.
- self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
- credentials_file=credentials_file,
- ssl_credentials=ssl_credentials,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- options=[
- ("grpc.max_send_message_length", -1),
- ("grpc.max_receive_message_length", -1),
- ],
- )
- self._ssl_channel_credentials = ssl_credentials
- else:
- host = host if ":" in host else host + ":443"
+ if client_cert_source_for_mtls and not ssl_channel_credentials:
+ cert, key = client_cert_source_for_mtls()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
+ # The base transport sets the host, credentials and scopes
+ super().__init__(
+ host=host,
+ credentials=credentials,
+ credentials_file=credentials_file,
+ scopes=scopes,
+ quota_project_id=quota_project_id,
+ client_info=client_info,
+ )
- # create a new channel. The provided one is ignored.
+ if not self._grpc_channel:
self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
+ self._host,
+ credentials=self._credentials,
credentials_file=credentials_file,
- ssl_credentials=ssl_channel_credentials,
- scopes=scopes or self.AUTH_SCOPES,
+ scopes=self._scopes,
+ ssl_credentials=self._ssl_channel_credentials,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
@@ -180,17 +173,8 @@ def __init__(
],
)
- self._stubs = {} # type: Dict[str, Callable]
-
- # Run the base constructor.
- super().__init__(
- host=host,
- credentials=credentials,
- credentials_file=credentials_file,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- client_info=client_info,
- )
+ # Wrap messages. This must be done after self._grpc_channel exists
+ self._prep_wrapped_messages(client_info)
@classmethod
def create_channel(
@@ -204,7 +188,7 @@ def create_channel(
) -> grpc.Channel:
"""Create and return a gRPC channel object.
Args:
- address (Optional[str]): The host for the channel to use.
+ host (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
diff --git a/google/cloud/recommender_v1beta1/services/recommender/transports/grpc_asyncio.py b/google/cloud/recommender_v1beta1/services/recommender/transports/grpc_asyncio.py
index 3f4fb2b..a99ea36 100644
--- a/google/cloud/recommender_v1beta1/services/recommender/transports/grpc_asyncio.py
+++ b/google/cloud/recommender_v1beta1/services/recommender/transports/grpc_asyncio.py
@@ -67,7 +67,7 @@ def create_channel(
) -> aio.Channel:
"""Create and return a gRPC AsyncIO channel object.
Args:
- address (Optional[str]): The host for the channel to use.
+ host (Optional[str]): The host for the channel to use.
credentials (Optional[~.Credentials]): The
authorization credentials to attach to requests. These
credentials identify this application to the service. If
@@ -107,6 +107,7 @@ def __init__(
api_mtls_endpoint: str = None,
client_cert_source: Callable[[], Tuple[bytes, bytes]] = None,
ssl_channel_credentials: grpc.ChannelCredentials = None,
+ client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None,
quota_project_id=None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
) -> None:
@@ -138,12 +139,16 @@ def __init__(
``api_mtls_endpoint`` is None.
ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials
for grpc channel. It is ignored if ``channel`` is provided.
+ client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]):
+ A callback to provide client certificate bytes and private key bytes,
+ both in PEM format. It is used to configure mutual TLS channel. It is
+ ignored if ``channel`` or ``ssl_channel_credentials`` is provided.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
- client_info (google.api_core.gapic_v1.client_info.ClientInfo):
- 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
+ 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:
@@ -152,72 +157,60 @@ def __init__(
google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials``
and ``credentials_file`` are passed.
"""
+ self._grpc_channel = None
self._ssl_channel_credentials = ssl_channel_credentials
+ self._stubs: Dict[str, Callable] = {}
+
+ if api_mtls_endpoint:
+ warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning)
+ if client_cert_source:
+ warnings.warn("client_cert_source is deprecated", DeprecationWarning)
if channel:
- # Sanity check: Ensure that channel and credentials are not both
- # provided.
+ # Ignore credentials if a channel was passed.
credentials = False
-
# If a channel was explicitly provided, set it.
self._grpc_channel = channel
self._ssl_channel_credentials = None
- elif api_mtls_endpoint:
- warnings.warn(
- "api_mtls_endpoint and client_cert_source are deprecated",
- DeprecationWarning,
- )
- host = (
- api_mtls_endpoint
- if ":" in api_mtls_endpoint
- else api_mtls_endpoint + ":443"
- )
+ else:
+ if api_mtls_endpoint:
+ host = api_mtls_endpoint
+
+ # Create SSL credentials with client_cert_source or application
+ # default SSL credentials.
+ if client_cert_source:
+ cert, key = client_cert_source()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
+ else:
+ self._ssl_channel_credentials = SslCredentials().ssl_credentials
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
-
- # Create SSL credentials with client_cert_source or application
- # default SSL credentials.
- if client_cert_source:
- cert, key = client_cert_source()
- ssl_credentials = grpc.ssl_channel_credentials(
- certificate_chain=cert, private_key=key
- )
else:
- ssl_credentials = SslCredentials().ssl_credentials
-
- # create a new channel. The provided one is ignored.
- self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
- credentials_file=credentials_file,
- ssl_credentials=ssl_credentials,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- options=[
- ("grpc.max_send_message_length", -1),
- ("grpc.max_receive_message_length", -1),
- ],
- )
- self._ssl_channel_credentials = ssl_credentials
- else:
- host = host if ":" in host else host + ":443"
+ if client_cert_source_for_mtls and not ssl_channel_credentials:
+ cert, key = client_cert_source_for_mtls()
+ self._ssl_channel_credentials = grpc.ssl_channel_credentials(
+ certificate_chain=cert, private_key=key
+ )
- if credentials is None:
- credentials, _ = auth.default(
- scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
- )
+ # The base transport sets the host, credentials and scopes
+ super().__init__(
+ host=host,
+ credentials=credentials,
+ credentials_file=credentials_file,
+ scopes=scopes,
+ quota_project_id=quota_project_id,
+ client_info=client_info,
+ )
- # create a new channel. The provided one is ignored.
+ if not self._grpc_channel:
self._grpc_channel = type(self).create_channel(
- host,
- credentials=credentials,
+ self._host,
+ credentials=self._credentials,
credentials_file=credentials_file,
- ssl_credentials=ssl_channel_credentials,
- scopes=scopes or self.AUTH_SCOPES,
+ scopes=self._scopes,
+ ssl_credentials=self._ssl_channel_credentials,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
@@ -225,17 +218,8 @@ def __init__(
],
)
- # Run the base constructor.
- super().__init__(
- host=host,
- credentials=credentials,
- credentials_file=credentials_file,
- scopes=scopes or self.AUTH_SCOPES,
- quota_project_id=quota_project_id,
- client_info=client_info,
- )
-
- self._stubs = {}
+ # Wrap messages. This must be done after self._grpc_channel exists
+ self._prep_wrapped_messages(client_info)
@property
def grpc_channel(self) -> aio.Channel:
diff --git a/google/cloud/recommender_v1beta1/types/__init__.py b/google/cloud/recommender_v1beta1/types/__init__.py
index f75c4f2..395fe1a 100644
--- a/google/cloud/recommender_v1beta1/types/__init__.py
+++ b/google/cloud/recommender_v1beta1/types/__init__.py
@@ -20,47 +20,47 @@
InsightStateInfo,
)
from .recommendation import (
- Recommendation,
- RecommendationContent,
- OperationGroup,
- Operation,
- ValueMatcher,
CostProjection,
Impact,
+ Operation,
+ OperationGroup,
+ Recommendation,
+ RecommendationContent,
RecommendationStateInfo,
+ ValueMatcher,
)
from .recommender_service import (
+ GetInsightRequest,
+ GetRecommendationRequest,
ListInsightsRequest,
ListInsightsResponse,
- GetInsightRequest,
- MarkInsightAcceptedRequest,
ListRecommendationsRequest,
ListRecommendationsResponse,
- GetRecommendationRequest,
+ MarkInsightAcceptedRequest,
MarkRecommendationClaimedRequest,
- MarkRecommendationSucceededRequest,
MarkRecommendationFailedRequest,
+ MarkRecommendationSucceededRequest,
)
__all__ = (
"Insight",
"InsightStateInfo",
- "Recommendation",
- "RecommendationContent",
- "OperationGroup",
- "Operation",
- "ValueMatcher",
"CostProjection",
"Impact",
+ "Operation",
+ "OperationGroup",
+ "Recommendation",
+ "RecommendationContent",
"RecommendationStateInfo",
+ "ValueMatcher",
+ "GetInsightRequest",
+ "GetRecommendationRequest",
"ListInsightsRequest",
"ListInsightsResponse",
- "GetInsightRequest",
- "MarkInsightAcceptedRequest",
"ListRecommendationsRequest",
"ListRecommendationsResponse",
- "GetRecommendationRequest",
+ "MarkInsightAcceptedRequest",
"MarkRecommendationClaimedRequest",
- "MarkRecommendationSucceededRequest",
"MarkRecommendationFailedRequest",
+ "MarkRecommendationSucceededRequest",
)
diff --git a/google/cloud/recommender_v1beta1/types/insight.py b/google/cloud/recommender_v1beta1/types/insight.py
index d842980..8f76281 100644
--- a/google/cloud/recommender_v1beta1/types/insight.py
+++ b/google/cloud/recommender_v1beta1/types/insight.py
@@ -45,25 +45,25 @@ class Insight(proto.Message):
insight_subtype (str):
Insight subtype. Insight content schema will
be stable for a given subtype.
- content (~.struct.Struct):
+ content (google.protobuf.struct_pb2.Struct):
A struct of custom fields to explain the
insight. Example: "grantedPermissionsCount":
"1000".
- last_refresh_time (~.timestamp.Timestamp):
+ last_refresh_time (google.protobuf.timestamp_pb2.Timestamp):
Timestamp of the latest data used to generate
the insight.
- observation_period (~.duration.Duration):
+ observation_period (google.protobuf.duration_pb2.Duration):
Observation period that led to the insight. The source data
used to generate the insight ends at last_refresh_time and
begins at (last_refresh_time - observation_period).
- state_info (~.insight.InsightStateInfo):
+ state_info (google.cloud.recommender_v1beta1.types.InsightStateInfo):
Information state and metadata.
- category (~.insight.Insight.Category):
+ category (google.cloud.recommender_v1beta1.types.Insight.Category):
Category being targeted by the insight.
etag (str):
Fingerprint of the Insight. Provides
optimistic locking when updating states.
- associated_recommendations (Sequence[~.insight.Insight.RecommendationReference]):
+ associated_recommendations (Sequence[google.cloud.recommender_v1beta1.types.Insight.RecommendationReference]):
Recommendations derived from this insight.
"""
@@ -119,9 +119,9 @@ class InsightStateInfo(proto.Message):
r"""Information related to insight state.
Attributes:
- state (~.insight.InsightStateInfo.State):
+ state (google.cloud.recommender_v1beta1.types.InsightStateInfo.State):
Insight state.
- state_metadata (Sequence[~.insight.InsightStateInfo.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.InsightStateInfo.StateMetadataEntry]):
A map of metadata for the state, provided by
user or automations systems.
"""
diff --git a/google/cloud/recommender_v1beta1/types/recommendation.py b/google/cloud/recommender_v1beta1/types/recommendation.py
index f05060f..d3a1dd7 100644
--- a/google/cloud/recommender_v1beta1/types/recommendation.py
+++ b/google/cloud/recommender_v1beta1/types/recommendation.py
@@ -62,29 +62,29 @@ class Recommendation(proto.Message):
Examples: For recommender = "google.iam.policy.Recommender",
recommender_subtype can be one of
"REMOVE_ROLE"/"REPLACE_ROLE".
- last_refresh_time (~.timestamp.Timestamp):
+ last_refresh_time (google.protobuf.timestamp_pb2.Timestamp):
Last time this recommendation was refreshed
by the system that created it in the first
place.
- primary_impact (~.recommendation.Impact):
+ primary_impact (google.cloud.recommender_v1beta1.types.Impact):
The primary impact that this recommendation
can have while trying to optimize for one
category.
- additional_impact (Sequence[~.recommendation.Impact]):
+ additional_impact (Sequence[google.cloud.recommender_v1beta1.types.Impact]):
Optional set of additional impact that this
recommendation may have when trying to optimize
for the primary category. These may be positive
or negative.
- content (~.recommendation.RecommendationContent):
+ content (google.cloud.recommender_v1beta1.types.RecommendationContent):
Content of the recommendation describing
recommended changes to resources.
- state_info (~.recommendation.RecommendationStateInfo):
+ state_info (google.cloud.recommender_v1beta1.types.RecommendationStateInfo):
Information for state. Contains state and
metadata.
etag (str):
Fingerprint of the Recommendation. Provides
optimistic locking when updating states.
- associated_insights (Sequence[~.recommendation.Recommendation.InsightReference]):
+ associated_insights (Sequence[google.cloud.recommender_v1beta1.types.Recommendation.InsightReference]):
Insights that led to this recommendation.
"""
@@ -131,7 +131,7 @@ class RecommendationContent(proto.Message):
changing.
Attributes:
- operation_groups (Sequence[~.recommendation.OperationGroup]):
+ operation_groups (Sequence[google.cloud.recommender_v1beta1.types.OperationGroup]):
Operations to one or more Google Cloud
resources grouped in such a way that, all
operations within one group are expected to be
@@ -147,7 +147,7 @@ class OperationGroup(proto.Message):
r"""Group of operations that need to be performed atomically.
Attributes:
- operations (Sequence[~.recommendation.Operation]):
+ operations (Sequence[google.cloud.recommender_v1beta1.types.Operation]):
List of operations across one or more
resources that belong to this group. Loosely
based on RFC6902 and should be performed in the
@@ -199,16 +199,16 @@ class Operation(proto.Message):
Can be set with action 'copy' or 'move' to indicate the
source field within resource or source_resource, ignored if
provided for other operation types.
- value (~.struct.Value):
+ value (google.protobuf.struct_pb2.Value):
Value for the ``path`` field. Will be set for
actions:'add'/'replace'. Maybe set for action: 'test'.
Either this or ``value_matcher`` will be set for 'test'
operation. An exact match must be performed.
- value_matcher (~.recommendation.ValueMatcher):
+ value_matcher (google.cloud.recommender_v1beta1.types.ValueMatcher):
Can be set for action 'test' for advanced matching for the
value of 'path' field. Either this or ``value`` will be set
for 'test' operation.
- path_filters (Sequence[~.recommendation.Operation.PathFiltersEntry]):
+ path_filters (Sequence[google.cloud.recommender_v1beta1.types.Operation.PathFiltersEntry]):
Set of filters to apply if ``path`` refers to array elements
or nested array elements in order to narrow down to a single
unique element that is being tested/modified. This is
@@ -224,7 +224,7 @@ class Operation(proto.Message):
"y@example.com"] }`` When both path_filters and
path_value_matchers are set, an implicit AND must be
performed.
- path_value_matchers (Sequence[~.recommendation.Operation.PathValueMatchersEntry]):
+ path_value_matchers (Sequence[google.cloud.recommender_v1beta1.types.Operation.PathValueMatchersEntry]):
Similar to path_filters, this contains set of filters to
apply if ``path`` field referes to array elements. This is
meant to support value matching beyond exact match. To
@@ -283,13 +283,13 @@ class CostProjection(proto.Message):
save or incur.
Attributes:
- cost (~.money.Money):
+ cost (google.type.money_pb2.Money):
An approximate projection on amount saved or
amount incurred. Negative cost units indicate
cost savings and positive cost units indicate
increase. See google.type.Money documentation
for positive/negative units.
- duration (~.gp_duration.Duration):
+ duration (google.protobuf.duration_pb2.Duration):
Duration for which this cost applies.
"""
@@ -303,9 +303,9 @@ class Impact(proto.Message):
category.
Attributes:
- category (~.recommendation.Impact.Category):
+ category (google.cloud.recommender_v1beta1.types.Impact.Category):
Category that is being targeted.
- cost_projection (~.recommendation.CostProjection):
+ cost_projection (google.cloud.recommender_v1beta1.types.CostProjection):
Use with CategoryType.COST
"""
@@ -328,10 +328,10 @@ class RecommendationStateInfo(proto.Message):
r"""Information for state. Contains state and metadata.
Attributes:
- state (~.recommendation.RecommendationStateInfo.State):
+ state (google.cloud.recommender_v1beta1.types.RecommendationStateInfo.State):
The state of the recommendation, Eg ACTIVE,
SUCCEEDED, FAILED.
- state_metadata (Sequence[~.recommendation.RecommendationStateInfo.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.RecommendationStateInfo.StateMetadataEntry]):
A map of metadata for the state, provided by
user or automations systems.
"""
diff --git a/google/cloud/recommender_v1beta1/types/recommender_service.py b/google/cloud/recommender_v1beta1/types/recommender_service.py
index 402b510..2c0fcd7 100644
--- a/google/cloud/recommender_v1beta1/types/recommender_service.py
+++ b/google/cloud/recommender_v1beta1/types/recommender_service.py
@@ -85,7 +85,7 @@ class ListInsightsResponse(proto.Message):
r"""Response to the ``ListInsights`` method.
Attributes:
- insights (Sequence[~.insight.Insight]):
+ insights (Sequence[google.cloud.recommender_v1beta1.types.Insight]):
The set of insights for the ``parent`` resource.
next_page_token (str):
A token that can be used to request the next
@@ -119,7 +119,7 @@ class MarkInsightAcceptedRequest(proto.Message):
Attributes:
name (str):
Required. Name of the insight.
- state_metadata (Sequence[~.recommender_service.MarkInsightAcceptedRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkInsightAcceptedRequest.StateMetadataEntry]):
Optional. State properties user wish to include with this
state. Full replace of the current state_metadata.
etag (str):
@@ -180,7 +180,7 @@ class ListRecommendationsResponse(proto.Message):
r"""Response to the ``ListRecommendations`` method.
Attributes:
- recommendations (Sequence[~.recommendation.Recommendation]):
+ recommendations (Sequence[google.cloud.recommender_v1beta1.types.Recommendation]):
The set of recommendations for the ``parent`` resource.
next_page_token (str):
A token that can be used to request the next
@@ -216,7 +216,7 @@ class MarkRecommendationClaimedRequest(proto.Message):
Attributes:
name (str):
Required. Name of the recommendation.
- state_metadata (Sequence[~.recommender_service.MarkRecommendationClaimedRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationClaimedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites any
existing ``state_metadata``. Keys must match the regex
``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match the regex
@@ -239,7 +239,7 @@ class MarkRecommendationSucceededRequest(proto.Message):
Attributes:
name (str):
Required. Name of the recommendation.
- state_metadata (Sequence[~.recommender_service.MarkRecommendationSucceededRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationSucceededRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites any
existing ``state_metadata``. Keys must match the regex
``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match the regex
@@ -262,7 +262,7 @@ class MarkRecommendationFailedRequest(proto.Message):
Attributes:
name (str):
Required. Name of the recommendation.
- state_metadata (Sequence[~.recommender_service.MarkRecommendationFailedRequest.StateMetadataEntry]):
+ state_metadata (Sequence[google.cloud.recommender_v1beta1.types.MarkRecommendationFailedRequest.StateMetadataEntry]):
State properties to include with this state. Overwrites any
existing ``state_metadata``. Keys must match the regex
``/^[a-z0-9][a-z0-9_.-]{0,62}$/``. Values must match the regex
diff --git a/noxfile.py b/noxfile.py
index 8004482..43dd302 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -18,6 +18,7 @@
from __future__ import absolute_import
import os
+import pathlib
import shutil
import nox
@@ -30,6 +31,22 @@
SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"]
UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"]
+CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
+
+# 'docfx' is excluded since it only needs to run in 'docs-presubmit'
+nox.options.sessions = [
+ "unit",
+ "system",
+ "cover",
+ "lint",
+ "lint_setup_py",
+ "blacken",
+ "docs",
+]
+
+# Error if a python version is missing
+nox.options.error_on_missing_interpreters = True
+
@nox.session(python=DEFAULT_PYTHON_VERSION)
def lint(session):
@@ -70,17 +87,21 @@ def lint_setup_py(session):
def default(session):
# Install all test dependencies, then install this package in-place.
- session.install("asyncmock", "pytest-asyncio")
- session.install(
- "mock", "pytest", "pytest-cov",
+ constraints_path = str(
+ CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
)
- session.install("-e", ".")
+ session.install("asyncmock", "pytest-asyncio", "-c", constraints_path)
+
+ session.install("mock", "pytest", "pytest-cov", "-c", constraints_path)
+
+ session.install("-e", ".", "-c", constraints_path)
# Run py.test against the unit tests.
session.run(
"py.test",
"--quiet",
+ f"--junitxml=unit_{session.python}_sponge_log.xml",
"--cov=google/cloud",
"--cov=tests/unit",
"--cov-append",
@@ -101,6 +122,9 @@ def unit(session):
@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
def system(session):
"""Run the system test suite."""
+ constraints_path = str(
+ CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
+ )
system_test_path = os.path.join("tests", "system.py")
system_test_folder_path = os.path.join("tests", "system")
@@ -110,6 +134,9 @@ def system(session):
# Sanity check: Only run tests if the environment variable is set.
if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", ""):
session.skip("Credentials must be set via environment variable")
+ # Install pyopenssl for mTLS testing.
+ if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true":
+ session.install("pyopenssl")
system_test_exists = os.path.exists(system_test_path)
system_test_folder_exists = os.path.exists(system_test_folder_path)
@@ -122,16 +149,26 @@ def system(session):
# Install all test dependencies, then install this package into the
# virtualenv's dist-packages.
- session.install(
- "mock", "pytest", "google-cloud-testutils",
- )
- session.install("-e", ".")
+ session.install("mock", "pytest", "google-cloud-testutils", "-c", constraints_path)
+ session.install("-e", ".", "-c", constraints_path)
# Run py.test against the system tests.
if system_test_exists:
- session.run("py.test", "--quiet", system_test_path, *session.posargs)
+ session.run(
+ "py.test",
+ "--quiet",
+ f"--junitxml=system_{session.python}_sponge_log.xml",
+ system_test_path,
+ *session.posargs,
+ )
if system_test_folder_exists:
- session.run("py.test", "--quiet", system_test_folder_path, *session.posargs)
+ session.run(
+ "py.test",
+ "--quiet",
+ f"--junitxml=system_{session.python}_sponge_log.xml",
+ system_test_folder_path,
+ *session.posargs,
+ )
@nox.session(python=DEFAULT_PYTHON_VERSION)
@@ -142,7 +179,7 @@ def cover(session):
test runs (not system test runs), and then erases coverage data.
"""
session.install("coverage", "pytest-cov")
- session.run("coverage", "report", "--show-missing", "--fail-under=100")
+ session.run("coverage", "report", "--show-missing", "--fail-under=98")
session.run("coverage", "erase")
diff --git a/renovate.json b/renovate.json
index 4fa9493..f08bc22 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,5 +1,6 @@
{
"extends": [
"config:base", ":preserveSemverRanges"
- ]
+ ],
+ "ignorePaths": [".pre-commit-config.yaml"]
}
diff --git a/setup.py b/setup.py
index e90242a..2482921 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@
description = "Cloud Recommender API client library"
version = "2.1.0"
release_status = "Development Status :: 5 - Production/Stable"
-dependencies = ["google-api-core[grpc] >= 1.22.0, < 2.0.0dev", "proto-plus >= 1.10.0"]
+dependencies = ["google-api-core[grpc] >= 1.22.2, < 2.0.0dev", "proto-plus >= 1.10.0"]
extras = {"libcst": "libcst >= 0.2.5"}
scripts = [
"scripts/fixup_recommender_v1_keywords.py",
diff --git a/synth.metadata b/synth.metadata
index b870cc8..1c6bc7b 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -3,30 +3,30 @@
{
"git": {
"name": ".",
- "remote": "https://github.com/googleapis/python-recommender.git",
- "sha": "5bb9b2c6b627e58a831be24d038f7b3f6bf55e3b"
+ "remote": "git@github.com:googleapis/python-recommender.git",
+ "sha": "9cec8cec00b3bd5f59fdebe16c65d8721af8ba0f"
}
},
{
"git": {
"name": "googleapis",
"remote": "https://github.com/googleapis/googleapis.git",
- "sha": "5256ab60f3d396a3d1bd393043776936b9651c5b",
- "internalRef": "347703845"
+ "sha": "95dd24960cf9f794ef583e59ad9f1fabe1c4a924",
+ "internalRef": "365882072"
}
},
{
"git": {
"name": "synthtool",
"remote": "https://github.com/googleapis/synthtool.git",
- "sha": "373861061648b5fe5e0ac4f8a38b32d639ee93e4"
+ "sha": "4dc31ac1ece23dc555f574aa701e5857b5bf2d3f"
}
},
{
"git": {
"name": "synthtool",
"remote": "https://github.com/googleapis/synthtool.git",
- "sha": "373861061648b5fe5e0ac4f8a38b32d639ee93e4"
+ "sha": "4dc31ac1ece23dc555f574aa701e5857b5bf2d3f"
}
}
],
@@ -49,116 +49,5 @@
"generator": "bazel"
}
}
- ],
- "generatedFiles": [
- ".flake8",
- ".github/CONTRIBUTING.md",
- ".github/ISSUE_TEMPLATE/bug_report.md",
- ".github/ISSUE_TEMPLATE/feature_request.md",
- ".github/ISSUE_TEMPLATE/support_request.md",
- ".github/PULL_REQUEST_TEMPLATE.md",
- ".github/release-please.yml",
- ".github/snippet-bot.yml",
- ".gitignore",
- ".kokoro/build.sh",
- ".kokoro/continuous/common.cfg",
- ".kokoro/continuous/continuous.cfg",
- ".kokoro/docker/docs/Dockerfile",
- ".kokoro/docker/docs/fetch_gpg_keys.sh",
- ".kokoro/docs/common.cfg",
- ".kokoro/docs/docs-presubmit.cfg",
- ".kokoro/docs/docs.cfg",
- ".kokoro/populate-secrets.sh",
- ".kokoro/presubmit/common.cfg",
- ".kokoro/presubmit/presubmit.cfg",
- ".kokoro/publish-docs.sh",
- ".kokoro/release.sh",
- ".kokoro/release/common.cfg",
- ".kokoro/release/release.cfg",
- ".kokoro/samples/lint/common.cfg",
- ".kokoro/samples/lint/continuous.cfg",
- ".kokoro/samples/lint/periodic.cfg",
- ".kokoro/samples/lint/presubmit.cfg",
- ".kokoro/samples/python3.6/common.cfg",
- ".kokoro/samples/python3.6/continuous.cfg",
- ".kokoro/samples/python3.6/periodic.cfg",
- ".kokoro/samples/python3.6/presubmit.cfg",
- ".kokoro/samples/python3.7/common.cfg",
- ".kokoro/samples/python3.7/continuous.cfg",
- ".kokoro/samples/python3.7/periodic.cfg",
- ".kokoro/samples/python3.7/presubmit.cfg",
- ".kokoro/samples/python3.8/common.cfg",
- ".kokoro/samples/python3.8/continuous.cfg",
- ".kokoro/samples/python3.8/periodic.cfg",
- ".kokoro/samples/python3.8/presubmit.cfg",
- ".kokoro/test-samples.sh",
- ".kokoro/trampoline.sh",
- ".kokoro/trampoline_v2.sh",
- ".pre-commit-config.yaml",
- ".trampolinerc",
- "CODE_OF_CONDUCT.md",
- "CONTRIBUTING.rst",
- "LICENSE",
- "MANIFEST.in",
- "docs/_static/custom.css",
- "docs/_templates/layout.html",
- "docs/conf.py",
- "docs/multiprocessing.rst",
- "docs/recommender_v1/services.rst",
- "docs/recommender_v1/types.rst",
- "docs/recommender_v1beta1/services.rst",
- "docs/recommender_v1beta1/types.rst",
- "google/cloud/recommender/__init__.py",
- "google/cloud/recommender/py.typed",
- "google/cloud/recommender_v1/__init__.py",
- "google/cloud/recommender_v1/py.typed",
- "google/cloud/recommender_v1/services/__init__.py",
- "google/cloud/recommender_v1/services/recommender/__init__.py",
- "google/cloud/recommender_v1/services/recommender/async_client.py",
- "google/cloud/recommender_v1/services/recommender/client.py",
- "google/cloud/recommender_v1/services/recommender/pagers.py",
- "google/cloud/recommender_v1/services/recommender/transports/__init__.py",
- "google/cloud/recommender_v1/services/recommender/transports/base.py",
- "google/cloud/recommender_v1/services/recommender/transports/grpc.py",
- "google/cloud/recommender_v1/services/recommender/transports/grpc_asyncio.py",
- "google/cloud/recommender_v1/types/__init__.py",
- "google/cloud/recommender_v1/types/insight.py",
- "google/cloud/recommender_v1/types/recommendation.py",
- "google/cloud/recommender_v1/types/recommender_service.py",
- "google/cloud/recommender_v1beta1/__init__.py",
- "google/cloud/recommender_v1beta1/py.typed",
- "google/cloud/recommender_v1beta1/services/__init__.py",
- "google/cloud/recommender_v1beta1/services/recommender/__init__.py",
- "google/cloud/recommender_v1beta1/services/recommender/async_client.py",
- "google/cloud/recommender_v1beta1/services/recommender/client.py",
- "google/cloud/recommender_v1beta1/services/recommender/pagers.py",
- "google/cloud/recommender_v1beta1/services/recommender/transports/__init__.py",
- "google/cloud/recommender_v1beta1/services/recommender/transports/base.py",
- "google/cloud/recommender_v1beta1/services/recommender/transports/grpc.py",
- "google/cloud/recommender_v1beta1/services/recommender/transports/grpc_asyncio.py",
- "google/cloud/recommender_v1beta1/types/__init__.py",
- "google/cloud/recommender_v1beta1/types/insight.py",
- "google/cloud/recommender_v1beta1/types/recommendation.py",
- "google/cloud/recommender_v1beta1/types/recommender_service.py",
- "mypy.ini",
- "noxfile.py",
- "renovate.json",
- "samples/AUTHORING_GUIDE.md",
- "samples/CONTRIBUTING.md",
- "scripts/decrypt-secrets.sh",
- "scripts/fixup_recommender_v1_keywords.py",
- "scripts/fixup_recommender_v1beta1_keywords.py",
- "scripts/readme-gen/readme_gen.py",
- "scripts/readme-gen/templates/README.tmpl.rst",
- "scripts/readme-gen/templates/auth.tmpl.rst",
- "scripts/readme-gen/templates/auth_api_key.tmpl.rst",
- "scripts/readme-gen/templates/install_deps.tmpl.rst",
- "scripts/readme-gen/templates/install_portaudio.tmpl.rst",
- "setup.cfg",
- "testing/.gitignore",
- "tests/unit/gapic/recommender_v1/__init__.py",
- "tests/unit/gapic/recommender_v1/test_recommender.py",
- "tests/unit/gapic/recommender_v1beta1/__init__.py",
- "tests/unit/gapic/recommender_v1beta1/test_recommender.py"
]
}
\ No newline at end of file
diff --git a/synth.py b/synth.py
index 359fece..04523a5 100644
--- a/synth.py
+++ b/synth.py
@@ -61,6 +61,7 @@
templated_files = common.py_library(
samples=False, # set to True only if there are samples
microgenerator=True,
+ cov_level=98,
)
s.move(
templated_files, excludes=[".coveragerc"]
diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt
new file mode 100644
index 0000000..a929701
--- /dev/null
+++ b/testing/constraints-3.6.txt
@@ -0,0 +1,9 @@
+# This constraints file is used to check that lower bounds
+# are correct in setup.py
+# List all library dependencies and extras in this file.
+# Pin the version to the lower bound.
+
+# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0dev",
+# Then this file should have google-cloud-foo==1.14.0
+google-api-core==1.22.2
+proto-plus==1.15.0
diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt
new file mode 100644
index 0000000..da93009
--- /dev/null
+++ b/testing/constraints-3.7.txt
@@ -0,0 +1,2 @@
+# This constraints file is left inentionally empty
+# so the latest version of dependencies is installed
\ No newline at end of file
diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt
new file mode 100644
index 0000000..da93009
--- /dev/null
+++ b/testing/constraints-3.8.txt
@@ -0,0 +1,2 @@
+# This constraints file is left inentionally empty
+# so the latest version of dependencies is installed
\ No newline at end of file
diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt
new file mode 100644
index 0000000..da93009
--- /dev/null
+++ b/testing/constraints-3.9.txt
@@ -0,0 +1,2 @@
+# This constraints file is left inentionally empty
+# so the latest version of dependencies is installed
\ No newline at end of file
diff --git a/tests/unit/gapic/recommender_v1/__init__.py b/tests/unit/gapic/recommender_v1/__init__.py
index 8b13789..42ffdf2 100644
--- a/tests/unit/gapic/recommender_v1/__init__.py
+++ b/tests/unit/gapic/recommender_v1/__init__.py
@@ -1 +1,16 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/tests/unit/gapic/recommender_v1/test_recommender.py b/tests/unit/gapic/recommender_v1/test_recommender.py
index 1ffd245..bd6b694 100644
--- a/tests/unit/gapic/recommender_v1/test_recommender.py
+++ b/tests/unit/gapic/recommender_v1/test_recommender.py
@@ -86,7 +86,22 @@ def test__get_default_mtls_endpoint():
assert RecommenderClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi
-@pytest.mark.parametrize("client_class", [RecommenderClient, RecommenderAsyncClient])
+@pytest.mark.parametrize("client_class", [RecommenderClient, RecommenderAsyncClient,])
+def test_recommender_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "recommender.googleapis.com:443"
+
+
+@pytest.mark.parametrize("client_class", [RecommenderClient, RecommenderAsyncClient,])
def test_recommender_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
with mock.patch.object(
@@ -95,16 +110,21 @@ def test_recommender_client_from_service_account_file(client_class):
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "recommender.googleapis.com:443"
def test_recommender_client_get_transport_class():
transport = RecommenderClient.get_transport_class()
- assert transport == transports.RecommenderGrpcTransport
+ available_transports = [
+ transports.RecommenderGrpcTransport,
+ ]
+ assert transport in available_transports
transport = RecommenderClient.get_transport_class("grpc")
assert transport == transports.RecommenderGrpcTransport
@@ -153,7 +173,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host="squid.clam.whelk",
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -169,7 +189,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host=client.DEFAULT_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -185,7 +205,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host=client.DEFAULT_MTLS_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -213,7 +233,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host=client.DEFAULT_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id="octopus",
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -262,29 +282,25 @@ def test_recommender_client_mtls_env_auto(
client_cert_source=client_cert_source_callback
)
with mock.patch.object(transport_class, "__init__") as patched:
- ssl_channel_creds = mock.Mock()
- with mock.patch(
- "grpc.ssl_channel_credentials", return_value=ssl_channel_creds
- ):
- patched.return_value = None
- client = client_class(client_options=options)
+ patched.return_value = None
+ client = client_class(client_options=options)
- if use_client_cert_env == "false":
- expected_ssl_channel_creds = None
- expected_host = client.DEFAULT_ENDPOINT
- else:
- expected_ssl_channel_creds = ssl_channel_creds
- expected_host = client.DEFAULT_MTLS_ENDPOINT
+ if use_client_cert_env == "false":
+ expected_client_cert_source = None
+ expected_host = client.DEFAULT_ENDPOINT
+ else:
+ expected_client_cert_source = client_cert_source_callback
+ expected_host = client.DEFAULT_MTLS_ENDPOINT
- patched.assert_called_once_with(
- credentials=None,
- credentials_file=None,
- host=expected_host,
- scopes=None,
- ssl_channel_credentials=expected_ssl_channel_creds,
- quota_project_id=None,
- client_info=transports.base.DEFAULT_CLIENT_INFO,
- )
+ patched.assert_called_once_with(
+ credentials=None,
+ credentials_file=None,
+ host=expected_host,
+ scopes=None,
+ client_cert_source_for_mtls=expected_client_cert_source,
+ quota_project_id=None,
+ client_info=transports.base.DEFAULT_CLIENT_INFO,
+ )
# Check the case ADC client cert is provided. Whether client cert is used depends on
# GOOGLE_API_USE_CLIENT_CERTIFICATE value.
@@ -293,66 +309,53 @@ def test_recommender_client_mtls_env_auto(
):
with mock.patch.object(transport_class, "__init__") as patched:
with mock.patch(
- "google.auth.transport.grpc.SslCredentials.__init__", return_value=None
+ "google.auth.transport.mtls.has_default_client_cert_source",
+ return_value=True,
):
with mock.patch(
- "google.auth.transport.grpc.SslCredentials.is_mtls",
- new_callable=mock.PropertyMock,
- ) as is_mtls_mock:
- with mock.patch(
- "google.auth.transport.grpc.SslCredentials.ssl_credentials",
- new_callable=mock.PropertyMock,
- ) as ssl_credentials_mock:
- if use_client_cert_env == "false":
- is_mtls_mock.return_value = False
- ssl_credentials_mock.return_value = None
- expected_host = client.DEFAULT_ENDPOINT
- expected_ssl_channel_creds = None
- else:
- is_mtls_mock.return_value = True
- ssl_credentials_mock.return_value = mock.Mock()
- expected_host = client.DEFAULT_MTLS_ENDPOINT
- expected_ssl_channel_creds = (
- ssl_credentials_mock.return_value
- )
-
- patched.return_value = None
- client = client_class()
- patched.assert_called_once_with(
- credentials=None,
- credentials_file=None,
- host=expected_host,
- scopes=None,
- ssl_channel_credentials=expected_ssl_channel_creds,
- quota_project_id=None,
- client_info=transports.base.DEFAULT_CLIENT_INFO,
- )
+ "google.auth.transport.mtls.default_client_cert_source",
+ return_value=client_cert_source_callback,
+ ):
+ if use_client_cert_env == "false":
+ expected_host = client.DEFAULT_ENDPOINT
+ expected_client_cert_source = None
+ else:
+ expected_host = client.DEFAULT_MTLS_ENDPOINT
+ expected_client_cert_source = client_cert_source_callback
- # Check the case client_cert_source and ADC client cert are not provided.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env}
- ):
- with mock.patch.object(transport_class, "__init__") as patched:
- with mock.patch(
- "google.auth.transport.grpc.SslCredentials.__init__", return_value=None
- ):
- with mock.patch(
- "google.auth.transport.grpc.SslCredentials.is_mtls",
- new_callable=mock.PropertyMock,
- ) as is_mtls_mock:
- is_mtls_mock.return_value = False
patched.return_value = None
client = client_class()
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=expected_host,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=expected_client_cert_source,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
+ # Check the case client_cert_source and ADC client cert are not provided.
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env}
+ ):
+ with mock.patch.object(transport_class, "__init__") as patched:
+ with mock.patch(
+ "google.auth.transport.mtls.has_default_client_cert_source",
+ return_value=False,
+ ):
+ patched.return_value = None
+ client = client_class()
+ patched.assert_called_once_with(
+ credentials=None,
+ credentials_file=None,
+ host=client.DEFAULT_ENDPOINT,
+ scopes=None,
+ client_cert_source_for_mtls=None,
+ quota_project_id=None,
+ client_info=transports.base.DEFAULT_CLIENT_INFO,
+ )
+
@pytest.mark.parametrize(
"client_class,transport_class,transport_name",
@@ -378,7 +381,7 @@ def test_recommender_client_client_options_scopes(
credentials_file=None,
host=client.DEFAULT_ENDPOINT,
scopes=["1", "2"],
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -408,7 +411,7 @@ def test_recommender_client_client_options_credentials_file(
credentials_file="credentials.json",
host=client.DEFAULT_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -425,7 +428,7 @@ def test_recommender_client_client_options_from_dict():
credentials_file=None,
host="squid.clam.whelk",
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -468,6 +471,22 @@ def test_list_insights_from_dict():
test_list_insights(request_type=dict)
+def test_list_insights_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.list_insights), "__call__") as call:
+ client.list_insights()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.ListInsightsRequest()
+
+
@pytest.mark.asyncio
async def test_list_insights_async(
transport: str = "grpc_asyncio",
@@ -809,6 +828,22 @@ def test_get_insight_from_dict():
test_get_insight(request_type=dict)
+def test_get_insight_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.get_insight), "__call__") as call:
+ client.get_insight()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.GetInsightRequest()
+
+
@pytest.mark.asyncio
async def test_get_insight_async(
transport: str = "grpc_asyncio", request_type=recommender_service.GetInsightRequest
@@ -1032,6 +1067,24 @@ def test_mark_insight_accepted_from_dict():
test_mark_insight_accepted(request_type=dict)
+def test_mark_insight_accepted_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_insight_accepted), "__call__"
+ ) as call:
+ client.mark_insight_accepted()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkInsightAcceptedRequest()
+
+
@pytest.mark.asyncio
async def test_mark_insight_accepted_async(
transport: str = "grpc_asyncio",
@@ -1273,6 +1326,24 @@ def test_list_recommendations_from_dict():
test_list_recommendations(request_type=dict)
+def test_list_recommendations_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_recommendations), "__call__"
+ ) as call:
+ client.list_recommendations()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.ListRecommendationsRequest()
+
+
@pytest.mark.asyncio
async def test_list_recommendations_async(
transport: str = "grpc_asyncio",
@@ -1672,6 +1743,24 @@ def test_get_recommendation_from_dict():
test_get_recommendation(request_type=dict)
+def test_get_recommendation_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.get_recommendation), "__call__"
+ ) as call:
+ client.get_recommendation()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.GetRecommendationRequest()
+
+
@pytest.mark.asyncio
async def test_get_recommendation_async(
transport: str = "grpc_asyncio",
@@ -1899,6 +1988,24 @@ def test_mark_recommendation_claimed_from_dict():
test_mark_recommendation_claimed(request_type=dict)
+def test_mark_recommendation_claimed_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_recommendation_claimed), "__call__"
+ ) as call:
+ client.mark_recommendation_claimed()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkRecommendationClaimedRequest()
+
+
@pytest.mark.asyncio
async def test_mark_recommendation_claimed_async(
transport: str = "grpc_asyncio",
@@ -2148,6 +2255,24 @@ def test_mark_recommendation_succeeded_from_dict():
test_mark_recommendation_succeeded(request_type=dict)
+def test_mark_recommendation_succeeded_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_recommendation_succeeded), "__call__"
+ ) as call:
+ client.mark_recommendation_succeeded()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkRecommendationSucceededRequest()
+
+
@pytest.mark.asyncio
async def test_mark_recommendation_succeeded_async(
transport: str = "grpc_asyncio",
@@ -2397,6 +2522,24 @@ def test_mark_recommendation_failed_from_dict():
test_mark_recommendation_failed(request_type=dict)
+def test_mark_recommendation_failed_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_recommendation_failed), "__call__"
+ ) as call:
+ client.mark_recommendation_failed()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkRecommendationFailedRequest()
+
+
@pytest.mark.asyncio
async def test_mark_recommendation_failed_async(
transport: str = "grpc_asyncio",
@@ -2653,7 +2796,7 @@ def test_transport_get_channel():
@pytest.mark.parametrize(
"transport_class",
- [transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
+ [transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport,],
)
def test_transport_adc(transport_class):
# Test default credentials are used if not provided.
@@ -2760,6 +2903,48 @@ def test_recommender_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
+)
+def test_recommender_grpc_transport_client_cert_source_for_mtls(transport_class):
+ cred = credentials.AnonymousCredentials()
+
+ # Check ssl_channel_credentials is used if provided.
+ with mock.patch.object(transport_class, "create_channel") as mock_create_channel:
+ mock_ssl_channel_creds = mock.Mock()
+ transport_class(
+ host="squid.clam.whelk",
+ credentials=cred,
+ ssl_channel_credentials=mock_ssl_channel_creds,
+ )
+ mock_create_channel.assert_called_once_with(
+ "squid.clam.whelk:443",
+ credentials=cred,
+ credentials_file=None,
+ scopes=("https://www.googleapis.com/auth/cloud-platform",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls
+ # is used.
+ with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()):
+ with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred:
+ transport_class(
+ credentials=cred,
+ client_cert_source_for_mtls=client_cert_source_callback,
+ )
+ expected_cert, expected_key = client_cert_source_callback()
+ mock_ssl_cred.assert_called_once_with(
+ certificate_chain=expected_cert, private_key=expected_key
+ )
+
+
def test_recommender_host_no_port():
client = RecommenderClient(
credentials=credentials.AnonymousCredentials(),
@@ -2781,7 +2966,7 @@ def test_recommender_host_with_port():
def test_recommender_grpc_transport_channel():
- channel = grpc.insecure_channel("http://localhost/")
+ channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials())
# Check that channel is used if provided.
transport = transports.RecommenderGrpcTransport(
@@ -2793,7 +2978,7 @@ def test_recommender_grpc_transport_channel():
def test_recommender_grpc_asyncio_transport_channel():
- channel = aio.insecure_channel("http://localhost/")
+ channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials())
# Check that channel is used if provided.
transport = transports.RecommenderGrpcAsyncIOTransport(
@@ -2804,6 +2989,8 @@ def test_recommender_grpc_asyncio_transport_channel():
assert transport._ssl_channel_credentials == None
+# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are
+# removed from grpc/grpc_asyncio transport constructor.
@pytest.mark.parametrize(
"transport_class",
[transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
@@ -2813,7 +3000,7 @@ def test_recommender_transport_channel_mtls_with_client_cert_source(transport_cl
"grpc.ssl_channel_credentials", autospec=True
) as grpc_ssl_channel_cred:
with mock.patch.object(
- transport_class, "create_channel", autospec=True
+ transport_class, "create_channel"
) as grpc_create_channel:
mock_ssl_cred = mock.Mock()
grpc_ssl_channel_cred.return_value = mock_ssl_cred
@@ -2851,6 +3038,8 @@ def test_recommender_transport_channel_mtls_with_client_cert_source(transport_cl
assert transport._ssl_channel_credentials == mock_ssl_cred
+# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are
+# removed from grpc/grpc_asyncio transport constructor.
@pytest.mark.parametrize(
"transport_class",
[transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
@@ -2863,7 +3052,7 @@ def test_recommender_transport_channel_mtls_with_adc(transport_class):
ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred),
):
with mock.patch.object(
- transport_class, "create_channel", autospec=True
+ transport_class, "create_channel"
) as grpc_create_channel:
mock_grpc_channel = mock.Mock()
grpc_create_channel.return_value = mock_grpc_channel
diff --git a/tests/unit/gapic/recommender_v1beta1/__init__.py b/tests/unit/gapic/recommender_v1beta1/__init__.py
index 8b13789..42ffdf2 100644
--- a/tests/unit/gapic/recommender_v1beta1/__init__.py
+++ b/tests/unit/gapic/recommender_v1beta1/__init__.py
@@ -1 +1,16 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/tests/unit/gapic/recommender_v1beta1/test_recommender.py b/tests/unit/gapic/recommender_v1beta1/test_recommender.py
index 3ea1c2c..91de2d8 100644
--- a/tests/unit/gapic/recommender_v1beta1/test_recommender.py
+++ b/tests/unit/gapic/recommender_v1beta1/test_recommender.py
@@ -86,7 +86,22 @@ def test__get_default_mtls_endpoint():
assert RecommenderClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi
-@pytest.mark.parametrize("client_class", [RecommenderClient, RecommenderAsyncClient])
+@pytest.mark.parametrize("client_class", [RecommenderClient, RecommenderAsyncClient,])
+def test_recommender_client_from_service_account_info(client_class):
+ creds = credentials.AnonymousCredentials()
+ with mock.patch.object(
+ service_account.Credentials, "from_service_account_info"
+ ) as factory:
+ factory.return_value = creds
+ info = {"valid": True}
+ client = client_class.from_service_account_info(info)
+ assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
+
+ assert client.transport._host == "recommender.googleapis.com:443"
+
+
+@pytest.mark.parametrize("client_class", [RecommenderClient, RecommenderAsyncClient,])
def test_recommender_client_from_service_account_file(client_class):
creds = credentials.AnonymousCredentials()
with mock.patch.object(
@@ -95,16 +110,21 @@ def test_recommender_client_from_service_account_file(client_class):
factory.return_value = creds
client = client_class.from_service_account_file("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
client = client_class.from_service_account_json("dummy/file/path.json")
assert client.transport._credentials == creds
+ assert isinstance(client, client_class)
assert client.transport._host == "recommender.googleapis.com:443"
def test_recommender_client_get_transport_class():
transport = RecommenderClient.get_transport_class()
- assert transport == transports.RecommenderGrpcTransport
+ available_transports = [
+ transports.RecommenderGrpcTransport,
+ ]
+ assert transport in available_transports
transport = RecommenderClient.get_transport_class("grpc")
assert transport == transports.RecommenderGrpcTransport
@@ -153,7 +173,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host="squid.clam.whelk",
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -169,7 +189,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host=client.DEFAULT_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -185,7 +205,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host=client.DEFAULT_MTLS_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -213,7 +233,7 @@ def test_recommender_client_client_options(
credentials_file=None,
host=client.DEFAULT_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id="octopus",
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -262,29 +282,25 @@ def test_recommender_client_mtls_env_auto(
client_cert_source=client_cert_source_callback
)
with mock.patch.object(transport_class, "__init__") as patched:
- ssl_channel_creds = mock.Mock()
- with mock.patch(
- "grpc.ssl_channel_credentials", return_value=ssl_channel_creds
- ):
- patched.return_value = None
- client = client_class(client_options=options)
+ patched.return_value = None
+ client = client_class(client_options=options)
- if use_client_cert_env == "false":
- expected_ssl_channel_creds = None
- expected_host = client.DEFAULT_ENDPOINT
- else:
- expected_ssl_channel_creds = ssl_channel_creds
- expected_host = client.DEFAULT_MTLS_ENDPOINT
+ if use_client_cert_env == "false":
+ expected_client_cert_source = None
+ expected_host = client.DEFAULT_ENDPOINT
+ else:
+ expected_client_cert_source = client_cert_source_callback
+ expected_host = client.DEFAULT_MTLS_ENDPOINT
- patched.assert_called_once_with(
- credentials=None,
- credentials_file=None,
- host=expected_host,
- scopes=None,
- ssl_channel_credentials=expected_ssl_channel_creds,
- quota_project_id=None,
- client_info=transports.base.DEFAULT_CLIENT_INFO,
- )
+ patched.assert_called_once_with(
+ credentials=None,
+ credentials_file=None,
+ host=expected_host,
+ scopes=None,
+ client_cert_source_for_mtls=expected_client_cert_source,
+ quota_project_id=None,
+ client_info=transports.base.DEFAULT_CLIENT_INFO,
+ )
# Check the case ADC client cert is provided. Whether client cert is used depends on
# GOOGLE_API_USE_CLIENT_CERTIFICATE value.
@@ -293,66 +309,53 @@ def test_recommender_client_mtls_env_auto(
):
with mock.patch.object(transport_class, "__init__") as patched:
with mock.patch(
- "google.auth.transport.grpc.SslCredentials.__init__", return_value=None
+ "google.auth.transport.mtls.has_default_client_cert_source",
+ return_value=True,
):
with mock.patch(
- "google.auth.transport.grpc.SslCredentials.is_mtls",
- new_callable=mock.PropertyMock,
- ) as is_mtls_mock:
- with mock.patch(
- "google.auth.transport.grpc.SslCredentials.ssl_credentials",
- new_callable=mock.PropertyMock,
- ) as ssl_credentials_mock:
- if use_client_cert_env == "false":
- is_mtls_mock.return_value = False
- ssl_credentials_mock.return_value = None
- expected_host = client.DEFAULT_ENDPOINT
- expected_ssl_channel_creds = None
- else:
- is_mtls_mock.return_value = True
- ssl_credentials_mock.return_value = mock.Mock()
- expected_host = client.DEFAULT_MTLS_ENDPOINT
- expected_ssl_channel_creds = (
- ssl_credentials_mock.return_value
- )
-
- patched.return_value = None
- client = client_class()
- patched.assert_called_once_with(
- credentials=None,
- credentials_file=None,
- host=expected_host,
- scopes=None,
- ssl_channel_credentials=expected_ssl_channel_creds,
- quota_project_id=None,
- client_info=transports.base.DEFAULT_CLIENT_INFO,
- )
+ "google.auth.transport.mtls.default_client_cert_source",
+ return_value=client_cert_source_callback,
+ ):
+ if use_client_cert_env == "false":
+ expected_host = client.DEFAULT_ENDPOINT
+ expected_client_cert_source = None
+ else:
+ expected_host = client.DEFAULT_MTLS_ENDPOINT
+ expected_client_cert_source = client_cert_source_callback
- # Check the case client_cert_source and ADC client cert are not provided.
- with mock.patch.dict(
- os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env}
- ):
- with mock.patch.object(transport_class, "__init__") as patched:
- with mock.patch(
- "google.auth.transport.grpc.SslCredentials.__init__", return_value=None
- ):
- with mock.patch(
- "google.auth.transport.grpc.SslCredentials.is_mtls",
- new_callable=mock.PropertyMock,
- ) as is_mtls_mock:
- is_mtls_mock.return_value = False
patched.return_value = None
client = client_class()
patched.assert_called_once_with(
credentials=None,
credentials_file=None,
- host=client.DEFAULT_ENDPOINT,
+ host=expected_host,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=expected_client_cert_source,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
+ # Check the case client_cert_source and ADC client cert are not provided.
+ with mock.patch.dict(
+ os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env}
+ ):
+ with mock.patch.object(transport_class, "__init__") as patched:
+ with mock.patch(
+ "google.auth.transport.mtls.has_default_client_cert_source",
+ return_value=False,
+ ):
+ patched.return_value = None
+ client = client_class()
+ patched.assert_called_once_with(
+ credentials=None,
+ credentials_file=None,
+ host=client.DEFAULT_ENDPOINT,
+ scopes=None,
+ client_cert_source_for_mtls=None,
+ quota_project_id=None,
+ client_info=transports.base.DEFAULT_CLIENT_INFO,
+ )
+
@pytest.mark.parametrize(
"client_class,transport_class,transport_name",
@@ -378,7 +381,7 @@ def test_recommender_client_client_options_scopes(
credentials_file=None,
host=client.DEFAULT_ENDPOINT,
scopes=["1", "2"],
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -408,7 +411,7 @@ def test_recommender_client_client_options_credentials_file(
credentials_file="credentials.json",
host=client.DEFAULT_ENDPOINT,
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -425,7 +428,7 @@ def test_recommender_client_client_options_from_dict():
credentials_file=None,
host="squid.clam.whelk",
scopes=None,
- ssl_channel_credentials=None,
+ client_cert_source_for_mtls=None,
quota_project_id=None,
client_info=transports.base.DEFAULT_CLIENT_INFO,
)
@@ -468,6 +471,22 @@ def test_list_insights_from_dict():
test_list_insights(request_type=dict)
+def test_list_insights_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.list_insights), "__call__") as call:
+ client.list_insights()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.ListInsightsRequest()
+
+
@pytest.mark.asyncio
async def test_list_insights_async(
transport: str = "grpc_asyncio",
@@ -809,6 +828,22 @@ def test_get_insight_from_dict():
test_get_insight(request_type=dict)
+def test_get_insight_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(type(client.transport.get_insight), "__call__") as call:
+ client.get_insight()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.GetInsightRequest()
+
+
@pytest.mark.asyncio
async def test_get_insight_async(
transport: str = "grpc_asyncio", request_type=recommender_service.GetInsightRequest
@@ -1032,6 +1067,24 @@ def test_mark_insight_accepted_from_dict():
test_mark_insight_accepted(request_type=dict)
+def test_mark_insight_accepted_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_insight_accepted), "__call__"
+ ) as call:
+ client.mark_insight_accepted()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkInsightAcceptedRequest()
+
+
@pytest.mark.asyncio
async def test_mark_insight_accepted_async(
transport: str = "grpc_asyncio",
@@ -1273,6 +1326,24 @@ def test_list_recommendations_from_dict():
test_list_recommendations(request_type=dict)
+def test_list_recommendations_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.list_recommendations), "__call__"
+ ) as call:
+ client.list_recommendations()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.ListRecommendationsRequest()
+
+
@pytest.mark.asyncio
async def test_list_recommendations_async(
transport: str = "grpc_asyncio",
@@ -1672,6 +1743,24 @@ def test_get_recommendation_from_dict():
test_get_recommendation(request_type=dict)
+def test_get_recommendation_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.get_recommendation), "__call__"
+ ) as call:
+ client.get_recommendation()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.GetRecommendationRequest()
+
+
@pytest.mark.asyncio
async def test_get_recommendation_async(
transport: str = "grpc_asyncio",
@@ -1899,6 +1988,24 @@ def test_mark_recommendation_claimed_from_dict():
test_mark_recommendation_claimed(request_type=dict)
+def test_mark_recommendation_claimed_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_recommendation_claimed), "__call__"
+ ) as call:
+ client.mark_recommendation_claimed()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkRecommendationClaimedRequest()
+
+
@pytest.mark.asyncio
async def test_mark_recommendation_claimed_async(
transport: str = "grpc_asyncio",
@@ -2148,6 +2255,24 @@ def test_mark_recommendation_succeeded_from_dict():
test_mark_recommendation_succeeded(request_type=dict)
+def test_mark_recommendation_succeeded_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_recommendation_succeeded), "__call__"
+ ) as call:
+ client.mark_recommendation_succeeded()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkRecommendationSucceededRequest()
+
+
@pytest.mark.asyncio
async def test_mark_recommendation_succeeded_async(
transport: str = "grpc_asyncio",
@@ -2397,6 +2522,24 @@ def test_mark_recommendation_failed_from_dict():
test_mark_recommendation_failed(request_type=dict)
+def test_mark_recommendation_failed_empty_call():
+ # This test is a coverage failsafe to make sure that totally empty calls,
+ # i.e. request == None and no flattened fields passed, work.
+ client = RecommenderClient(
+ credentials=credentials.AnonymousCredentials(), transport="grpc",
+ )
+
+ # Mock the actual call within the gRPC stub, and fake the request.
+ with mock.patch.object(
+ type(client.transport.mark_recommendation_failed), "__call__"
+ ) as call:
+ client.mark_recommendation_failed()
+ call.assert_called()
+ _, args, _ = call.mock_calls[0]
+
+ assert args[0] == recommender_service.MarkRecommendationFailedRequest()
+
+
@pytest.mark.asyncio
async def test_mark_recommendation_failed_async(
transport: str = "grpc_asyncio",
@@ -2653,7 +2796,7 @@ def test_transport_get_channel():
@pytest.mark.parametrize(
"transport_class",
- [transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
+ [transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport,],
)
def test_transport_adc(transport_class):
# Test default credentials are used if not provided.
@@ -2760,6 +2903,48 @@ def test_recommender_transport_auth_adc():
)
+@pytest.mark.parametrize(
+ "transport_class",
+ [transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
+)
+def test_recommender_grpc_transport_client_cert_source_for_mtls(transport_class):
+ cred = credentials.AnonymousCredentials()
+
+ # Check ssl_channel_credentials is used if provided.
+ with mock.patch.object(transport_class, "create_channel") as mock_create_channel:
+ mock_ssl_channel_creds = mock.Mock()
+ transport_class(
+ host="squid.clam.whelk",
+ credentials=cred,
+ ssl_channel_credentials=mock_ssl_channel_creds,
+ )
+ mock_create_channel.assert_called_once_with(
+ "squid.clam.whelk:443",
+ credentials=cred,
+ credentials_file=None,
+ scopes=("https://www.googleapis.com/auth/cloud-platform",),
+ ssl_credentials=mock_ssl_channel_creds,
+ quota_project_id=None,
+ options=[
+ ("grpc.max_send_message_length", -1),
+ ("grpc.max_receive_message_length", -1),
+ ],
+ )
+
+ # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls
+ # is used.
+ with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()):
+ with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred:
+ transport_class(
+ credentials=cred,
+ client_cert_source_for_mtls=client_cert_source_callback,
+ )
+ expected_cert, expected_key = client_cert_source_callback()
+ mock_ssl_cred.assert_called_once_with(
+ certificate_chain=expected_cert, private_key=expected_key
+ )
+
+
def test_recommender_host_no_port():
client = RecommenderClient(
credentials=credentials.AnonymousCredentials(),
@@ -2781,7 +2966,7 @@ def test_recommender_host_with_port():
def test_recommender_grpc_transport_channel():
- channel = grpc.insecure_channel("http://localhost/")
+ channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials())
# Check that channel is used if provided.
transport = transports.RecommenderGrpcTransport(
@@ -2793,7 +2978,7 @@ def test_recommender_grpc_transport_channel():
def test_recommender_grpc_asyncio_transport_channel():
- channel = aio.insecure_channel("http://localhost/")
+ channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials())
# Check that channel is used if provided.
transport = transports.RecommenderGrpcAsyncIOTransport(
@@ -2804,6 +2989,8 @@ def test_recommender_grpc_asyncio_transport_channel():
assert transport._ssl_channel_credentials == None
+# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are
+# removed from grpc/grpc_asyncio transport constructor.
@pytest.mark.parametrize(
"transport_class",
[transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
@@ -2813,7 +3000,7 @@ def test_recommender_transport_channel_mtls_with_client_cert_source(transport_cl
"grpc.ssl_channel_credentials", autospec=True
) as grpc_ssl_channel_cred:
with mock.patch.object(
- transport_class, "create_channel", autospec=True
+ transport_class, "create_channel"
) as grpc_create_channel:
mock_ssl_cred = mock.Mock()
grpc_ssl_channel_cred.return_value = mock_ssl_cred
@@ -2851,6 +3038,8 @@ def test_recommender_transport_channel_mtls_with_client_cert_source(transport_cl
assert transport._ssl_channel_credentials == mock_ssl_cred
+# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are
+# removed from grpc/grpc_asyncio transport constructor.
@pytest.mark.parametrize(
"transport_class",
[transports.RecommenderGrpcTransport, transports.RecommenderGrpcAsyncIOTransport],
@@ -2863,7 +3052,7 @@ def test_recommender_transport_channel_mtls_with_adc(transport_class):
ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred),
):
with mock.patch.object(
- transport_class, "create_channel", autospec=True
+ transport_class, "create_channel"
) as grpc_create_channel:
mock_grpc_channel = mock.Mock()
grpc_create_channel.return_value = mock_grpc_channel