diff --git a/.flake8 b/.flake8 index ed93163..29227d4 100644 --- a/.flake8 +++ b/.flake8 @@ -26,6 +26,7 @@ exclude = *_pb2.py # Standard linting exemptions. + **/.nox/** __pycache__, .git, *.pyc, diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml new file mode 100644 index 0000000..cb06536 --- /dev/null +++ b/.github/.OwlBot.lock.yaml @@ -0,0 +1,3 @@ +docker: + image: gcr.io/repo-automation-bots/owlbot-python:latest + digest: sha256:5ff7446edeaede81c3ed58b23a4e76a5403fba1350ce28478045657303b6479d diff --git a/google/cloud/resource_manager/__init__.py b/.github/.OwlBot.yaml similarity index 58% rename from google/cloud/resource_manager/__init__.py rename to .github/.OwlBot.yaml index bf71be7..c892567 100644 --- a/google/cloud/resource_manager/__init__.py +++ b/.github/.OwlBot.yaml @@ -1,4 +1,4 @@ -# Copyright 2015 Google LLC +# 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. @@ -12,17 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Google Cloud Resource Manager API wrapper.""" +docker: + image: gcr.io/repo-automation-bots/owlbot-python:latest +deep-remove-regex: + - /owl-bot-staging -from pkg_resources import get_distribution +deep-copy-regex: + - source: /google/cloud/resourcemanager/(v.*)/.*-py/(.*) + dest: /owl-bot-staging/$1/$2 -__version__ = get_distribution("google-cloud-resource-manager").version - -from google.cloud.resource_manager.client import Client -from google.cloud.resource_manager.project import Project - - -SCOPE = Client.SCOPE - -__all__ = ["__version__", "Client", "Project", "SCOPE"] +begin-after-commit-hash: 40278112d2922ec917140dcb5cc6d5ef2923aeb2 \ No newline at end of file diff --git a/.github/header-checker-lint.yml b/.github/header-checker-lint.yml new file mode 100644 index 0000000..6fe78aa --- /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", "**/__init__.py", "samples/**/constraints.txt", "samples/**/constraints-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 9f53744..eea9818 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -15,7 +15,11 @@ set -eo pipefail -cd github/python-resource-manager +if [[ -z "${PROJECT_ROOT:-}" ]]; then + PROJECT_ROOT="github/python-resource-manager" +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/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index 412b0b5..4e1b1fb 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -40,6 +40,7 @@ RUN apt-get update \ libssl-dev \ libsqlite3-dev \ portaudio19-dev \ + python3-distutils \ redis-server \ software-properties-common \ ssh \ @@ -59,40 +60,8 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && rm -f /var/cache/apt/archives/*.deb - -COPY fetch_gpg_keys.sh /tmp -# Install the desired versions of Python. -RUN set -ex \ - && export GNUPGHOME="$(mktemp -d)" \ - && echo "disable-ipv6" >> "${GNUPGHOME}/dirmngr.conf" \ - && /tmp/fetch_gpg_keys.sh \ - && for PYTHON_VERSION in 3.7.8 3.8.5; do \ - wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \ - && wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \ - && gpg --batch --verify python-${PYTHON_VERSION}.tar.xz.asc python-${PYTHON_VERSION}.tar.xz \ - && rm -r python-${PYTHON_VERSION}.tar.xz.asc \ - && mkdir -p /usr/src/python-${PYTHON_VERSION} \ - && tar -xJC /usr/src/python-${PYTHON_VERSION} --strip-components=1 -f python-${PYTHON_VERSION}.tar.xz \ - && rm python-${PYTHON_VERSION}.tar.xz \ - && cd /usr/src/python-${PYTHON_VERSION} \ - && ./configure \ - --enable-shared \ - # This works only on Python 2.7 and throws a warning on every other - # version, but seems otherwise harmless. - --enable-unicode=ucs4 \ - --with-system-ffi \ - --without-ensurepip \ - && make -j$(nproc) \ - && make install \ - && ldconfig \ - ; done \ - && rm -rf "${GNUPGHOME}" \ - && rm -rf /usr/src/python* \ - && rm -rf ~/.cache/ - RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ - && python3.7 /tmp/get-pip.py \ && python3.8 /tmp/get-pip.py \ && rm /tmp/get-pip.py -CMD ["python3.7"] +CMD ["python3.8"] diff --git a/.kokoro/docker/docs/fetch_gpg_keys.sh b/.kokoro/docker/docs/fetch_gpg_keys.sh deleted file mode 100755 index d653dd8..0000000 --- a/.kokoro/docker/docs/fetch_gpg_keys.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# 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. - -# A script to fetch gpg keys with retry. -# Avoid jinja parsing the file. -# - -function retry { - if [[ "${#}" -le 1 ]]; then - echo "Usage: ${0} retry_count commands.." - exit 1 - fi - local retries=${1} - local command="${@:2}" - until [[ "${retries}" -le 0 ]]; do - $command && return 0 - if [[ $? -ne 0 ]]; then - echo "command failed, retrying" - ((retries--)) - fi - done - return 1 -} - -# 3.6.9, 3.7.5 (Ned Deily) -retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ - 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D - -# 3.8.0 (Ɓukasz Langa) -retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ - E3FF2839C048B25C084DEBE9B26995E310250568 - -# diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 1118107..b117ef9 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-resource-manager/.kokoro/build.sh" +} + +# Only run this nox session. +env_vars: { + key: "NOX_SESSION" + value: "docs docfx" +} diff --git a/.kokoro/release.sh b/.kokoro/release.sh index bd270e6..c1d9f09 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -26,7 +26,7 @@ python3 -m pip install --upgrade twine wheel setuptools export PYTHONUNBUFFERED=1 # Move into the package, build the distribution and upload. -TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google_cloud_pypi_password") +TWINE_PASSWORD=$(cat "${KOKORO_GFILE_DIR}/secret_manager/google-cloud-pypi-token") cd github/python-resource-manager python3 setup.py sdist bdist_wheel -twine upload --username gcloudpypi --password "${TWINE_PASSWORD}" dist/* +twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index 3541438..130bd41 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -23,18 +23,8 @@ env_vars: { value: "github/python-resource-manager/.kokoro/release.sh" } -# Fetch PyPI password -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "google_cloud_pypi_password" - } - } -} - # Tokens needed to report release status back to GitHub env_vars: { key: "SECRET_MANAGER_KEYS" - value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" -} \ No newline at end of file + value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem,google-cloud-pypi-token" +} 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/samples/python3.9/common.cfg b/.kokoro/samples/python3.9/common.cfg new file mode 100644 index 0000000..80446e1 --- /dev/null +++ b/.kokoro/samples/python3.9/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.9" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py39" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-resource-manager/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-resource-manager/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.9/continuous.cfg b/.kokoro/samples/python3.9/continuous.cfg new file mode 100644 index 0000000..a1c8d97 --- /dev/null +++ b/.kokoro/samples/python3.9/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.9/periodic-head.cfg b/.kokoro/samples/python3.9/periodic-head.cfg new file mode 100644 index 0000000..f9cfcd3 --- /dev/null +++ b/.kokoro/samples/python3.9/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.9/periodic.cfg b/.kokoro/samples/python3.9/periodic.cfg new file mode 100644 index 0000000..50fec96 --- /dev/null +++ b/.kokoro/samples/python3.9/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.9/presubmit.cfg b/.kokoro/samples/python3.9/presubmit.cfg new file mode 100644 index 0000000..a1c8d97 --- /dev/null +++ b/.kokoro/samples/python3.9/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh new file mode 100755 index 0000000..d00da99 --- /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-resource-manager + +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..311a8d5 --- /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 don't exist +if ! find samples -name 'requirements.txt' | grep -q .; then + echo "No tests run. './samples/**/requirements.txt' 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 0e2c174..c773dec 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-resource-manager # 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 Build Cop Bot. - # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/buildcop. - if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - chmod +x $KOKORO_GFILE_DIR/linux_amd64/buildcop - $KOKORO_GFILE_DIR/linux_amd64/buildcop + 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/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 719bcd5..4af6cdc 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -159,7 +159,7 @@ if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then "KOKORO_GITHUB_COMMIT" "KOKORO_GITHUB_PULL_REQUEST_NUMBER" "KOKORO_GITHUB_PULL_REQUEST_COMMIT" - # For Build Cop Bot + # For FlakyBot "KOKORO_GITHUB_COMMIT_URL" "KOKORO_GITHUB_PULL_REQUEST_URL" ) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ad8334..62eb5a7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.3.0 + rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -12,6 +26,6 @@ repos: hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.2 hooks: - id: flake8 diff --git a/.repo-metadata.json b/.repo-metadata.json index 5e46229..07c8c5b 100644 --- a/.repo-metadata.json +++ b/.repo-metadata.json @@ -4,8 +4,9 @@ "product_documentation": "https://cloud.google.com/resource-manager", "client_documentation": "https://googleapis.dev/python/cloudresourcemanager/latest", "issue_tracker": "https://issuetracker.google.com/savedsearches/559757", - "release_level": "alpha", + "release_level": "beta", "language": "python", + "library_type": "GAPIC_MANUAL", "repo": "googleapis/python-resource-manager", "distribution_name": "google-cloud-resource-manager", "api_id": "cloudresourcemanager.googleapis.com", 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/CHANGELOG.md b/CHANGELOG.md index 8d4398c..d6e3bec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,50 @@ [1]: https://pypi.org/project/google-cloud-resource-manager/#history +## [1.0.0](https://www.github.com/googleapis/python-resource-manager/compare/v0.30.3...v1.0.0) (2021-07-18) + + +### Features + +* add always_use_jwt_access ([#73](https://www.github.com/googleapis/python-resource-manager/issues/73)) ([9c0bc88](https://www.github.com/googleapis/python-resource-manager/commit/9c0bc888c685f2dbcbc66ca73e7fd4f27d5be47e)) +* add v3 ([#62](https://www.github.com/googleapis/python-resource-manager/issues/62)) ([72f69f0](https://www.github.com/googleapis/python-resource-manager/commit/72f69f0f3a2205ef3bb49ca3e3ae670fd103f6cb)) + + +### Bug Fixes + +* disable always_use_jwt_access ([#76](https://www.github.com/googleapis/python-resource-manager/issues/76)) ([9f36514](https://www.github.com/googleapis/python-resource-manager/commit/9f365141716b2acc90bb16132dba48b38e470a9b)) +* remove v1beta1 ([72f69f0](https://www.github.com/googleapis/python-resource-manager/commit/72f69f0f3a2205ef3bb49ca3e3ae670fd103f6cb)) +* require google-cloud-core >= 1.3.0 ([#43](https://www.github.com/googleapis/python-resource-manager/issues/43)) ([16df2d0](https://www.github.com/googleapis/python-resource-manager/commit/16df2d064b25ac75234cbbd736b16fba53a51f2d)) + + +### Documentation + +* omit mention of Python 2.7 in 'CONTRIBUTING.rst' ([#1127](https://www.github.com/googleapis/python-resource-manager/issues/1127)) ([#68](https://www.github.com/googleapis/python-resource-manager/issues/68)) ([d6e699e](https://www.github.com/googleapis/python-resource-manager/commit/d6e699eb0492c979871ed69f6badbec8ab3427f4)), closes [#1126](https://www.github.com/googleapis/python-resource-manager/issues/1126) + + +### Miscellaneous Chores + +* release as 1.0.0 ([#78](https://www.github.com/googleapis/python-resource-manager/issues/78)) ([fc852aa](https://www.github.com/googleapis/python-resource-manager/commit/fc852aa0b0e42a37324ca94901c34015e6127df2)) +* release as 1.0.0-rc1 ([#64](https://www.github.com/googleapis/python-resource-manager/issues/64)) ([cce4608](https://www.github.com/googleapis/python-resource-manager/commit/cce46083be8cd73cbe921ee8ac917806507b6084)) + +## [1.0.0-rc1](https://www.github.com/googleapis/python-resource-manager/compare/v0.30.3...v1.0.0-rc1) (2021-06-14) + + +### Features + +* add v3 ([#62](https://www.github.com/googleapis/python-resource-manager/issues/62)) ([72f69f0](https://www.github.com/googleapis/python-resource-manager/commit/72f69f0f3a2205ef3bb49ca3e3ae670fd103f6cb)) + + +### Bug Fixes + +* remove v1beta1 ([72f69f0](https://www.github.com/googleapis/python-resource-manager/commit/72f69f0f3a2205ef3bb49ca3e3ae670fd103f6cb)) +* require google-cloud-core >= 1.3.0 ([#43](https://www.github.com/googleapis/python-resource-manager/issues/43)) ([16df2d0](https://www.github.com/googleapis/python-resource-manager/commit/16df2d064b25ac75234cbbd736b16fba53a51f2d)) + + +### Miscellaneous Chores + +* release as 1.0.0-rc1 ([#64](https://www.github.com/googleapis/python-resource-manager/issues/64)) ([cce4608](https://www.github.com/googleapis/python-resource-manager/commit/cce46083be8cd73cbe921ee8ac917806507b6084)) + ### [0.30.3](https://www.github.com/googleapis/python-resource-manager/compare/v0.30.2...v0.30.3) (2020-12-10) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 69f7476..4fb8362 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -21,8 +21,8 @@ In order to add a feature: - The feature must be documented in both the API and narrative documentation. -- The feature must work fully on the following CPython versions: 2.7, - 3.5, 3.6, 3.7 and 3.8 on both UNIX and Windows. +- The feature must work fully on the following CPython versions: + 3.6, 3.7, 3.8 and 3.9 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -68,10 +68,12 @@ Using ``nox`` We use `nox `__ to instrument our tests. - To test your changes, run unit tests with ``nox``:: + $ nox -s unit + +- To run a single unit test:: + + $ nox -s unit-3.9 -- -k - $ nox -s unit-2.7 - $ nox -s unit-3.7 - $ ... .. note:: @@ -93,8 +95,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,34 +139,23 @@ Running System Tests - To run system tests, you can execute:: - $ nox -s system-3.7 - $ nox -s system-2.7 + # Run all system tests + $ nox -s system + + # 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 - of Python 3. + System tests are only configured to run under 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 auth settings and change some configuration in your project to run all the tests. -- System tests will be run against an actual project and - so you'll need to provide some environment variables to facilitate - authentication to your project: - - - ``GOOGLE_APPLICATION_CREDENTIALS``: The path to a JSON key file; - Such a file can be downloaded directly from the developer's console by clicking - "Generate new JSON key". See private key - `docs `__ - for more details. - -- Once you have downloaded your json keys, set the environment variable - ``GOOGLE_APPLICATION_CREDENTIALS`` to the absolute path of the json file:: - - $ export GOOGLE_APPLICATION_CREDENTIALS="/Users//path/to/app_credentials.json" - +- System tests will be run against an actual project. You should use local credentials from gcloud when possible. See `Best practices for application authentication `__. Some tests require a service account. For those tests see `Authenticating as a service account `__. ************* Test Coverage @@ -202,25 +197,24 @@ Supported Python Versions We support: -- `Python 3.5`_ - `Python 3.6`_ - `Python 3.7`_ - `Python 3.8`_ +- `Python 3.9`_ -.. _Python 3.5: https://docs.python.org/3.5/ .. _Python 3.6: https://docs.python.org/3.6/ .. _Python 3.7: https://docs.python.org/3.7/ .. _Python 3.8: https://docs.python.org/3.8/ +.. _Python 3.9: https://docs.python.org/3.9/ Supported versions can be found in our ``noxfile.py`` `config`_. .. _config: https://github.com/googleapis/python-resource-manager/blob/master/noxfile.py -Python 2.7 support is deprecated. All code changes should maintain Python 2.7 compatibility until January 1, 2020. -We also explicitly decided to support Python 3 beginning with version -3.5. Reasons for this include: +We also explicitly decided to support Python 3 beginning with version 3.6. +Reasons for this include: - Encouraging use of newest versions of Python 3 - Taking the lead of `prominent`_ open-source `projects`_ diff --git a/LICENSE b/LICENSE index 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/README.rst b/README.rst index aede2aa..3a8b6f2 100644 --- a/README.rst +++ b/README.rst @@ -1,36 +1,24 @@ Python Client for Google Cloud Resource Manager =============================================== -|alpha| |pypi| |versions| +|beta| |pypi| |versions| `Google Cloud Resource Manager`_ API provides methods that you can use to programmatically manage your projects in the Google Cloud Platform. With this API, you can do the following: -- Get a list of all projects associated with an account -- Create new projects -- Update existing projects -- Delete projects -- Undelete, or recover, projects that you don't want to delete - - `Client Library Documentation`_ - `Product Documentation`_ - -.. |alpha| image:: https://img.shields.io/badge/support-alpha-orange.svg - :target: https://github.com/googleapis/google-cloud-python/blob/master/README.rst#alpha-support +.. |beta| image:: https://img.shields.io/badge/support-beta-orange.svg + :target: https://github.com/googleapis/google-cloud-python/blob/master/README.rst#beta-support .. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-resource-manager.svg :target: https://pypi.org/project/google-cloud-resource-manager/ .. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-resource-manager.svg :target: https://pypi.org/project/google-cloud-resource-manager/ -.. _Google Cloud Resource Manager: https://cloud.google.com/resource-manager/ +.. _Google Cloud Resource Manager: https://cloud.google.com/resource-manager .. _Client Library Documentation: https://googleapis.dev/python/cloudresourcemanager/latest -.. _Product Documentation: https://cloud.google.com/resource-manager/docs/ - -.. note:: - - Don't forget to look at the `Authentication`_ section below. - It's slightly different from the rest of this library. +.. _Product Documentation: https://cloud.google.com/resource-manager Quick Start ----------- @@ -39,11 +27,13 @@ In order to use this library, you first need to go through the following steps: 1. `Select or create a Cloud Platform project.`_ 2. `Enable billing for your project.`_ -3. `Enable the Google Cloud Resource Manager API.`_ +3. `Enable the Resource Manager API.`_ +4. `Setup Authentication.`_ .. _Select or create a Cloud Platform project.: https://console.cloud.google.com/project .. _Enable billing for your project.: https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_project -.. _Enable the Google Cloud Resource Manager API.: https://cloud.google.com/resource-manager +.. _Enable the Resource Manager API.: https://cloud.google.com/resource-manager/docs/quickstart-organizations#before-you-begin +.. _Setup Authentication.: https://googleapis.dev/python/google-api-core/latest/auth.html Installation ~~~~~~~~~~~~ @@ -59,15 +49,6 @@ dependencies. .. _`virtualenv`: https://virtualenv.pypa.io/en/latest/ -Supported Python Versions -^^^^^^^^^^^^^^^^^^^^^^^^^ -Python >= 3.5 - -Deprecated Python Versions -^^^^^^^^^^^^^^^^^^^^^^^^^^ -Python == 2.7. Python 2.7 support will be removed on January 1, 2020. - - Mac/Linux ^^^^^^^^^ @@ -89,71 +70,15 @@ Windows \Scripts\activate \Scripts\pip.exe install google-cloud-resource-manager +Next Steps +~~~~~~~~~~ -Authentication -~~~~~~~~~~~~~~ - -Unlike the other APIs, the Resource Manager API is focused on managing your -various projects inside Google Cloud Platform. What this means (currently, as -of August 2015) is that you can't use a Service Account to work with some -parts of this API (for example, creating projects). - -The reason is actually pretty simple: if your API call is trying to do -something like create a project, what project's Service Account can you use? -Currently none. - -This means that for this API you should always use the credentials -provided by the `Google Cloud SDK`_, which you can get by running -``gcloud auth login``. - -.. _Google Cloud SDK: http://cloud.google.com/sdk - -Once you run that command, ``google-cloud-python`` will automatically pick up -the credentials, and you can use the "automatic discovery" feature of the -library. - -Start by authenticating: - -.. code-block:: bash - - $ gcloud auth application-default login - -And then simply create a client: - -.. code-block:: python - - from google.cloud import resource_manager - client = resource_manager.Client() - -Using the API -------------- - -Here's a quick example of the full life-cycle: - -.. code-block:: python - - from google.cloud import resource_manager - - client = resource_manager.Client() - - # List all projects you have access to - for project in client.list_projects(): - print(project) - - # Create a new project - new_project = client.new_project( - 'your-project-id-here', name='My new project') - new_project.create() - - # Update an existing project - project = client.fetch_project('my-existing-project') - project.name = 'Modified name' - project.update() - - # Delete a project - project = client.new_project('my-existing-project') - project.delete() +- Read the `Client Library Documentation`_ for Resource Manager + to see other available methods on the client. +- Read the `Resource Manager Product documentation`_ to learn + more about the product and see How-to Guides. +- View this `README`_ to see the full list of Cloud + APIs that we cover. - # Undelete a project - project = client.new_project('my-existing-project') - project.undelete() +.. _Resource Manager Product documentation: https://cloud.google.com/resource-manager/docs +.. _README: https://github.com/googleapis/google-cloud-python/blob/master/README.rst \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..8b58ae9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +To report a security issue, please use [g.co/vulnz](https://g.co/vulnz). + +The Google Security Team will respond within 5 working days of your report on g.co/vulnz. + +We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue. diff --git a/docs/README.rst b/docs/README.rst deleted file mode 120000 index 89a0106..0000000 --- a/docs/README.rst +++ /dev/null @@ -1 +0,0 @@ -../README.rst \ No newline at end of file diff --git a/docs/README.rst b/docs/README.rst new file mode 100644 index 0000000..3a8b6f2 --- /dev/null +++ b/docs/README.rst @@ -0,0 +1,84 @@ +Python Client for Google Cloud Resource Manager +=============================================== + +|beta| |pypi| |versions| + +`Google Cloud Resource Manager`_ API provides methods that you can use +to programmatically manage your projects in the Google Cloud Platform. +With this API, you can do the following: + +- `Client Library Documentation`_ +- `Product Documentation`_ + +.. |beta| image:: https://img.shields.io/badge/support-beta-orange.svg + :target: https://github.com/googleapis/google-cloud-python/blob/master/README.rst#beta-support +.. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-resource-manager.svg + :target: https://pypi.org/project/google-cloud-resource-manager/ +.. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-resource-manager.svg + :target: https://pypi.org/project/google-cloud-resource-manager/ +.. _Google Cloud Resource Manager: https://cloud.google.com/resource-manager +.. _Client Library Documentation: https://googleapis.dev/python/cloudresourcemanager/latest +.. _Product Documentation: https://cloud.google.com/resource-manager + +Quick Start +----------- + +In order to use this library, you first need to go through the following steps: + +1. `Select or create a Cloud Platform project.`_ +2. `Enable billing for your project.`_ +3. `Enable the Resource Manager API.`_ +4. `Setup Authentication.`_ + +.. _Select or create a Cloud Platform project.: https://console.cloud.google.com/project +.. _Enable billing for your project.: https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_project +.. _Enable the Resource Manager API.: https://cloud.google.com/resource-manager/docs/quickstart-organizations#before-you-begin +.. _Setup Authentication.: https://googleapis.dev/python/google-api-core/latest/auth.html + +Installation +~~~~~~~~~~~~ + +Install this library in a `virtualenv`_ using pip. `virtualenv`_ is a tool to +create isolated Python environments. The basic problem it addresses is one of +dependencies and versions, and indirectly permissions. + +With `virtualenv`_, it's possible to install this library without needing system +install permissions, and without clashing with the installed system +dependencies. + +.. _`virtualenv`: https://virtualenv.pypa.io/en/latest/ + + +Mac/Linux +^^^^^^^^^ + +.. code-block:: console + + pip install virtualenv + virtualenv + source /bin/activate + /bin/pip install google-cloud-resource-manager + + +Windows +^^^^^^^ + +.. code-block:: console + + pip install virtualenv + virtualenv + \Scripts\activate + \Scripts\pip.exe install google-cloud-resource-manager + +Next Steps +~~~~~~~~~~ + +- Read the `Client Library Documentation`_ for Resource Manager + to see other available methods on the client. +- Read the `Resource Manager Product documentation`_ to learn + more about the product and see How-to Guides. +- View this `README`_ to see the full list of Cloud + APIs that we cover. + +.. _Resource Manager Product documentation: https://cloud.google.com/resource-manager/docs +.. _README: https://github.com/googleapis/google-cloud-python/blob/master/README.rst \ No newline at end of file diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 0abaf22..b0a2954 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,4 +1,20 @@ 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 +} + +/* Insert space between methods for readability */ +dl.method { + padding-top: 10px; + padding-bottom: 10px +} + +/* Insert empty space between classes */ +dl.class { + padding-bottom: 50px +} diff --git a/docs/client.rst b/docs/client.rst deleted file mode 100644 index 1678f85..0000000 --- a/docs/client.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. toctree:: - :maxdepth: 0 - :hidden: - -Client ------- - -.. automodule:: google.cloud.resource_manager.client - :members: - :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py index a3dddc8..13b70d9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,17 @@ # -*- coding: utf-8 -*- +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # google-cloud-resource-manager documentation build configuration file # @@ -67,9 +80,9 @@ master_doc = "index" # General information about the project. -project = u"google-cloud-resource-manager" -copyright = u"2019, Google" -author = u"Google APIs" +project = "google-cloud-resource-manager" +copyright = "2019, Google" +author = "Google APIs" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -268,7 +281,7 @@ ( master_doc, "google-cloud-resource-manager.tex", - u"google-cloud-resource-manager Documentation", + "google-cloud-resource-manager Documentation", author, "manual", ) @@ -303,7 +316,7 @@ ( master_doc, "google-cloud-resource-manager", - u"google-cloud-resource-manager Documentation", + "google-cloud-resource-manager Documentation", [author], 1, ) @@ -322,7 +335,7 @@ ( master_doc, "google-cloud-resource-manager", - u"google-cloud-resource-manager Documentation", + "google-cloud-resource-manager Documentation", author, "google-cloud-resource-manager", "google-cloud-resource-manager Library", @@ -350,6 +363,7 @@ "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), "grpc": ("https://grpc.github.io/grpc/python/", None), "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), + "protobuf": ("https://googleapis.dev/python/protobuf/latest/", None), } diff --git a/docs/index.rst b/docs/index.rst index 14c5767..4eb3a30 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,13 +1,15 @@ .. include:: README.rst +.. include:: multiprocessing.rst -API Reference + +Api Reference ------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 2 - client - project + resourcemanager_v3/services + resourcemanager_v3/types Changelog @@ -16,6 +18,6 @@ Changelog For a list of all ``google-cloud-resource-manager`` releases: .. toctree:: - :maxdepth: 2 + :maxdepth: 2 - changelog + changelog \ No newline at end of file diff --git a/docs/multiprocessing.rst b/docs/multiprocessing.rst new file mode 100644 index 0000000..536d17b --- /dev/null +++ b/docs/multiprocessing.rst @@ -0,0 +1,7 @@ +.. note:: + + Because this client uses :mod:`grpc` library, it is safe to + share instances across threads. In multiprocessing scenarios, the best + practice is to create client instances *after* the invocation of + :func:`os.fork` by :class:`multiprocessing.pool.Pool` or + :class:`multiprocessing.Process`. diff --git a/docs/project.rst b/docs/project.rst deleted file mode 100644 index e6f8a90..0000000 --- a/docs/project.rst +++ /dev/null @@ -1,6 +0,0 @@ -Projects -~~~~~~~~ - -.. automodule:: google.cloud.resource_manager.project - :members: - :show-inheritance: diff --git a/docs/resourcemanager_v3/folders.rst b/docs/resourcemanager_v3/folders.rst new file mode 100644 index 0000000..3e84ddf --- /dev/null +++ b/docs/resourcemanager_v3/folders.rst @@ -0,0 +1,10 @@ +Folders +------------------------- + +.. automodule:: google.cloud.resourcemanager_v3.services.folders + :members: + :inherited-members: + +.. automodule:: google.cloud.resourcemanager_v3.services.folders.pagers + :members: + :inherited-members: diff --git a/docs/resourcemanager_v3/organizations.rst b/docs/resourcemanager_v3/organizations.rst new file mode 100644 index 0000000..30d36b0 --- /dev/null +++ b/docs/resourcemanager_v3/organizations.rst @@ -0,0 +1,10 @@ +Organizations +------------------------------- + +.. automodule:: google.cloud.resourcemanager_v3.services.organizations + :members: + :inherited-members: + +.. automodule:: google.cloud.resourcemanager_v3.services.organizations.pagers + :members: + :inherited-members: diff --git a/docs/resourcemanager_v3/projects.rst b/docs/resourcemanager_v3/projects.rst new file mode 100644 index 0000000..496baf0 --- /dev/null +++ b/docs/resourcemanager_v3/projects.rst @@ -0,0 +1,10 @@ +Projects +-------------------------- + +.. automodule:: google.cloud.resourcemanager_v3.services.projects + :members: + :inherited-members: + +.. automodule:: google.cloud.resourcemanager_v3.services.projects.pagers + :members: + :inherited-members: diff --git a/docs/resourcemanager_v3/services.rst b/docs/resourcemanager_v3/services.rst new file mode 100644 index 0000000..c542a30 --- /dev/null +++ b/docs/resourcemanager_v3/services.rst @@ -0,0 +1,11 @@ +Services for Google Cloud Resourcemanager v3 API +================================================ +.. toctree:: + :maxdepth: 2 + + folders + organizations + projects + tag_bindings + tag_keys + tag_values diff --git a/docs/resourcemanager_v3/tag_bindings.rst b/docs/resourcemanager_v3/tag_bindings.rst new file mode 100644 index 0000000..313ea3d --- /dev/null +++ b/docs/resourcemanager_v3/tag_bindings.rst @@ -0,0 +1,10 @@ +TagBindings +----------------------------- + +.. automodule:: google.cloud.resourcemanager_v3.services.tag_bindings + :members: + :inherited-members: + +.. automodule:: google.cloud.resourcemanager_v3.services.tag_bindings.pagers + :members: + :inherited-members: diff --git a/docs/resourcemanager_v3/tag_keys.rst b/docs/resourcemanager_v3/tag_keys.rst new file mode 100644 index 0000000..f8d408f --- /dev/null +++ b/docs/resourcemanager_v3/tag_keys.rst @@ -0,0 +1,10 @@ +TagKeys +------------------------- + +.. automodule:: google.cloud.resourcemanager_v3.services.tag_keys + :members: + :inherited-members: + +.. automodule:: google.cloud.resourcemanager_v3.services.tag_keys.pagers + :members: + :inherited-members: diff --git a/docs/resourcemanager_v3/tag_values.rst b/docs/resourcemanager_v3/tag_values.rst new file mode 100644 index 0000000..f4fa247 --- /dev/null +++ b/docs/resourcemanager_v3/tag_values.rst @@ -0,0 +1,10 @@ +TagValues +--------------------------- + +.. automodule:: google.cloud.resourcemanager_v3.services.tag_values + :members: + :inherited-members: + +.. automodule:: google.cloud.resourcemanager_v3.services.tag_values.pagers + :members: + :inherited-members: diff --git a/docs/resourcemanager_v3/types.rst b/docs/resourcemanager_v3/types.rst new file mode 100644 index 0000000..9198b3b --- /dev/null +++ b/docs/resourcemanager_v3/types.rst @@ -0,0 +1,7 @@ +Types for Google Cloud Resourcemanager v3 API +============================================= + +.. automodule:: google.cloud.resourcemanager_v3.types + :members: + :undoc-members: + :show-inheritance: diff --git a/google/cloud/resource_manager/_http.py b/google/cloud/resource_manager/_http.py deleted file mode 100644 index dcf1aeb..0000000 --- a/google/cloud/resource_manager/_http.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2015 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. - -"""Create / interact with Google Cloud Resource Manager connections.""" - - -from google.cloud import _http - -from google.cloud.resource_manager import __version__ - - -class Connection(_http.JSONConnection): - """A connection to Google Cloud Resource Manager via the JSON REST API. - - :type client: :class:`~google.cloud.resource_manager.client.Client` - :param client: The client that owns the current connection. - - :type client_info: :class:`~google.api_core.client_info.ClientInfo` - :param client_info: (Optional) instance used to generate user agent. - - :type client_options: :class:`~google.api_core.client_options.ClientOptions` - :param client_options (Optional) Client options used to set user options - on the client. API Endpoint should be set through client_options. - """ - - DEFAULT_API_ENDPOINT = "https://cloudresourcemanager.googleapis.com" - - def __init__(self, client, client_info=None, api_endpoint=DEFAULT_API_ENDPOINT): - super(Connection, self).__init__(client, client_info) - self.API_BASE_URL = api_endpoint - self._client_info.gapic_version = __version__ - self._client_info.client_library_version = __version__ - - API_VERSION = "v1beta1" - """The version of the API, used in building the API call's URL.""" - - API_URL_TEMPLATE = "{api_base_url}/{api_version}{path}" - """A template for the URL of a particular API call.""" diff --git a/google/cloud/resource_manager/client.py b/google/cloud/resource_manager/client.py deleted file mode 100644 index 557c7f4..0000000 --- a/google/cloud/resource_manager/client.py +++ /dev/null @@ -1,219 +0,0 @@ -# Copyright 2015 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. - -"""A Client for interacting with the Resource Manager API.""" - -import six - -import google.api_core.client_options -from google.api_core import page_iterator -from google.cloud.client import Client as BaseClient - -from google.cloud.resource_manager._http import Connection -from google.cloud.resource_manager.project import Project - - -class Client(BaseClient): - """Client to bundle configuration needed for API requests. - - See - https://cloud.google.com/resource-manager/reference/rest/ - for more information on this API. - - Automatically get credentials:: - - >>> from google.cloud import resource_manager - >>> client = resource_manager.Client() - - :type credentials: :class:`~google.auth.credentials.Credentials` - :param credentials: (Optional) The OAuth2 Credentials to use for this - client. If not passed (and if no ``_http`` object is - passed), falls back to the default inferred from the - environment. - - :type _http: :class:`~requests.Session` - :param _http: (Optional) HTTP object to make requests. Can be any object - that defines ``request()`` with the same interface as - :meth:`requests.Session.request`. If not passed, an - ``_http`` object is created that is bound to the - ``credentials`` for the current object. - This parameter should be considered private, and could - change in the future. - - :type client_info: :class:`~google.api_core.client_info.ClientInfo` - :param client_info: - 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 library - or partner tool. - :type client_options: :class:`~google.api_core.client_options.ClientOptions` - or :class:`dict` - :param client_options: (Optional) Client options used to set user options - on the client. API Endpoint should be set through client_options. - """ - - SCOPE = ("https://www.googleapis.com/auth/cloud-platform",) - """The scopes required for authenticating as a Resouce Manager consumer.""" - - def __init__( - self, credentials=None, _http=None, client_info=None, client_options=None - ): - super(Client, self).__init__(credentials=credentials, _http=_http) - - kw_args = {"client_info": client_info} - if client_options: - if type(client_options) == dict: - client_options = google.api_core.client_options.from_dict( - client_options - ) - if client_options.api_endpoint: - api_endpoint = client_options.api_endpoint - kw_args["api_endpoint"] = api_endpoint - - self._connection = Connection(self, **kw_args) - - def new_project(self, project_id, name=None, labels=None): - """Create a project bound to the current client. - - Use :meth:`Project.reload() \ - ` to retrieve - project metadata after creating a - :class:`~google.cloud.resource_manager.project.Project` instance. - - .. note: - - This does not make an API call. - - :type project_id: str - :param project_id: The ID for this project. - - :type name: str - :param name: The display name of the project. - - :type labels: dict - :param labels: A list of labels associated with the project. - - :rtype: :class:`~google.cloud.resource_manager.project.Project` - :returns: A new instance of a - :class:`~google.cloud.resource_manager.project.Project` - **without** any metadata loaded. - """ - return Project(project_id=project_id, client=self, name=name, labels=labels) - - def fetch_project(self, project_id): - """Fetch an existing project and it's relevant metadata by ID. - - .. note:: - - If the project does not exist, this will raise a - :class:`NotFound ` error. - - :type project_id: str - :param project_id: The ID for this project. - - :rtype: :class:`~google.cloud.resource_manager.project.Project` - :returns: A :class:`~google.cloud.resource_manager.project.Project` - with metadata fetched from the API. - """ - project = self.new_project(project_id) - project.reload() - return project - - def list_projects(self, filter_params=None, page_size=None): - """List the projects visible to this client. - - Example:: - - >>> from google.cloud import resource_manager - >>> client = resource_manager.Client() - >>> for project in client.list_projects(): - ... print(project.project_id) - - List all projects with label ``'environment'`` set to ``'prod'`` - (filtering by labels):: - - >>> from google.cloud import resource_manager - >>> client = resource_manager.Client() - >>> env_filter = {'labels.environment': 'prod'} - >>> for project in client.list_projects(env_filter): - ... print(project.project_id) - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/list - - Complete filtering example:: - - >>> project_filter = { # Return projects with... - ... 'name': 'My Project', # name set to 'My Project'. - ... 'id': 'my-project-id', # id set to 'my-project-id'. - ... 'labels.stage': 'prod', # the label 'stage' set to 'prod' - ... 'labels.color': '*' # a label 'color' set to anything. - ... } - >>> client.list_projects(project_filter) - - :type filter_params: dict - :param filter_params: (Optional) A dictionary of filter options where - each key is a property to filter on, and each - value is the (case-insensitive) value to check - (or the glob ``*`` to check for existence of the - property). See the example above for more - details. - - :type page_size: int - :param page_size: (Optional) The maximum number of projects in each - page of results from this request. Non-positive - values are ignored. Defaults to a sensible value - set by the API. - - :rtype: :class:`~google.api_core.page_iterator.Iterator` - :returns: Iterator of all - :class:`~google.cloud.resource_manager.project.Project`. - that the current user has access to. - """ - extra_params = {} - - if page_size is not None: - extra_params["pageSize"] = page_size - - if filter_params is not None: - extra_params["filter"] = " ".join( - [ - "{}:{}".format(key, value) - for key, value in six.iteritems(filter_params) - ] - ) - - return page_iterator.HTTPIterator( - client=self, - api_request=self._connection.api_request, - path="/projects", - item_to_value=_item_to_project, - items_key="projects", - extra_params=extra_params, - ) - - -def _item_to_project(iterator, resource): - """Convert a JSON project to the native object. - - :type iterator: :class:`~google.api_core.page_iterator.Iterator` - :param iterator: The iterator that has retrieved the item. - - :type resource: dict - :param resource: A resource to be converted to a project. - - :rtype: :class:`.Project` - :returns: The next project in the page. - """ - return Project.from_api_repr(resource, client=iterator.client) diff --git a/google/cloud/resource_manager/project.py b/google/cloud/resource_manager/project.py deleted file mode 100644 index cf499fa..0000000 --- a/google/cloud/resource_manager/project.py +++ /dev/null @@ -1,270 +0,0 @@ -# Copyright 2015 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. - -"""Utility for managing projects via the Cloud Resource Manager API.""" - - -from google.cloud.exceptions import NotFound - - -class Project(object): - """Projects are containers for your work on Google Cloud Platform. - - .. note:: - - A :class:`Project` can also be created via - :meth:`Client.new_project() \ - ` - - To manage labels on a :class:`Project`:: - - >>> from google.cloud import resource_manager - >>> client = resource_manager.Client() - >>> project = client.new_project('purple-spaceship-123') - >>> project.labels = {'color': 'purple'} - >>> project.labels['environment'] = 'production' - >>> project.update() - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects - - :type project_id: str - :param project_id: The globally unique ID of the project. - - :type client: :class:`google.cloud.resource_manager.client.Client` - :param client: The Client used with this project. - - :type name: str - :param name: The display name of the project. - - :type labels: dict - :param labels: A list of labels associated with the project. - """ - - def __init__(self, project_id, client, name=None, labels=None): - self._client = client - self.project_id = project_id - self.name = name - self.number = None - self.labels = labels or {} - self.status = None - self.parent = None - - def __repr__(self): - return "" % (self.name, self.project_id) - - @classmethod - def from_api_repr(cls, resource, client): - """Factory: construct a project given its API representation. - - :type resource: dict - :param resource: project resource representation returned from the API - - :type client: :class:`google.cloud.resource_manager.client.Client` - :param client: The Client used with this project. - - :rtype: :class:`google.cloud.resource_manager.project.Project` - :returns: The project created. - """ - project = cls(project_id=resource["projectId"], client=client) - project.set_properties_from_api_repr(resource) - return project - - def set_properties_from_api_repr(self, resource): - """Update specific properties from its API representation.""" - self.name = resource.get("name") - self.number = resource["projectNumber"] - self.labels = resource.get("labels", {}) - self.status = resource["lifecycleState"] - if "parent" in resource: - self.parent = resource["parent"] - - @property - def full_name(self): - """Fully-qualified name (ie, ``'projects/purple-spaceship-123'``).""" - if not self.project_id: - raise ValueError("Missing project ID.") - return "projects/%s" % (self.project_id) - - @property - def path(self): - """URL for the project (ie, ``'/projects/purple-spaceship-123'``).""" - return "/%s" % (self.full_name) - - def _require_client(self, client): - """Check client or verify over-ride. - - :type client: :class:`google.cloud.resource_manager.client.Client` or - ``NoneType`` - :param client: the client to use. If not passed, falls back to the - ``client`` stored on the current project. - - :rtype: :class:`google.cloud.resource_manager.client.Client` - :returns: The client passed in or the currently bound client. - """ - if client is None: - client = self._client - return client - - def create(self, client=None): - """API call: create the project via a ``POST`` request. - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/create - - :type client: :class:`google.cloud.resource_manager.client.Client` or - :data:`NoneType ` - :param client: the client to use. If not passed, falls back to - the client stored on the current project. - """ - client = self._require_client(client) - - data = {"projectId": self.project_id, "name": self.name, "labels": self.labels} - resp = client._connection.api_request( - method="POST", path="/projects", data=data - ) - self.set_properties_from_api_repr(resource=resp) - - def reload(self, client=None): - """API call: reload the project via a ``GET`` request. - - This method will reload the newest metadata for the project. If you've - created a new :class:`Project` instance via - :meth:`Client.new_project() \ - `, - this method will retrieve project metadata. - - .. warning:: - - This will overwrite any local changes you've made and not saved - via :meth:`update`. - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/get - - :type client: :class:`google.cloud.resource_manager.client.Client` or - :data:`NoneType ` - :param client: the client to use. If not passed, falls back to - the client stored on the current project. - """ - client = self._require_client(client) - - # We assume the project exists. If it doesn't it will raise a NotFound - # exception. - resp = client._connection.api_request(method="GET", path=self.path) - self.set_properties_from_api_repr(resource=resp) - - def exists(self, client=None): - """API call: test the existence of a project via a ``GET`` request. - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/get - - :type client: :class:`google.cloud.resource_manager.client.Client` or - :data:`NoneType ` - :param client: the client to use. If not passed, falls back to - the client stored on the current project. - - :rtype: bool - :returns: Boolean indicating existence of the project. - """ - client = self._require_client(client) - - try: - # Note that we have to request the entire resource as the API - # doesn't provide a way tocheck for existence only. - client._connection.api_request(method="GET", path=self.path) - except NotFound: - return False - else: - return True - - def update(self, client=None): - """API call: update the project via a ``PUT`` request. - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/update - - :type client: :class:`google.cloud.resource_manager.client.Client` or - :data:`NoneType ` - :param client: the client to use. If not passed, falls back to - the client stored on the current project. - """ - client = self._require_client(client) - - data = {"name": self.name, "labels": self.labels, "parent": self.parent} - - resp = client._connection.api_request(method="PUT", path=self.path, data=data) - self.set_properties_from_api_repr(resp) - - def delete(self, client=None, reload_data=False): - """API call: delete the project via a ``DELETE`` request. - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/delete - - This actually changes the status (``lifecycleState``) from ``ACTIVE`` - to ``DELETE_REQUESTED``. - Later (it's not specified when), the project will move into the - ``DELETE_IN_PROGRESS`` state, which means the deleting has actually - begun. - - :type client: :class:`google.cloud.resource_manager.client.Client` or - :data:`NoneType ` - :param client: the client to use. If not passed, falls back to - the client stored on the current project. - - :type reload_data: bool - :param reload_data: Whether to reload the project with the latest - state. If you want to get the updated status, - you'll want this set to :data:`True` as the DELETE - method doesn't send back the updated project. - Default: :data:`False`. - """ - client = self._require_client(client) - client._connection.api_request(method="DELETE", path=self.path) - - # If the reload flag is set, reload the project. - if reload_data: - self.reload() - - def undelete(self, client=None, reload_data=False): - """API call: undelete the project via a ``POST`` request. - - See - https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/undelete - - This actually changes the project status (``lifecycleState``) from - ``DELETE_REQUESTED`` to ``ACTIVE``. - If the project has already reached a status of ``DELETE_IN_PROGRESS``, - this request will fail and the project cannot be restored. - - :type client: :class:`google.cloud.resource_manager.client.Client` or - :data:`NoneType ` - :param client: the client to use. If not passed, falls back to - the client stored on the current project. - - :type reload_data: bool - :param reload_data: Whether to reload the project with the latest - state. If you want to get the updated status, - you'll want this set to :data:`True` as the DELETE - method doesn't send back the updated project. - Default: :data:`False`. - """ - client = self._require_client(client) - client._connection.api_request(method="POST", path=self.path + ":undelete") - - # If the reload flag is set, reload the project. - if reload_data: - self.reload() diff --git a/google/cloud/resourcemanager/__init__.py b/google/cloud/resourcemanager/__init__.py new file mode 100644 index 0000000..bc066e7 --- /dev/null +++ b/google/cloud/resourcemanager/__init__.py @@ -0,0 +1,198 @@ +# -*- 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. +# + +from google.cloud.resourcemanager_v3.services.folders.client import FoldersClient +from google.cloud.resourcemanager_v3.services.folders.async_client import ( + FoldersAsyncClient, +) +from google.cloud.resourcemanager_v3.services.organizations.client import ( + OrganizationsClient, +) +from google.cloud.resourcemanager_v3.services.organizations.async_client import ( + OrganizationsAsyncClient, +) +from google.cloud.resourcemanager_v3.services.projects.client import ProjectsClient +from google.cloud.resourcemanager_v3.services.projects.async_client import ( + ProjectsAsyncClient, +) +from google.cloud.resourcemanager_v3.services.tag_bindings.client import ( + TagBindingsClient, +) +from google.cloud.resourcemanager_v3.services.tag_bindings.async_client import ( + TagBindingsAsyncClient, +) +from google.cloud.resourcemanager_v3.services.tag_keys.client import TagKeysClient +from google.cloud.resourcemanager_v3.services.tag_keys.async_client import ( + TagKeysAsyncClient, +) +from google.cloud.resourcemanager_v3.services.tag_values.client import TagValuesClient +from google.cloud.resourcemanager_v3.services.tag_values.async_client import ( + TagValuesAsyncClient, +) + +from google.cloud.resourcemanager_v3.types.folders import CreateFolderMetadata +from google.cloud.resourcemanager_v3.types.folders import CreateFolderRequest +from google.cloud.resourcemanager_v3.types.folders import DeleteFolderMetadata +from google.cloud.resourcemanager_v3.types.folders import DeleteFolderRequest +from google.cloud.resourcemanager_v3.types.folders import Folder +from google.cloud.resourcemanager_v3.types.folders import GetFolderRequest +from google.cloud.resourcemanager_v3.types.folders import ListFoldersRequest +from google.cloud.resourcemanager_v3.types.folders import ListFoldersResponse +from google.cloud.resourcemanager_v3.types.folders import MoveFolderMetadata +from google.cloud.resourcemanager_v3.types.folders import MoveFolderRequest +from google.cloud.resourcemanager_v3.types.folders import SearchFoldersRequest +from google.cloud.resourcemanager_v3.types.folders import SearchFoldersResponse +from google.cloud.resourcemanager_v3.types.folders import UndeleteFolderMetadata +from google.cloud.resourcemanager_v3.types.folders import UndeleteFolderRequest +from google.cloud.resourcemanager_v3.types.folders import UpdateFolderMetadata +from google.cloud.resourcemanager_v3.types.folders import UpdateFolderRequest +from google.cloud.resourcemanager_v3.types.organizations import ( + DeleteOrganizationMetadata, +) +from google.cloud.resourcemanager_v3.types.organizations import GetOrganizationRequest +from google.cloud.resourcemanager_v3.types.organizations import Organization +from google.cloud.resourcemanager_v3.types.organizations import ( + SearchOrganizationsRequest, +) +from google.cloud.resourcemanager_v3.types.organizations import ( + SearchOrganizationsResponse, +) +from google.cloud.resourcemanager_v3.types.organizations import ( + UndeleteOrganizationMetadata, +) +from google.cloud.resourcemanager_v3.types.projects import CreateProjectMetadata +from google.cloud.resourcemanager_v3.types.projects import CreateProjectRequest +from google.cloud.resourcemanager_v3.types.projects import DeleteProjectMetadata +from google.cloud.resourcemanager_v3.types.projects import DeleteProjectRequest +from google.cloud.resourcemanager_v3.types.projects import GetProjectRequest +from google.cloud.resourcemanager_v3.types.projects import ListProjectsRequest +from google.cloud.resourcemanager_v3.types.projects import ListProjectsResponse +from google.cloud.resourcemanager_v3.types.projects import MoveProjectMetadata +from google.cloud.resourcemanager_v3.types.projects import MoveProjectRequest +from google.cloud.resourcemanager_v3.types.projects import Project +from google.cloud.resourcemanager_v3.types.projects import SearchProjectsRequest +from google.cloud.resourcemanager_v3.types.projects import SearchProjectsResponse +from google.cloud.resourcemanager_v3.types.projects import UndeleteProjectMetadata +from google.cloud.resourcemanager_v3.types.projects import UndeleteProjectRequest +from google.cloud.resourcemanager_v3.types.projects import UpdateProjectMetadata +from google.cloud.resourcemanager_v3.types.projects import UpdateProjectRequest +from google.cloud.resourcemanager_v3.types.tag_bindings import CreateTagBindingMetadata +from google.cloud.resourcemanager_v3.types.tag_bindings import CreateTagBindingRequest +from google.cloud.resourcemanager_v3.types.tag_bindings import DeleteTagBindingMetadata +from google.cloud.resourcemanager_v3.types.tag_bindings import DeleteTagBindingRequest +from google.cloud.resourcemanager_v3.types.tag_bindings import ListTagBindingsRequest +from google.cloud.resourcemanager_v3.types.tag_bindings import ListTagBindingsResponse +from google.cloud.resourcemanager_v3.types.tag_bindings import TagBinding +from google.cloud.resourcemanager_v3.types.tag_keys import CreateTagKeyMetadata +from google.cloud.resourcemanager_v3.types.tag_keys import CreateTagKeyRequest +from google.cloud.resourcemanager_v3.types.tag_keys import DeleteTagKeyMetadata +from google.cloud.resourcemanager_v3.types.tag_keys import DeleteTagKeyRequest +from google.cloud.resourcemanager_v3.types.tag_keys import GetTagKeyRequest +from google.cloud.resourcemanager_v3.types.tag_keys import ListTagKeysRequest +from google.cloud.resourcemanager_v3.types.tag_keys import ListTagKeysResponse +from google.cloud.resourcemanager_v3.types.tag_keys import TagKey +from google.cloud.resourcemanager_v3.types.tag_keys import UpdateTagKeyMetadata +from google.cloud.resourcemanager_v3.types.tag_keys import UpdateTagKeyRequest +from google.cloud.resourcemanager_v3.types.tag_values import CreateTagValueMetadata +from google.cloud.resourcemanager_v3.types.tag_values import CreateTagValueRequest +from google.cloud.resourcemanager_v3.types.tag_values import DeleteTagValueMetadata +from google.cloud.resourcemanager_v3.types.tag_values import DeleteTagValueRequest +from google.cloud.resourcemanager_v3.types.tag_values import GetTagValueRequest +from google.cloud.resourcemanager_v3.types.tag_values import ListTagValuesRequest +from google.cloud.resourcemanager_v3.types.tag_values import ListTagValuesResponse +from google.cloud.resourcemanager_v3.types.tag_values import TagValue +from google.cloud.resourcemanager_v3.types.tag_values import UpdateTagValueMetadata +from google.cloud.resourcemanager_v3.types.tag_values import UpdateTagValueRequest + +__all__ = ( + "FoldersClient", + "FoldersAsyncClient", + "OrganizationsClient", + "OrganizationsAsyncClient", + "ProjectsClient", + "ProjectsAsyncClient", + "TagBindingsClient", + "TagBindingsAsyncClient", + "TagKeysClient", + "TagKeysAsyncClient", + "TagValuesClient", + "TagValuesAsyncClient", + "CreateFolderMetadata", + "CreateFolderRequest", + "DeleteFolderMetadata", + "DeleteFolderRequest", + "Folder", + "GetFolderRequest", + "ListFoldersRequest", + "ListFoldersResponse", + "MoveFolderMetadata", + "MoveFolderRequest", + "SearchFoldersRequest", + "SearchFoldersResponse", + "UndeleteFolderMetadata", + "UndeleteFolderRequest", + "UpdateFolderMetadata", + "UpdateFolderRequest", + "DeleteOrganizationMetadata", + "GetOrganizationRequest", + "Organization", + "SearchOrganizationsRequest", + "SearchOrganizationsResponse", + "UndeleteOrganizationMetadata", + "CreateProjectMetadata", + "CreateProjectRequest", + "DeleteProjectMetadata", + "DeleteProjectRequest", + "GetProjectRequest", + "ListProjectsRequest", + "ListProjectsResponse", + "MoveProjectMetadata", + "MoveProjectRequest", + "Project", + "SearchProjectsRequest", + "SearchProjectsResponse", + "UndeleteProjectMetadata", + "UndeleteProjectRequest", + "UpdateProjectMetadata", + "UpdateProjectRequest", + "CreateTagBindingMetadata", + "CreateTagBindingRequest", + "DeleteTagBindingMetadata", + "DeleteTagBindingRequest", + "ListTagBindingsRequest", + "ListTagBindingsResponse", + "TagBinding", + "CreateTagKeyMetadata", + "CreateTagKeyRequest", + "DeleteTagKeyMetadata", + "DeleteTagKeyRequest", + "GetTagKeyRequest", + "ListTagKeysRequest", + "ListTagKeysResponse", + "TagKey", + "UpdateTagKeyMetadata", + "UpdateTagKeyRequest", + "CreateTagValueMetadata", + "CreateTagValueRequest", + "DeleteTagValueMetadata", + "DeleteTagValueRequest", + "GetTagValueRequest", + "ListTagValuesRequest", + "ListTagValuesResponse", + "TagValue", + "UpdateTagValueMetadata", + "UpdateTagValueRequest", +) diff --git a/google/cloud/resourcemanager/py.typed b/google/cloud/resourcemanager/py.typed new file mode 100644 index 0000000..dc148de --- /dev/null +++ b/google/cloud/resourcemanager/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-resourcemanager package uses inline types. diff --git a/google/cloud/resourcemanager_v3/__init__.py b/google/cloud/resourcemanager_v3/__init__.py new file mode 100644 index 0000000..929e3f9 --- /dev/null +++ b/google/cloud/resourcemanager_v3/__init__.py @@ -0,0 +1,174 @@ +# -*- 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. +# + +from .services.folders import FoldersClient +from .services.folders import FoldersAsyncClient +from .services.organizations import OrganizationsClient +from .services.organizations import OrganizationsAsyncClient +from .services.projects import ProjectsClient +from .services.projects import ProjectsAsyncClient +from .services.tag_bindings import TagBindingsClient +from .services.tag_bindings import TagBindingsAsyncClient +from .services.tag_keys import TagKeysClient +from .services.tag_keys import TagKeysAsyncClient +from .services.tag_values import TagValuesClient +from .services.tag_values import TagValuesAsyncClient + +from .types.folders import CreateFolderMetadata +from .types.folders import CreateFolderRequest +from .types.folders import DeleteFolderMetadata +from .types.folders import DeleteFolderRequest +from .types.folders import Folder +from .types.folders import GetFolderRequest +from .types.folders import ListFoldersRequest +from .types.folders import ListFoldersResponse +from .types.folders import MoveFolderMetadata +from .types.folders import MoveFolderRequest +from .types.folders import SearchFoldersRequest +from .types.folders import SearchFoldersResponse +from .types.folders import UndeleteFolderMetadata +from .types.folders import UndeleteFolderRequest +from .types.folders import UpdateFolderMetadata +from .types.folders import UpdateFolderRequest +from .types.organizations import DeleteOrganizationMetadata +from .types.organizations import GetOrganizationRequest +from .types.organizations import Organization +from .types.organizations import SearchOrganizationsRequest +from .types.organizations import SearchOrganizationsResponse +from .types.organizations import UndeleteOrganizationMetadata +from .types.projects import CreateProjectMetadata +from .types.projects import CreateProjectRequest +from .types.projects import DeleteProjectMetadata +from .types.projects import DeleteProjectRequest +from .types.projects import GetProjectRequest +from .types.projects import ListProjectsRequest +from .types.projects import ListProjectsResponse +from .types.projects import MoveProjectMetadata +from .types.projects import MoveProjectRequest +from .types.projects import Project +from .types.projects import SearchProjectsRequest +from .types.projects import SearchProjectsResponse +from .types.projects import UndeleteProjectMetadata +from .types.projects import UndeleteProjectRequest +from .types.projects import UpdateProjectMetadata +from .types.projects import UpdateProjectRequest +from .types.tag_bindings import CreateTagBindingMetadata +from .types.tag_bindings import CreateTagBindingRequest +from .types.tag_bindings import DeleteTagBindingMetadata +from .types.tag_bindings import DeleteTagBindingRequest +from .types.tag_bindings import ListTagBindingsRequest +from .types.tag_bindings import ListTagBindingsResponse +from .types.tag_bindings import TagBinding +from .types.tag_keys import CreateTagKeyMetadata +from .types.tag_keys import CreateTagKeyRequest +from .types.tag_keys import DeleteTagKeyMetadata +from .types.tag_keys import DeleteTagKeyRequest +from .types.tag_keys import GetTagKeyRequest +from .types.tag_keys import ListTagKeysRequest +from .types.tag_keys import ListTagKeysResponse +from .types.tag_keys import TagKey +from .types.tag_keys import UpdateTagKeyMetadata +from .types.tag_keys import UpdateTagKeyRequest +from .types.tag_values import CreateTagValueMetadata +from .types.tag_values import CreateTagValueRequest +from .types.tag_values import DeleteTagValueMetadata +from .types.tag_values import DeleteTagValueRequest +from .types.tag_values import GetTagValueRequest +from .types.tag_values import ListTagValuesRequest +from .types.tag_values import ListTagValuesResponse +from .types.tag_values import TagValue +from .types.tag_values import UpdateTagValueMetadata +from .types.tag_values import UpdateTagValueRequest + +__all__ = ( + "FoldersAsyncClient", + "OrganizationsAsyncClient", + "ProjectsAsyncClient", + "TagBindingsAsyncClient", + "TagKeysAsyncClient", + "TagValuesAsyncClient", + "CreateFolderMetadata", + "CreateFolderRequest", + "CreateProjectMetadata", + "CreateProjectRequest", + "CreateTagBindingMetadata", + "CreateTagBindingRequest", + "CreateTagKeyMetadata", + "CreateTagKeyRequest", + "CreateTagValueMetadata", + "CreateTagValueRequest", + "DeleteFolderMetadata", + "DeleteFolderRequest", + "DeleteOrganizationMetadata", + "DeleteProjectMetadata", + "DeleteProjectRequest", + "DeleteTagBindingMetadata", + "DeleteTagBindingRequest", + "DeleteTagKeyMetadata", + "DeleteTagKeyRequest", + "DeleteTagValueMetadata", + "DeleteTagValueRequest", + "Folder", + "FoldersClient", + "GetFolderRequest", + "GetOrganizationRequest", + "GetProjectRequest", + "GetTagKeyRequest", + "GetTagValueRequest", + "ListFoldersRequest", + "ListFoldersResponse", + "ListProjectsRequest", + "ListProjectsResponse", + "ListTagBindingsRequest", + "ListTagBindingsResponse", + "ListTagKeysRequest", + "ListTagKeysResponse", + "ListTagValuesRequest", + "ListTagValuesResponse", + "MoveFolderMetadata", + "MoveFolderRequest", + "MoveProjectMetadata", + "MoveProjectRequest", + "Organization", + "OrganizationsClient", + "Project", + "ProjectsClient", + "SearchFoldersRequest", + "SearchFoldersResponse", + "SearchOrganizationsRequest", + "SearchOrganizationsResponse", + "SearchProjectsRequest", + "SearchProjectsResponse", + "TagBinding", + "TagBindingsClient", + "TagKey", + "TagKeysClient", + "TagValue", + "TagValuesClient", + "UndeleteFolderMetadata", + "UndeleteFolderRequest", + "UndeleteOrganizationMetadata", + "UndeleteProjectMetadata", + "UndeleteProjectRequest", + "UpdateFolderMetadata", + "UpdateFolderRequest", + "UpdateProjectMetadata", + "UpdateProjectRequest", + "UpdateTagKeyMetadata", + "UpdateTagKeyRequest", + "UpdateTagValueMetadata", + "UpdateTagValueRequest", +) diff --git a/google/cloud/resourcemanager_v3/gapic_metadata.json b/google/cloud/resourcemanager_v3/gapic_metadata.json new file mode 100644 index 0000000..df1a851 --- /dev/null +++ b/google/cloud/resourcemanager_v3/gapic_metadata.json @@ -0,0 +1,553 @@ + { + "comment": "This file maps proto services/RPCs to the corresponding library clients/methods", + "language": "python", + "libraryPackage": "google.cloud.resourcemanager_v3", + "protoPackage": "google.cloud.resourcemanager.v3", + "schema": "1.0", + "services": { + "Folders": { + "clients": { + "grpc": { + "libraryClient": "FoldersClient", + "rpcs": { + "CreateFolder": { + "methods": [ + "create_folder" + ] + }, + "DeleteFolder": { + "methods": [ + "delete_folder" + ] + }, + "GetFolder": { + "methods": [ + "get_folder" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "ListFolders": { + "methods": [ + "list_folders" + ] + }, + "MoveFolder": { + "methods": [ + "move_folder" + ] + }, + "SearchFolders": { + "methods": [ + "search_folders" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UndeleteFolder": { + "methods": [ + "undelete_folder" + ] + }, + "UpdateFolder": { + "methods": [ + "update_folder" + ] + } + } + }, + "grpc-async": { + "libraryClient": "FoldersAsyncClient", + "rpcs": { + "CreateFolder": { + "methods": [ + "create_folder" + ] + }, + "DeleteFolder": { + "methods": [ + "delete_folder" + ] + }, + "GetFolder": { + "methods": [ + "get_folder" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "ListFolders": { + "methods": [ + "list_folders" + ] + }, + "MoveFolder": { + "methods": [ + "move_folder" + ] + }, + "SearchFolders": { + "methods": [ + "search_folders" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UndeleteFolder": { + "methods": [ + "undelete_folder" + ] + }, + "UpdateFolder": { + "methods": [ + "update_folder" + ] + } + } + } + } + }, + "Organizations": { + "clients": { + "grpc": { + "libraryClient": "OrganizationsClient", + "rpcs": { + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetOrganization": { + "methods": [ + "get_organization" + ] + }, + "SearchOrganizations": { + "methods": [ + "search_organizations" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + } + } + }, + "grpc-async": { + "libraryClient": "OrganizationsAsyncClient", + "rpcs": { + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetOrganization": { + "methods": [ + "get_organization" + ] + }, + "SearchOrganizations": { + "methods": [ + "search_organizations" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + } + } + } + } + }, + "Projects": { + "clients": { + "grpc": { + "libraryClient": "ProjectsClient", + "rpcs": { + "CreateProject": { + "methods": [ + "create_project" + ] + }, + "DeleteProject": { + "methods": [ + "delete_project" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetProject": { + "methods": [ + "get_project" + ] + }, + "ListProjects": { + "methods": [ + "list_projects" + ] + }, + "MoveProject": { + "methods": [ + "move_project" + ] + }, + "SearchProjects": { + "methods": [ + "search_projects" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UndeleteProject": { + "methods": [ + "undelete_project" + ] + }, + "UpdateProject": { + "methods": [ + "update_project" + ] + } + } + }, + "grpc-async": { + "libraryClient": "ProjectsAsyncClient", + "rpcs": { + "CreateProject": { + "methods": [ + "create_project" + ] + }, + "DeleteProject": { + "methods": [ + "delete_project" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetProject": { + "methods": [ + "get_project" + ] + }, + "ListProjects": { + "methods": [ + "list_projects" + ] + }, + "MoveProject": { + "methods": [ + "move_project" + ] + }, + "SearchProjects": { + "methods": [ + "search_projects" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UndeleteProject": { + "methods": [ + "undelete_project" + ] + }, + "UpdateProject": { + "methods": [ + "update_project" + ] + } + } + } + } + }, + "TagBindings": { + "clients": { + "grpc": { + "libraryClient": "TagBindingsClient", + "rpcs": { + "CreateTagBinding": { + "methods": [ + "create_tag_binding" + ] + }, + "DeleteTagBinding": { + "methods": [ + "delete_tag_binding" + ] + }, + "ListTagBindings": { + "methods": [ + "list_tag_bindings" + ] + } + } + }, + "grpc-async": { + "libraryClient": "TagBindingsAsyncClient", + "rpcs": { + "CreateTagBinding": { + "methods": [ + "create_tag_binding" + ] + }, + "DeleteTagBinding": { + "methods": [ + "delete_tag_binding" + ] + }, + "ListTagBindings": { + "methods": [ + "list_tag_bindings" + ] + } + } + } + } + }, + "TagKeys": { + "clients": { + "grpc": { + "libraryClient": "TagKeysClient", + "rpcs": { + "CreateTagKey": { + "methods": [ + "create_tag_key" + ] + }, + "DeleteTagKey": { + "methods": [ + "delete_tag_key" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetTagKey": { + "methods": [ + "get_tag_key" + ] + }, + "ListTagKeys": { + "methods": [ + "list_tag_keys" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UpdateTagKey": { + "methods": [ + "update_tag_key" + ] + } + } + }, + "grpc-async": { + "libraryClient": "TagKeysAsyncClient", + "rpcs": { + "CreateTagKey": { + "methods": [ + "create_tag_key" + ] + }, + "DeleteTagKey": { + "methods": [ + "delete_tag_key" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetTagKey": { + "methods": [ + "get_tag_key" + ] + }, + "ListTagKeys": { + "methods": [ + "list_tag_keys" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UpdateTagKey": { + "methods": [ + "update_tag_key" + ] + } + } + } + } + }, + "TagValues": { + "clients": { + "grpc": { + "libraryClient": "TagValuesClient", + "rpcs": { + "CreateTagValue": { + "methods": [ + "create_tag_value" + ] + }, + "DeleteTagValue": { + "methods": [ + "delete_tag_value" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetTagValue": { + "methods": [ + "get_tag_value" + ] + }, + "ListTagValues": { + "methods": [ + "list_tag_values" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UpdateTagValue": { + "methods": [ + "update_tag_value" + ] + } + } + }, + "grpc-async": { + "libraryClient": "TagValuesAsyncClient", + "rpcs": { + "CreateTagValue": { + "methods": [ + "create_tag_value" + ] + }, + "DeleteTagValue": { + "methods": [ + "delete_tag_value" + ] + }, + "GetIamPolicy": { + "methods": [ + "get_iam_policy" + ] + }, + "GetTagValue": { + "methods": [ + "get_tag_value" + ] + }, + "ListTagValues": { + "methods": [ + "list_tag_values" + ] + }, + "SetIamPolicy": { + "methods": [ + "set_iam_policy" + ] + }, + "TestIamPermissions": { + "methods": [ + "test_iam_permissions" + ] + }, + "UpdateTagValue": { + "methods": [ + "update_tag_value" + ] + } + } + } + } + } + } +} diff --git a/google/cloud/resourcemanager_v3/py.typed b/google/cloud/resourcemanager_v3/py.typed new file mode 100644 index 0000000..dc148de --- /dev/null +++ b/google/cloud/resourcemanager_v3/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-resourcemanager package uses inline types. diff --git a/google/cloud/resourcemanager_v3/services/__init__.py b/google/cloud/resourcemanager_v3/services/__init__.py new file mode 100644 index 0000000..4de6597 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/__init__.py @@ -0,0 +1,15 @@ +# -*- 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/google/cloud/__init__.py b/google/cloud/resourcemanager_v3/services/folders/__init__.py similarity index 73% rename from google/cloud/__init__.py rename to google/cloud/resourcemanager_v3/services/folders/__init__.py index 0e1bc51..4a7daf2 100644 --- a/google/cloud/__init__.py +++ b/google/cloud/resourcemanager_v3/services/folders/__init__.py @@ -1,4 +1,5 @@ -# Copyright 2016 Google LLC +# -*- 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. @@ -11,12 +12,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +from .client import FoldersClient +from .async_client import FoldersAsyncClient -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) +__all__ = ( + "FoldersClient", + "FoldersAsyncClient", +) diff --git a/google/cloud/resourcemanager_v3/services/folders/async_client.py b/google/cloud/resourcemanager_v3/services/folders/async_client.py new file mode 100644 index 0000000..e847d11 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/folders/async_client.py @@ -0,0 +1,1333 @@ +# -*- 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. +# +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.folders import pagers +from google.cloud.resourcemanager_v3.types import folders +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import FoldersTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import FoldersGrpcAsyncIOTransport +from .client import FoldersClient + + +class FoldersAsyncClient: + """Manages Cloud Platform folder resources. + Folders can be used to organize the resources under an + organization and to control the policies applied to groups of + resources. + """ + + _client: FoldersClient + + DEFAULT_ENDPOINT = FoldersClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = FoldersClient.DEFAULT_MTLS_ENDPOINT + + folder_path = staticmethod(FoldersClient.folder_path) + parse_folder_path = staticmethod(FoldersClient.parse_folder_path) + common_billing_account_path = staticmethod( + FoldersClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + FoldersClient.parse_common_billing_account_path + ) + common_folder_path = staticmethod(FoldersClient.common_folder_path) + parse_common_folder_path = staticmethod(FoldersClient.parse_common_folder_path) + common_organization_path = staticmethod(FoldersClient.common_organization_path) + parse_common_organization_path = staticmethod( + FoldersClient.parse_common_organization_path + ) + common_project_path = staticmethod(FoldersClient.common_project_path) + parse_common_project_path = staticmethod(FoldersClient.parse_common_project_path) + common_location_path = staticmethod(FoldersClient.common_location_path) + parse_common_location_path = staticmethod(FoldersClient.parse_common_location_path) + + @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: + FoldersAsyncClient: The constructed client. + """ + return FoldersClient.from_service_account_info.__func__(FoldersAsyncClient, 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: + FoldersAsyncClient: The constructed client. + """ + return FoldersClient.from_service_account_file.__func__(FoldersAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> FoldersTransport: + """Returns the transport used by the client instance. + + Returns: + FoldersTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(FoldersClient).get_transport_class, type(FoldersClient) + ) + + def __init__( + self, + *, + credentials: ga_credentials.Credentials = None, + transport: Union[str, FoldersTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the folders client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.FoldersTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + self._client = FoldersClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def get_folder( + self, + request: folders.GetFolderRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> folders.Folder: + r"""Retrieves a folder identified by the supplied resource name. + Valid folder resource names have the format + ``folders/{folder_id}`` (for example, ``folders/1234``). The + caller must have ``resourcemanager.folders.get`` permission on + the identified folder. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.GetFolderRequest`): + The request object. The GetFolder request message. + name (:class:`str`): + Required. The resource name of the folder to retrieve. + Must be of the form ``folders/{folder_id}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.Folder: + A folder in an organization's + resource hierarchy, used to organize + that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.GetFolderRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_folder, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def list_folders( + self, + request: folders.ListFoldersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListFoldersAsyncPager: + r"""Lists the folders that are direct descendants of supplied parent + resource. ``list()`` provides a strongly consistent view of the + folders underneath the specified parent resource. ``list()`` + returns folders sorted based upon the (ascending) lexical + ordering of their display_name. The caller must have + ``resourcemanager.folders.list`` permission on the identified + parent. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.ListFoldersRequest`): + The request object. The ListFolders request message. + parent (:class:`str`): + Required. The resource name of the organization or + folder whose folders are being listed. Must be of the + form ``folders/{folder_id}`` or + ``organizations/{org_id}``. Access to this method is + controlled by checking the + ``resourcemanager.folders.list`` permission on the + ``parent``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.folders.pagers.ListFoldersAsyncPager: + The ListFolders response message. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.ListFoldersRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.list_folders, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.ListFoldersAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def search_folders( + self, + request: folders.SearchFoldersRequest = None, + *, + query: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.SearchFoldersAsyncPager: + r"""Search for folders that match specific filter criteria. + ``search()`` provides an eventually consistent view of the + folders a user has access to which meet the specified filter + criteria. + + This will only return folders on which the caller has the + permission ``resourcemanager.folders.get``. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.SearchFoldersRequest`): + The request object. The request message for searching + folders. + query (:class:`str`): + Optional. Search criteria used to select the folders to + return. If no search criteria is specified then all + accessible folders will be returned. + + Query expressions can be used to restrict results based + upon displayName, state and parent, where the operators + ``=`` (``:``) ``NOT``, ``AND`` and ``OR`` can be used + along with the suffix wildcard symbol ``*``. + + The ``displayName`` field in a query expression should + use escaped quotes for values that include whitespace to + prevent unexpected behavior. + + \| Field \| Description \| + \|-------------------------\|----------------------------------------\| + \| displayName \| Filters by displayName. \| \| parent + \| Filters by parent (for example: folders/123). \| \| + state, lifecycleState \| Filters by state. \| + + Some example queries are: + + - Query ``displayName=Test*`` returns Folder resources + whose display name starts with "Test". + - Query ``state=ACTIVE`` returns Folder resources with + ``state`` set to ``ACTIVE``. + - Query ``parent=folders/123`` returns Folder resources + that have ``folders/123`` as a parent resource. + - Query ``parent=folders/123 AND state=ACTIVE`` returns + active Folder resources that have ``folders/123`` as + a parent resource. + - Query ``displayName=\\"Test String\\"`` returns + Folder resources with display names that include both + "Test" and "String". + + This corresponds to the ``query`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.folders.pagers.SearchFoldersAsyncPager: + The response message for searching + folders. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([query]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.SearchFoldersRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if query is not None: + request.query = query + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.search_folders, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.SearchFoldersAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def create_folder( + self, + request: folders.CreateFolderRequest = None, + *, + folder: folders.Folder = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Creates a folder in the resource hierarchy. Returns an + ``Operation`` which can be used to track the progress of the + folder creation workflow. Upon success, the + ``Operation.response`` field will be populated with the created + Folder. + + In order to succeed, the addition of this new folder must not + violate the folder naming, height, or fanout constraints. + + - The folder's ``display_name`` must be distinct from all other + folders that share its parent. + - The addition of the folder must not cause the active folder + hierarchy to exceed a height of 10. Note, the full active + + deleted folder hierarchy is allowed to reach a height of 20; + this provides additional headroom when moving folders that + contain deleted folders. + - The addition of the folder must not cause the total number of + folders under its parent to exceed 300. + + If the operation fails due to a folder constraint violation, + some errors may be returned by the ``CreateFolder`` request, + with status code ``FAILED_PRECONDITION`` and an error + description. Other folder constraint violations will be + communicated in the ``Operation``, with the specific + ``PreconditionFailure`` returned in the details list in the + ``Operation.error`` field. + + The caller must have ``resourcemanager.folders.create`` + permission on the identified parent. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.CreateFolderRequest`): + The request object. The CreateFolder request message. + folder (:class:`google.cloud.resourcemanager_v3.types.Folder`): + Required. The folder being created, + only the display name and parent will be + consulted. All other fields will be + ignored. + + This corresponds to the ``folder`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([folder]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.CreateFolderRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if folder is not None: + request.folder = folder + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_folder, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + folders.Folder, + metadata_type=folders.CreateFolderMetadata, + ) + + # Done; return the response. + return response + + async def update_folder( + self, + request: folders.UpdateFolderRequest = None, + *, + folder: folders.Folder = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Updates a folder, changing its ``display_name``. Changes to the + folder ``display_name`` will be rejected if they violate either + the ``display_name`` formatting rules or the naming constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. + + The folder's ``display_name`` must start and end with a letter + or digit, may contain letters, digits, spaces, hyphens and + underscores and can be between 3 and 30 characters. This is + captured by the regular expression: + ``[\p{L}\p{N}][\p{L}\p{N}_- ]{1,28}[\p{L}\p{N}]``. The caller + must have ``resourcemanager.folders.update`` permission on the + identified folder. + + If the update fails due to the unique name constraint then a + ``PreconditionFailure`` explaining this violation will be + returned in the Status.details field. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.UpdateFolderRequest`): + The request object. The request sent to the + [UpdateFolder][google.cloud.resourcemanager.v3.Folder.UpdateFolder] + method. + + Only the `display_name` field can be changed. All other + fields will be ignored. Use the + [MoveFolder][google.cloud.resourcemanager.v3.Folders.MoveFolder] + method to change the `parent` field. + folder (:class:`google.cloud.resourcemanager_v3.types.Folder`): + Required. The new definition of the Folder. It must + include the ``name`` field, which cannot be changed. + + This corresponds to the ``folder`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Required. Fields to be updated. Only the + ``display_name`` can be updated. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([folder, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.UpdateFolderRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if folder is not None: + request.folder = folder + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_folder, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("folder.name", request.folder.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + folders.Folder, + metadata_type=folders.UpdateFolderMetadata, + ) + + # Done; return the response. + return response + + async def move_folder( + self, + request: folders.MoveFolderRequest = None, + *, + name: str = None, + destination_parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Moves a folder under a new resource parent. Returns an + ``Operation`` which can be used to track the progress of the + folder move workflow. Upon success, the ``Operation.response`` + field will be populated with the moved folder. Upon failure, a + ``FolderOperationError`` categorizing the failure cause will be + returned - if the failure occurs synchronously then the + ``FolderOperationError`` will be returned in the + ``Status.details`` field. If it occurs asynchronously, then the + FolderOperation will be returned in the ``Operation.error`` + field. In addition, the ``Operation.metadata`` field will be + populated with a ``FolderOperation`` message as an aid to + stateless clients. Folder moves will be rejected if they violate + either the naming, height, or fanout constraints described in + the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.move`` permission on the folder's + current and proposed new parent. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.MoveFolderRequest`): + The request object. The MoveFolder request message. + name (:class:`str`): + Required. The resource name of the Folder to move. Must + be of the form folders/{folder_id} + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + destination_parent (:class:`str`): + Required. The resource name of the folder or + organization which should be the folder's new parent. + Must be of the form ``folders/{folder_id}`` or + ``organizations/{org_id}``. + + This corresponds to the ``destination_parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name, destination_parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.MoveFolderRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + if destination_parent is not None: + request.destination_parent = destination_parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.move_folder, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + folders.Folder, + metadata_type=folders.MoveFolderMetadata, + ) + + # Done; return the response. + return response + + async def delete_folder( + self, + request: folders.DeleteFolderRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Requests deletion of a folder. The folder is moved into the + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state immediately, and is deleted approximately 30 days later. + This method may only be called on an empty folder, where a + folder is empty if it doesn't contain any folders or projects in + the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. If called on a folder in + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state the operation will result in a no-op success. The caller + must have ``resourcemanager.folders.delete`` permission on the + identified folder. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.DeleteFolderRequest`): + The request object. The DeleteFolder request message. + name (:class:`str`): + Required. The resource name of the folder to be deleted. + Must be of the form ``folders/{folder_id}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.DeleteFolderRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.delete_folder, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + folders.Folder, + metadata_type=folders.DeleteFolderMetadata, + ) + + # Done; return the response. + return response + + async def undelete_folder( + self, + request: folders.UndeleteFolderRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Cancels the deletion request for a folder. This method may be + called on a folder in any state. If the folder is in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state the result will be a no-op success. In order to succeed, + the folder's parent must be in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. In addition, reintroducing the folder into the tree must + not violate folder naming, height, and fanout constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.undelete`` permission on the + identified folder. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.UndeleteFolderRequest`): + The request object. The UndeleteFolder request message. + name (:class:`str`): + Required. The resource name of the folder to undelete. + Must be of the form ``folders/{folder_id}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = folders.UndeleteFolderRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.undelete_folder, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + folders.Folder, + metadata_type=folders.UndeleteFolderMetadata, + ) + + # Done; return the response. + return response + + async def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for a folder. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the folder's resource name, for + example: "folders/1234". The caller must have + ``resourcemanager.folders.getIamPolicy`` permission on the + identified folder. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.GetIamPolicyRequest`): + The request object. Request message for `GetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.GetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on a folder, replacing any + existing policy. The ``resource`` field should be the folder's + resource name, for example: "folders/1234". The caller must have + ``resourcemanager.folders.setIamPolicy`` permission on the + identified folder. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.SetIamPolicyRequest`): + The request object. Request message for `SetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.SetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.set_iam_policy, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the specified folder. + The ``resource`` field should be the folder's resource name, for + example: "folders/1234". + + There are no permissions required for making this API call. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest`): + The request object. Request message for + `TestIamPermissions` method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (:class:`Sequence[str]`): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + request = iam_policy_pb2.TestIamPermissionsRequest( + resource=resource, permissions=permissions, + ) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.test_iam_permissions, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("FoldersAsyncClient",) diff --git a/google/cloud/resourcemanager_v3/services/folders/client.py b/google/cloud/resourcemanager_v3/services/folders/client.py new file mode 100644 index 0000000..7fbb7f4 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/folders/client.py @@ -0,0 +1,1489 @@ +# -*- 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. +# +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.folders import pagers +from google.cloud.resourcemanager_v3.types import folders +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import FoldersTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import FoldersGrpcTransport +from .transports.grpc_asyncio import FoldersGrpcAsyncIOTransport + + +class FoldersClientMeta(type): + """Metaclass for the Folders client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[FoldersTransport]] + _transport_registry["grpc"] = FoldersGrpcTransport + _transport_registry["grpc_asyncio"] = FoldersGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[FoldersTransport]: + """Returns an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class FoldersClient(metaclass=FoldersClientMeta): + """Manages Cloud Platform folder resources. + Folders can be used to organize the resources under an + organization and to control the policies applied to groups of + resources. + """ + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudresourcemanager.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + 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: + FoldersClient: 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 + 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: + FoldersClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> FoldersTransport: + """Returns the transport used by the client instance. + + Returns: + FoldersTransport: The transport used by the client + instance. + """ + return self._transport + + @staticmethod + def folder_path(folder: str,) -> str: + """Returns a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_folder_path(path: str) -> Dict[str, str]: + """Parses a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Returns a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Returns a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Returns a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Returns a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Returns a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[ga_credentials.Credentials] = None, + transport: Union[str, FoldersTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the folders client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, FoldersTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + if is_mtls: + client_cert_source_func = mtls.default_client_cert_source() + else: + client_cert_source_func = None + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + if is_mtls: + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = self.DEFAULT_ENDPOINT + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted " + "values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, FoldersTransport): + # transport is a FoldersTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, provide its scopes " + "directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def get_folder( + self, + request: folders.GetFolderRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> folders.Folder: + r"""Retrieves a folder identified by the supplied resource name. + Valid folder resource names have the format + ``folders/{folder_id}`` (for example, ``folders/1234``). The + caller must have ``resourcemanager.folders.get`` permission on + the identified folder. + + Args: + request (google.cloud.resourcemanager_v3.types.GetFolderRequest): + The request object. The GetFolder request message. + name (str): + Required. The resource name of the folder to retrieve. + Must be of the form ``folders/{folder_id}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.Folder: + A folder in an organization's + resource hierarchy, used to organize + that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.GetFolderRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.GetFolderRequest): + request = folders.GetFolderRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_folder] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def list_folders( + self, + request: folders.ListFoldersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListFoldersPager: + r"""Lists the folders that are direct descendants of supplied parent + resource. ``list()`` provides a strongly consistent view of the + folders underneath the specified parent resource. ``list()`` + returns folders sorted based upon the (ascending) lexical + ordering of their display_name. The caller must have + ``resourcemanager.folders.list`` permission on the identified + parent. + + Args: + request (google.cloud.resourcemanager_v3.types.ListFoldersRequest): + The request object. The ListFolders request message. + parent (str): + Required. The resource name of the organization or + folder whose folders are being listed. Must be of the + form ``folders/{folder_id}`` or + ``organizations/{org_id}``. Access to this method is + controlled by checking the + ``resourcemanager.folders.list`` permission on the + ``parent``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.folders.pagers.ListFoldersPager: + The ListFolders response message. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.ListFoldersRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.ListFoldersRequest): + request = folders.ListFoldersRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_folders] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.ListFoldersPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def search_folders( + self, + request: folders.SearchFoldersRequest = None, + *, + query: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.SearchFoldersPager: + r"""Search for folders that match specific filter criteria. + ``search()`` provides an eventually consistent view of the + folders a user has access to which meet the specified filter + criteria. + + This will only return folders on which the caller has the + permission ``resourcemanager.folders.get``. + + Args: + request (google.cloud.resourcemanager_v3.types.SearchFoldersRequest): + The request object. The request message for searching + folders. + query (str): + Optional. Search criteria used to select the folders to + return. If no search criteria is specified then all + accessible folders will be returned. + + Query expressions can be used to restrict results based + upon displayName, state and parent, where the operators + ``=`` (``:``) ``NOT``, ``AND`` and ``OR`` can be used + along with the suffix wildcard symbol ``*``. + + The ``displayName`` field in a query expression should + use escaped quotes for values that include whitespace to + prevent unexpected behavior. + + \| Field \| Description \| + \|-------------------------\|----------------------------------------\| + \| displayName \| Filters by displayName. \| \| parent + \| Filters by parent (for example: folders/123). \| \| + state, lifecycleState \| Filters by state. \| + + Some example queries are: + + - Query ``displayName=Test*`` returns Folder resources + whose display name starts with "Test". + - Query ``state=ACTIVE`` returns Folder resources with + ``state`` set to ``ACTIVE``. + - Query ``parent=folders/123`` returns Folder resources + that have ``folders/123`` as a parent resource. + - Query ``parent=folders/123 AND state=ACTIVE`` returns + active Folder resources that have ``folders/123`` as + a parent resource. + - Query ``displayName=\\"Test String\\"`` returns + Folder resources with display names that include both + "Test" and "String". + + This corresponds to the ``query`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.folders.pagers.SearchFoldersPager: + The response message for searching + folders. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([query]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.SearchFoldersRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.SearchFoldersRequest): + request = folders.SearchFoldersRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if query is not None: + request.query = query + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.search_folders] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.SearchFoldersPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def create_folder( + self, + request: folders.CreateFolderRequest = None, + *, + folder: folders.Folder = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Creates a folder in the resource hierarchy. Returns an + ``Operation`` which can be used to track the progress of the + folder creation workflow. Upon success, the + ``Operation.response`` field will be populated with the created + Folder. + + In order to succeed, the addition of this new folder must not + violate the folder naming, height, or fanout constraints. + + - The folder's ``display_name`` must be distinct from all other + folders that share its parent. + - The addition of the folder must not cause the active folder + hierarchy to exceed a height of 10. Note, the full active + + deleted folder hierarchy is allowed to reach a height of 20; + this provides additional headroom when moving folders that + contain deleted folders. + - The addition of the folder must not cause the total number of + folders under its parent to exceed 300. + + If the operation fails due to a folder constraint violation, + some errors may be returned by the ``CreateFolder`` request, + with status code ``FAILED_PRECONDITION`` and an error + description. Other folder constraint violations will be + communicated in the ``Operation``, with the specific + ``PreconditionFailure`` returned in the details list in the + ``Operation.error`` field. + + The caller must have ``resourcemanager.folders.create`` + permission on the identified parent. + + Args: + request (google.cloud.resourcemanager_v3.types.CreateFolderRequest): + The request object. The CreateFolder request message. + folder (google.cloud.resourcemanager_v3.types.Folder): + Required. The folder being created, + only the display name and parent will be + consulted. All other fields will be + ignored. + + This corresponds to the ``folder`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([folder]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.CreateFolderRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.CreateFolderRequest): + request = folders.CreateFolderRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if folder is not None: + request.folder = folder + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_folder] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + folders.Folder, + metadata_type=folders.CreateFolderMetadata, + ) + + # Done; return the response. + return response + + def update_folder( + self, + request: folders.UpdateFolderRequest = None, + *, + folder: folders.Folder = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Updates a folder, changing its ``display_name``. Changes to the + folder ``display_name`` will be rejected if they violate either + the ``display_name`` formatting rules or the naming constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. + + The folder's ``display_name`` must start and end with a letter + or digit, may contain letters, digits, spaces, hyphens and + underscores and can be between 3 and 30 characters. This is + captured by the regular expression: + ``[\p{L}\p{N}][\p{L}\p{N}_- ]{1,28}[\p{L}\p{N}]``. The caller + must have ``resourcemanager.folders.update`` permission on the + identified folder. + + If the update fails due to the unique name constraint then a + ``PreconditionFailure`` explaining this violation will be + returned in the Status.details field. + + Args: + request (google.cloud.resourcemanager_v3.types.UpdateFolderRequest): + The request object. The request sent to the + [UpdateFolder][google.cloud.resourcemanager.v3.Folder.UpdateFolder] + method. + + Only the `display_name` field can be changed. All other + fields will be ignored. Use the + [MoveFolder][google.cloud.resourcemanager.v3.Folders.MoveFolder] + method to change the `parent` field. + folder (google.cloud.resourcemanager_v3.types.Folder): + Required. The new definition of the Folder. It must + include the ``name`` field, which cannot be changed. + + This corresponds to the ``folder`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. Fields to be updated. Only the + ``display_name`` can be updated. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([folder, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.UpdateFolderRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.UpdateFolderRequest): + request = folders.UpdateFolderRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if folder is not None: + request.folder = folder + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_folder] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("folder.name", request.folder.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + folders.Folder, + metadata_type=folders.UpdateFolderMetadata, + ) + + # Done; return the response. + return response + + def move_folder( + self, + request: folders.MoveFolderRequest = None, + *, + name: str = None, + destination_parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Moves a folder under a new resource parent. Returns an + ``Operation`` which can be used to track the progress of the + folder move workflow. Upon success, the ``Operation.response`` + field will be populated with the moved folder. Upon failure, a + ``FolderOperationError`` categorizing the failure cause will be + returned - if the failure occurs synchronously then the + ``FolderOperationError`` will be returned in the + ``Status.details`` field. If it occurs asynchronously, then the + FolderOperation will be returned in the ``Operation.error`` + field. In addition, the ``Operation.metadata`` field will be + populated with a ``FolderOperation`` message as an aid to + stateless clients. Folder moves will be rejected if they violate + either the naming, height, or fanout constraints described in + the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.move`` permission on the folder's + current and proposed new parent. + + Args: + request (google.cloud.resourcemanager_v3.types.MoveFolderRequest): + The request object. The MoveFolder request message. + name (str): + Required. The resource name of the Folder to move. Must + be of the form folders/{folder_id} + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + destination_parent (str): + Required. The resource name of the folder or + organization which should be the folder's new parent. + Must be of the form ``folders/{folder_id}`` or + ``organizations/{org_id}``. + + This corresponds to the ``destination_parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name, destination_parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.MoveFolderRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.MoveFolderRequest): + request = folders.MoveFolderRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + if destination_parent is not None: + request.destination_parent = destination_parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.move_folder] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + folders.Folder, + metadata_type=folders.MoveFolderMetadata, + ) + + # Done; return the response. + return response + + def delete_folder( + self, + request: folders.DeleteFolderRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Requests deletion of a folder. The folder is moved into the + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state immediately, and is deleted approximately 30 days later. + This method may only be called on an empty folder, where a + folder is empty if it doesn't contain any folders or projects in + the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. If called on a folder in + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state the operation will result in a no-op success. The caller + must have ``resourcemanager.folders.delete`` permission on the + identified folder. + + Args: + request (google.cloud.resourcemanager_v3.types.DeleteFolderRequest): + The request object. The DeleteFolder request message. + name (str): + Required. The resource name of the folder to be deleted. + Must be of the form ``folders/{folder_id}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.DeleteFolderRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.DeleteFolderRequest): + request = folders.DeleteFolderRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.delete_folder] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + folders.Folder, + metadata_type=folders.DeleteFolderMetadata, + ) + + # Done; return the response. + return response + + def undelete_folder( + self, + request: folders.UndeleteFolderRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Cancels the deletion request for a folder. This method may be + called on a folder in any state. If the folder is in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state the result will be a no-op success. In order to succeed, + the folder's parent must be in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. In addition, reintroducing the folder into the tree must + not violate folder naming, height, and fanout constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.undelete`` permission on the + identified folder. + + Args: + request (google.cloud.resourcemanager_v3.types.UndeleteFolderRequest): + The request object. The UndeleteFolder request message. + name (str): + Required. The resource name of the folder to undelete. + Must be of the form ``folders/{folder_id}``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Folder` A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a folders.UndeleteFolderRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, folders.UndeleteFolderRequest): + request = folders.UndeleteFolderRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.undelete_folder] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + folders.Folder, + metadata_type=folders.UndeleteFolderMetadata, + ) + + # Done; return the response. + return response + + def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for a folder. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the folder's resource name, for + example: "folders/1234". The caller must have + ``resourcemanager.folders.getIamPolicy`` permission on the + identified folder. + + Args: + request (google.iam.v1.iam_policy_pb2.GetIamPolicyRequest): + The request object. Request message for `GetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.GetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on a folder, replacing any + existing policy. The ``resource`` field should be the folder's + resource name, for example: "folders/1234". The caller must have + ``resourcemanager.folders.setIamPolicy`` permission on the + identified folder. + + Args: + request (google.iam.v1.iam_policy_pb2.SetIamPolicyRequest): + The request object. Request message for `SetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.SetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the specified folder. + The ``resource`` field should be the folder's resource name, for + example: "folders/1234". + + There are no permissions required for making this API call. + + Args: + request (google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest): + The request object. Request message for + `TestIamPermissions` method. + resource (str): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (Sequence[str]): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.TestIamPermissionsRequest() + if resource is not None: + request.resource = resource + if permissions: + request.permissions.extend(permissions) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("FoldersClient",) diff --git a/google/cloud/resourcemanager_v3/services/folders/pagers.py b/google/cloud/resourcemanager_v3/services/folders/pagers.py new file mode 100644 index 0000000..99fdaf1 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/folders/pagers.py @@ -0,0 +1,283 @@ +# -*- 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. +# +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.resourcemanager_v3.types import folders + + +class ListFoldersPager: + """A pager for iterating through ``list_folders`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListFoldersResponse` object, and + provides an ``__iter__`` method to iterate through its + ``folders`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListFolders`` requests and continue to iterate + through the ``folders`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListFoldersResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., folders.ListFoldersResponse], + request: folders.ListFoldersRequest, + response: folders.ListFoldersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListFoldersRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListFoldersResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = folders.ListFoldersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[folders.ListFoldersResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[folders.Folder]: + for page in self.pages: + yield from page.folders + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListFoldersAsyncPager: + """A pager for iterating through ``list_folders`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListFoldersResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``folders`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListFolders`` requests and continue to iterate + through the ``folders`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListFoldersResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[folders.ListFoldersResponse]], + request: folders.ListFoldersRequest, + response: folders.ListFoldersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListFoldersRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListFoldersResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = folders.ListFoldersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[folders.ListFoldersResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[folders.Folder]: + async def async_generator(): + async for page in self.pages: + for response in page.folders: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class SearchFoldersPager: + """A pager for iterating through ``search_folders`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.SearchFoldersResponse` object, and + provides an ``__iter__`` method to iterate through its + ``folders`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``SearchFolders`` requests and continue to iterate + through the ``folders`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.SearchFoldersResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., folders.SearchFoldersResponse], + request: folders.SearchFoldersRequest, + response: folders.SearchFoldersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.SearchFoldersRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.SearchFoldersResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = folders.SearchFoldersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[folders.SearchFoldersResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[folders.Folder]: + for page in self.pages: + yield from page.folders + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class SearchFoldersAsyncPager: + """A pager for iterating through ``search_folders`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.SearchFoldersResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``folders`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``SearchFolders`` requests and continue to iterate + through the ``folders`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.SearchFoldersResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[folders.SearchFoldersResponse]], + request: folders.SearchFoldersRequest, + response: folders.SearchFoldersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.SearchFoldersRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.SearchFoldersResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = folders.SearchFoldersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[folders.SearchFoldersResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[folders.Folder]: + async def async_generator(): + async for page in self.pages: + for response in page.folders: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/resourcemanager_v3/services/folders/transports/__init__.py b/google/cloud/resourcemanager_v3/services/folders/transports/__init__.py new file mode 100644 index 0000000..580c181 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/folders/transports/__init__.py @@ -0,0 +1,33 @@ +# -*- 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. +# +from collections import OrderedDict +from typing import Dict, Type + +from .base import FoldersTransport +from .grpc import FoldersGrpcTransport +from .grpc_asyncio import FoldersGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[FoldersTransport]] +_transport_registry["grpc"] = FoldersGrpcTransport +_transport_registry["grpc_asyncio"] = FoldersGrpcAsyncIOTransport + +__all__ = ( + "FoldersTransport", + "FoldersGrpcTransport", + "FoldersGrpcAsyncIOTransport", +) diff --git a/google/cloud/resourcemanager_v3/services/folders/transports/base.py b/google/cloud/resourcemanager_v3/services/folders/transports/base.py new file mode 100644 index 0000000..423d3f2 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/folders/transports/base.py @@ -0,0 +1,341 @@ +# -*- 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. +# +import abc +from typing import Awaitable, Callable, Dict, Optional, Sequence, Union +import packaging.version +import pkg_resources + +import google.auth # type: ignore +import google.api_core # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.types import folders +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = google.auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + + +class FoldersTransport(abc.ABC): + """Abstract transport class for Folders.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ) + + DEFAULT_HOST: str = "cloudresourcemanager.googleapis.com" + + def __init__( + self, + *, + host: str = DEFAULT_HOST, + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + scopes_kwargs = self._get_scopes_kwargs(self._host, scopes) + + # Save the scopes. + self._scopes = scopes + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise core_exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, **scopes_kwargs, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = google.auth.default( + **scopes_kwargs, quota_project_id=quota_project_id + ) + + # If the credentials is service account credentials, then always try to use self signed JWT. + if ( + always_use_jwt_access + and isinstance(credentials, service_account.Credentials) + and hasattr(service_account.Credentials, "with_always_use_jwt_access") + ): + credentials = credentials.with_always_use_jwt_access(True) + + # Save the credentials. + self._credentials = credentials + + # TODO(busunkim): This method is in the base transport + # to avoid duplicating code across the transport classes. These functions + # should be deleted once the minimum required versions of google-auth is increased. + + # TODO: Remove this function once google-auth >= 1.25.0 is required + @classmethod + def _get_scopes_kwargs( + cls, host: str, scopes: Optional[Sequence[str]] + ) -> Dict[str, Optional[Sequence[str]]]: + """Returns scopes kwargs to pass to google-auth methods depending on the google-auth version""" + + scopes_kwargs = {} + + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": cls.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or cls.AUTH_SCOPES} + + return scopes_kwargs + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.get_folder: gapic_v1.method.wrap_method( + self.get_folder, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_folders: gapic_v1.method.wrap_method( + self.list_folders, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.search_folders: gapic_v1.method.wrap_method( + self.search_folders, default_timeout=60.0, client_info=client_info, + ), + self.create_folder: gapic_v1.method.wrap_method( + self.create_folder, default_timeout=60.0, client_info=client_info, + ), + self.update_folder: gapic_v1.method.wrap_method( + self.update_folder, default_timeout=60.0, client_info=client_info, + ), + self.move_folder: gapic_v1.method.wrap_method( + self.move_folder, default_timeout=60.0, client_info=client_info, + ), + self.delete_folder: gapic_v1.method.wrap_method( + self.delete_folder, default_timeout=60.0, client_info=client_info, + ), + self.undelete_folder: gapic_v1.method.wrap_method( + self.undelete_folder, default_timeout=60.0, client_info=client_info, + ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, default_timeout=60.0, client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def get_folder( + self, + ) -> Callable[ + [folders.GetFolderRequest], Union[folders.Folder, Awaitable[folders.Folder]] + ]: + raise NotImplementedError() + + @property + def list_folders( + self, + ) -> Callable[ + [folders.ListFoldersRequest], + Union[folders.ListFoldersResponse, Awaitable[folders.ListFoldersResponse]], + ]: + raise NotImplementedError() + + @property + def search_folders( + self, + ) -> Callable[ + [folders.SearchFoldersRequest], + Union[folders.SearchFoldersResponse, Awaitable[folders.SearchFoldersResponse]], + ]: + raise NotImplementedError() + + @property + def create_folder( + self, + ) -> Callable[ + [folders.CreateFolderRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def update_folder( + self, + ) -> Callable[ + [folders.UpdateFolderRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def move_folder( + self, + ) -> Callable[ + [folders.MoveFolderRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def delete_folder( + self, + ) -> Callable[ + [folders.DeleteFolderRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def undelete_folder( + self, + ) -> Callable[ + [folders.UndeleteFolderRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def get_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.GetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def set_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.SetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Union[ + iam_policy_pb2.TestIamPermissionsResponse, + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("FoldersTransport",) diff --git a/google/cloud/resourcemanager_v3/services/folders/transports/grpc.py b/google/cloud/resourcemanager_v3/services/folders/transports/grpc.py new file mode 100644 index 0000000..1286d53 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/folders/transports/grpc.py @@ -0,0 +1,652 @@ +# -*- 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. +# +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.api_core import gapic_v1 # type: ignore +import google.auth # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.cloud.resourcemanager_v3.types import folders +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import FoldersTransport, DEFAULT_CLIENT_INFO + + +class FoldersGrpcTransport(FoldersTransport): + """gRPC backend transport for Folders. + + Manages Cloud Platform folder resources. + Folders can be used to organize the resources under an + organization and to control the policies applied to groups of + resources. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsClient(self.grpc_channel) + + # Return the client from cache. + return self._operations_client + + @property + def get_folder(self) -> Callable[[folders.GetFolderRequest], folders.Folder]: + r"""Return a callable for the get folder method over gRPC. + + Retrieves a folder identified by the supplied resource name. + Valid folder resource names have the format + ``folders/{folder_id}`` (for example, ``folders/1234``). The + caller must have ``resourcemanager.folders.get`` permission on + the identified folder. + + Returns: + Callable[[~.GetFolderRequest], + ~.Folder]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_folder" not in self._stubs: + self._stubs["get_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/GetFolder", + request_serializer=folders.GetFolderRequest.serialize, + response_deserializer=folders.Folder.deserialize, + ) + return self._stubs["get_folder"] + + @property + def list_folders( + self, + ) -> Callable[[folders.ListFoldersRequest], folders.ListFoldersResponse]: + r"""Return a callable for the list folders method over gRPC. + + Lists the folders that are direct descendants of supplied parent + resource. ``list()`` provides a strongly consistent view of the + folders underneath the specified parent resource. ``list()`` + returns folders sorted based upon the (ascending) lexical + ordering of their display_name. The caller must have + ``resourcemanager.folders.list`` permission on the identified + parent. + + Returns: + Callable[[~.ListFoldersRequest], + ~.ListFoldersResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_folders" not in self._stubs: + self._stubs["list_folders"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/ListFolders", + request_serializer=folders.ListFoldersRequest.serialize, + response_deserializer=folders.ListFoldersResponse.deserialize, + ) + return self._stubs["list_folders"] + + @property + def search_folders( + self, + ) -> Callable[[folders.SearchFoldersRequest], folders.SearchFoldersResponse]: + r"""Return a callable for the search folders method over gRPC. + + Search for folders that match specific filter criteria. + ``search()`` provides an eventually consistent view of the + folders a user has access to which meet the specified filter + criteria. + + This will only return folders on which the caller has the + permission ``resourcemanager.folders.get``. + + Returns: + Callable[[~.SearchFoldersRequest], + ~.SearchFoldersResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "search_folders" not in self._stubs: + self._stubs["search_folders"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/SearchFolders", + request_serializer=folders.SearchFoldersRequest.serialize, + response_deserializer=folders.SearchFoldersResponse.deserialize, + ) + return self._stubs["search_folders"] + + @property + def create_folder( + self, + ) -> Callable[[folders.CreateFolderRequest], operations_pb2.Operation]: + r"""Return a callable for the create folder method over gRPC. + + Creates a folder in the resource hierarchy. Returns an + ``Operation`` which can be used to track the progress of the + folder creation workflow. Upon success, the + ``Operation.response`` field will be populated with the created + Folder. + + In order to succeed, the addition of this new folder must not + violate the folder naming, height, or fanout constraints. + + - The folder's ``display_name`` must be distinct from all other + folders that share its parent. + - The addition of the folder must not cause the active folder + hierarchy to exceed a height of 10. Note, the full active + + deleted folder hierarchy is allowed to reach a height of 20; + this provides additional headroom when moving folders that + contain deleted folders. + - The addition of the folder must not cause the total number of + folders under its parent to exceed 300. + + If the operation fails due to a folder constraint violation, + some errors may be returned by the ``CreateFolder`` request, + with status code ``FAILED_PRECONDITION`` and an error + description. Other folder constraint violations will be + communicated in the ``Operation``, with the specific + ``PreconditionFailure`` returned in the details list in the + ``Operation.error`` field. + + The caller must have ``resourcemanager.folders.create`` + permission on the identified parent. + + Returns: + Callable[[~.CreateFolderRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_folder" not in self._stubs: + self._stubs["create_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/CreateFolder", + request_serializer=folders.CreateFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_folder"] + + @property + def update_folder( + self, + ) -> Callable[[folders.UpdateFolderRequest], operations_pb2.Operation]: + r"""Return a callable for the update folder method over gRPC. + + Updates a folder, changing its ``display_name``. Changes to the + folder ``display_name`` will be rejected if they violate either + the ``display_name`` formatting rules or the naming constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. + + The folder's ``display_name`` must start and end with a letter + or digit, may contain letters, digits, spaces, hyphens and + underscores and can be between 3 and 30 characters. This is + captured by the regular expression: + ``[\p{L}\p{N}][\p{L}\p{N}_- ]{1,28}[\p{L}\p{N}]``. The caller + must have ``resourcemanager.folders.update`` permission on the + identified folder. + + If the update fails due to the unique name constraint then a + ``PreconditionFailure`` explaining this violation will be + returned in the Status.details field. + + Returns: + Callable[[~.UpdateFolderRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_folder" not in self._stubs: + self._stubs["update_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/UpdateFolder", + request_serializer=folders.UpdateFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_folder"] + + @property + def move_folder( + self, + ) -> Callable[[folders.MoveFolderRequest], operations_pb2.Operation]: + r"""Return a callable for the move folder method over gRPC. + + Moves a folder under a new resource parent. Returns an + ``Operation`` which can be used to track the progress of the + folder move workflow. Upon success, the ``Operation.response`` + field will be populated with the moved folder. Upon failure, a + ``FolderOperationError`` categorizing the failure cause will be + returned - if the failure occurs synchronously then the + ``FolderOperationError`` will be returned in the + ``Status.details`` field. If it occurs asynchronously, then the + FolderOperation will be returned in the ``Operation.error`` + field. In addition, the ``Operation.metadata`` field will be + populated with a ``FolderOperation`` message as an aid to + stateless clients. Folder moves will be rejected if they violate + either the naming, height, or fanout constraints described in + the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.move`` permission on the folder's + current and proposed new parent. + + Returns: + Callable[[~.MoveFolderRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "move_folder" not in self._stubs: + self._stubs["move_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/MoveFolder", + request_serializer=folders.MoveFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["move_folder"] + + @property + def delete_folder( + self, + ) -> Callable[[folders.DeleteFolderRequest], operations_pb2.Operation]: + r"""Return a callable for the delete folder method over gRPC. + + Requests deletion of a folder. The folder is moved into the + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state immediately, and is deleted approximately 30 days later. + This method may only be called on an empty folder, where a + folder is empty if it doesn't contain any folders or projects in + the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. If called on a folder in + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state the operation will result in a no-op success. The caller + must have ``resourcemanager.folders.delete`` permission on the + identified folder. + + Returns: + Callable[[~.DeleteFolderRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_folder" not in self._stubs: + self._stubs["delete_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/DeleteFolder", + request_serializer=folders.DeleteFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_folder"] + + @property + def undelete_folder( + self, + ) -> Callable[[folders.UndeleteFolderRequest], operations_pb2.Operation]: + r"""Return a callable for the undelete folder method over gRPC. + + Cancels the deletion request for a folder. This method may be + called on a folder in any state. If the folder is in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state the result will be a no-op success. In order to succeed, + the folder's parent must be in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. In addition, reintroducing the folder into the tree must + not violate folder naming, height, and fanout constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.undelete`` permission on the + identified folder. + + Returns: + Callable[[~.UndeleteFolderRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "undelete_folder" not in self._stubs: + self._stubs["undelete_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/UndeleteFolder", + request_serializer=folders.UndeleteFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["undelete_folder"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a folder. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the folder's resource name, for + example: "folders/1234". The caller must have + ``resourcemanager.folders.getIamPolicy`` permission on the + identified folder. + + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on a folder, replacing any + existing policy. The ``resource`` field should be the folder's + resource name, for example: "folders/1234". The caller must have + ``resourcemanager.folders.setIamPolicy`` permission on the + identified folder. + + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the specified folder. + The ``resource`` field should be the folder's resource name, for + example: "folders/1234". + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("FoldersGrpcTransport",) diff --git a/google/cloud/resourcemanager_v3/services/folders/transports/grpc_asyncio.py b/google/cloud/resourcemanager_v3/services/folders/transports/grpc_asyncio.py new file mode 100644 index 0000000..a504f97 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/folders/transports/grpc_asyncio.py @@ -0,0 +1,661 @@ +# -*- 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. +# +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.resourcemanager_v3.types import folders +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import FoldersTransport, DEFAULT_CLIENT_INFO +from .grpc import FoldersGrpcTransport + + +class FoldersGrpcAsyncIOTransport(FoldersTransport): + """gRPC AsyncIO backend transport for Folders. + + Manages Cloud Platform folder resources. + Folders can be used to organize the resources under an + organization and to control the policies applied to groups of + resources. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self._operations_client + + @property + def get_folder( + self, + ) -> Callable[[folders.GetFolderRequest], Awaitable[folders.Folder]]: + r"""Return a callable for the get folder method over gRPC. + + Retrieves a folder identified by the supplied resource name. + Valid folder resource names have the format + ``folders/{folder_id}`` (for example, ``folders/1234``). The + caller must have ``resourcemanager.folders.get`` permission on + the identified folder. + + Returns: + Callable[[~.GetFolderRequest], + Awaitable[~.Folder]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_folder" not in self._stubs: + self._stubs["get_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/GetFolder", + request_serializer=folders.GetFolderRequest.serialize, + response_deserializer=folders.Folder.deserialize, + ) + return self._stubs["get_folder"] + + @property + def list_folders( + self, + ) -> Callable[[folders.ListFoldersRequest], Awaitable[folders.ListFoldersResponse]]: + r"""Return a callable for the list folders method over gRPC. + + Lists the folders that are direct descendants of supplied parent + resource. ``list()`` provides a strongly consistent view of the + folders underneath the specified parent resource. ``list()`` + returns folders sorted based upon the (ascending) lexical + ordering of their display_name. The caller must have + ``resourcemanager.folders.list`` permission on the identified + parent. + + Returns: + Callable[[~.ListFoldersRequest], + Awaitable[~.ListFoldersResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_folders" not in self._stubs: + self._stubs["list_folders"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/ListFolders", + request_serializer=folders.ListFoldersRequest.serialize, + response_deserializer=folders.ListFoldersResponse.deserialize, + ) + return self._stubs["list_folders"] + + @property + def search_folders( + self, + ) -> Callable[ + [folders.SearchFoldersRequest], Awaitable[folders.SearchFoldersResponse] + ]: + r"""Return a callable for the search folders method over gRPC. + + Search for folders that match specific filter criteria. + ``search()`` provides an eventually consistent view of the + folders a user has access to which meet the specified filter + criteria. + + This will only return folders on which the caller has the + permission ``resourcemanager.folders.get``. + + Returns: + Callable[[~.SearchFoldersRequest], + Awaitable[~.SearchFoldersResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "search_folders" not in self._stubs: + self._stubs["search_folders"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/SearchFolders", + request_serializer=folders.SearchFoldersRequest.serialize, + response_deserializer=folders.SearchFoldersResponse.deserialize, + ) + return self._stubs["search_folders"] + + @property + def create_folder( + self, + ) -> Callable[[folders.CreateFolderRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the create folder method over gRPC. + + Creates a folder in the resource hierarchy. Returns an + ``Operation`` which can be used to track the progress of the + folder creation workflow. Upon success, the + ``Operation.response`` field will be populated with the created + Folder. + + In order to succeed, the addition of this new folder must not + violate the folder naming, height, or fanout constraints. + + - The folder's ``display_name`` must be distinct from all other + folders that share its parent. + - The addition of the folder must not cause the active folder + hierarchy to exceed a height of 10. Note, the full active + + deleted folder hierarchy is allowed to reach a height of 20; + this provides additional headroom when moving folders that + contain deleted folders. + - The addition of the folder must not cause the total number of + folders under its parent to exceed 300. + + If the operation fails due to a folder constraint violation, + some errors may be returned by the ``CreateFolder`` request, + with status code ``FAILED_PRECONDITION`` and an error + description. Other folder constraint violations will be + communicated in the ``Operation``, with the specific + ``PreconditionFailure`` returned in the details list in the + ``Operation.error`` field. + + The caller must have ``resourcemanager.folders.create`` + permission on the identified parent. + + Returns: + Callable[[~.CreateFolderRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_folder" not in self._stubs: + self._stubs["create_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/CreateFolder", + request_serializer=folders.CreateFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_folder"] + + @property + def update_folder( + self, + ) -> Callable[[folders.UpdateFolderRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the update folder method over gRPC. + + Updates a folder, changing its ``display_name``. Changes to the + folder ``display_name`` will be rejected if they violate either + the ``display_name`` formatting rules or the naming constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. + + The folder's ``display_name`` must start and end with a letter + or digit, may contain letters, digits, spaces, hyphens and + underscores and can be between 3 and 30 characters. This is + captured by the regular expression: + ``[\p{L}\p{N}][\p{L}\p{N}_- ]{1,28}[\p{L}\p{N}]``. The caller + must have ``resourcemanager.folders.update`` permission on the + identified folder. + + If the update fails due to the unique name constraint then a + ``PreconditionFailure`` explaining this violation will be + returned in the Status.details field. + + Returns: + Callable[[~.UpdateFolderRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_folder" not in self._stubs: + self._stubs["update_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/UpdateFolder", + request_serializer=folders.UpdateFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_folder"] + + @property + def move_folder( + self, + ) -> Callable[[folders.MoveFolderRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the move folder method over gRPC. + + Moves a folder under a new resource parent. Returns an + ``Operation`` which can be used to track the progress of the + folder move workflow. Upon success, the ``Operation.response`` + field will be populated with the moved folder. Upon failure, a + ``FolderOperationError`` categorizing the failure cause will be + returned - if the failure occurs synchronously then the + ``FolderOperationError`` will be returned in the + ``Status.details`` field. If it occurs asynchronously, then the + FolderOperation will be returned in the ``Operation.error`` + field. In addition, the ``Operation.metadata`` field will be + populated with a ``FolderOperation`` message as an aid to + stateless clients. Folder moves will be rejected if they violate + either the naming, height, or fanout constraints described in + the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.move`` permission on the folder's + current and proposed new parent. + + Returns: + Callable[[~.MoveFolderRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "move_folder" not in self._stubs: + self._stubs["move_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/MoveFolder", + request_serializer=folders.MoveFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["move_folder"] + + @property + def delete_folder( + self, + ) -> Callable[[folders.DeleteFolderRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the delete folder method over gRPC. + + Requests deletion of a folder. The folder is moved into the + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state immediately, and is deleted approximately 30 days later. + This method may only be called on an empty folder, where a + folder is empty if it doesn't contain any folders or projects in + the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. If called on a folder in + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state the operation will result in a no-op success. The caller + must have ``resourcemanager.folders.delete`` permission on the + identified folder. + + Returns: + Callable[[~.DeleteFolderRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_folder" not in self._stubs: + self._stubs["delete_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/DeleteFolder", + request_serializer=folders.DeleteFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_folder"] + + @property + def undelete_folder( + self, + ) -> Callable[[folders.UndeleteFolderRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the undelete folder method over gRPC. + + Cancels the deletion request for a folder. This method may be + called on a folder in any state. If the folder is in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state the result will be a no-op success. In order to succeed, + the folder's parent must be in the + [ACTIVE][google.cloud.resourcemanager.v3.Folder.State.ACTIVE] + state. In addition, reintroducing the folder into the tree must + not violate folder naming, height, and fanout constraints + described in the + [CreateFolder][google.cloud.resourcemanager.v3.Folders.CreateFolder] + documentation. The caller must have + ``resourcemanager.folders.undelete`` permission on the + identified folder. + + Returns: + Callable[[~.UndeleteFolderRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "undelete_folder" not in self._stubs: + self._stubs["undelete_folder"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/UndeleteFolder", + request_serializer=folders.UndeleteFolderRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["undelete_folder"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a folder. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the folder's resource name, for + example: "folders/1234". The caller must have + ``resourcemanager.folders.getIamPolicy`` permission on the + identified folder. + + Returns: + Callable[[~.GetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on a folder, replacing any + existing policy. The ``resource`` field should be the folder's + resource name, for example: "folders/1234". The caller must have + ``resourcemanager.folders.setIamPolicy`` permission on the + identified folder. + + Returns: + Callable[[~.SetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the specified folder. + The ``resource`` field should be the folder's resource name, for + example: "folders/1234". + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + Awaitable[~.TestIamPermissionsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Folders/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("FoldersGrpcAsyncIOTransport",) diff --git a/synth.py b/google/cloud/resourcemanager_v3/services/organizations/__init__.py similarity index 52% rename from synth.py rename to google/cloud/resourcemanager_v3/services/organizations/__init__.py index 4b47590..0081d94 100644 --- a/synth.py +++ b/google/cloud/resourcemanager_v3/services/organizations/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,20 +12,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +from .client import OrganizationsClient +from .async_client import OrganizationsAsyncClient -"""This script is used to synthesize generated parts of this library.""" - -import re - -import synthtool as s -from synthtool import gcp - -common = gcp.CommonTemplates() - -# ---------------------------------------------------------------------------- -# Add templated files -# ---------------------------------------------------------------------------- -templated_files = common.py_library(cov_level=99) -s.move(templated_files, excludes=["docs/multiprocessing.rst"]) - -s.shell.run(["nox", "-s", "blacken"], hide_output=False) +__all__ = ( + "OrganizationsClient", + "OrganizationsAsyncClient", +) diff --git a/google/cloud/resourcemanager_v3/services/organizations/async_client.py b/google/cloud/resourcemanager_v3/services/organizations/async_client.py new file mode 100644 index 0000000..c64e4b0 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/organizations/async_client.py @@ -0,0 +1,725 @@ +# -*- 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. +# +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.services.organizations import pagers +from google.cloud.resourcemanager_v3.types import organizations +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import OrganizationsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import OrganizationsGrpcAsyncIOTransport +from .client import OrganizationsClient + + +class OrganizationsAsyncClient: + """Allows users to manage their organization resources.""" + + _client: OrganizationsClient + + DEFAULT_ENDPOINT = OrganizationsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = OrganizationsClient.DEFAULT_MTLS_ENDPOINT + + organization_path = staticmethod(OrganizationsClient.organization_path) + parse_organization_path = staticmethod(OrganizationsClient.parse_organization_path) + common_billing_account_path = staticmethod( + OrganizationsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + OrganizationsClient.parse_common_billing_account_path + ) + common_folder_path = staticmethod(OrganizationsClient.common_folder_path) + parse_common_folder_path = staticmethod( + OrganizationsClient.parse_common_folder_path + ) + common_organization_path = staticmethod( + OrganizationsClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + OrganizationsClient.parse_common_organization_path + ) + common_project_path = staticmethod(OrganizationsClient.common_project_path) + parse_common_project_path = staticmethod( + OrganizationsClient.parse_common_project_path + ) + common_location_path = staticmethod(OrganizationsClient.common_location_path) + parse_common_location_path = staticmethod( + OrganizationsClient.parse_common_location_path + ) + + @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: + OrganizationsAsyncClient: The constructed client. + """ + return OrganizationsClient.from_service_account_info.__func__(OrganizationsAsyncClient, 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: + OrganizationsAsyncClient: The constructed client. + """ + return OrganizationsClient.from_service_account_file.__func__(OrganizationsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> OrganizationsTransport: + """Returns the transport used by the client instance. + + Returns: + OrganizationsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(OrganizationsClient).get_transport_class, type(OrganizationsClient) + ) + + def __init__( + self, + *, + credentials: ga_credentials.Credentials = None, + transport: Union[str, OrganizationsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the organizations client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.OrganizationsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + self._client = OrganizationsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def get_organization( + self, + request: organizations.GetOrganizationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> organizations.Organization: + r"""Fetches an organization resource identified by the + specified resource name. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.GetOrganizationRequest`): + The request object. The request sent to the + `GetOrganization` method. The `name` field is required. + `organization_id` is no longer accepted. + name (:class:`str`): + Required. The resource name of the Organization to + fetch. This is the organization's relative path in the + API, formatted as "organizations/[organizationId]". For + example, "organizations/1234". + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.Organization: + The root node in the resource + hierarchy to which a particular entity's + (a company, for example) resources + belong. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = organizations.GetOrganizationRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_organization, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def search_organizations( + self, + request: organizations.SearchOrganizationsRequest = None, + *, + query: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.SearchOrganizationsAsyncPager: + r"""Searches organization resources that are visible to the user and + satisfy the specified filter. This method returns organizations + in an unspecified order. New organizations do not necessarily + appear at the end of the results, and may take a small amount of + time to appear. + + Search will only return organizations on which the user has the + permission ``resourcemanager.organizations.get`` + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.SearchOrganizationsRequest`): + The request object. The request sent to the + `SearchOrganizations` method. + query (:class:`str`): + Optional. An optional query string used to filter the + Organizations to return in the response. Query rules are + case-insensitive. + + \| Field \| Description \| + \|------------------\|--------------------------------------------\| + \| directoryCustomerId, owner.directoryCustomerId \| + Filters by directory customer id. \| \| domain \| + Filters by domain. \| + + Organizations may be queried by ``directoryCustomerId`` + or by ``domain``, where the domain is a G Suite domain, + for example: + + - Query ``directorycustomerid:123456789`` returns + Organization resources with + ``owner.directory_customer_id`` equal to + ``123456789``. + - Query ``domain:google.com`` returns Organization + resources corresponding to the domain ``google.com``. + + This corresponds to the ``query`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.organizations.pagers.SearchOrganizationsAsyncPager: + The response returned from the SearchOrganizations + method. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([query]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = organizations.SearchOrganizationsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if query is not None: + request.query = query + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.search_organizations, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.SearchOrganizationsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for an organization resource. The + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the organization's resource name, + for example: "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.getIamPolicy`` on the specified + organization. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.GetIamPolicyRequest`): + The request object. Request message for `GetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.GetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on an organization resource. + Replaces any existing policy. The ``resource`` field should be + the organization's resource name, for example: + "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.setIamPolicy`` on the specified + organization. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.SetIamPolicyRequest`): + The request object. Request message for `SetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.SetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.set_iam_policy, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns the permissions that a caller has on the specified + organization. The ``resource`` field should be the + organization's resource name, for example: "organizations/123". + + There are no permissions required for making this API call. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest`): + The request object. Request message for + `TestIamPermissions` method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (:class:`Sequence[str]`): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + request = iam_policy_pb2.TestIamPermissionsRequest( + resource=resource, permissions=permissions, + ) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.test_iam_permissions, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("OrganizationsAsyncClient",) diff --git a/google/cloud/resourcemanager_v3/services/organizations/client.py b/google/cloud/resourcemanager_v3/services/organizations/client.py new file mode 100644 index 0000000..d23e569 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/organizations/client.py @@ -0,0 +1,882 @@ +# -*- 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. +# +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.services.organizations import pagers +from google.cloud.resourcemanager_v3.types import organizations +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import OrganizationsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import OrganizationsGrpcTransport +from .transports.grpc_asyncio import OrganizationsGrpcAsyncIOTransport + + +class OrganizationsClientMeta(type): + """Metaclass for the Organizations client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[OrganizationsTransport]] + _transport_registry["grpc"] = OrganizationsGrpcTransport + _transport_registry["grpc_asyncio"] = OrganizationsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[OrganizationsTransport]: + """Returns an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class OrganizationsClient(metaclass=OrganizationsClientMeta): + """Allows users to manage their organization resources.""" + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudresourcemanager.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + 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: + OrganizationsClient: 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 + 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: + OrganizationsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> OrganizationsTransport: + """Returns the transport used by the client instance. + + Returns: + OrganizationsTransport: The transport used by the client + instance. + """ + return self._transport + + @staticmethod + def organization_path(organization: str,) -> str: + """Returns a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_organization_path(path: str) -> Dict[str, str]: + """Parses a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Returns a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Returns a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Returns a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Returns a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Returns a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[ga_credentials.Credentials] = None, + transport: Union[str, OrganizationsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the organizations client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, OrganizationsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + if is_mtls: + client_cert_source_func = mtls.default_client_cert_source() + else: + client_cert_source_func = None + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + if is_mtls: + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = self.DEFAULT_ENDPOINT + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted " + "values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, OrganizationsTransport): + # transport is a OrganizationsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, provide its scopes " + "directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def get_organization( + self, + request: organizations.GetOrganizationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> organizations.Organization: + r"""Fetches an organization resource identified by the + specified resource name. + + Args: + request (google.cloud.resourcemanager_v3.types.GetOrganizationRequest): + The request object. The request sent to the + `GetOrganization` method. The `name` field is required. + `organization_id` is no longer accepted. + name (str): + Required. The resource name of the Organization to + fetch. This is the organization's relative path in the + API, formatted as "organizations/[organizationId]". For + example, "organizations/1234". + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.Organization: + The root node in the resource + hierarchy to which a particular entity's + (a company, for example) resources + belong. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a organizations.GetOrganizationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, organizations.GetOrganizationRequest): + request = organizations.GetOrganizationRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_organization] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def search_organizations( + self, + request: organizations.SearchOrganizationsRequest = None, + *, + query: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.SearchOrganizationsPager: + r"""Searches organization resources that are visible to the user and + satisfy the specified filter. This method returns organizations + in an unspecified order. New organizations do not necessarily + appear at the end of the results, and may take a small amount of + time to appear. + + Search will only return organizations on which the user has the + permission ``resourcemanager.organizations.get`` + + Args: + request (google.cloud.resourcemanager_v3.types.SearchOrganizationsRequest): + The request object. The request sent to the + `SearchOrganizations` method. + query (str): + Optional. An optional query string used to filter the + Organizations to return in the response. Query rules are + case-insensitive. + + \| Field \| Description \| + \|------------------\|--------------------------------------------\| + \| directoryCustomerId, owner.directoryCustomerId \| + Filters by directory customer id. \| \| domain \| + Filters by domain. \| + + Organizations may be queried by ``directoryCustomerId`` + or by ``domain``, where the domain is a G Suite domain, + for example: + + - Query ``directorycustomerid:123456789`` returns + Organization resources with + ``owner.directory_customer_id`` equal to + ``123456789``. + - Query ``domain:google.com`` returns Organization + resources corresponding to the domain ``google.com``. + + This corresponds to the ``query`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.organizations.pagers.SearchOrganizationsPager: + The response returned from the SearchOrganizations + method. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([query]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a organizations.SearchOrganizationsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, organizations.SearchOrganizationsRequest): + request = organizations.SearchOrganizationsRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if query is not None: + request.query = query + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.search_organizations] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.SearchOrganizationsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for an organization resource. The + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the organization's resource name, + for example: "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.getIamPolicy`` on the specified + organization. + + Args: + request (google.iam.v1.iam_policy_pb2.GetIamPolicyRequest): + The request object. Request message for `GetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.GetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on an organization resource. + Replaces any existing policy. The ``resource`` field should be + the organization's resource name, for example: + "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.setIamPolicy`` on the specified + organization. + + Args: + request (google.iam.v1.iam_policy_pb2.SetIamPolicyRequest): + The request object. Request message for `SetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.SetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns the permissions that a caller has on the specified + organization. The ``resource`` field should be the + organization's resource name, for example: "organizations/123". + + There are no permissions required for making this API call. + + Args: + request (google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest): + The request object. Request message for + `TestIamPermissions` method. + resource (str): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (Sequence[str]): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.TestIamPermissionsRequest() + if resource is not None: + request.resource = resource + if permissions: + request.permissions.extend(permissions) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("OrganizationsClient",) diff --git a/google/cloud/resourcemanager_v3/services/organizations/pagers.py b/google/cloud/resourcemanager_v3/services/organizations/pagers.py new file mode 100644 index 0000000..3ad810a --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/organizations/pagers.py @@ -0,0 +1,155 @@ +# -*- 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. +# +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.resourcemanager_v3.types import organizations + + +class SearchOrganizationsPager: + """A pager for iterating through ``search_organizations`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.SearchOrganizationsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``organizations`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``SearchOrganizations`` requests and continue to iterate + through the ``organizations`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.SearchOrganizationsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., organizations.SearchOrganizationsResponse], + request: organizations.SearchOrganizationsRequest, + response: organizations.SearchOrganizationsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.SearchOrganizationsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.SearchOrganizationsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = organizations.SearchOrganizationsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[organizations.SearchOrganizationsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[organizations.Organization]: + for page in self.pages: + yield from page.organizations + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class SearchOrganizationsAsyncPager: + """A pager for iterating through ``search_organizations`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.SearchOrganizationsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``organizations`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``SearchOrganizations`` requests and continue to iterate + through the ``organizations`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.SearchOrganizationsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[organizations.SearchOrganizationsResponse]], + request: organizations.SearchOrganizationsRequest, + response: organizations.SearchOrganizationsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.SearchOrganizationsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.SearchOrganizationsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = organizations.SearchOrganizationsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[organizations.SearchOrganizationsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[organizations.Organization]: + async def async_generator(): + async for page in self.pages: + for response in page.organizations: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/resourcemanager_v3/services/organizations/transports/__init__.py b/google/cloud/resourcemanager_v3/services/organizations/transports/__init__.py new file mode 100644 index 0000000..dd61661 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/organizations/transports/__init__.py @@ -0,0 +1,33 @@ +# -*- 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. +# +from collections import OrderedDict +from typing import Dict, Type + +from .base import OrganizationsTransport +from .grpc import OrganizationsGrpcTransport +from .grpc_asyncio import OrganizationsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[OrganizationsTransport]] +_transport_registry["grpc"] = OrganizationsGrpcTransport +_transport_registry["grpc_asyncio"] = OrganizationsGrpcAsyncIOTransport + +__all__ = ( + "OrganizationsTransport", + "OrganizationsGrpcTransport", + "OrganizationsGrpcAsyncIOTransport", +) diff --git a/google/cloud/resourcemanager_v3/services/organizations/transports/base.py b/google/cloud/resourcemanager_v3/services/organizations/transports/base.py new file mode 100644 index 0000000..2e52f75 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/organizations/transports/base.py @@ -0,0 +1,257 @@ +# -*- 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. +# +import abc +from typing import Awaitable, Callable, Dict, Optional, Sequence, Union +import packaging.version +import pkg_resources + +import google.auth # type: ignore +import google.api_core # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.types import organizations +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = google.auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + + +class OrganizationsTransport(abc.ABC): + """Abstract transport class for Organizations.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ) + + DEFAULT_HOST: str = "cloudresourcemanager.googleapis.com" + + def __init__( + self, + *, + host: str = DEFAULT_HOST, + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + scopes_kwargs = self._get_scopes_kwargs(self._host, scopes) + + # Save the scopes. + self._scopes = scopes + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise core_exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, **scopes_kwargs, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = google.auth.default( + **scopes_kwargs, quota_project_id=quota_project_id + ) + + # If the credentials is service account credentials, then always try to use self signed JWT. + if ( + always_use_jwt_access + and isinstance(credentials, service_account.Credentials) + and hasattr(service_account.Credentials, "with_always_use_jwt_access") + ): + credentials = credentials.with_always_use_jwt_access(True) + + # Save the credentials. + self._credentials = credentials + + # TODO(busunkim): This method is in the base transport + # to avoid duplicating code across the transport classes. These functions + # should be deleted once the minimum required versions of google-auth is increased. + + # TODO: Remove this function once google-auth >= 1.25.0 is required + @classmethod + def _get_scopes_kwargs( + cls, host: str, scopes: Optional[Sequence[str]] + ) -> Dict[str, Optional[Sequence[str]]]: + """Returns scopes kwargs to pass to google-auth methods depending on the google-auth version""" + + scopes_kwargs = {} + + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": cls.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or cls.AUTH_SCOPES} + + return scopes_kwargs + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.get_organization: gapic_v1.method.wrap_method( + self.get_organization, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.search_organizations: gapic_v1.method.wrap_method( + self.search_organizations, + default_timeout=60.0, + client_info=client_info, + ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, default_timeout=60.0, client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def get_organization( + self, + ) -> Callable[ + [organizations.GetOrganizationRequest], + Union[organizations.Organization, Awaitable[organizations.Organization]], + ]: + raise NotImplementedError() + + @property + def search_organizations( + self, + ) -> Callable[ + [organizations.SearchOrganizationsRequest], + Union[ + organizations.SearchOrganizationsResponse, + Awaitable[organizations.SearchOrganizationsResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.GetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def set_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.SetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Union[ + iam_policy_pb2.TestIamPermissionsResponse, + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("OrganizationsTransport",) diff --git a/google/cloud/resourcemanager_v3/services/organizations/transports/grpc.py b/google/cloud/resourcemanager_v3/services/organizations/transports/grpc.py new file mode 100644 index 0000000..84e13ef --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/organizations/transports/grpc.py @@ -0,0 +1,393 @@ +# -*- 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. +# +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import gapic_v1 # type: ignore +import google.auth # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.cloud.resourcemanager_v3.types import organizations +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from .base import OrganizationsTransport, DEFAULT_CLIENT_INFO + + +class OrganizationsGrpcTransport(OrganizationsTransport): + """gRPC backend transport for Organizations. + + Allows users to manage their organization resources. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + 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: + # 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 + + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def get_organization( + self, + ) -> Callable[[organizations.GetOrganizationRequest], organizations.Organization]: + r"""Return a callable for the get organization method over gRPC. + + Fetches an organization resource identified by the + specified resource name. + + Returns: + Callable[[~.GetOrganizationRequest], + ~.Organization]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_organization" not in self._stubs: + self._stubs["get_organization"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/GetOrganization", + request_serializer=organizations.GetOrganizationRequest.serialize, + response_deserializer=organizations.Organization.deserialize, + ) + return self._stubs["get_organization"] + + @property + def search_organizations( + self, + ) -> Callable[ + [organizations.SearchOrganizationsRequest], + organizations.SearchOrganizationsResponse, + ]: + r"""Return a callable for the search organizations method over gRPC. + + Searches organization resources that are visible to the user and + satisfy the specified filter. This method returns organizations + in an unspecified order. New organizations do not necessarily + appear at the end of the results, and may take a small amount of + time to appear. + + Search will only return organizations on which the user has the + permission ``resourcemanager.organizations.get`` + + Returns: + Callable[[~.SearchOrganizationsRequest], + ~.SearchOrganizationsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "search_organizations" not in self._stubs: + self._stubs["search_organizations"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/SearchOrganizations", + request_serializer=organizations.SearchOrganizationsRequest.serialize, + response_deserializer=organizations.SearchOrganizationsResponse.deserialize, + ) + return self._stubs["search_organizations"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for an organization resource. The + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the organization's resource name, + for example: "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.getIamPolicy`` on the specified + organization. + + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on an organization resource. + Replaces any existing policy. The ``resource`` field should be + the organization's resource name, for example: + "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.setIamPolicy`` on the specified + organization. + + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns the permissions that a caller has on the specified + organization. The ``resource`` field should be the + organization's resource name, for example: "organizations/123". + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("OrganizationsGrpcTransport",) diff --git a/google/cloud/resourcemanager_v3/services/organizations/transports/grpc_asyncio.py b/google/cloud/resourcemanager_v3/services/organizations/transports/grpc_asyncio.py new file mode 100644 index 0000000..f37ec99 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/organizations/transports/grpc_asyncio.py @@ -0,0 +1,398 @@ +# -*- 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. +# +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.resourcemanager_v3.types import organizations +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from .base import OrganizationsTransport, DEFAULT_CLIENT_INFO +from .grpc import OrganizationsGrpcTransport + + +class OrganizationsGrpcAsyncIOTransport(OrganizationsTransport): + """gRPC AsyncIO backend transport for Organizations. + + Allows users to manage their organization resources. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + 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: + # 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 + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def get_organization( + self, + ) -> Callable[ + [organizations.GetOrganizationRequest], Awaitable[organizations.Organization] + ]: + r"""Return a callable for the get organization method over gRPC. + + Fetches an organization resource identified by the + specified resource name. + + Returns: + Callable[[~.GetOrganizationRequest], + Awaitable[~.Organization]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_organization" not in self._stubs: + self._stubs["get_organization"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/GetOrganization", + request_serializer=organizations.GetOrganizationRequest.serialize, + response_deserializer=organizations.Organization.deserialize, + ) + return self._stubs["get_organization"] + + @property + def search_organizations( + self, + ) -> Callable[ + [organizations.SearchOrganizationsRequest], + Awaitable[organizations.SearchOrganizationsResponse], + ]: + r"""Return a callable for the search organizations method over gRPC. + + Searches organization resources that are visible to the user and + satisfy the specified filter. This method returns organizations + in an unspecified order. New organizations do not necessarily + appear at the end of the results, and may take a small amount of + time to appear. + + Search will only return organizations on which the user has the + permission ``resourcemanager.organizations.get`` + + Returns: + Callable[[~.SearchOrganizationsRequest], + Awaitable[~.SearchOrganizationsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "search_organizations" not in self._stubs: + self._stubs["search_organizations"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/SearchOrganizations", + request_serializer=organizations.SearchOrganizationsRequest.serialize, + response_deserializer=organizations.SearchOrganizationsResponse.deserialize, + ) + return self._stubs["search_organizations"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for an organization resource. The + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the organization's resource name, + for example: "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.getIamPolicy`` on the specified + organization. + + Returns: + Callable[[~.GetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on an organization resource. + Replaces any existing policy. The ``resource`` field should be + the organization's resource name, for example: + "organizations/123". + + Authorization requires the IAM permission + ``resourcemanager.organizations.setIamPolicy`` on the specified + organization. + + Returns: + Callable[[~.SetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns the permissions that a caller has on the specified + organization. The ``resource`` field should be the + organization's resource name, for example: "organizations/123". + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + Awaitable[~.TestIamPermissionsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Organizations/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("OrganizationsGrpcAsyncIOTransport",) diff --git a/google/__init__.py b/google/cloud/resourcemanager_v3/services/projects/__init__.py similarity index 73% rename from google/__init__.py rename to google/cloud/resourcemanager_v3/services/projects/__init__.py index 0e1bc51..777be13 100644 --- a/google/__init__.py +++ b/google/cloud/resourcemanager_v3/services/projects/__init__.py @@ -1,4 +1,5 @@ -# Copyright 2016 Google LLC +# -*- 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. @@ -11,12 +12,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +from .client import ProjectsClient +from .async_client import ProjectsAsyncClient -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) +__all__ = ( + "ProjectsClient", + "ProjectsAsyncClient", +) diff --git a/google/cloud/resourcemanager_v3/services/projects/async_client.py b/google/cloud/resourcemanager_v3/services/projects/async_client.py new file mode 100644 index 0000000..00f47fe --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/projects/async_client.py @@ -0,0 +1,1398 @@ +# -*- 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. +# +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.projects import pagers +from google.cloud.resourcemanager_v3.types import projects +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import ProjectsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import ProjectsGrpcAsyncIOTransport +from .client import ProjectsClient + + +class ProjectsAsyncClient: + """Manages Google Cloud Projects.""" + + _client: ProjectsClient + + DEFAULT_ENDPOINT = ProjectsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ProjectsClient.DEFAULT_MTLS_ENDPOINT + + project_path = staticmethod(ProjectsClient.project_path) + parse_project_path = staticmethod(ProjectsClient.parse_project_path) + common_billing_account_path = staticmethod( + ProjectsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + ProjectsClient.parse_common_billing_account_path + ) + common_folder_path = staticmethod(ProjectsClient.common_folder_path) + parse_common_folder_path = staticmethod(ProjectsClient.parse_common_folder_path) + common_organization_path = staticmethod(ProjectsClient.common_organization_path) + parse_common_organization_path = staticmethod( + ProjectsClient.parse_common_organization_path + ) + common_project_path = staticmethod(ProjectsClient.common_project_path) + parse_common_project_path = staticmethod(ProjectsClient.parse_common_project_path) + common_location_path = staticmethod(ProjectsClient.common_location_path) + parse_common_location_path = staticmethod(ProjectsClient.parse_common_location_path) + + @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: + ProjectsAsyncClient: The constructed client. + """ + return ProjectsClient.from_service_account_info.__func__(ProjectsAsyncClient, 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: + ProjectsAsyncClient: The constructed client. + """ + return ProjectsClient.from_service_account_file.__func__(ProjectsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ProjectsTransport: + """Returns the transport used by the client instance. + + Returns: + ProjectsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(ProjectsClient).get_transport_class, type(ProjectsClient) + ) + + def __init__( + self, + *, + credentials: ga_credentials.Credentials = None, + transport: Union[str, ProjectsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the projects client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.ProjectsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + self._client = ProjectsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def get_project( + self, + request: projects.GetProjectRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> projects.Project: + r"""Retrieves the project identified by the specified ``name`` (for + example, ``projects/415104041262``). + + The caller must have ``resourcemanager.projects.get`` permission + for this project. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.GetProjectRequest`): + The request object. The request sent to the + [GetProject][google.cloud.resourcemanager.v3.Projects.GetProject] + method. + name (:class:`str`): + Required. The name of the project (for example, + ``projects/415104041262``). + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.Project: + A project is a high-level Google + Cloud entity. It is a container for + ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.GetProjectRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_project, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def list_projects( + self, + request: projects.ListProjectsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListProjectsAsyncPager: + r"""Lists projects that are direct children of the specified folder + or organization resource. ``list()`` provides a strongly + consistent view of the projects underneath the specified parent + resource. ``list()`` returns projects sorted based upon the + (ascending) lexical ordering of their ``display_name``. The + caller must have ``resourcemanager.projects.list`` permission on + the identified parent. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.ListProjectsRequest`): + The request object. The request sent to the + [ListProjects][google.cloud.resourcemanager.v3.Projects.ListProjects] + method. + parent (:class:`str`): + Required. The name of the parent + resource to list projects under. + For example, setting this field to + 'folders/1234' would list all projects + directly under that folder. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.projects.pagers.ListProjectsAsyncPager: + A page of the response received from the + [ListProjects][google.cloud.resourcemanager.v3.Projects.ListProjects] + method. + + A paginated response where more pages are available + has next_page_token set. This token can be used in a + subsequent request to retrieve the next request page. + + NOTE: A response may contain fewer elements than the + request page_size and still have a next_page_token. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.ListProjectsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.list_projects, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.ListProjectsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def search_projects( + self, + request: projects.SearchProjectsRequest = None, + *, + query: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.SearchProjectsAsyncPager: + r"""Search for projects that the caller has both + ``resourcemanager.projects.get`` permission on, and also satisfy + the specified query. + + This method returns projects in an unspecified order. + + This method is eventually consistent with project mutations; + this means that a newly created project may not appear in the + results or recent updates to an existing project may not be + reflected in the results. To retrieve the latest state of a + project, use the + [GetProject][google.cloud.resourcemanager.v3.Projects.GetProject] + method. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.SearchProjectsRequest`): + The request object. The request sent to the + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + method. + query (:class:`str`): + Optional. A query string for searching for projects that + the caller has ``resourcemanager.projects.get`` + permission to. If multiple fields are included in the + query, the it will return results that match any of the + fields. Some eligible fields are: + + \| Field \| Description \| + \|-------------------------\|----------------------------------------------\| + \| displayName, name \| Filters by displayName. \| \| + parent \| Project's parent. (for example: folders/123, + organizations/*) Prefer parent field over parent.type + and parent.id. \| \| parent.type \| Parent's type: + ``folder`` or ``organization``. \| \| parent.id \| + Parent's id number (for example: 123) \| \| id, + projectId \| Filters by projectId. \| \| state, + lifecycleState \| Filters by state. \| \| labels \| + Filters by label name or value. \| \| labels. (where + *key* is the name of a label) \| Filters by label name. + \| + + Search expressions are case insensitive. + + Some examples queries: + + \| Query \| Description \| + \|------------------\|-----------------------------------------------------\| + \| name:how\* \| The project's name starts with "how". + \| \| name:Howl \| The project's name is ``Howl`` or + ``howl``. \| \| name:HOWL \| Equivalent to above. \| \| + NAME:howl \| Equivalent to above. \| \| labels.color:\* + \| The project has the label ``color``. \| \| + labels.color:red \| The project's label ``color`` has + the value ``red``. \| \| + labels.color:red labels.size:big \| The project's label + ``color`` has the value ``red`` and its label ``size`` + has the value ``big``. \| + + If no query is specified, the call will return projects + for which the user has the + ``resourcemanager.projects.get`` permission. + + This corresponds to the ``query`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.projects.pagers.SearchProjectsAsyncPager: + A page of the response received from the + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + method. + + A paginated response where more pages are available + has next_page_token set. This token can be used in a + subsequent request to retrieve the next request page. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([query]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.SearchProjectsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if query is not None: + request.query = query + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.search_projects, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.SearchProjectsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def create_project( + self, + request: projects.CreateProjectRequest = None, + *, + project: projects.Project = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Request that a new project be created. The result is an + ``Operation`` which can be used to track the creation process. + This process usually takes a few seconds, but can sometimes take + much longer. The tracking ``Operation`` is automatically deleted + after a few hours, so there is no need to call + ``DeleteOperation``. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.CreateProjectRequest`): + The request object. The request sent to the + [CreateProject][google.cloud.resourcemanager.v3.Projects.CreateProject] + method. + project (:class:`google.cloud.resourcemanager_v3.types.Project`): + Required. The Project to create. + + Project ID is required. If the requested ID is + unavailable, the request fails. + + If the ``parent`` field is set, the + ``resourcemanager.projects.create`` permission is + checked on the parent resource. If no parent is set and + the authorization credentials belong to an Organziation, + the parent will be set to that Organization. + + This corresponds to the ``project`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.CreateProjectRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if project is not None: + request.project = project + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_project, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + projects.Project, + metadata_type=projects.CreateProjectMetadata, + ) + + # Done; return the response. + return response + + async def update_project( + self, + request: projects.UpdateProjectRequest = None, + *, + project: projects.Project = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Updates the ``display_name`` and labels of the project + identified by the specified ``name`` (for example, + ``projects/415104041262``). Deleting all labels requires an + update mask for labels field. + + The caller must have ``resourcemanager.projects.update`` + permission for this project. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.UpdateProjectRequest`): + The request object. The request sent to the + [UpdateProject][google.cloud.resourcemanager.v3.Projects.UpdateProject] + method. + + Only the `display_name` and `labels` fields can be + change. Use the + [MoveProject][google.cloud.resourcemanager.v3.Projects.MoveProject] + method to change the `parent` field. + project (:class:`google.cloud.resourcemanager_v3.types.Project`): + Required. The new definition of the + project. + + This corresponds to the ``project`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Optional. An update mask to + selectively update fields. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.UpdateProjectRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if project is not None: + request.project = project + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_project, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("project.name", request.project.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + projects.Project, + metadata_type=projects.UpdateProjectMetadata, + ) + + # Done; return the response. + return response + + async def move_project( + self, + request: projects.MoveProjectRequest = None, + *, + name: str = None, + destination_parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Move a project to another place in your resource hierarchy, + under a new resource parent. + + Returns an operation which can be used to track the process of + the project move workflow. Upon success, the + ``Operation.response`` field will be populated with the moved + project. + + The caller must have ``resourcemanager.projects.update`` + permission on the project and have + ``resourcemanager.projects.move`` permission on the project's + current and proposed new parent. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.MoveProjectRequest`): + The request object. The request sent to + [MoveProject][google.cloud.resourcemanager.v3.Projects.MoveProject] + method. + name (:class:`str`): + Required. The name of the project to + move. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + destination_parent (:class:`str`): + Required. The new parent to move the + Project under. + + This corresponds to the ``destination_parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name, destination_parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.MoveProjectRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + if destination_parent is not None: + request.destination_parent = destination_parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.move_project, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + projects.Project, + metadata_type=projects.MoveProjectMetadata, + ) + + # Done; return the response. + return response + + async def delete_project( + self, + request: projects.DeleteProjectRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Marks the project identified by the specified ``name`` (for + example, ``projects/415104041262``) for deletion. + + This method will only affect the project if it has a lifecycle + state of + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE]. + + This method changes the Project's lifecycle state from + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE] + to + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Project.State.DELETE_REQUESTED]. + The deletion starts at an unspecified time, at which point the + Project is no longer accessible. + + Until the deletion completes, you can check the lifecycle state + checked by retrieving the project with [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], and the + project remains visible to [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects]. + However, you cannot update the project. + + After the deletion completes, the project is not retrievable by + the [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], + [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects], and + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + methods. + + This method behaves idempotently, such that deleting a + ``DELETE_REQUESTED`` project will not cause an error, but also + won't do anything. + + The caller must have ``resourcemanager.projects.delete`` + permissions for this project. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.DeleteProjectRequest`): + The request object. [DeleteProject][google.cloud.resourcemanager.v3.Projects.DeleteProject] + method. + name (:class:`str`): + Required. The name of the Project (for example, + ``projects/415104041262``). + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.DeleteProjectRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.delete_project, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + projects.Project, + metadata_type=projects.DeleteProjectMetadata, + ) + + # Done; return the response. + return response + + async def undelete_project( + self, + request: projects.UndeleteProjectRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Restores the project identified by the specified ``name`` (for + example, ``projects/415104041262``). You can only use this + method for a project that has a lifecycle state of + [DELETE_REQUESTED] [Projects.State.DELETE_REQUESTED]. After + deletion starts, the project cannot be restored. + + The caller must have ``resourcemanager.projects.undelete`` + permission for this project. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.UndeleteProjectRequest`): + The request object. The request sent to the + [UndeleteProject] + [google.cloud.resourcemanager.v3.Projects.UndeleteProject] + method. + name (:class:`str`): + Required. The name of the project (for example, + ``projects/415104041262``). + + Required. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = projects.UndeleteProjectRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.undelete_project, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + projects.Project, + metadata_type=projects.UndeleteProjectMetadata, + ) + + # Done; return the response. + return response + + async def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Returns the IAM access control policy for the + specified project. Permission is denied if the policy or + the resource do not exist. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.GetIamPolicyRequest`): + The request object. Request message for `GetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.GetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the IAM access control policy for the specified project. + + CAUTION: This method will replace the existing policy, and + cannot be used to append additional IAM settings. + + Note: Removing service accounts from policies or changing their + roles can render services completely inoperable. It is important + to understand how the service account is being used before + removing or updating its roles. + + The following constraints apply when using ``setIamPolicy()``: + + - Project does not support ``allUsers`` and + ``allAuthenticatedUsers`` as ``members`` in a ``Binding`` of + a ``Policy``. + + - The owner role can be granted to a ``user``, + ``serviceAccount``, or a group that is part of an + organization. For example, group@myownpersonaldomain.com + could be added as an owner to a project in the + myownpersonaldomain.com organization, but not the + examplepetstore.com organization. + + - Service accounts can be made owners of a project directly + without any restrictions. However, to be added as an owner, a + user must be invited using the Cloud Platform console and + must accept the invitation. + + - A user cannot be granted the owner role using + ``setIamPolicy()``. The user must be granted the owner role + using the Cloud Platform Console and must explicitly accept + the invitation. + + - Invitations to grant the owner role cannot be sent using + ``setIamPolicy()``; they must be sent only using the Cloud + Platform Console. + + - Membership changes that leave the project without any owners + that have accepted the Terms of Service (ToS) will be + rejected. + + - If the project is not part of an organization, there must be + at least one owner who has accepted the Terms of Service + (ToS) agreement in the policy. Calling ``setIamPolicy()`` to + remove the last ToS-accepted owner from the policy will fail. + This restriction also applies to legacy projects that no + longer have owners who have accepted the ToS. Edits to IAM + policies will be rejected until the lack of a ToS-accepting + owner is rectified. + + - Calling this method requires enabling the App Engine Admin + API. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.SetIamPolicyRequest`): + The request object. Request message for `SetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.SetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.set_iam_policy, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the + specified project. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest`): + The request object. Request message for + `TestIamPermissions` method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (:class:`Sequence[str]`): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + request = iam_policy_pb2.TestIamPermissionsRequest( + resource=resource, permissions=permissions, + ) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.test_iam_permissions, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ProjectsAsyncClient",) diff --git a/google/cloud/resourcemanager_v3/services/projects/client.py b/google/cloud/resourcemanager_v3/services/projects/client.py new file mode 100644 index 0000000..f06fd35 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/projects/client.py @@ -0,0 +1,1554 @@ +# -*- 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. +# +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.projects import pagers +from google.cloud.resourcemanager_v3.types import projects +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import ProjectsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import ProjectsGrpcTransport +from .transports.grpc_asyncio import ProjectsGrpcAsyncIOTransport + + +class ProjectsClientMeta(type): + """Metaclass for the Projects client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[ProjectsTransport]] + _transport_registry["grpc"] = ProjectsGrpcTransport + _transport_registry["grpc_asyncio"] = ProjectsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[ProjectsTransport]: + """Returns an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class ProjectsClient(metaclass=ProjectsClientMeta): + """Manages Google Cloud Projects.""" + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudresourcemanager.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + 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: + ProjectsClient: 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 + 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: + ProjectsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ProjectsTransport: + """Returns the transport used by the client instance. + + Returns: + ProjectsTransport: The transport used by the client + instance. + """ + return self._transport + + @staticmethod + def project_path(project: str,) -> str: + """Returns a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_project_path(path: str) -> Dict[str, str]: + """Parses a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Returns a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Returns a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Returns a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Returns a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Returns a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[ga_credentials.Credentials] = None, + transport: Union[str, ProjectsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the projects client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ProjectsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + if is_mtls: + client_cert_source_func = mtls.default_client_cert_source() + else: + client_cert_source_func = None + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + if is_mtls: + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = self.DEFAULT_ENDPOINT + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted " + "values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ProjectsTransport): + # transport is a ProjectsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, provide its scopes " + "directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def get_project( + self, + request: projects.GetProjectRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> projects.Project: + r"""Retrieves the project identified by the specified ``name`` (for + example, ``projects/415104041262``). + + The caller must have ``resourcemanager.projects.get`` permission + for this project. + + Args: + request (google.cloud.resourcemanager_v3.types.GetProjectRequest): + The request object. The request sent to the + [GetProject][google.cloud.resourcemanager.v3.Projects.GetProject] + method. + name (str): + Required. The name of the project (for example, + ``projects/415104041262``). + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.Project: + A project is a high-level Google + Cloud entity. It is a container for + ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.GetProjectRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.GetProjectRequest): + request = projects.GetProjectRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_project] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def list_projects( + self, + request: projects.ListProjectsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListProjectsPager: + r"""Lists projects that are direct children of the specified folder + or organization resource. ``list()`` provides a strongly + consistent view of the projects underneath the specified parent + resource. ``list()`` returns projects sorted based upon the + (ascending) lexical ordering of their ``display_name``. The + caller must have ``resourcemanager.projects.list`` permission on + the identified parent. + + Args: + request (google.cloud.resourcemanager_v3.types.ListProjectsRequest): + The request object. The request sent to the + [ListProjects][google.cloud.resourcemanager.v3.Projects.ListProjects] + method. + parent (str): + Required. The name of the parent + resource to list projects under. + For example, setting this field to + 'folders/1234' would list all projects + directly under that folder. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.projects.pagers.ListProjectsPager: + A page of the response received from the + [ListProjects][google.cloud.resourcemanager.v3.Projects.ListProjects] + method. + + A paginated response where more pages are available + has next_page_token set. This token can be used in a + subsequent request to retrieve the next request page. + + NOTE: A response may contain fewer elements than the + request page_size and still have a next_page_token. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.ListProjectsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.ListProjectsRequest): + request = projects.ListProjectsRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_projects] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.ListProjectsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def search_projects( + self, + request: projects.SearchProjectsRequest = None, + *, + query: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.SearchProjectsPager: + r"""Search for projects that the caller has both + ``resourcemanager.projects.get`` permission on, and also satisfy + the specified query. + + This method returns projects in an unspecified order. + + This method is eventually consistent with project mutations; + this means that a newly created project may not appear in the + results or recent updates to an existing project may not be + reflected in the results. To retrieve the latest state of a + project, use the + [GetProject][google.cloud.resourcemanager.v3.Projects.GetProject] + method. + + Args: + request (google.cloud.resourcemanager_v3.types.SearchProjectsRequest): + The request object. The request sent to the + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + method. + query (str): + Optional. A query string for searching for projects that + the caller has ``resourcemanager.projects.get`` + permission to. If multiple fields are included in the + query, the it will return results that match any of the + fields. Some eligible fields are: + + \| Field \| Description \| + \|-------------------------\|----------------------------------------------\| + \| displayName, name \| Filters by displayName. \| \| + parent \| Project's parent. (for example: folders/123, + organizations/*) Prefer parent field over parent.type + and parent.id. \| \| parent.type \| Parent's type: + ``folder`` or ``organization``. \| \| parent.id \| + Parent's id number (for example: 123) \| \| id, + projectId \| Filters by projectId. \| \| state, + lifecycleState \| Filters by state. \| \| labels \| + Filters by label name or value. \| \| labels. (where + *key* is the name of a label) \| Filters by label name. + \| + + Search expressions are case insensitive. + + Some examples queries: + + \| Query \| Description \| + \|------------------\|-----------------------------------------------------\| + \| name:how\* \| The project's name starts with "how". + \| \| name:Howl \| The project's name is ``Howl`` or + ``howl``. \| \| name:HOWL \| Equivalent to above. \| \| + NAME:howl \| Equivalent to above. \| \| labels.color:\* + \| The project has the label ``color``. \| \| + labels.color:red \| The project's label ``color`` has + the value ``red``. \| \| + labels.color:red labels.size:big \| The project's label + ``color`` has the value ``red`` and its label ``size`` + has the value ``big``. \| + + If no query is specified, the call will return projects + for which the user has the + ``resourcemanager.projects.get`` permission. + + This corresponds to the ``query`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.projects.pagers.SearchProjectsPager: + A page of the response received from the + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + method. + + A paginated response where more pages are available + has next_page_token set. This token can be used in a + subsequent request to retrieve the next request page. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([query]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.SearchProjectsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.SearchProjectsRequest): + request = projects.SearchProjectsRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if query is not None: + request.query = query + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.search_projects] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.SearchProjectsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def create_project( + self, + request: projects.CreateProjectRequest = None, + *, + project: projects.Project = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Request that a new project be created. The result is an + ``Operation`` which can be used to track the creation process. + This process usually takes a few seconds, but can sometimes take + much longer. The tracking ``Operation`` is automatically deleted + after a few hours, so there is no need to call + ``DeleteOperation``. + + Args: + request (google.cloud.resourcemanager_v3.types.CreateProjectRequest): + The request object. The request sent to the + [CreateProject][google.cloud.resourcemanager.v3.Projects.CreateProject] + method. + project (google.cloud.resourcemanager_v3.types.Project): + Required. The Project to create. + + Project ID is required. If the requested ID is + unavailable, the request fails. + + If the ``parent`` field is set, the + ``resourcemanager.projects.create`` permission is + checked on the parent resource. If no parent is set and + the authorization credentials belong to an Organziation, + the parent will be set to that Organization. + + This corresponds to the ``project`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.CreateProjectRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.CreateProjectRequest): + request = projects.CreateProjectRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if project is not None: + request.project = project + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_project] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + projects.Project, + metadata_type=projects.CreateProjectMetadata, + ) + + # Done; return the response. + return response + + def update_project( + self, + request: projects.UpdateProjectRequest = None, + *, + project: projects.Project = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Updates the ``display_name`` and labels of the project + identified by the specified ``name`` (for example, + ``projects/415104041262``). Deleting all labels requires an + update mask for labels field. + + The caller must have ``resourcemanager.projects.update`` + permission for this project. + + Args: + request (google.cloud.resourcemanager_v3.types.UpdateProjectRequest): + The request object. The request sent to the + [UpdateProject][google.cloud.resourcemanager.v3.Projects.UpdateProject] + method. + + Only the `display_name` and `labels` fields can be + change. Use the + [MoveProject][google.cloud.resourcemanager.v3.Projects.MoveProject] + method to change the `parent` field. + project (google.cloud.resourcemanager_v3.types.Project): + Required. The new definition of the + project. + + This corresponds to the ``project`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. An update mask to + selectively update fields. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([project, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.UpdateProjectRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.UpdateProjectRequest): + request = projects.UpdateProjectRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if project is not None: + request.project = project + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_project] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("project.name", request.project.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + projects.Project, + metadata_type=projects.UpdateProjectMetadata, + ) + + # Done; return the response. + return response + + def move_project( + self, + request: projects.MoveProjectRequest = None, + *, + name: str = None, + destination_parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Move a project to another place in your resource hierarchy, + under a new resource parent. + + Returns an operation which can be used to track the process of + the project move workflow. Upon success, the + ``Operation.response`` field will be populated with the moved + project. + + The caller must have ``resourcemanager.projects.update`` + permission on the project and have + ``resourcemanager.projects.move`` permission on the project's + current and proposed new parent. + + Args: + request (google.cloud.resourcemanager_v3.types.MoveProjectRequest): + The request object. The request sent to + [MoveProject][google.cloud.resourcemanager.v3.Projects.MoveProject] + method. + name (str): + Required. The name of the project to + move. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + destination_parent (str): + Required. The new parent to move the + Project under. + + This corresponds to the ``destination_parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name, destination_parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.MoveProjectRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.MoveProjectRequest): + request = projects.MoveProjectRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + if destination_parent is not None: + request.destination_parent = destination_parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.move_project] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + projects.Project, + metadata_type=projects.MoveProjectMetadata, + ) + + # Done; return the response. + return response + + def delete_project( + self, + request: projects.DeleteProjectRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Marks the project identified by the specified ``name`` (for + example, ``projects/415104041262``) for deletion. + + This method will only affect the project if it has a lifecycle + state of + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE]. + + This method changes the Project's lifecycle state from + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE] + to + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Project.State.DELETE_REQUESTED]. + The deletion starts at an unspecified time, at which point the + Project is no longer accessible. + + Until the deletion completes, you can check the lifecycle state + checked by retrieving the project with [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], and the + project remains visible to [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects]. + However, you cannot update the project. + + After the deletion completes, the project is not retrievable by + the [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], + [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects], and + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + methods. + + This method behaves idempotently, such that deleting a + ``DELETE_REQUESTED`` project will not cause an error, but also + won't do anything. + + The caller must have ``resourcemanager.projects.delete`` + permissions for this project. + + Args: + request (google.cloud.resourcemanager_v3.types.DeleteProjectRequest): + The request object. [DeleteProject][google.cloud.resourcemanager.v3.Projects.DeleteProject] + method. + name (str): + Required. The name of the Project (for example, + ``projects/415104041262``). + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.DeleteProjectRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.DeleteProjectRequest): + request = projects.DeleteProjectRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.delete_project] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + projects.Project, + metadata_type=projects.DeleteProjectMetadata, + ) + + # Done; return the response. + return response + + def undelete_project( + self, + request: projects.UndeleteProjectRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Restores the project identified by the specified ``name`` (for + example, ``projects/415104041262``). You can only use this + method for a project that has a lifecycle state of + [DELETE_REQUESTED] [Projects.State.DELETE_REQUESTED]. After + deletion starts, the project cannot be restored. + + The caller must have ``resourcemanager.projects.undelete`` + permission for this project. + + Args: + request (google.cloud.resourcemanager_v3.types.UndeleteProjectRequest): + The request object. The request sent to the + [UndeleteProject] + [google.cloud.resourcemanager.v3.Projects.UndeleteProject] + method. + name (str): + Required. The name of the project (for example, + ``projects/415104041262``). + + Required. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.Project` A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and + other Google Cloud Platform resources. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a projects.UndeleteProjectRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, projects.UndeleteProjectRequest): + request = projects.UndeleteProjectRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.undelete_project] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + projects.Project, + metadata_type=projects.UndeleteProjectMetadata, + ) + + # Done; return the response. + return response + + def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Returns the IAM access control policy for the + specified project. Permission is denied if the policy or + the resource do not exist. + + Args: + request (google.iam.v1.iam_policy_pb2.GetIamPolicyRequest): + The request object. Request message for `GetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.GetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the IAM access control policy for the specified project. + + CAUTION: This method will replace the existing policy, and + cannot be used to append additional IAM settings. + + Note: Removing service accounts from policies or changing their + roles can render services completely inoperable. It is important + to understand how the service account is being used before + removing or updating its roles. + + The following constraints apply when using ``setIamPolicy()``: + + - Project does not support ``allUsers`` and + ``allAuthenticatedUsers`` as ``members`` in a ``Binding`` of + a ``Policy``. + + - The owner role can be granted to a ``user``, + ``serviceAccount``, or a group that is part of an + organization. For example, group@myownpersonaldomain.com + could be added as an owner to a project in the + myownpersonaldomain.com organization, but not the + examplepetstore.com organization. + + - Service accounts can be made owners of a project directly + without any restrictions. However, to be added as an owner, a + user must be invited using the Cloud Platform console and + must accept the invitation. + + - A user cannot be granted the owner role using + ``setIamPolicy()``. The user must be granted the owner role + using the Cloud Platform Console and must explicitly accept + the invitation. + + - Invitations to grant the owner role cannot be sent using + ``setIamPolicy()``; they must be sent only using the Cloud + Platform Console. + + - Membership changes that leave the project without any owners + that have accepted the Terms of Service (ToS) will be + rejected. + + - If the project is not part of an organization, there must be + at least one owner who has accepted the Terms of Service + (ToS) agreement in the policy. Calling ``setIamPolicy()`` to + remove the last ToS-accepted owner from the policy will fail. + This restriction also applies to legacy projects that no + longer have owners who have accepted the ToS. Edits to IAM + policies will be rejected until the lack of a ToS-accepting + owner is rectified. + + - Calling this method requires enabling the App Engine Admin + API. + + Args: + request (google.iam.v1.iam_policy_pb2.SetIamPolicyRequest): + The request object. Request message for `SetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.SetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the + specified project. + + Args: + request (google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest): + The request object. Request message for + `TestIamPermissions` method. + resource (str): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (Sequence[str]): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.TestIamPermissionsRequest() + if resource is not None: + request.resource = resource + if permissions: + request.permissions.extend(permissions) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ProjectsClient",) diff --git a/google/cloud/resourcemanager_v3/services/projects/pagers.py b/google/cloud/resourcemanager_v3/services/projects/pagers.py new file mode 100644 index 0000000..e55ba19 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/projects/pagers.py @@ -0,0 +1,283 @@ +# -*- 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. +# +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.resourcemanager_v3.types import projects + + +class ListProjectsPager: + """A pager for iterating through ``list_projects`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListProjectsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``projects`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListProjects`` requests and continue to iterate + through the ``projects`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListProjectsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., projects.ListProjectsResponse], + request: projects.ListProjectsRequest, + response: projects.ListProjectsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListProjectsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListProjectsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = projects.ListProjectsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[projects.ListProjectsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[projects.Project]: + for page in self.pages: + yield from page.projects + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListProjectsAsyncPager: + """A pager for iterating through ``list_projects`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListProjectsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``projects`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListProjects`` requests and continue to iterate + through the ``projects`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListProjectsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[projects.ListProjectsResponse]], + request: projects.ListProjectsRequest, + response: projects.ListProjectsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListProjectsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListProjectsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = projects.ListProjectsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[projects.ListProjectsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[projects.Project]: + async def async_generator(): + async for page in self.pages: + for response in page.projects: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class SearchProjectsPager: + """A pager for iterating through ``search_projects`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.SearchProjectsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``projects`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``SearchProjects`` requests and continue to iterate + through the ``projects`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.SearchProjectsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., projects.SearchProjectsResponse], + request: projects.SearchProjectsRequest, + response: projects.SearchProjectsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.SearchProjectsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.SearchProjectsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = projects.SearchProjectsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[projects.SearchProjectsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[projects.Project]: + for page in self.pages: + yield from page.projects + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class SearchProjectsAsyncPager: + """A pager for iterating through ``search_projects`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.SearchProjectsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``projects`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``SearchProjects`` requests and continue to iterate + through the ``projects`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.SearchProjectsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[projects.SearchProjectsResponse]], + request: projects.SearchProjectsRequest, + response: projects.SearchProjectsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.SearchProjectsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.SearchProjectsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = projects.SearchProjectsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[projects.SearchProjectsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[projects.Project]: + async def async_generator(): + async for page in self.pages: + for response in page.projects: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/resourcemanager_v3/services/projects/transports/__init__.py b/google/cloud/resourcemanager_v3/services/projects/transports/__init__.py new file mode 100644 index 0000000..d9b2806 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/projects/transports/__init__.py @@ -0,0 +1,33 @@ +# -*- 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. +# +from collections import OrderedDict +from typing import Dict, Type + +from .base import ProjectsTransport +from .grpc import ProjectsGrpcTransport +from .grpc_asyncio import ProjectsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[ProjectsTransport]] +_transport_registry["grpc"] = ProjectsGrpcTransport +_transport_registry["grpc_asyncio"] = ProjectsGrpcAsyncIOTransport + +__all__ = ( + "ProjectsTransport", + "ProjectsGrpcTransport", + "ProjectsGrpcAsyncIOTransport", +) diff --git a/google/cloud/resourcemanager_v3/services/projects/transports/base.py b/google/cloud/resourcemanager_v3/services/projects/transports/base.py new file mode 100644 index 0000000..61c7b18 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/projects/transports/base.py @@ -0,0 +1,344 @@ +# -*- 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. +# +import abc +from typing import Awaitable, Callable, Dict, Optional, Sequence, Union +import packaging.version +import pkg_resources + +import google.auth # type: ignore +import google.api_core # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.types import projects +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = google.auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + + +class ProjectsTransport(abc.ABC): + """Abstract transport class for Projects.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ) + + DEFAULT_HOST: str = "cloudresourcemanager.googleapis.com" + + def __init__( + self, + *, + host: str = DEFAULT_HOST, + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + scopes_kwargs = self._get_scopes_kwargs(self._host, scopes) + + # Save the scopes. + self._scopes = scopes + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise core_exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, **scopes_kwargs, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = google.auth.default( + **scopes_kwargs, quota_project_id=quota_project_id + ) + + # If the credentials is service account credentials, then always try to use self signed JWT. + if ( + always_use_jwt_access + and isinstance(credentials, service_account.Credentials) + and hasattr(service_account.Credentials, "with_always_use_jwt_access") + ): + credentials = credentials.with_always_use_jwt_access(True) + + # Save the credentials. + self._credentials = credentials + + # TODO(busunkim): This method is in the base transport + # to avoid duplicating code across the transport classes. These functions + # should be deleted once the minimum required versions of google-auth is increased. + + # TODO: Remove this function once google-auth >= 1.25.0 is required + @classmethod + def _get_scopes_kwargs( + cls, host: str, scopes: Optional[Sequence[str]] + ) -> Dict[str, Optional[Sequence[str]]]: + """Returns scopes kwargs to pass to google-auth methods depending on the google-auth version""" + + scopes_kwargs = {} + + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": cls.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or cls.AUTH_SCOPES} + + return scopes_kwargs + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.get_project: gapic_v1.method.wrap_method( + self.get_project, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_projects: gapic_v1.method.wrap_method( + self.list_projects, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.search_projects: gapic_v1.method.wrap_method( + self.search_projects, default_timeout=60.0, client_info=client_info, + ), + self.create_project: gapic_v1.method.wrap_method( + self.create_project, default_timeout=60.0, client_info=client_info, + ), + self.update_project: gapic_v1.method.wrap_method( + self.update_project, default_timeout=60.0, client_info=client_info, + ), + self.move_project: gapic_v1.method.wrap_method( + self.move_project, default_timeout=60.0, client_info=client_info, + ), + self.delete_project: gapic_v1.method.wrap_method( + self.delete_project, default_timeout=60.0, client_info=client_info, + ), + self.undelete_project: gapic_v1.method.wrap_method( + self.undelete_project, default_timeout=60.0, client_info=client_info, + ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, default_timeout=60.0, client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def get_project( + self, + ) -> Callable[ + [projects.GetProjectRequest], + Union[projects.Project, Awaitable[projects.Project]], + ]: + raise NotImplementedError() + + @property + def list_projects( + self, + ) -> Callable[ + [projects.ListProjectsRequest], + Union[projects.ListProjectsResponse, Awaitable[projects.ListProjectsResponse]], + ]: + raise NotImplementedError() + + @property + def search_projects( + self, + ) -> Callable[ + [projects.SearchProjectsRequest], + Union[ + projects.SearchProjectsResponse, Awaitable[projects.SearchProjectsResponse] + ], + ]: + raise NotImplementedError() + + @property + def create_project( + self, + ) -> Callable[ + [projects.CreateProjectRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def update_project( + self, + ) -> Callable[ + [projects.UpdateProjectRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def move_project( + self, + ) -> Callable[ + [projects.MoveProjectRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def delete_project( + self, + ) -> Callable[ + [projects.DeleteProjectRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def undelete_project( + self, + ) -> Callable[ + [projects.UndeleteProjectRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def get_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.GetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def set_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.SetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Union[ + iam_policy_pb2.TestIamPermissionsResponse, + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("ProjectsTransport",) diff --git a/google/cloud/resourcemanager_v3/services/projects/transports/grpc.py b/google/cloud/resourcemanager_v3/services/projects/transports/grpc.py new file mode 100644 index 0000000..b3cf37c --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/projects/transports/grpc.py @@ -0,0 +1,674 @@ +# -*- 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. +# +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.api_core import gapic_v1 # type: ignore +import google.auth # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.cloud.resourcemanager_v3.types import projects +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import ProjectsTransport, DEFAULT_CLIENT_INFO + + +class ProjectsGrpcTransport(ProjectsTransport): + """gRPC backend transport for Projects. + + Manages Google Cloud Projects. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsClient(self.grpc_channel) + + # Return the client from cache. + return self._operations_client + + @property + def get_project(self) -> Callable[[projects.GetProjectRequest], projects.Project]: + r"""Return a callable for the get project method over gRPC. + + Retrieves the project identified by the specified ``name`` (for + example, ``projects/415104041262``). + + The caller must have ``resourcemanager.projects.get`` permission + for this project. + + Returns: + Callable[[~.GetProjectRequest], + ~.Project]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_project" not in self._stubs: + self._stubs["get_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/GetProject", + request_serializer=projects.GetProjectRequest.serialize, + response_deserializer=projects.Project.deserialize, + ) + return self._stubs["get_project"] + + @property + def list_projects( + self, + ) -> Callable[[projects.ListProjectsRequest], projects.ListProjectsResponse]: + r"""Return a callable for the list projects method over gRPC. + + Lists projects that are direct children of the specified folder + or organization resource. ``list()`` provides a strongly + consistent view of the projects underneath the specified parent + resource. ``list()`` returns projects sorted based upon the + (ascending) lexical ordering of their ``display_name``. The + caller must have ``resourcemanager.projects.list`` permission on + the identified parent. + + Returns: + Callable[[~.ListProjectsRequest], + ~.ListProjectsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_projects" not in self._stubs: + self._stubs["list_projects"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/ListProjects", + request_serializer=projects.ListProjectsRequest.serialize, + response_deserializer=projects.ListProjectsResponse.deserialize, + ) + return self._stubs["list_projects"] + + @property + def search_projects( + self, + ) -> Callable[[projects.SearchProjectsRequest], projects.SearchProjectsResponse]: + r"""Return a callable for the search projects method over gRPC. + + Search for projects that the caller has both + ``resourcemanager.projects.get`` permission on, and also satisfy + the specified query. + + This method returns projects in an unspecified order. + + This method is eventually consistent with project mutations; + this means that a newly created project may not appear in the + results or recent updates to an existing project may not be + reflected in the results. To retrieve the latest state of a + project, use the + [GetProject][google.cloud.resourcemanager.v3.Projects.GetProject] + method. + + Returns: + Callable[[~.SearchProjectsRequest], + ~.SearchProjectsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "search_projects" not in self._stubs: + self._stubs["search_projects"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/SearchProjects", + request_serializer=projects.SearchProjectsRequest.serialize, + response_deserializer=projects.SearchProjectsResponse.deserialize, + ) + return self._stubs["search_projects"] + + @property + def create_project( + self, + ) -> Callable[[projects.CreateProjectRequest], operations_pb2.Operation]: + r"""Return a callable for the create project method over gRPC. + + Request that a new project be created. The result is an + ``Operation`` which can be used to track the creation process. + This process usually takes a few seconds, but can sometimes take + much longer. The tracking ``Operation`` is automatically deleted + after a few hours, so there is no need to call + ``DeleteOperation``. + + Returns: + Callable[[~.CreateProjectRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_project" not in self._stubs: + self._stubs["create_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/CreateProject", + request_serializer=projects.CreateProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_project"] + + @property + def update_project( + self, + ) -> Callable[[projects.UpdateProjectRequest], operations_pb2.Operation]: + r"""Return a callable for the update project method over gRPC. + + Updates the ``display_name`` and labels of the project + identified by the specified ``name`` (for example, + ``projects/415104041262``). Deleting all labels requires an + update mask for labels field. + + The caller must have ``resourcemanager.projects.update`` + permission for this project. + + Returns: + Callable[[~.UpdateProjectRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_project" not in self._stubs: + self._stubs["update_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/UpdateProject", + request_serializer=projects.UpdateProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_project"] + + @property + def move_project( + self, + ) -> Callable[[projects.MoveProjectRequest], operations_pb2.Operation]: + r"""Return a callable for the move project method over gRPC. + + Move a project to another place in your resource hierarchy, + under a new resource parent. + + Returns an operation which can be used to track the process of + the project move workflow. Upon success, the + ``Operation.response`` field will be populated with the moved + project. + + The caller must have ``resourcemanager.projects.update`` + permission on the project and have + ``resourcemanager.projects.move`` permission on the project's + current and proposed new parent. + + Returns: + Callable[[~.MoveProjectRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "move_project" not in self._stubs: + self._stubs["move_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/MoveProject", + request_serializer=projects.MoveProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["move_project"] + + @property + def delete_project( + self, + ) -> Callable[[projects.DeleteProjectRequest], operations_pb2.Operation]: + r"""Return a callable for the delete project method over gRPC. + + Marks the project identified by the specified ``name`` (for + example, ``projects/415104041262``) for deletion. + + This method will only affect the project if it has a lifecycle + state of + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE]. + + This method changes the Project's lifecycle state from + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE] + to + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Project.State.DELETE_REQUESTED]. + The deletion starts at an unspecified time, at which point the + Project is no longer accessible. + + Until the deletion completes, you can check the lifecycle state + checked by retrieving the project with [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], and the + project remains visible to [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects]. + However, you cannot update the project. + + After the deletion completes, the project is not retrievable by + the [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], + [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects], and + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + methods. + + This method behaves idempotently, such that deleting a + ``DELETE_REQUESTED`` project will not cause an error, but also + won't do anything. + + The caller must have ``resourcemanager.projects.delete`` + permissions for this project. + + Returns: + Callable[[~.DeleteProjectRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_project" not in self._stubs: + self._stubs["delete_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/DeleteProject", + request_serializer=projects.DeleteProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_project"] + + @property + def undelete_project( + self, + ) -> Callable[[projects.UndeleteProjectRequest], operations_pb2.Operation]: + r"""Return a callable for the undelete project method over gRPC. + + Restores the project identified by the specified ``name`` (for + example, ``projects/415104041262``). You can only use this + method for a project that has a lifecycle state of + [DELETE_REQUESTED] [Projects.State.DELETE_REQUESTED]. After + deletion starts, the project cannot be restored. + + The caller must have ``resourcemanager.projects.undelete`` + permission for this project. + + Returns: + Callable[[~.UndeleteProjectRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "undelete_project" not in self._stubs: + self._stubs["undelete_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/UndeleteProject", + request_serializer=projects.UndeleteProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["undelete_project"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + + Returns the IAM access control policy for the + specified project. Permission is denied if the policy or + the resource do not exist. + + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the IAM access control policy for the specified project. + + CAUTION: This method will replace the existing policy, and + cannot be used to append additional IAM settings. + + Note: Removing service accounts from policies or changing their + roles can render services completely inoperable. It is important + to understand how the service account is being used before + removing or updating its roles. + + The following constraints apply when using ``setIamPolicy()``: + + - Project does not support ``allUsers`` and + ``allAuthenticatedUsers`` as ``members`` in a ``Binding`` of + a ``Policy``. + + - The owner role can be granted to a ``user``, + ``serviceAccount``, or a group that is part of an + organization. For example, group@myownpersonaldomain.com + could be added as an owner to a project in the + myownpersonaldomain.com organization, but not the + examplepetstore.com organization. + + - Service accounts can be made owners of a project directly + without any restrictions. However, to be added as an owner, a + user must be invited using the Cloud Platform console and + must accept the invitation. + + - A user cannot be granted the owner role using + ``setIamPolicy()``. The user must be granted the owner role + using the Cloud Platform Console and must explicitly accept + the invitation. + + - Invitations to grant the owner role cannot be sent using + ``setIamPolicy()``; they must be sent only using the Cloud + Platform Console. + + - Membership changes that leave the project without any owners + that have accepted the Terms of Service (ToS) will be + rejected. + + - If the project is not part of an organization, there must be + at least one owner who has accepted the Terms of Service + (ToS) agreement in the policy. Calling ``setIamPolicy()`` to + remove the last ToS-accepted owner from the policy will fail. + This restriction also applies to legacy projects that no + longer have owners who have accepted the ToS. Edits to IAM + policies will be rejected until the lack of a ToS-accepting + owner is rectified. + + - Calling this method requires enabling the App Engine Admin + API. + + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the + specified project. + + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("ProjectsGrpcTransport",) diff --git a/google/cloud/resourcemanager_v3/services/projects/transports/grpc_asyncio.py b/google/cloud/resourcemanager_v3/services/projects/transports/grpc_asyncio.py new file mode 100644 index 0000000..0b942dc --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/projects/transports/grpc_asyncio.py @@ -0,0 +1,687 @@ +# -*- 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. +# +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.resourcemanager_v3.types import projects +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import ProjectsTransport, DEFAULT_CLIENT_INFO +from .grpc import ProjectsGrpcTransport + + +class ProjectsGrpcAsyncIOTransport(ProjectsTransport): + """gRPC AsyncIO backend transport for Projects. + + Manages Google Cloud Projects. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self._operations_client + + @property + def get_project( + self, + ) -> Callable[[projects.GetProjectRequest], Awaitable[projects.Project]]: + r"""Return a callable for the get project method over gRPC. + + Retrieves the project identified by the specified ``name`` (for + example, ``projects/415104041262``). + + The caller must have ``resourcemanager.projects.get`` permission + for this project. + + Returns: + Callable[[~.GetProjectRequest], + Awaitable[~.Project]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_project" not in self._stubs: + self._stubs["get_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/GetProject", + request_serializer=projects.GetProjectRequest.serialize, + response_deserializer=projects.Project.deserialize, + ) + return self._stubs["get_project"] + + @property + def list_projects( + self, + ) -> Callable[ + [projects.ListProjectsRequest], Awaitable[projects.ListProjectsResponse] + ]: + r"""Return a callable for the list projects method over gRPC. + + Lists projects that are direct children of the specified folder + or organization resource. ``list()`` provides a strongly + consistent view of the projects underneath the specified parent + resource. ``list()`` returns projects sorted based upon the + (ascending) lexical ordering of their ``display_name``. The + caller must have ``resourcemanager.projects.list`` permission on + the identified parent. + + Returns: + Callable[[~.ListProjectsRequest], + Awaitable[~.ListProjectsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_projects" not in self._stubs: + self._stubs["list_projects"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/ListProjects", + request_serializer=projects.ListProjectsRequest.serialize, + response_deserializer=projects.ListProjectsResponse.deserialize, + ) + return self._stubs["list_projects"] + + @property + def search_projects( + self, + ) -> Callable[ + [projects.SearchProjectsRequest], Awaitable[projects.SearchProjectsResponse] + ]: + r"""Return a callable for the search projects method over gRPC. + + Search for projects that the caller has both + ``resourcemanager.projects.get`` permission on, and also satisfy + the specified query. + + This method returns projects in an unspecified order. + + This method is eventually consistent with project mutations; + this means that a newly created project may not appear in the + results or recent updates to an existing project may not be + reflected in the results. To retrieve the latest state of a + project, use the + [GetProject][google.cloud.resourcemanager.v3.Projects.GetProject] + method. + + Returns: + Callable[[~.SearchProjectsRequest], + Awaitable[~.SearchProjectsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "search_projects" not in self._stubs: + self._stubs["search_projects"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/SearchProjects", + request_serializer=projects.SearchProjectsRequest.serialize, + response_deserializer=projects.SearchProjectsResponse.deserialize, + ) + return self._stubs["search_projects"] + + @property + def create_project( + self, + ) -> Callable[[projects.CreateProjectRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the create project method over gRPC. + + Request that a new project be created. The result is an + ``Operation`` which can be used to track the creation process. + This process usually takes a few seconds, but can sometimes take + much longer. The tracking ``Operation`` is automatically deleted + after a few hours, so there is no need to call + ``DeleteOperation``. + + Returns: + Callable[[~.CreateProjectRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_project" not in self._stubs: + self._stubs["create_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/CreateProject", + request_serializer=projects.CreateProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_project"] + + @property + def update_project( + self, + ) -> Callable[[projects.UpdateProjectRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the update project method over gRPC. + + Updates the ``display_name`` and labels of the project + identified by the specified ``name`` (for example, + ``projects/415104041262``). Deleting all labels requires an + update mask for labels field. + + The caller must have ``resourcemanager.projects.update`` + permission for this project. + + Returns: + Callable[[~.UpdateProjectRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_project" not in self._stubs: + self._stubs["update_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/UpdateProject", + request_serializer=projects.UpdateProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_project"] + + @property + def move_project( + self, + ) -> Callable[[projects.MoveProjectRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the move project method over gRPC. + + Move a project to another place in your resource hierarchy, + under a new resource parent. + + Returns an operation which can be used to track the process of + the project move workflow. Upon success, the + ``Operation.response`` field will be populated with the moved + project. + + The caller must have ``resourcemanager.projects.update`` + permission on the project and have + ``resourcemanager.projects.move`` permission on the project's + current and proposed new parent. + + Returns: + Callable[[~.MoveProjectRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "move_project" not in self._stubs: + self._stubs["move_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/MoveProject", + request_serializer=projects.MoveProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["move_project"] + + @property + def delete_project( + self, + ) -> Callable[[projects.DeleteProjectRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the delete project method over gRPC. + + Marks the project identified by the specified ``name`` (for + example, ``projects/415104041262``) for deletion. + + This method will only affect the project if it has a lifecycle + state of + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE]. + + This method changes the Project's lifecycle state from + [ACTIVE][google.cloud.resourcemanager.v3.Project.State.ACTIVE] + to + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Project.State.DELETE_REQUESTED]. + The deletion starts at an unspecified time, at which point the + Project is no longer accessible. + + Until the deletion completes, you can check the lifecycle state + checked by retrieving the project with [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], and the + project remains visible to [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects]. + However, you cannot update the project. + + After the deletion completes, the project is not retrievable by + the [GetProject] + [google.cloud.resourcemanager.v3.Projects.GetProject], + [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects], and + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + methods. + + This method behaves idempotently, such that deleting a + ``DELETE_REQUESTED`` project will not cause an error, but also + won't do anything. + + The caller must have ``resourcemanager.projects.delete`` + permissions for this project. + + Returns: + Callable[[~.DeleteProjectRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_project" not in self._stubs: + self._stubs["delete_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/DeleteProject", + request_serializer=projects.DeleteProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_project"] + + @property + def undelete_project( + self, + ) -> Callable[ + [projects.UndeleteProjectRequest], Awaitable[operations_pb2.Operation] + ]: + r"""Return a callable for the undelete project method over gRPC. + + Restores the project identified by the specified ``name`` (for + example, ``projects/415104041262``). You can only use this + method for a project that has a lifecycle state of + [DELETE_REQUESTED] [Projects.State.DELETE_REQUESTED]. After + deletion starts, the project cannot be restored. + + The caller must have ``resourcemanager.projects.undelete`` + permission for this project. + + Returns: + Callable[[~.UndeleteProjectRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "undelete_project" not in self._stubs: + self._stubs["undelete_project"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/UndeleteProject", + request_serializer=projects.UndeleteProjectRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["undelete_project"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the get iam policy method over gRPC. + + Returns the IAM access control policy for the + specified project. Permission is denied if the policy or + the resource do not exist. + + Returns: + Callable[[~.GetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the IAM access control policy for the specified project. + + CAUTION: This method will replace the existing policy, and + cannot be used to append additional IAM settings. + + Note: Removing service accounts from policies or changing their + roles can render services completely inoperable. It is important + to understand how the service account is being used before + removing or updating its roles. + + The following constraints apply when using ``setIamPolicy()``: + + - Project does not support ``allUsers`` and + ``allAuthenticatedUsers`` as ``members`` in a ``Binding`` of + a ``Policy``. + + - The owner role can be granted to a ``user``, + ``serviceAccount``, or a group that is part of an + organization. For example, group@myownpersonaldomain.com + could be added as an owner to a project in the + myownpersonaldomain.com organization, but not the + examplepetstore.com organization. + + - Service accounts can be made owners of a project directly + without any restrictions. However, to be added as an owner, a + user must be invited using the Cloud Platform console and + must accept the invitation. + + - A user cannot be granted the owner role using + ``setIamPolicy()``. The user must be granted the owner role + using the Cloud Platform Console and must explicitly accept + the invitation. + + - Invitations to grant the owner role cannot be sent using + ``setIamPolicy()``; they must be sent only using the Cloud + Platform Console. + + - Membership changes that leave the project without any owners + that have accepted the Terms of Service (ToS) will be + rejected. + + - If the project is not part of an organization, there must be + at least one owner who has accepted the Terms of Service + (ToS) agreement in the policy. Calling ``setIamPolicy()`` to + remove the last ToS-accepted owner from the policy will fail. + This restriction also applies to legacy projects that no + longer have owners who have accepted the ToS. Edits to IAM + policies will be rejected until the lack of a ToS-accepting + owner is rectified. + + - Calling this method requires enabling the App Engine Admin + API. + + Returns: + Callable[[~.SetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the + specified project. + + Returns: + Callable[[~.TestIamPermissionsRequest], + Awaitable[~.TestIamPermissionsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.Projects/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("ProjectsGrpcAsyncIOTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/__init__.py b/google/cloud/resourcemanager_v3/services/tag_bindings/__init__.py new file mode 100644 index 0000000..5de379d --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/__init__.py @@ -0,0 +1,22 @@ +# -*- 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. +# +from .client import TagBindingsClient +from .async_client import TagBindingsAsyncClient + +__all__ = ( + "TagBindingsClient", + "TagBindingsAsyncClient", +) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/async_client.py b/google/cloud/resourcemanager_v3/services/tag_bindings/async_client.py new file mode 100644 index 0000000..f9d187e --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/async_client.py @@ -0,0 +1,438 @@ +# -*- 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. +# +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.tag_bindings import pagers +from google.cloud.resourcemanager_v3.types import tag_bindings +from google.protobuf import empty_pb2 # type: ignore +from .transports.base import TagBindingsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import TagBindingsGrpcAsyncIOTransport +from .client import TagBindingsClient + + +class TagBindingsAsyncClient: + """Allow users to create and manage TagBindings between + TagValues and different cloud resources throughout the GCP + resource hierarchy. + """ + + _client: TagBindingsClient + + DEFAULT_ENDPOINT = TagBindingsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = TagBindingsClient.DEFAULT_MTLS_ENDPOINT + + tag_binding_path = staticmethod(TagBindingsClient.tag_binding_path) + parse_tag_binding_path = staticmethod(TagBindingsClient.parse_tag_binding_path) + common_billing_account_path = staticmethod( + TagBindingsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TagBindingsClient.parse_common_billing_account_path + ) + common_folder_path = staticmethod(TagBindingsClient.common_folder_path) + parse_common_folder_path = staticmethod(TagBindingsClient.parse_common_folder_path) + common_organization_path = staticmethod(TagBindingsClient.common_organization_path) + parse_common_organization_path = staticmethod( + TagBindingsClient.parse_common_organization_path + ) + common_project_path = staticmethod(TagBindingsClient.common_project_path) + parse_common_project_path = staticmethod( + TagBindingsClient.parse_common_project_path + ) + common_location_path = staticmethod(TagBindingsClient.common_location_path) + parse_common_location_path = staticmethod( + TagBindingsClient.parse_common_location_path + ) + + @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: + TagBindingsAsyncClient: The constructed client. + """ + return TagBindingsClient.from_service_account_info.__func__(TagBindingsAsyncClient, 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: + TagBindingsAsyncClient: The constructed client. + """ + return TagBindingsClient.from_service_account_file.__func__(TagBindingsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TagBindingsTransport: + """Returns the transport used by the client instance. + + Returns: + TagBindingsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(TagBindingsClient).get_transport_class, type(TagBindingsClient) + ) + + def __init__( + self, + *, + credentials: ga_credentials.Credentials = None, + transport: Union[str, TagBindingsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the tag bindings client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.TagBindingsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + self._client = TagBindingsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_tag_bindings( + self, + request: tag_bindings.ListTagBindingsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTagBindingsAsyncPager: + r"""Lists the TagBindings for the given cloud resource, as specified + with ``parent``. + + NOTE: The ``parent`` field is expected to be a full resource + name: + https://cloud.google.com/apis/design/resource_names#full_resource_name + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.ListTagBindingsRequest`): + The request object. The request message to list all + TagBindings for a parent. + parent (:class:`str`): + Required. The full resource name of a + resource for which you want to list + existing TagBindings. E.g. + "//cloudresourcemanager.googleapis.com/projects/123" + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.tag_bindings.pagers.ListTagBindingsAsyncPager: + The ListTagBindings response. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_bindings.ListTagBindingsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.list_tag_bindings, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.ListTagBindingsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def create_tag_binding( + self, + request: tag_bindings.CreateTagBindingRequest = None, + *, + tag_binding: tag_bindings.TagBinding = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Creates a TagBinding between a TagValue and a cloud + resource (currently project, folder, or organization). + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.CreateTagBindingRequest`): + The request object. The request message to create a + TagBinding. + tag_binding (:class:`google.cloud.resourcemanager_v3.types.TagBinding`): + Required. The TagBinding to be + created. + + This corresponds to the ``tag_binding`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagBinding` A TagBinding represents a connection between a TagValue and a cloud + resource (currently project, folder, or + organization). Once a TagBinding is created, the + TagValue is applied to all the descendants of the + cloud resource. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_binding]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_bindings.CreateTagBindingRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_binding is not None: + request.tag_binding = tag_binding + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_tag_binding, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + tag_bindings.TagBinding, + metadata_type=tag_bindings.CreateTagBindingMetadata, + ) + + # Done; return the response. + return response + + async def delete_tag_binding( + self, + request: tag_bindings.DeleteTagBindingRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Deletes a TagBinding. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.DeleteTagBindingRequest`): + The request object. The request message to delete a + TagBinding. + name (:class:`str`): + Required. The name of the TagBinding. This is a String + of the form: ``tagBindings/{id}`` (e.g. + ``tagBindings/%2F%2Fcloudresourcemanager.googleapis.com%2Fprojects%2F123/tagValues/456``). + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: + + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); + + } + + The JSON representation for Empty is empty JSON + object {}. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_bindings.DeleteTagBindingRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.delete_tag_binding, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + empty_pb2.Empty, + metadata_type=tag_bindings.DeleteTagBindingMetadata, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TagBindingsAsyncClient",) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/client.py b/google/cloud/resourcemanager_v3/services/tag_bindings/client.py new file mode 100644 index 0000000..c0def4d --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/client.py @@ -0,0 +1,611 @@ +# -*- 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. +# +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.tag_bindings import pagers +from google.cloud.resourcemanager_v3.types import tag_bindings +from google.protobuf import empty_pb2 # type: ignore +from .transports.base import TagBindingsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import TagBindingsGrpcTransport +from .transports.grpc_asyncio import TagBindingsGrpcAsyncIOTransport + + +class TagBindingsClientMeta(type): + """Metaclass for the TagBindings client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[TagBindingsTransport]] + _transport_registry["grpc"] = TagBindingsGrpcTransport + _transport_registry["grpc_asyncio"] = TagBindingsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[TagBindingsTransport]: + """Returns an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class TagBindingsClient(metaclass=TagBindingsClientMeta): + """Allow users to create and manage TagBindings between + TagValues and different cloud resources throughout the GCP + resource hierarchy. + """ + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudresourcemanager.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + 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: + TagBindingsClient: 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 + 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: + TagBindingsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TagBindingsTransport: + """Returns the transport used by the client instance. + + Returns: + TagBindingsTransport: The transport used by the client + instance. + """ + return self._transport + + @staticmethod + def tag_binding_path(tag_binding: str,) -> str: + """Returns a fully-qualified tag_binding string.""" + return "tagBindings/{tag_binding}".format(tag_binding=tag_binding,) + + @staticmethod + def parse_tag_binding_path(path: str) -> Dict[str, str]: + """Parses a tag_binding path into its component segments.""" + m = re.match(r"^tagBindings/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Returns a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Returns a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Returns a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Returns a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Returns a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[ga_credentials.Credentials] = None, + transport: Union[str, TagBindingsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the tag bindings client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, TagBindingsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + if is_mtls: + client_cert_source_func = mtls.default_client_cert_source() + else: + client_cert_source_func = None + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + if is_mtls: + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = self.DEFAULT_ENDPOINT + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted " + "values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, TagBindingsTransport): + # transport is a TagBindingsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, provide its scopes " + "directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_tag_bindings( + self, + request: tag_bindings.ListTagBindingsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTagBindingsPager: + r"""Lists the TagBindings for the given cloud resource, as specified + with ``parent``. + + NOTE: The ``parent`` field is expected to be a full resource + name: + https://cloud.google.com/apis/design/resource_names#full_resource_name + + Args: + request (google.cloud.resourcemanager_v3.types.ListTagBindingsRequest): + The request object. The request message to list all + TagBindings for a parent. + parent (str): + Required. The full resource name of a + resource for which you want to list + existing TagBindings. E.g. + "//cloudresourcemanager.googleapis.com/projects/123" + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.tag_bindings.pagers.ListTagBindingsPager: + The ListTagBindings response. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_bindings.ListTagBindingsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_bindings.ListTagBindingsRequest): + request = tag_bindings.ListTagBindingsRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_tag_bindings] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.ListTagBindingsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def create_tag_binding( + self, + request: tag_bindings.CreateTagBindingRequest = None, + *, + tag_binding: tag_bindings.TagBinding = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Creates a TagBinding between a TagValue and a cloud + resource (currently project, folder, or organization). + + Args: + request (google.cloud.resourcemanager_v3.types.CreateTagBindingRequest): + The request object. The request message to create a + TagBinding. + tag_binding (google.cloud.resourcemanager_v3.types.TagBinding): + Required. The TagBinding to be + created. + + This corresponds to the ``tag_binding`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagBinding` A TagBinding represents a connection between a TagValue and a cloud + resource (currently project, folder, or + organization). Once a TagBinding is created, the + TagValue is applied to all the descendants of the + cloud resource. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_binding]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_bindings.CreateTagBindingRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_bindings.CreateTagBindingRequest): + request = tag_bindings.CreateTagBindingRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_binding is not None: + request.tag_binding = tag_binding + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_tag_binding] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + tag_bindings.TagBinding, + metadata_type=tag_bindings.CreateTagBindingMetadata, + ) + + # Done; return the response. + return response + + def delete_tag_binding( + self, + request: tag_bindings.DeleteTagBindingRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Deletes a TagBinding. + + Args: + request (google.cloud.resourcemanager_v3.types.DeleteTagBindingRequest): + The request object. The request message to delete a + TagBinding. + name (str): + Required. The name of the TagBinding. This is a String + of the form: ``tagBindings/{id}`` (e.g. + ``tagBindings/%2F%2Fcloudresourcemanager.googleapis.com%2Fprojects%2F123/tagValues/456``). + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: + + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); + + } + + The JSON representation for Empty is empty JSON + object {}. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_bindings.DeleteTagBindingRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_bindings.DeleteTagBindingRequest): + request = tag_bindings.DeleteTagBindingRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.delete_tag_binding] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + empty_pb2.Empty, + metadata_type=tag_bindings.DeleteTagBindingMetadata, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TagBindingsClient",) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/pagers.py b/google/cloud/resourcemanager_v3/services/tag_bindings/pagers.py new file mode 100644 index 0000000..f910c1a --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/pagers.py @@ -0,0 +1,155 @@ +# -*- 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. +# +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.resourcemanager_v3.types import tag_bindings + + +class ListTagBindingsPager: + """A pager for iterating through ``list_tag_bindings`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListTagBindingsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``tag_bindings`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListTagBindings`` requests and continue to iterate + through the ``tag_bindings`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListTagBindingsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., tag_bindings.ListTagBindingsResponse], + request: tag_bindings.ListTagBindingsRequest, + response: tag_bindings.ListTagBindingsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListTagBindingsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListTagBindingsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = tag_bindings.ListTagBindingsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[tag_bindings.ListTagBindingsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[tag_bindings.TagBinding]: + for page in self.pages: + yield from page.tag_bindings + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListTagBindingsAsyncPager: + """A pager for iterating through ``list_tag_bindings`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListTagBindingsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``tag_bindings`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListTagBindings`` requests and continue to iterate + through the ``tag_bindings`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListTagBindingsResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[tag_bindings.ListTagBindingsResponse]], + request: tag_bindings.ListTagBindingsRequest, + response: tag_bindings.ListTagBindingsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListTagBindingsRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListTagBindingsResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = tag_bindings.ListTagBindingsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[tag_bindings.ListTagBindingsResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[tag_bindings.TagBinding]: + async def async_generator(): + async for page in self.pages: + for response in page.tag_bindings: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/transports/__init__.py b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/__init__.py new file mode 100644 index 0000000..5bdc64f --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/__init__.py @@ -0,0 +1,33 @@ +# -*- 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. +# +from collections import OrderedDict +from typing import Dict, Type + +from .base import TagBindingsTransport +from .grpc import TagBindingsGrpcTransport +from .grpc_asyncio import TagBindingsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[TagBindingsTransport]] +_transport_registry["grpc"] = TagBindingsGrpcTransport +_transport_registry["grpc_asyncio"] = TagBindingsGrpcAsyncIOTransport + +__all__ = ( + "TagBindingsTransport", + "TagBindingsGrpcTransport", + "TagBindingsGrpcAsyncIOTransport", +) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/transports/base.py b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/base.py new file mode 100644 index 0000000..d3bf3d8 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/base.py @@ -0,0 +1,220 @@ +# -*- 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. +# +import abc +from typing import Awaitable, Callable, Dict, Optional, Sequence, Union +import packaging.version +import pkg_resources + +import google.auth # type: ignore +import google.api_core # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_bindings +from google.longrunning import operations_pb2 # type: ignore + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = google.auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + + +class TagBindingsTransport(abc.ABC): + """Abstract transport class for TagBindings.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ) + + DEFAULT_HOST: str = "cloudresourcemanager.googleapis.com" + + def __init__( + self, + *, + host: str = DEFAULT_HOST, + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + scopes_kwargs = self._get_scopes_kwargs(self._host, scopes) + + # Save the scopes. + self._scopes = scopes + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise core_exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, **scopes_kwargs, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = google.auth.default( + **scopes_kwargs, quota_project_id=quota_project_id + ) + + # If the credentials is service account credentials, then always try to use self signed JWT. + if ( + always_use_jwt_access + and isinstance(credentials, service_account.Credentials) + and hasattr(service_account.Credentials, "with_always_use_jwt_access") + ): + credentials = credentials.with_always_use_jwt_access(True) + + # Save the credentials. + self._credentials = credentials + + # TODO(busunkim): This method is in the base transport + # to avoid duplicating code across the transport classes. These functions + # should be deleted once the minimum required versions of google-auth is increased. + + # TODO: Remove this function once google-auth >= 1.25.0 is required + @classmethod + def _get_scopes_kwargs( + cls, host: str, scopes: Optional[Sequence[str]] + ) -> Dict[str, Optional[Sequence[str]]]: + """Returns scopes kwargs to pass to google-auth methods depending on the google-auth version""" + + scopes_kwargs = {} + + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": cls.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or cls.AUTH_SCOPES} + + return scopes_kwargs + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.list_tag_bindings: gapic_v1.method.wrap_method( + self.list_tag_bindings, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.create_tag_binding: gapic_v1.method.wrap_method( + self.create_tag_binding, default_timeout=60.0, client_info=client_info, + ), + self.delete_tag_binding: gapic_v1.method.wrap_method( + self.delete_tag_binding, default_timeout=60.0, client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def list_tag_bindings( + self, + ) -> Callable[ + [tag_bindings.ListTagBindingsRequest], + Union[ + tag_bindings.ListTagBindingsResponse, + Awaitable[tag_bindings.ListTagBindingsResponse], + ], + ]: + raise NotImplementedError() + + @property + def create_tag_binding( + self, + ) -> Callable[ + [tag_bindings.CreateTagBindingRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def delete_tag_binding( + self, + ) -> Callable[ + [tag_bindings.DeleteTagBindingRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + +__all__ = ("TagBindingsTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/transports/grpc.py b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/grpc.py new file mode 100644 index 0000000..7042b9d --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/grpc.py @@ -0,0 +1,334 @@ +# -*- 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. +# +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.api_core import gapic_v1 # type: ignore +import google.auth # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_bindings +from google.longrunning import operations_pb2 # type: ignore +from .base import TagBindingsTransport, DEFAULT_CLIENT_INFO + + +class TagBindingsGrpcTransport(TagBindingsTransport): + """gRPC backend transport for TagBindings. + + Allow users to create and manage TagBindings between + TagValues and different cloud resources throughout the GCP + resource hierarchy. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsClient(self.grpc_channel) + + # Return the client from cache. + return self._operations_client + + @property + def list_tag_bindings( + self, + ) -> Callable[ + [tag_bindings.ListTagBindingsRequest], tag_bindings.ListTagBindingsResponse + ]: + r"""Return a callable for the list tag bindings method over gRPC. + + Lists the TagBindings for the given cloud resource, as specified + with ``parent``. + + NOTE: The ``parent`` field is expected to be a full resource + name: + https://cloud.google.com/apis/design/resource_names#full_resource_name + + Returns: + Callable[[~.ListTagBindingsRequest], + ~.ListTagBindingsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_tag_bindings" not in self._stubs: + self._stubs["list_tag_bindings"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagBindings/ListTagBindings", + request_serializer=tag_bindings.ListTagBindingsRequest.serialize, + response_deserializer=tag_bindings.ListTagBindingsResponse.deserialize, + ) + return self._stubs["list_tag_bindings"] + + @property + def create_tag_binding( + self, + ) -> Callable[[tag_bindings.CreateTagBindingRequest], operations_pb2.Operation]: + r"""Return a callable for the create tag binding method over gRPC. + + Creates a TagBinding between a TagValue and a cloud + resource (currently project, folder, or organization). + + Returns: + Callable[[~.CreateTagBindingRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_tag_binding" not in self._stubs: + self._stubs["create_tag_binding"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagBindings/CreateTagBinding", + request_serializer=tag_bindings.CreateTagBindingRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_tag_binding"] + + @property + def delete_tag_binding( + self, + ) -> Callable[[tag_bindings.DeleteTagBindingRequest], operations_pb2.Operation]: + r"""Return a callable for the delete tag binding method over gRPC. + + Deletes a TagBinding. + + Returns: + Callable[[~.DeleteTagBindingRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_tag_binding" not in self._stubs: + self._stubs["delete_tag_binding"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagBindings/DeleteTagBinding", + request_serializer=tag_bindings.DeleteTagBindingRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_tag_binding"] + + +__all__ = ("TagBindingsGrpcTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_bindings/transports/grpc_asyncio.py b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/grpc_asyncio.py new file mode 100644 index 0000000..1d46a05 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_bindings/transports/grpc_asyncio.py @@ -0,0 +1,344 @@ +# -*- 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. +# +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_bindings +from google.longrunning import operations_pb2 # type: ignore +from .base import TagBindingsTransport, DEFAULT_CLIENT_INFO +from .grpc import TagBindingsGrpcTransport + + +class TagBindingsGrpcAsyncIOTransport(TagBindingsTransport): + """gRPC AsyncIO backend transport for TagBindings. + + Allow users to create and manage TagBindings between + TagValues and different cloud resources throughout the GCP + resource hierarchy. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self._operations_client + + @property + def list_tag_bindings( + self, + ) -> Callable[ + [tag_bindings.ListTagBindingsRequest], + Awaitable[tag_bindings.ListTagBindingsResponse], + ]: + r"""Return a callable for the list tag bindings method over gRPC. + + Lists the TagBindings for the given cloud resource, as specified + with ``parent``. + + NOTE: The ``parent`` field is expected to be a full resource + name: + https://cloud.google.com/apis/design/resource_names#full_resource_name + + Returns: + Callable[[~.ListTagBindingsRequest], + Awaitable[~.ListTagBindingsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_tag_bindings" not in self._stubs: + self._stubs["list_tag_bindings"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagBindings/ListTagBindings", + request_serializer=tag_bindings.ListTagBindingsRequest.serialize, + response_deserializer=tag_bindings.ListTagBindingsResponse.deserialize, + ) + return self._stubs["list_tag_bindings"] + + @property + def create_tag_binding( + self, + ) -> Callable[ + [tag_bindings.CreateTagBindingRequest], Awaitable[operations_pb2.Operation] + ]: + r"""Return a callable for the create tag binding method over gRPC. + + Creates a TagBinding between a TagValue and a cloud + resource (currently project, folder, or organization). + + Returns: + Callable[[~.CreateTagBindingRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_tag_binding" not in self._stubs: + self._stubs["create_tag_binding"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagBindings/CreateTagBinding", + request_serializer=tag_bindings.CreateTagBindingRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_tag_binding"] + + @property + def delete_tag_binding( + self, + ) -> Callable[ + [tag_bindings.DeleteTagBindingRequest], Awaitable[operations_pb2.Operation] + ]: + r"""Return a callable for the delete tag binding method over gRPC. + + Deletes a TagBinding. + + Returns: + Callable[[~.DeleteTagBindingRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_tag_binding" not in self._stubs: + self._stubs["delete_tag_binding"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagBindings/DeleteTagBinding", + request_serializer=tag_bindings.DeleteTagBindingRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_tag_binding"] + + +__all__ = ("TagBindingsGrpcAsyncIOTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/__init__.py b/google/cloud/resourcemanager_v3/services/tag_keys/__init__.py new file mode 100644 index 0000000..b95fa11 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/__init__.py @@ -0,0 +1,22 @@ +# -*- 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. +# +from .client import TagKeysClient +from .async_client import TagKeysAsyncClient + +__all__ = ( + "TagKeysClient", + "TagKeysAsyncClient", +) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/async_client.py b/google/cloud/resourcemanager_v3/services/tag_keys/async_client.py new file mode 100644 index 0000000..0c5c81f --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/async_client.py @@ -0,0 +1,960 @@ +# -*- 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. +# +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.tag_keys import pagers +from google.cloud.resourcemanager_v3.types import tag_keys +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import TagKeysTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import TagKeysGrpcAsyncIOTransport +from .client import TagKeysClient + + +class TagKeysAsyncClient: + """Allow users to create and manage tag keys.""" + + _client: TagKeysClient + + DEFAULT_ENDPOINT = TagKeysClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = TagKeysClient.DEFAULT_MTLS_ENDPOINT + + tag_key_path = staticmethod(TagKeysClient.tag_key_path) + parse_tag_key_path = staticmethod(TagKeysClient.parse_tag_key_path) + common_billing_account_path = staticmethod( + TagKeysClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TagKeysClient.parse_common_billing_account_path + ) + common_folder_path = staticmethod(TagKeysClient.common_folder_path) + parse_common_folder_path = staticmethod(TagKeysClient.parse_common_folder_path) + common_organization_path = staticmethod(TagKeysClient.common_organization_path) + parse_common_organization_path = staticmethod( + TagKeysClient.parse_common_organization_path + ) + common_project_path = staticmethod(TagKeysClient.common_project_path) + parse_common_project_path = staticmethod(TagKeysClient.parse_common_project_path) + common_location_path = staticmethod(TagKeysClient.common_location_path) + parse_common_location_path = staticmethod(TagKeysClient.parse_common_location_path) + + @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: + TagKeysAsyncClient: The constructed client. + """ + return TagKeysClient.from_service_account_info.__func__(TagKeysAsyncClient, 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: + TagKeysAsyncClient: The constructed client. + """ + return TagKeysClient.from_service_account_file.__func__(TagKeysAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TagKeysTransport: + """Returns the transport used by the client instance. + + Returns: + TagKeysTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(TagKeysClient).get_transport_class, type(TagKeysClient) + ) + + def __init__( + self, + *, + credentials: ga_credentials.Credentials = None, + transport: Union[str, TagKeysTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the tag keys client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.TagKeysTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + self._client = TagKeysClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_tag_keys( + self, + request: tag_keys.ListTagKeysRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTagKeysAsyncPager: + r"""Lists all TagKeys for a parent resource. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.ListTagKeysRequest`): + The request object. The request message for listing all + TagKeys under a parent resource. + parent (:class:`str`): + Required. The resource name of the new TagKey's parent. + Must be of the form ``folders/{folder_id}`` or + ``organizations/{org_id}``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.tag_keys.pagers.ListTagKeysAsyncPager: + The ListTagKeys response message. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_keys.ListTagKeysRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.list_tag_keys, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.ListTagKeysAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_tag_key( + self, + request: tag_keys.GetTagKeyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> tag_keys.TagKey: + r"""Retrieves a TagKey. This method will return + ``PERMISSION_DENIED`` if the key does not exist or the user does + not have permission to view it. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.GetTagKeyRequest`): + The request object. The request message for getting a + TagKey. + name (:class:`str`): + Required. A resource name in the format + ``tagKeys/{id}``, such as ``tagKeys/123``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.TagKey: + A TagKey, used to group a set of + TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_keys.GetTagKeyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_tag_key, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def create_tag_key( + self, + request: tag_keys.CreateTagKeyRequest = None, + *, + tag_key: tag_keys.TagKey = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Creates a new TagKey. If another request with the + same parameters is sent while the original request is in + process, the second request will receive an error. A + maximum of 300 TagKeys can exist under a parent at any + given time. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.CreateTagKeyRequest`): + The request object. The request message for creating a + TagKey. + tag_key (:class:`google.cloud.resourcemanager_v3.types.TagKey`): + Required. The TagKey to be created. Only fields + ``short_name``, ``description``, and ``parent`` are + considered during the creation request. + + This corresponds to the ``tag_key`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.resourcemanager_v3.types.TagKey` A + TagKey, used to group a set of TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_key]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_keys.CreateTagKeyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_key is not None: + request.tag_key = tag_key + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_tag_key, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + tag_keys.TagKey, + metadata_type=tag_keys.CreateTagKeyMetadata, + ) + + # Done; return the response. + return response + + async def update_tag_key( + self, + request: tag_keys.UpdateTagKeyRequest = None, + *, + tag_key: tag_keys.TagKey = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Updates the attributes of the TagKey resource. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.UpdateTagKeyRequest`): + The request object. The request message for updating a + TagKey. + tag_key (:class:`google.cloud.resourcemanager_v3.types.TagKey`): + Required. The new definition of the TagKey. Only the + ``description`` and ``etag`` fields can be updated by + this request. If the ``etag`` field is not empty, it + must match the ``etag`` field of the existing tag key. + Otherwise, ``FAILED_PRECONDITION`` will be returned. + + This corresponds to the ``tag_key`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Fields to be updated. The mask may only contain + ``description`` or ``etag``. If omitted entirely, both + ``description`` and ``etag`` are assumed to be + significant. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.resourcemanager_v3.types.TagKey` A + TagKey, used to group a set of TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_key, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_keys.UpdateTagKeyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_key is not None: + request.tag_key = tag_key + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_tag_key, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("tag_key.name", request.tag_key.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + tag_keys.TagKey, + metadata_type=tag_keys.UpdateTagKeyMetadata, + ) + + # Done; return the response. + return response + + async def delete_tag_key( + self, + request: tag_keys.DeleteTagKeyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Deletes a TagKey. The TagKey cannot be deleted if it + has any child TagValues. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.DeleteTagKeyRequest`): + The request object. The request message for deleting a + TagKey. + name (:class:`str`): + Required. The resource name of a TagKey to be deleted in + the format ``tagKeys/123``. The TagKey cannot be a + parent of any existing TagValues or it will not be + deleted successfully. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.resourcemanager_v3.types.TagKey` A + TagKey, used to group a set of TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_keys.DeleteTagKeyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.delete_tag_key, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + tag_keys.TagKey, + metadata_type=tag_keys.DeleteTagKeyMetadata, + ) + + # Done; return the response. + return response + + async def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for a TagKey. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". The caller must have + ``cloudresourcemanager.googleapis.com/tagKeys.getIamPolicy`` + permission on the specified TagKey. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.GetIamPolicyRequest`): + The request object. Request message for `GetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.GetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on a TagKey, replacing any + existing policy. The ``resource`` field should be the TagKey's + resource name. For example, "tagKeys/1234". The caller must have + ``resourcemanager.tagKeys.setIamPolicy`` permission on the + identified tagValue. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.SetIamPolicyRequest`): + The request object. Request message for `SetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.SetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.set_iam_policy, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the specified TagKey. + The ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". + + There are no permissions required for making this API call. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest`): + The request object. Request message for + `TestIamPermissions` method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (:class:`Sequence[str]`): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + request = iam_policy_pb2.TestIamPermissionsRequest( + resource=resource, permissions=permissions, + ) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.test_iam_permissions, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TagKeysAsyncClient",) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/client.py b/google/cloud/resourcemanager_v3/services/tag_keys/client.py new file mode 100644 index 0000000..f8f1c92 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/client.py @@ -0,0 +1,1116 @@ +# -*- 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. +# +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.tag_keys import pagers +from google.cloud.resourcemanager_v3.types import tag_keys +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import TagKeysTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import TagKeysGrpcTransport +from .transports.grpc_asyncio import TagKeysGrpcAsyncIOTransport + + +class TagKeysClientMeta(type): + """Metaclass for the TagKeys client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[TagKeysTransport]] + _transport_registry["grpc"] = TagKeysGrpcTransport + _transport_registry["grpc_asyncio"] = TagKeysGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[TagKeysTransport]: + """Returns an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class TagKeysClient(metaclass=TagKeysClientMeta): + """Allow users to create and manage tag keys.""" + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudresourcemanager.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + 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: + TagKeysClient: 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 + 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: + TagKeysClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TagKeysTransport: + """Returns the transport used by the client instance. + + Returns: + TagKeysTransport: The transport used by the client + instance. + """ + return self._transport + + @staticmethod + def tag_key_path(tag_key: str,) -> str: + """Returns a fully-qualified tag_key string.""" + return "tagKeys/{tag_key}".format(tag_key=tag_key,) + + @staticmethod + def parse_tag_key_path(path: str) -> Dict[str, str]: + """Parses a tag_key path into its component segments.""" + m = re.match(r"^tagKeys/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Returns a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Returns a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Returns a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Returns a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Returns a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[ga_credentials.Credentials] = None, + transport: Union[str, TagKeysTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the tag keys client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, TagKeysTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + if is_mtls: + client_cert_source_func = mtls.default_client_cert_source() + else: + client_cert_source_func = None + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + if is_mtls: + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = self.DEFAULT_ENDPOINT + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted " + "values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, TagKeysTransport): + # transport is a TagKeysTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, provide its scopes " + "directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_tag_keys( + self, + request: tag_keys.ListTagKeysRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTagKeysPager: + r"""Lists all TagKeys for a parent resource. + + Args: + request (google.cloud.resourcemanager_v3.types.ListTagKeysRequest): + The request object. The request message for listing all + TagKeys under a parent resource. + parent (str): + Required. The resource name of the new TagKey's parent. + Must be of the form ``folders/{folder_id}`` or + ``organizations/{org_id}``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.tag_keys.pagers.ListTagKeysPager: + The ListTagKeys response message. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_keys.ListTagKeysRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_keys.ListTagKeysRequest): + request = tag_keys.ListTagKeysRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_tag_keys] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.ListTagKeysPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_tag_key( + self, + request: tag_keys.GetTagKeyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> tag_keys.TagKey: + r"""Retrieves a TagKey. This method will return + ``PERMISSION_DENIED`` if the key does not exist or the user does + not have permission to view it. + + Args: + request (google.cloud.resourcemanager_v3.types.GetTagKeyRequest): + The request object. The request message for getting a + TagKey. + name (str): + Required. A resource name in the format + ``tagKeys/{id}``, such as ``tagKeys/123``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.TagKey: + A TagKey, used to group a set of + TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_keys.GetTagKeyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_keys.GetTagKeyRequest): + request = tag_keys.GetTagKeyRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_tag_key] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def create_tag_key( + self, + request: tag_keys.CreateTagKeyRequest = None, + *, + tag_key: tag_keys.TagKey = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Creates a new TagKey. If another request with the + same parameters is sent while the original request is in + process, the second request will receive an error. A + maximum of 300 TagKeys can exist under a parent at any + given time. + + Args: + request (google.cloud.resourcemanager_v3.types.CreateTagKeyRequest): + The request object. The request message for creating a + TagKey. + tag_key (google.cloud.resourcemanager_v3.types.TagKey): + Required. The TagKey to be created. Only fields + ``short_name``, ``description``, and ``parent`` are + considered during the creation request. + + This corresponds to the ``tag_key`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.resourcemanager_v3.types.TagKey` A + TagKey, used to group a set of TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_key]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_keys.CreateTagKeyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_keys.CreateTagKeyRequest): + request = tag_keys.CreateTagKeyRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_key is not None: + request.tag_key = tag_key + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_tag_key] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + tag_keys.TagKey, + metadata_type=tag_keys.CreateTagKeyMetadata, + ) + + # Done; return the response. + return response + + def update_tag_key( + self, + request: tag_keys.UpdateTagKeyRequest = None, + *, + tag_key: tag_keys.TagKey = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Updates the attributes of the TagKey resource. + + Args: + request (google.cloud.resourcemanager_v3.types.UpdateTagKeyRequest): + The request object. The request message for updating a + TagKey. + tag_key (google.cloud.resourcemanager_v3.types.TagKey): + Required. The new definition of the TagKey. Only the + ``description`` and ``etag`` fields can be updated by + this request. If the ``etag`` field is not empty, it + must match the ``etag`` field of the existing tag key. + Otherwise, ``FAILED_PRECONDITION`` will be returned. + + This corresponds to the ``tag_key`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Fields to be updated. The mask may only contain + ``description`` or ``etag``. If omitted entirely, both + ``description`` and ``etag`` are assumed to be + significant. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.resourcemanager_v3.types.TagKey` A + TagKey, used to group a set of TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_key, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_keys.UpdateTagKeyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_keys.UpdateTagKeyRequest): + request = tag_keys.UpdateTagKeyRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_key is not None: + request.tag_key = tag_key + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_tag_key] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("tag_key.name", request.tag_key.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + tag_keys.TagKey, + metadata_type=tag_keys.UpdateTagKeyMetadata, + ) + + # Done; return the response. + return response + + def delete_tag_key( + self, + request: tag_keys.DeleteTagKeyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Deletes a TagKey. The TagKey cannot be deleted if it + has any child TagValues. + + Args: + request (google.cloud.resourcemanager_v3.types.DeleteTagKeyRequest): + The request object. The request message for deleting a + TagKey. + name (str): + Required. The resource name of a TagKey to be deleted in + the format ``tagKeys/123``. The TagKey cannot be a + parent of any existing TagValues or it will not be + deleted successfully. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.resourcemanager_v3.types.TagKey` A + TagKey, used to group a set of TagValues. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_keys.DeleteTagKeyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_keys.DeleteTagKeyRequest): + request = tag_keys.DeleteTagKeyRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.delete_tag_key] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + tag_keys.TagKey, + metadata_type=tag_keys.DeleteTagKeyMetadata, + ) + + # Done; return the response. + return response + + def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for a TagKey. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". The caller must have + ``cloudresourcemanager.googleapis.com/tagKeys.getIamPolicy`` + permission on the specified TagKey. + + Args: + request (google.iam.v1.iam_policy_pb2.GetIamPolicyRequest): + The request object. Request message for `GetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.GetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on a TagKey, replacing any + existing policy. The ``resource`` field should be the TagKey's + resource name. For example, "tagKeys/1234". The caller must have + ``resourcemanager.tagKeys.setIamPolicy`` permission on the + identified tagValue. + + Args: + request (google.iam.v1.iam_policy_pb2.SetIamPolicyRequest): + The request object. Request message for `SetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.SetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the specified TagKey. + The ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". + + There are no permissions required for making this API call. + + Args: + request (google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest): + The request object. Request message for + `TestIamPermissions` method. + resource (str): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (Sequence[str]): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.TestIamPermissionsRequest() + if resource is not None: + request.resource = resource + if permissions: + request.permissions.extend(permissions) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TagKeysClient",) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/pagers.py b/google/cloud/resourcemanager_v3/services/tag_keys/pagers.py new file mode 100644 index 0000000..dfcffc7 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/pagers.py @@ -0,0 +1,155 @@ +# -*- 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. +# +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.resourcemanager_v3.types import tag_keys + + +class ListTagKeysPager: + """A pager for iterating through ``list_tag_keys`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListTagKeysResponse` object, and + provides an ``__iter__`` method to iterate through its + ``tag_keys`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListTagKeys`` requests and continue to iterate + through the ``tag_keys`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListTagKeysResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., tag_keys.ListTagKeysResponse], + request: tag_keys.ListTagKeysRequest, + response: tag_keys.ListTagKeysResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListTagKeysRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListTagKeysResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = tag_keys.ListTagKeysRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[tag_keys.ListTagKeysResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[tag_keys.TagKey]: + for page in self.pages: + yield from page.tag_keys + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListTagKeysAsyncPager: + """A pager for iterating through ``list_tag_keys`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListTagKeysResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``tag_keys`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListTagKeys`` requests and continue to iterate + through the ``tag_keys`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListTagKeysResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[tag_keys.ListTagKeysResponse]], + request: tag_keys.ListTagKeysRequest, + response: tag_keys.ListTagKeysResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListTagKeysRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListTagKeysResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = tag_keys.ListTagKeysRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[tag_keys.ListTagKeysResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[tag_keys.TagKey]: + async def async_generator(): + async for page in self.pages: + for response in page.tag_keys: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/transports/__init__.py b/google/cloud/resourcemanager_v3/services/tag_keys/transports/__init__.py new file mode 100644 index 0000000..cda23f6 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/transports/__init__.py @@ -0,0 +1,33 @@ +# -*- 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. +# +from collections import OrderedDict +from typing import Dict, Type + +from .base import TagKeysTransport +from .grpc import TagKeysGrpcTransport +from .grpc_asyncio import TagKeysGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[TagKeysTransport]] +_transport_registry["grpc"] = TagKeysGrpcTransport +_transport_registry["grpc_asyncio"] = TagKeysGrpcAsyncIOTransport + +__all__ = ( + "TagKeysTransport", + "TagKeysGrpcTransport", + "TagKeysGrpcAsyncIOTransport", +) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/transports/base.py b/google/cloud/resourcemanager_v3/services/tag_keys/transports/base.py new file mode 100644 index 0000000..4ed1536 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/transports/base.py @@ -0,0 +1,305 @@ +# -*- 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. +# +import abc +from typing import Awaitable, Callable, Dict, Optional, Sequence, Union +import packaging.version +import pkg_resources + +import google.auth # type: ignore +import google.api_core # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_keys +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = google.auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + + +class TagKeysTransport(abc.ABC): + """Abstract transport class for TagKeys.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ) + + DEFAULT_HOST: str = "cloudresourcemanager.googleapis.com" + + def __init__( + self, + *, + host: str = DEFAULT_HOST, + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + scopes_kwargs = self._get_scopes_kwargs(self._host, scopes) + + # Save the scopes. + self._scopes = scopes + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise core_exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, **scopes_kwargs, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = google.auth.default( + **scopes_kwargs, quota_project_id=quota_project_id + ) + + # If the credentials is service account credentials, then always try to use self signed JWT. + if ( + always_use_jwt_access + and isinstance(credentials, service_account.Credentials) + and hasattr(service_account.Credentials, "with_always_use_jwt_access") + ): + credentials = credentials.with_always_use_jwt_access(True) + + # Save the credentials. + self._credentials = credentials + + # TODO(busunkim): This method is in the base transport + # to avoid duplicating code across the transport classes. These functions + # should be deleted once the minimum required versions of google-auth is increased. + + # TODO: Remove this function once google-auth >= 1.25.0 is required + @classmethod + def _get_scopes_kwargs( + cls, host: str, scopes: Optional[Sequence[str]] + ) -> Dict[str, Optional[Sequence[str]]]: + """Returns scopes kwargs to pass to google-auth methods depending on the google-auth version""" + + scopes_kwargs = {} + + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": cls.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or cls.AUTH_SCOPES} + + return scopes_kwargs + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.list_tag_keys: gapic_v1.method.wrap_method( + self.list_tag_keys, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_tag_key: gapic_v1.method.wrap_method( + self.get_tag_key, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.create_tag_key: gapic_v1.method.wrap_method( + self.create_tag_key, default_timeout=60.0, client_info=client_info, + ), + self.update_tag_key: gapic_v1.method.wrap_method( + self.update_tag_key, default_timeout=60.0, client_info=client_info, + ), + self.delete_tag_key: gapic_v1.method.wrap_method( + self.delete_tag_key, default_timeout=60.0, client_info=client_info, + ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, default_timeout=60.0, client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def list_tag_keys( + self, + ) -> Callable[ + [tag_keys.ListTagKeysRequest], + Union[tag_keys.ListTagKeysResponse, Awaitable[tag_keys.ListTagKeysResponse]], + ]: + raise NotImplementedError() + + @property + def get_tag_key( + self, + ) -> Callable[ + [tag_keys.GetTagKeyRequest], Union[tag_keys.TagKey, Awaitable[tag_keys.TagKey]] + ]: + raise NotImplementedError() + + @property + def create_tag_key( + self, + ) -> Callable[ + [tag_keys.CreateTagKeyRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def update_tag_key( + self, + ) -> Callable[ + [tag_keys.UpdateTagKeyRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def delete_tag_key( + self, + ) -> Callable[ + [tag_keys.DeleteTagKeyRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def get_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.GetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def set_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.SetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Union[ + iam_policy_pb2.TestIamPermissionsResponse, + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("TagKeysTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/transports/grpc.py b/google/cloud/resourcemanager_v3/services/tag_keys/transports/grpc.py new file mode 100644 index 0000000..125bbc6 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/transports/grpc.py @@ -0,0 +1,477 @@ +# -*- 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. +# +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.api_core import gapic_v1 # type: ignore +import google.auth # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_keys +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import TagKeysTransport, DEFAULT_CLIENT_INFO + + +class TagKeysGrpcTransport(TagKeysTransport): + """gRPC backend transport for TagKeys. + + Allow users to create and manage tag keys. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsClient(self.grpc_channel) + + # Return the client from cache. + return self._operations_client + + @property + def list_tag_keys( + self, + ) -> Callable[[tag_keys.ListTagKeysRequest], tag_keys.ListTagKeysResponse]: + r"""Return a callable for the list tag keys method over gRPC. + + Lists all TagKeys for a parent resource. + + Returns: + Callable[[~.ListTagKeysRequest], + ~.ListTagKeysResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_tag_keys" not in self._stubs: + self._stubs["list_tag_keys"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/ListTagKeys", + request_serializer=tag_keys.ListTagKeysRequest.serialize, + response_deserializer=tag_keys.ListTagKeysResponse.deserialize, + ) + return self._stubs["list_tag_keys"] + + @property + def get_tag_key(self) -> Callable[[tag_keys.GetTagKeyRequest], tag_keys.TagKey]: + r"""Return a callable for the get tag key method over gRPC. + + Retrieves a TagKey. This method will return + ``PERMISSION_DENIED`` if the key does not exist or the user does + not have permission to view it. + + Returns: + Callable[[~.GetTagKeyRequest], + ~.TagKey]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_tag_key" not in self._stubs: + self._stubs["get_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/GetTagKey", + request_serializer=tag_keys.GetTagKeyRequest.serialize, + response_deserializer=tag_keys.TagKey.deserialize, + ) + return self._stubs["get_tag_key"] + + @property + def create_tag_key( + self, + ) -> Callable[[tag_keys.CreateTagKeyRequest], operations_pb2.Operation]: + r"""Return a callable for the create tag key method over gRPC. + + Creates a new TagKey. If another request with the + same parameters is sent while the original request is in + process, the second request will receive an error. A + maximum of 300 TagKeys can exist under a parent at any + given time. + + Returns: + Callable[[~.CreateTagKeyRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_tag_key" not in self._stubs: + self._stubs["create_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/CreateTagKey", + request_serializer=tag_keys.CreateTagKeyRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_tag_key"] + + @property + def update_tag_key( + self, + ) -> Callable[[tag_keys.UpdateTagKeyRequest], operations_pb2.Operation]: + r"""Return a callable for the update tag key method over gRPC. + + Updates the attributes of the TagKey resource. + + Returns: + Callable[[~.UpdateTagKeyRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_tag_key" not in self._stubs: + self._stubs["update_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/UpdateTagKey", + request_serializer=tag_keys.UpdateTagKeyRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_tag_key"] + + @property + def delete_tag_key( + self, + ) -> Callable[[tag_keys.DeleteTagKeyRequest], operations_pb2.Operation]: + r"""Return a callable for the delete tag key method over gRPC. + + Deletes a TagKey. The TagKey cannot be deleted if it + has any child TagValues. + + Returns: + Callable[[~.DeleteTagKeyRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_tag_key" not in self._stubs: + self._stubs["delete_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/DeleteTagKey", + request_serializer=tag_keys.DeleteTagKeyRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_tag_key"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a TagKey. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". The caller must have + ``cloudresourcemanager.googleapis.com/tagKeys.getIamPolicy`` + permission on the specified TagKey. + + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on a TagKey, replacing any + existing policy. The ``resource`` field should be the TagKey's + resource name. For example, "tagKeys/1234". The caller must have + ``resourcemanager.tagKeys.setIamPolicy`` permission on the + identified tagValue. + + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the specified TagKey. + The ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("TagKeysGrpcTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_keys/transports/grpc_asyncio.py b/google/cloud/resourcemanager_v3/services/tag_keys/transports/grpc_asyncio.py new file mode 100644 index 0000000..c0d4d5d --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_keys/transports/grpc_asyncio.py @@ -0,0 +1,486 @@ +# -*- 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. +# +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_keys +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import TagKeysTransport, DEFAULT_CLIENT_INFO +from .grpc import TagKeysGrpcTransport + + +class TagKeysGrpcAsyncIOTransport(TagKeysTransport): + """gRPC AsyncIO backend transport for TagKeys. + + Allow users to create and manage tag keys. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self._operations_client + + @property + def list_tag_keys( + self, + ) -> Callable[ + [tag_keys.ListTagKeysRequest], Awaitable[tag_keys.ListTagKeysResponse] + ]: + r"""Return a callable for the list tag keys method over gRPC. + + Lists all TagKeys for a parent resource. + + Returns: + Callable[[~.ListTagKeysRequest], + Awaitable[~.ListTagKeysResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_tag_keys" not in self._stubs: + self._stubs["list_tag_keys"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/ListTagKeys", + request_serializer=tag_keys.ListTagKeysRequest.serialize, + response_deserializer=tag_keys.ListTagKeysResponse.deserialize, + ) + return self._stubs["list_tag_keys"] + + @property + def get_tag_key( + self, + ) -> Callable[[tag_keys.GetTagKeyRequest], Awaitable[tag_keys.TagKey]]: + r"""Return a callable for the get tag key method over gRPC. + + Retrieves a TagKey. This method will return + ``PERMISSION_DENIED`` if the key does not exist or the user does + not have permission to view it. + + Returns: + Callable[[~.GetTagKeyRequest], + Awaitable[~.TagKey]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_tag_key" not in self._stubs: + self._stubs["get_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/GetTagKey", + request_serializer=tag_keys.GetTagKeyRequest.serialize, + response_deserializer=tag_keys.TagKey.deserialize, + ) + return self._stubs["get_tag_key"] + + @property + def create_tag_key( + self, + ) -> Callable[[tag_keys.CreateTagKeyRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the create tag key method over gRPC. + + Creates a new TagKey. If another request with the + same parameters is sent while the original request is in + process, the second request will receive an error. A + maximum of 300 TagKeys can exist under a parent at any + given time. + + Returns: + Callable[[~.CreateTagKeyRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_tag_key" not in self._stubs: + self._stubs["create_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/CreateTagKey", + request_serializer=tag_keys.CreateTagKeyRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_tag_key"] + + @property + def update_tag_key( + self, + ) -> Callable[[tag_keys.UpdateTagKeyRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the update tag key method over gRPC. + + Updates the attributes of the TagKey resource. + + Returns: + Callable[[~.UpdateTagKeyRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_tag_key" not in self._stubs: + self._stubs["update_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/UpdateTagKey", + request_serializer=tag_keys.UpdateTagKeyRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_tag_key"] + + @property + def delete_tag_key( + self, + ) -> Callable[[tag_keys.DeleteTagKeyRequest], Awaitable[operations_pb2.Operation]]: + r"""Return a callable for the delete tag key method over gRPC. + + Deletes a TagKey. The TagKey cannot be deleted if it + has any child TagValues. + + Returns: + Callable[[~.DeleteTagKeyRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_tag_key" not in self._stubs: + self._stubs["delete_tag_key"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/DeleteTagKey", + request_serializer=tag_keys.DeleteTagKeyRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_tag_key"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a TagKey. The returned policy + may be empty if no such policy or resource exists. The + ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". The caller must have + ``cloudresourcemanager.googleapis.com/tagKeys.getIamPolicy`` + permission on the specified TagKey. + + Returns: + Callable[[~.GetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on a TagKey, replacing any + existing policy. The ``resource`` field should be the TagKey's + resource name. For example, "tagKeys/1234". The caller must have + ``resourcemanager.tagKeys.setIamPolicy`` permission on the + identified tagValue. + + Returns: + Callable[[~.SetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the specified TagKey. + The ``resource`` field should be the TagKey's resource name. For + example, "tagKeys/1234". + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + Awaitable[~.TestIamPermissionsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagKeys/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("TagKeysGrpcAsyncIOTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/__init__.py b/google/cloud/resourcemanager_v3/services/tag_values/__init__.py new file mode 100644 index 0000000..5895746 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/__init__.py @@ -0,0 +1,22 @@ +# -*- 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. +# +from .client import TagValuesClient +from .async_client import TagValuesAsyncClient + +__all__ = ( + "TagValuesClient", + "TagValuesAsyncClient", +) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/async_client.py b/google/cloud/resourcemanager_v3/services/tag_values/async_client.py new file mode 100644 index 0000000..3bcbe55 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/async_client.py @@ -0,0 +1,960 @@ +# -*- 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. +# +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.tag_values import pagers +from google.cloud.resourcemanager_v3.types import tag_values +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import TagValuesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import TagValuesGrpcAsyncIOTransport +from .client import TagValuesClient + + +class TagValuesAsyncClient: + """Allow users to create and manage tag values.""" + + _client: TagValuesClient + + DEFAULT_ENDPOINT = TagValuesClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = TagValuesClient.DEFAULT_MTLS_ENDPOINT + + tag_value_path = staticmethod(TagValuesClient.tag_value_path) + parse_tag_value_path = staticmethod(TagValuesClient.parse_tag_value_path) + common_billing_account_path = staticmethod( + TagValuesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TagValuesClient.parse_common_billing_account_path + ) + common_folder_path = staticmethod(TagValuesClient.common_folder_path) + parse_common_folder_path = staticmethod(TagValuesClient.parse_common_folder_path) + common_organization_path = staticmethod(TagValuesClient.common_organization_path) + parse_common_organization_path = staticmethod( + TagValuesClient.parse_common_organization_path + ) + common_project_path = staticmethod(TagValuesClient.common_project_path) + parse_common_project_path = staticmethod(TagValuesClient.parse_common_project_path) + common_location_path = staticmethod(TagValuesClient.common_location_path) + parse_common_location_path = staticmethod( + TagValuesClient.parse_common_location_path + ) + + @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: + TagValuesAsyncClient: The constructed client. + """ + return TagValuesClient.from_service_account_info.__func__(TagValuesAsyncClient, 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: + TagValuesAsyncClient: The constructed client. + """ + return TagValuesClient.from_service_account_file.__func__(TagValuesAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TagValuesTransport: + """Returns the transport used by the client instance. + + Returns: + TagValuesTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(TagValuesClient).get_transport_class, type(TagValuesClient) + ) + + def __init__( + self, + *, + credentials: ga_credentials.Credentials = None, + transport: Union[str, TagValuesTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the tag values client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.TagValuesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + self._client = TagValuesClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_tag_values( + self, + request: tag_values.ListTagValuesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTagValuesAsyncPager: + r"""Lists all TagValues for a specific TagKey. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.ListTagValuesRequest`): + The request object. The request message for listing + TagValues for the specified TagKey. + parent (:class:`str`): + Required. Resource name for TagKey, parent of the + TagValues to be listed, in the format ``tagKeys/123``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.tag_values.pagers.ListTagValuesAsyncPager: + The ListTagValues response. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_values.ListTagValuesRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.list_tag_values, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.ListTagValuesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_tag_value( + self, + request: tag_values.GetTagValueRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> tag_values.TagValue: + r"""Retrieves TagValue. If the TagValue or namespaced name does not + exist, or if the user does not have permission to view it, this + method will return ``PERMISSION_DENIED``. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.GetTagValueRequest`): + The request object. The request message for getting a + TagValue. + name (:class:`str`): + Required. Resource name for TagValue to be fetched in + the format ``tagValues/456``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.TagValue: + A TagValue is a child of a particular + TagKey. This is used to group cloud + resources for the purpose of controlling + them using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_values.GetTagValueRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_tag_value, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def create_tag_value( + self, + request: tag_values.CreateTagValueRequest = None, + *, + tag_value: tag_values.TagValue = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Creates a TagValue as a child of the specified + TagKey. If a another request with the same parameters is + sent while the original request is in process the second + request will receive an error. A maximum of 300 + TagValues can exist under a TagKey at any given time. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.CreateTagValueRequest`): + The request object. The request message for creating a + TagValue. + tag_value (:class:`google.cloud.resourcemanager_v3.types.TagValue`): + Required. The TagValue to be created. Only fields + ``short_name``, ``description``, and ``parent`` are + considered during the creation request. + + This corresponds to the ``tag_value`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagValue` A TagValue is a child of a particular TagKey. This is used to group + cloud resources for the purpose of controlling them + using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_value]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_values.CreateTagValueRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_value is not None: + request.tag_value = tag_value + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_tag_value, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + tag_values.TagValue, + metadata_type=tag_values.CreateTagValueMetadata, + ) + + # Done; return the response. + return response + + async def update_tag_value( + self, + request: tag_values.UpdateTagValueRequest = None, + *, + tag_value: tag_values.TagValue = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Updates the attributes of the TagValue resource. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.UpdateTagValueRequest`): + The request object. The request message for updating a + TagValue. + tag_value (:class:`google.cloud.resourcemanager_v3.types.TagValue`): + Required. The new definition of the TagValue. Only + fields ``description`` and ``etag`` fields can be + updated by this request. If the ``etag`` field is + nonempty, it must match the ``etag`` field of the + existing ControlGroup. Otherwise, + ``FAILED_PRECONDITION`` will be returned. + + This corresponds to the ``tag_value`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Optional. Fields to be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagValue` A TagValue is a child of a particular TagKey. This is used to group + cloud resources for the purpose of controlling them + using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_value, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_values.UpdateTagValueRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_value is not None: + request.tag_value = tag_value + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_tag_value, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("tag_value.name", request.tag_value.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + tag_values.TagValue, + metadata_type=tag_values.UpdateTagValueMetadata, + ) + + # Done; return the response. + return response + + async def delete_tag_value( + self, + request: tag_values.DeleteTagValueRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Deletes a TagValue. The TagValue cannot have any + bindings when it is deleted. + + Args: + request (:class:`google.cloud.resourcemanager_v3.types.DeleteTagValueRequest`): + The request object. The request message for deleting a + TagValue. + name (:class:`str`): + Required. Resource name for TagValue + to be deleted in the format + tagValues/456. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagValue` A TagValue is a child of a particular TagKey. This is used to group + cloud resources for the purpose of controlling them + using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = tag_values.DeleteTagValueRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.delete_tag_value, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + tag_values.TagValue, + metadata_type=tag_values.DeleteTagValueMetadata, + ) + + # Done; return the response. + return response + + async def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for a TagValue. The returned + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the TagValue's resource name. For + example: ``tagValues/1234``. The caller must have the + ``cloudresourcemanager.googleapis.com/tagValues.getIamPolicy`` + permission on the identified TagValue to get the access control + policy. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.GetIamPolicyRequest`): + The request object. Request message for `GetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.GetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on a TagValue, replacing any + existing policy. The ``resource`` field should be the TagValue's + resource name. For example: ``tagValues/1234``. The caller must + have ``resourcemanager.tagValues.setIamPolicy`` permission on + the identified tagValue. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.SetIamPolicyRequest`): + The request object. Request message for `SetIamPolicy` + method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + request = iam_policy_pb2.SetIamPolicyRequest(resource=resource,) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.set_iam_policy, + default_timeout=60.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the specified TagValue. + The ``resource`` field should be the TagValue's resource name. + For example: ``tagValues/1234``. + + There are no permissions required for making this API call. + + Args: + request (:class:`google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest`): + The request object. Request message for + `TestIamPermissions` method. + resource (:class:`str`): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (:class:`Sequence[str]`): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + request = iam_policy_pb2.TestIamPermissionsRequest( + resource=resource, permissions=permissions, + ) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.test_iam_permissions, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TagValuesAsyncClient",) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/client.py b/google/cloud/resourcemanager_v3/services/tag_values/client.py new file mode 100644 index 0000000..7134846 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/client.py @@ -0,0 +1,1114 @@ +# -*- 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. +# +from collections import OrderedDict +from distutils import util +import os +import re +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +import pkg_resources + +from google.api_core import client_options as client_options_lib # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.resourcemanager_v3.services.tag_values import pagers +from google.cloud.resourcemanager_v3.types import tag_values +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from .transports.base import TagValuesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import TagValuesGrpcTransport +from .transports.grpc_asyncio import TagValuesGrpcAsyncIOTransport + + +class TagValuesClientMeta(type): + """Metaclass for the TagValues client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = OrderedDict() # type: Dict[str, Type[TagValuesTransport]] + _transport_registry["grpc"] = TagValuesGrpcTransport + _transport_registry["grpc_asyncio"] = TagValuesGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[TagValuesTransport]: + """Returns an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class TagValuesClient(metaclass=TagValuesClientMeta): + """Allow users to create and manage tag values.""" + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "cloudresourcemanager.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + 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: + TagValuesClient: 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 + 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: + TagValuesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TagValuesTransport: + """Returns the transport used by the client instance. + + Returns: + TagValuesTransport: The transport used by the client + instance. + """ + return self._transport + + @staticmethod + def tag_value_path(tag_value: str,) -> str: + """Returns a fully-qualified tag_value string.""" + return "tagValues/{tag_value}".format(tag_value=tag_value,) + + @staticmethod + def parse_tag_value_path(path: str) -> Dict[str, str]: + """Parses a tag_value path into its component segments.""" + m = re.match(r"^tagValues/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Returns a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Returns a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Returns a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Returns a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Returns a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + + def __init__( + self, + *, + credentials: Optional[ga_credentials.Credentials] = None, + transport: Union[str, TagValuesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiates the tag values client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, TagValuesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + if is_mtls: + client_cert_source_func = mtls.default_client_cert_source() + else: + client_cert_source_func = None + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + if is_mtls: + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = self.DEFAULT_ENDPOINT + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted " + "values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, TagValuesTransport): + # transport is a TagValuesTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, provide its scopes " + "directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_tag_values( + self, + request: tag_values.ListTagValuesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTagValuesPager: + r"""Lists all TagValues for a specific TagKey. + + Args: + request (google.cloud.resourcemanager_v3.types.ListTagValuesRequest): + The request object. The request message for listing + TagValues for the specified TagKey. + parent (str): + Required. Resource name for TagKey, parent of the + TagValues to be listed, in the format ``tagKeys/123``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.services.tag_values.pagers.ListTagValuesPager: + The ListTagValues response. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([parent]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_values.ListTagValuesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_values.ListTagValuesRequest): + request = tag_values.ListTagValuesRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if parent is not None: + request.parent = parent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_tag_values] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.ListTagValuesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_tag_value( + self, + request: tag_values.GetTagValueRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> tag_values.TagValue: + r"""Retrieves TagValue. If the TagValue or namespaced name does not + exist, or if the user does not have permission to view it, this + method will return ``PERMISSION_DENIED``. + + Args: + request (google.cloud.resourcemanager_v3.types.GetTagValueRequest): + The request object. The request message for getting a + TagValue. + name (str): + Required. Resource name for TagValue to be fetched in + the format ``tagValues/456``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.resourcemanager_v3.types.TagValue: + A TagValue is a child of a particular + TagKey. This is used to group cloud + resources for the purpose of controlling + them using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_values.GetTagValueRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_values.GetTagValueRequest): + request = tag_values.GetTagValueRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_tag_value] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def create_tag_value( + self, + request: tag_values.CreateTagValueRequest = None, + *, + tag_value: tag_values.TagValue = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Creates a TagValue as a child of the specified + TagKey. If a another request with the same parameters is + sent while the original request is in process the second + request will receive an error. A maximum of 300 + TagValues can exist under a TagKey at any given time. + + Args: + request (google.cloud.resourcemanager_v3.types.CreateTagValueRequest): + The request object. The request message for creating a + TagValue. + tag_value (google.cloud.resourcemanager_v3.types.TagValue): + Required. The TagValue to be created. Only fields + ``short_name``, ``description``, and ``parent`` are + considered during the creation request. + + This corresponds to the ``tag_value`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagValue` A TagValue is a child of a particular TagKey. This is used to group + cloud resources for the purpose of controlling them + using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_value]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_values.CreateTagValueRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_values.CreateTagValueRequest): + request = tag_values.CreateTagValueRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_value is not None: + request.tag_value = tag_value + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_tag_value] + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + tag_values.TagValue, + metadata_type=tag_values.CreateTagValueMetadata, + ) + + # Done; return the response. + return response + + def update_tag_value( + self, + request: tag_values.UpdateTagValueRequest = None, + *, + tag_value: tag_values.TagValue = None, + update_mask: field_mask_pb2.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Updates the attributes of the TagValue resource. + + Args: + request (google.cloud.resourcemanager_v3.types.UpdateTagValueRequest): + The request object. The request message for updating a + TagValue. + tag_value (google.cloud.resourcemanager_v3.types.TagValue): + Required. The new definition of the TagValue. Only + fields ``description`` and ``etag`` fields can be + updated by this request. If the ``etag`` field is + nonempty, it must match the ``etag`` field of the + existing ControlGroup. Otherwise, + ``FAILED_PRECONDITION`` will be returned. + + This corresponds to the ``tag_value`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. Fields to be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagValue` A TagValue is a child of a particular TagKey. This is used to group + cloud resources for the purpose of controlling them + using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([tag_value, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_values.UpdateTagValueRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_values.UpdateTagValueRequest): + request = tag_values.UpdateTagValueRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if tag_value is not None: + request.tag_value = tag_value + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_tag_value] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("tag_value.name", request.tag_value.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + tag_values.TagValue, + metadata_type=tag_values.UpdateTagValueMetadata, + ) + + # Done; return the response. + return response + + def delete_tag_value( + self, + request: tag_values.DeleteTagValueRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Deletes a TagValue. The TagValue cannot have any + bindings when it is deleted. + + Args: + request (google.cloud.resourcemanager_v3.types.DeleteTagValueRequest): + The request object. The request message for deleting a + TagValue. + name (str): + Required. Resource name for TagValue + to be deleted in the format + tagValues/456. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.cloud.resourcemanager_v3.types.TagValue` A TagValue is a child of a particular TagKey. This is used to group + cloud resources for the purpose of controlling them + using policies. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a tag_values.DeleteTagValueRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, tag_values.DeleteTagValueRequest): + request = tag_values.DeleteTagValueRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.delete_tag_value] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + tag_values.TagValue, + metadata_type=tag_values.DeleteTagValueMetadata, + ) + + # Done; return the response. + return response + + def get_iam_policy( + self, + request: iam_policy_pb2.GetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Gets the access control policy for a TagValue. The returned + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the TagValue's resource name. For + example: ``tagValues/1234``. The caller must have the + ``cloudresourcemanager.googleapis.com/tagValues.getIamPolicy`` + permission on the identified TagValue to get the access control + policy. + + Args: + request (google.iam.v1.iam_policy_pb2.GetIamPolicyRequest): + The request object. Request message for `GetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being requested. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.GetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.GetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def set_iam_policy( + self, + request: iam_policy_pb2.SetIamPolicyRequest = None, + *, + resource: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> policy_pb2.Policy: + r"""Sets the access control policy on a TagValue, replacing any + existing policy. The ``resource`` field should be the TagValue's + resource name. For example: ``tagValues/1234``. The caller must + have ``resourcemanager.tagValues.setIamPolicy`` permission on + the identified tagValue. + + Args: + request (google.iam.v1.iam_policy_pb2.SetIamPolicyRequest): + The request object. Request message for `SetIamPolicy` + method. + resource (str): + REQUIRED: The resource for which the + policy is being specified. See the + operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.policy_pb2.Policy: + Defines an Identity and Access Management (IAM) policy. It is used to + specify access control policies for Cloud Platform + resources. + + A Policy is a collection of bindings. A binding binds + one or more members to a single role. Members can be + user accounts, service accounts, Google groups, and + domains (such as G Suite). A role is a named list of + permissions (defined by IAM or configured by users). + A binding can optionally specify a condition, which + is a logic expression that further constrains the + role binding based on attributes about the request + and/or target resource. + + **JSON Example** + + { + "bindings": [ + { + "role": + "roles/resourcemanager.organizationAdmin", + "members": [ "user:mike@example.com", + "group:admins@example.com", + "domain:google.com", + "serviceAccount:my-project-id@appspot.gserviceaccount.com" + ] + + }, { "role": + "roles/resourcemanager.organizationViewer", + "members": ["user:eve@example.com"], + "condition": { "title": "expirable access", + "description": "Does not grant access after + Sep 2020", "expression": "request.time < + timestamp('2020-10-01T00:00:00.000Z')", } } + + ] + + } + + **YAML Example** + + bindings: - members: - user:\ mike@example.com - + group:\ admins@example.com - domain:google.com - + serviceAccount:\ my-project-id@appspot.gserviceaccount.com + role: roles/resourcemanager.organizationAdmin - + members: - user:\ eve@example.com role: + roles/resourcemanager.organizationViewer + condition: title: expirable access description: + Does not grant access after Sep 2020 expression: + request.time < + timestamp('2020-10-01T00:00:00.000Z') + + For a description of IAM and its features, see the + [IAM developer's + guide](\ https://cloud.google.com/iam/docs). + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.SetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.SetIamPolicyRequest() + if resource is not None: + request.resource = resource + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def test_iam_permissions( + self, + request: iam_policy_pb2.TestIamPermissionsRequest = None, + *, + resource: str = None, + permissions: Sequence[str] = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> iam_policy_pb2.TestIamPermissionsResponse: + r"""Returns permissions that a caller has on the specified TagValue. + The ``resource`` field should be the TagValue's resource name. + For example: ``tagValues/1234``. + + There are no permissions required for making this API call. + + Args: + request (google.iam.v1.iam_policy_pb2.TestIamPermissionsRequest): + The request object. Request message for + `TestIamPermissions` method. + resource (str): + REQUIRED: The resource for which the + policy detail is being requested. See + the operation documentation for the + appropriate value for this field. + + This corresponds to the ``resource`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + permissions (Sequence[str]): + The set of permissions to check for the ``resource``. + Permissions with wildcards (such as '*' or 'storage.*') + are not allowed. For more information see `IAM + Overview `__. + + This corresponds to the ``permissions`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.iam.v1.iam_policy_pb2.TestIamPermissionsResponse: + Response message for TestIamPermissions method. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([resource, permissions]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + if isinstance(request, dict): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + request = iam_policy_pb2.TestIamPermissionsRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy_pb2.TestIamPermissionsRequest() + if resource is not None: + request.resource = resource + if permissions: + request.permissions.extend(permissions) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TagValuesClient",) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/pagers.py b/google/cloud/resourcemanager_v3/services/tag_values/pagers.py new file mode 100644 index 0000000..f976194 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/pagers.py @@ -0,0 +1,155 @@ +# -*- 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. +# +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.resourcemanager_v3.types import tag_values + + +class ListTagValuesPager: + """A pager for iterating through ``list_tag_values`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListTagValuesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``tag_values`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListTagValues`` requests and continue to iterate + through the ``tag_values`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListTagValuesResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., tag_values.ListTagValuesResponse], + request: tag_values.ListTagValuesRequest, + response: tag_values.ListTagValuesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListTagValuesRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListTagValuesResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = tag_values.ListTagValuesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[tag_values.ListTagValuesResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[tag_values.TagValue]: + for page in self.pages: + yield from page.tag_values + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListTagValuesAsyncPager: + """A pager for iterating through ``list_tag_values`` requests. + + This class thinly wraps an initial + :class:`google.cloud.resourcemanager_v3.types.ListTagValuesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``tag_values`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListTagValues`` requests and continue to iterate + through the ``tag_values`` field on the + corresponding responses. + + All the usual :class:`google.cloud.resourcemanager_v3.types.ListTagValuesResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[tag_values.ListTagValuesResponse]], + request: tag_values.ListTagValuesRequest, + response: tag_values.ListTagValuesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiates the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.resourcemanager_v3.types.ListTagValuesRequest): + The initial request object. + response (google.cloud.resourcemanager_v3.types.ListTagValuesResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = tag_values.ListTagValuesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[tag_values.ListTagValuesResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[tag_values.TagValue]: + async def async_generator(): + async for page in self.pages: + for response in page.tag_values: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/transports/__init__.py b/google/cloud/resourcemanager_v3/services/tag_values/transports/__init__.py new file mode 100644 index 0000000..be5feb5 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/transports/__init__.py @@ -0,0 +1,33 @@ +# -*- 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. +# +from collections import OrderedDict +from typing import Dict, Type + +from .base import TagValuesTransport +from .grpc import TagValuesGrpcTransport +from .grpc_asyncio import TagValuesGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[TagValuesTransport]] +_transport_registry["grpc"] = TagValuesGrpcTransport +_transport_registry["grpc_asyncio"] = TagValuesGrpcAsyncIOTransport + +__all__ = ( + "TagValuesTransport", + "TagValuesGrpcTransport", + "TagValuesGrpcAsyncIOTransport", +) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/transports/base.py b/google/cloud/resourcemanager_v3/services/tag_values/transports/base.py new file mode 100644 index 0000000..241ba33 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/transports/base.py @@ -0,0 +1,309 @@ +# -*- 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. +# +import abc +from typing import Awaitable, Callable, Dict, Optional, Sequence, Union +import packaging.version +import pkg_resources + +import google.auth # type: ignore +import google.api_core # type: ignore +from google.api_core import exceptions as core_exceptions # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google.api_core import retry as retries # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_values +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-resourcemanager", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = google.auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + + +class TagValuesTransport(abc.ABC): + """Abstract transport class for TagValues.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ) + + DEFAULT_HOST: str = "cloudresourcemanager.googleapis.com" + + def __init__( + self, + *, + host: str = DEFAULT_HOST, + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + scopes_kwargs = self._get_scopes_kwargs(self._host, scopes) + + # Save the scopes. + self._scopes = scopes + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise core_exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, **scopes_kwargs, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = google.auth.default( + **scopes_kwargs, quota_project_id=quota_project_id + ) + + # If the credentials is service account credentials, then always try to use self signed JWT. + if ( + always_use_jwt_access + and isinstance(credentials, service_account.Credentials) + and hasattr(service_account.Credentials, "with_always_use_jwt_access") + ): + credentials = credentials.with_always_use_jwt_access(True) + + # Save the credentials. + self._credentials = credentials + + # TODO(busunkim): This method is in the base transport + # to avoid duplicating code across the transport classes. These functions + # should be deleted once the minimum required versions of google-auth is increased. + + # TODO: Remove this function once google-auth >= 1.25.0 is required + @classmethod + def _get_scopes_kwargs( + cls, host: str, scopes: Optional[Sequence[str]] + ) -> Dict[str, Optional[Sequence[str]]]: + """Returns scopes kwargs to pass to google-auth methods depending on the google-auth version""" + + scopes_kwargs = {} + + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": cls.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or cls.AUTH_SCOPES} + + return scopes_kwargs + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.list_tag_values: gapic_v1.method.wrap_method( + self.list_tag_values, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_tag_value: gapic_v1.method.wrap_method( + self.get_tag_value, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.create_tag_value: gapic_v1.method.wrap_method( + self.create_tag_value, default_timeout=60.0, client_info=client_info, + ), + self.update_tag_value: gapic_v1.method.wrap_method( + self.update_tag_value, default_timeout=60.0, client_info=client_info, + ), + self.delete_tag_value: gapic_v1.method.wrap_method( + self.delete_tag_value, default_timeout=60.0, client_info=client_info, + ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, default_timeout=60.0, client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def list_tag_values( + self, + ) -> Callable[ + [tag_values.ListTagValuesRequest], + Union[ + tag_values.ListTagValuesResponse, + Awaitable[tag_values.ListTagValuesResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_tag_value( + self, + ) -> Callable[ + [tag_values.GetTagValueRequest], + Union[tag_values.TagValue, Awaitable[tag_values.TagValue]], + ]: + raise NotImplementedError() + + @property + def create_tag_value( + self, + ) -> Callable[ + [tag_values.CreateTagValueRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def update_tag_value( + self, + ) -> Callable[ + [tag_values.UpdateTagValueRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def delete_tag_value( + self, + ) -> Callable[ + [tag_values.DeleteTagValueRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def get_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.GetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def set_iam_policy( + self, + ) -> Callable[ + [iam_policy_pb2.SetIamPolicyRequest], + Union[policy_pb2.Policy, Awaitable[policy_pb2.Policy]], + ]: + raise NotImplementedError() + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Union[ + iam_policy_pb2.TestIamPermissionsResponse, + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("TagValuesTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/transports/grpc.py b/google/cloud/resourcemanager_v3/services/tag_values/transports/grpc.py new file mode 100644 index 0000000..5899fb2 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/transports/grpc.py @@ -0,0 +1,480 @@ +# -*- 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. +# +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.api_core import gapic_v1 # type: ignore +import google.auth # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_values +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import TagValuesTransport, DEFAULT_CLIENT_INFO + + +class TagValuesGrpcTransport(TagValuesTransport): + """gRPC backend transport for TagValues. + + Allow users to create and manage tag values. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsClient(self.grpc_channel) + + # Return the client from cache. + return self._operations_client + + @property + def list_tag_values( + self, + ) -> Callable[[tag_values.ListTagValuesRequest], tag_values.ListTagValuesResponse]: + r"""Return a callable for the list tag values method over gRPC. + + Lists all TagValues for a specific TagKey. + + Returns: + Callable[[~.ListTagValuesRequest], + ~.ListTagValuesResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_tag_values" not in self._stubs: + self._stubs["list_tag_values"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/ListTagValues", + request_serializer=tag_values.ListTagValuesRequest.serialize, + response_deserializer=tag_values.ListTagValuesResponse.deserialize, + ) + return self._stubs["list_tag_values"] + + @property + def get_tag_value( + self, + ) -> Callable[[tag_values.GetTagValueRequest], tag_values.TagValue]: + r"""Return a callable for the get tag value method over gRPC. + + Retrieves TagValue. If the TagValue or namespaced name does not + exist, or if the user does not have permission to view it, this + method will return ``PERMISSION_DENIED``. + + Returns: + Callable[[~.GetTagValueRequest], + ~.TagValue]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_tag_value" not in self._stubs: + self._stubs["get_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/GetTagValue", + request_serializer=tag_values.GetTagValueRequest.serialize, + response_deserializer=tag_values.TagValue.deserialize, + ) + return self._stubs["get_tag_value"] + + @property + def create_tag_value( + self, + ) -> Callable[[tag_values.CreateTagValueRequest], operations_pb2.Operation]: + r"""Return a callable for the create tag value method over gRPC. + + Creates a TagValue as a child of the specified + TagKey. If a another request with the same parameters is + sent while the original request is in process the second + request will receive an error. A maximum of 300 + TagValues can exist under a TagKey at any given time. + + Returns: + Callable[[~.CreateTagValueRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_tag_value" not in self._stubs: + self._stubs["create_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/CreateTagValue", + request_serializer=tag_values.CreateTagValueRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_tag_value"] + + @property + def update_tag_value( + self, + ) -> Callable[[tag_values.UpdateTagValueRequest], operations_pb2.Operation]: + r"""Return a callable for the update tag value method over gRPC. + + Updates the attributes of the TagValue resource. + + Returns: + Callable[[~.UpdateTagValueRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_tag_value" not in self._stubs: + self._stubs["update_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/UpdateTagValue", + request_serializer=tag_values.UpdateTagValueRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_tag_value"] + + @property + def delete_tag_value( + self, + ) -> Callable[[tag_values.DeleteTagValueRequest], operations_pb2.Operation]: + r"""Return a callable for the delete tag value method over gRPC. + + Deletes a TagValue. The TagValue cannot have any + bindings when it is deleted. + + Returns: + Callable[[~.DeleteTagValueRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_tag_value" not in self._stubs: + self._stubs["delete_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/DeleteTagValue", + request_serializer=tag_values.DeleteTagValueRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_tag_value"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a TagValue. The returned + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the TagValue's resource name. For + example: ``tagValues/1234``. The caller must have the + ``cloudresourcemanager.googleapis.com/tagValues.getIamPolicy`` + permission on the identified TagValue to get the access control + policy. + + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on a TagValue, replacing any + existing policy. The ``resource`` field should be the TagValue's + resource name. For example: ``tagValues/1234``. The caller must + have ``resourcemanager.tagValues.setIamPolicy`` permission on + the identified tagValue. + + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the specified TagValue. + The ``resource`` field should be the TagValue's resource name. + For example: ``tagValues/1234``. + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("TagValuesGrpcTransport",) diff --git a/google/cloud/resourcemanager_v3/services/tag_values/transports/grpc_asyncio.py b/google/cloud/resourcemanager_v3/services/tag_values/transports/grpc_asyncio.py new file mode 100644 index 0000000..71f3937 --- /dev/null +++ b/google/cloud/resourcemanager_v3/services/tag_values/transports/grpc_asyncio.py @@ -0,0 +1,493 @@ +# -*- 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. +# +import warnings +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.auth import credentials as ga_credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.resourcemanager_v3.types import tag_values +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore +from .base import TagValuesTransport, DEFAULT_CLIENT_INFO +from .grpc import TagValuesGrpcTransport + + +class TagValuesGrpcAsyncIOTransport(TagValuesTransport): + """gRPC AsyncIO backend transport for TagValues. + + Allow users to create and manage tag values. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + 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 + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + quota_project_id=quota_project_id, + default_scopes=cls.AUTH_SCOPES, + scopes=scopes, + default_host=cls.DEFAULT_HOST, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "cloudresourcemanager.googleapis.com", + credentials: ga_credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): + The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``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 + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + 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] = {} + self._operations_client = None + + 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: + # 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 + 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 + + else: + 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 + ) + + # 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, + always_use_jwt_access=always_use_jwt_access, + ) + + if not self._grpc_channel: + self._grpc_channel = type(self).create_channel( + self._host, + credentials=self._credentials, + credentials_file=credentials_file, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self._operations_client + + @property + def list_tag_values( + self, + ) -> Callable[ + [tag_values.ListTagValuesRequest], Awaitable[tag_values.ListTagValuesResponse] + ]: + r"""Return a callable for the list tag values method over gRPC. + + Lists all TagValues for a specific TagKey. + + Returns: + Callable[[~.ListTagValuesRequest], + Awaitable[~.ListTagValuesResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_tag_values" not in self._stubs: + self._stubs["list_tag_values"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/ListTagValues", + request_serializer=tag_values.ListTagValuesRequest.serialize, + response_deserializer=tag_values.ListTagValuesResponse.deserialize, + ) + return self._stubs["list_tag_values"] + + @property + def get_tag_value( + self, + ) -> Callable[[tag_values.GetTagValueRequest], Awaitable[tag_values.TagValue]]: + r"""Return a callable for the get tag value method over gRPC. + + Retrieves TagValue. If the TagValue or namespaced name does not + exist, or if the user does not have permission to view it, this + method will return ``PERMISSION_DENIED``. + + Returns: + Callable[[~.GetTagValueRequest], + Awaitable[~.TagValue]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_tag_value" not in self._stubs: + self._stubs["get_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/GetTagValue", + request_serializer=tag_values.GetTagValueRequest.serialize, + response_deserializer=tag_values.TagValue.deserialize, + ) + return self._stubs["get_tag_value"] + + @property + def create_tag_value( + self, + ) -> Callable[ + [tag_values.CreateTagValueRequest], Awaitable[operations_pb2.Operation] + ]: + r"""Return a callable for the create tag value method over gRPC. + + Creates a TagValue as a child of the specified + TagKey. If a another request with the same parameters is + sent while the original request is in process the second + request will receive an error. A maximum of 300 + TagValues can exist under a TagKey at any given time. + + Returns: + Callable[[~.CreateTagValueRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_tag_value" not in self._stubs: + self._stubs["create_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/CreateTagValue", + request_serializer=tag_values.CreateTagValueRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["create_tag_value"] + + @property + def update_tag_value( + self, + ) -> Callable[ + [tag_values.UpdateTagValueRequest], Awaitable[operations_pb2.Operation] + ]: + r"""Return a callable for the update tag value method over gRPC. + + Updates the attributes of the TagValue resource. + + Returns: + Callable[[~.UpdateTagValueRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_tag_value" not in self._stubs: + self._stubs["update_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/UpdateTagValue", + request_serializer=tag_values.UpdateTagValueRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["update_tag_value"] + + @property + def delete_tag_value( + self, + ) -> Callable[ + [tag_values.DeleteTagValueRequest], Awaitable[operations_pb2.Operation] + ]: + r"""Return a callable for the delete tag value method over gRPC. + + Deletes a TagValue. The TagValue cannot have any + bindings when it is deleted. + + Returns: + Callable[[~.DeleteTagValueRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_tag_value" not in self._stubs: + self._stubs["delete_tag_value"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/DeleteTagValue", + request_serializer=tag_values.DeleteTagValueRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["delete_tag_value"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the get iam policy method over gRPC. + + Gets the access control policy for a TagValue. The returned + policy may be empty if no such policy or resource exists. The + ``resource`` field should be the TagValue's resource name. For + example: ``tagValues/1234``. The caller must have the + ``cloudresourcemanager.googleapis.com/tagValues.getIamPolicy`` + permission on the identified TagValue to get the access control + policy. + + Returns: + Callable[[~.GetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: + r"""Return a callable for the set iam policy method over gRPC. + + Sets the access control policy on a TagValue, replacing any + existing policy. The ``resource`` field should be the TagValue's + resource name. For example: ``tagValues/1234``. The caller must + have ``resourcemanager.tagValues.setIamPolicy`` permission on + the identified tagValue. + + Returns: + Callable[[~.SetIamPolicyRequest], + Awaitable[~.Policy]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + Awaitable[iam_policy_pb2.TestIamPermissionsResponse], + ]: + r"""Return a callable for the test iam permissions method over gRPC. + + Returns permissions that a caller has on the specified TagValue. + The ``resource`` field should be the TagValue's resource name. + For example: ``tagValues/1234``. + + There are no permissions required for making this API call. + + Returns: + Callable[[~.TestIamPermissionsRequest], + Awaitable[~.TestIamPermissionsResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + "/google.cloud.resourcemanager.v3.TagValues/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + + +__all__ = ("TagValuesGrpcAsyncIOTransport",) diff --git a/google/cloud/resourcemanager_v3/types/__init__.py b/google/cloud/resourcemanager_v3/types/__init__.py new file mode 100644 index 0000000..554df9f --- /dev/null +++ b/google/cloud/resourcemanager_v3/types/__init__.py @@ -0,0 +1,160 @@ +# -*- 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. +# +from .folders import ( + CreateFolderMetadata, + CreateFolderRequest, + DeleteFolderMetadata, + DeleteFolderRequest, + Folder, + GetFolderRequest, + ListFoldersRequest, + ListFoldersResponse, + MoveFolderMetadata, + MoveFolderRequest, + SearchFoldersRequest, + SearchFoldersResponse, + UndeleteFolderMetadata, + UndeleteFolderRequest, + UpdateFolderMetadata, + UpdateFolderRequest, +) +from .organizations import ( + DeleteOrganizationMetadata, + GetOrganizationRequest, + Organization, + SearchOrganizationsRequest, + SearchOrganizationsResponse, + UndeleteOrganizationMetadata, +) +from .projects import ( + CreateProjectMetadata, + CreateProjectRequest, + DeleteProjectMetadata, + DeleteProjectRequest, + GetProjectRequest, + ListProjectsRequest, + ListProjectsResponse, + MoveProjectMetadata, + MoveProjectRequest, + Project, + SearchProjectsRequest, + SearchProjectsResponse, + UndeleteProjectMetadata, + UndeleteProjectRequest, + UpdateProjectMetadata, + UpdateProjectRequest, +) +from .tag_bindings import ( + CreateTagBindingMetadata, + CreateTagBindingRequest, + DeleteTagBindingMetadata, + DeleteTagBindingRequest, + ListTagBindingsRequest, + ListTagBindingsResponse, + TagBinding, +) +from .tag_keys import ( + CreateTagKeyMetadata, + CreateTagKeyRequest, + DeleteTagKeyMetadata, + DeleteTagKeyRequest, + GetTagKeyRequest, + ListTagKeysRequest, + ListTagKeysResponse, + TagKey, + UpdateTagKeyMetadata, + UpdateTagKeyRequest, +) +from .tag_values import ( + CreateTagValueMetadata, + CreateTagValueRequest, + DeleteTagValueMetadata, + DeleteTagValueRequest, + GetTagValueRequest, + ListTagValuesRequest, + ListTagValuesResponse, + TagValue, + UpdateTagValueMetadata, + UpdateTagValueRequest, +) + +__all__ = ( + "CreateFolderMetadata", + "CreateFolderRequest", + "DeleteFolderMetadata", + "DeleteFolderRequest", + "Folder", + "GetFolderRequest", + "ListFoldersRequest", + "ListFoldersResponse", + "MoveFolderMetadata", + "MoveFolderRequest", + "SearchFoldersRequest", + "SearchFoldersResponse", + "UndeleteFolderMetadata", + "UndeleteFolderRequest", + "UpdateFolderMetadata", + "UpdateFolderRequest", + "DeleteOrganizationMetadata", + "GetOrganizationRequest", + "Organization", + "SearchOrganizationsRequest", + "SearchOrganizationsResponse", + "UndeleteOrganizationMetadata", + "CreateProjectMetadata", + "CreateProjectRequest", + "DeleteProjectMetadata", + "DeleteProjectRequest", + "GetProjectRequest", + "ListProjectsRequest", + "ListProjectsResponse", + "MoveProjectMetadata", + "MoveProjectRequest", + "Project", + "SearchProjectsRequest", + "SearchProjectsResponse", + "UndeleteProjectMetadata", + "UndeleteProjectRequest", + "UpdateProjectMetadata", + "UpdateProjectRequest", + "CreateTagBindingMetadata", + "CreateTagBindingRequest", + "DeleteTagBindingMetadata", + "DeleteTagBindingRequest", + "ListTagBindingsRequest", + "ListTagBindingsResponse", + "TagBinding", + "CreateTagKeyMetadata", + "CreateTagKeyRequest", + "DeleteTagKeyMetadata", + "DeleteTagKeyRequest", + "GetTagKeyRequest", + "ListTagKeysRequest", + "ListTagKeysResponse", + "TagKey", + "UpdateTagKeyMetadata", + "UpdateTagKeyRequest", + "CreateTagValueMetadata", + "CreateTagValueRequest", + "DeleteTagValueMetadata", + "DeleteTagValueRequest", + "GetTagValueRequest", + "ListTagValuesRequest", + "ListTagValuesResponse", + "TagValue", + "UpdateTagValueMetadata", + "UpdateTagValueRequest", +) diff --git a/google/cloud/resourcemanager_v3/types/folders.py b/google/cloud/resourcemanager_v3/types/folders.py new file mode 100644 index 0000000..79a43c8 --- /dev/null +++ b/google/cloud/resourcemanager_v3/types/folders.py @@ -0,0 +1,374 @@ +# -*- 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. +# +import proto # type: ignore + +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.resourcemanager.v3", + manifest={ + "Folder", + "GetFolderRequest", + "ListFoldersRequest", + "ListFoldersResponse", + "SearchFoldersRequest", + "SearchFoldersResponse", + "CreateFolderRequest", + "CreateFolderMetadata", + "UpdateFolderRequest", + "UpdateFolderMetadata", + "MoveFolderRequest", + "MoveFolderMetadata", + "DeleteFolderRequest", + "DeleteFolderMetadata", + "UndeleteFolderRequest", + "UndeleteFolderMetadata", + }, +) + + +class Folder(proto.Message): + r"""A folder in an organization's resource hierarchy, used to + organize that organization's resources. + + Attributes: + name (str): + Output only. The resource name of the folder. Its format is + ``folders/{folder_id}``, for example: "folders/1234". + parent (str): + Required. The folder's parent's resource name. Updates to + the folder's parent must be performed using + [MoveFolder][google.cloud.resourcemanager.v3.Folders.MoveFolder]. + display_name (str): + The folder's display name. A folder's display name must be + unique amongst its siblings. For example, no two folders + with the same parent can share the same display name. The + display name must start and end with a letter or digit, may + contain letters, digits, spaces, hyphens and underscores and + can be no longer than 30 characters. This is captured by the + regular expression: + ``[\p{L}\p{N}]([\p{L}\p{N}_- ]{0,28}[\p{L}\p{N}])?``. + state (google.cloud.resourcemanager_v3.types.Folder.State): + Output only. The lifecycle state of the folder. Updates to + the state must be performed using + [DeleteFolder][google.cloud.resourcemanager.v3.Folders.DeleteFolder] + and + [UndeleteFolder][google.cloud.resourcemanager.v3.Folders.UndeleteFolder]. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Timestamp when the folder was + created. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Timestamp when the folder was + last modified. + delete_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Timestamp when the folder was + requested to be deleted. + etag (str): + Output only. A checksum computed by the + server based on the current value of the folder + resource. This may be sent on update and delete + requests to ensure the client has an up-to-date + value before proceeding. + """ + + class State(proto.Enum): + r"""Folder lifecycle states.""" + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + DELETE_REQUESTED = 2 + + name = proto.Field(proto.STRING, number=1,) + parent = proto.Field(proto.STRING, number=2,) + display_name = proto.Field(proto.STRING, number=3,) + state = proto.Field(proto.ENUM, number=4, enum=State,) + create_time = proto.Field(proto.MESSAGE, number=5, message=timestamp_pb2.Timestamp,) + update_time = proto.Field(proto.MESSAGE, number=6, message=timestamp_pb2.Timestamp,) + delete_time = proto.Field(proto.MESSAGE, number=7, message=timestamp_pb2.Timestamp,) + etag = proto.Field(proto.STRING, number=8,) + + +class GetFolderRequest(proto.Message): + r"""The GetFolder request message. + + Attributes: + name (str): + Required. The resource name of the folder to retrieve. Must + be of the form ``folders/{folder_id}``. + """ + + name = proto.Field(proto.STRING, number=1,) + + +class ListFoldersRequest(proto.Message): + r"""The ListFolders request message. + + Attributes: + parent (str): + Required. The resource name of the organization or folder + whose folders are being listed. Must be of the form + ``folders/{folder_id}`` or ``organizations/{org_id}``. + Access to this method is controlled by checking the + ``resourcemanager.folders.list`` permission on the + ``parent``. + page_size (int): + Optional. The maximum number of folders to + return in the response. If unspecified, server + picks an appropriate default. + page_token (str): + Optional. A pagination token returned from a previous call + to ``ListFolders`` that indicates where this listing should + continue from. + show_deleted (bool): + Optional. Controls whether folders in the + [DELETE_REQUESTED][google.cloud.resourcemanager.v3.Folder.State.DELETE_REQUESTED] + state should be returned. Defaults to false. + """ + + parent = proto.Field(proto.STRING, number=1,) + page_size = proto.Field(proto.INT32, number=2,) + page_token = proto.Field(proto.STRING, number=3,) + show_deleted = proto.Field(proto.BOOL, number=4,) + + +class ListFoldersResponse(proto.Message): + r"""The ListFolders response message. + + Attributes: + folders (Sequence[google.cloud.resourcemanager_v3.types.Folder]): + A possibly paginated list of folders that are + direct descendants of the specified parent + resource. + next_page_token (str): + A pagination token returned from a previous call to + ``ListFolders`` that indicates from where listing should + continue. + """ + + @property + def raw_page(self): + return self + + folders = proto.RepeatedField(proto.MESSAGE, number=1, message="Folder",) + next_page_token = proto.Field(proto.STRING, number=2,) + + +class SearchFoldersRequest(proto.Message): + r"""The request message for searching folders. + + Attributes: + page_size (int): + Optional. The maximum number of folders to + return in the response. If unspecified, server + picks an appropriate default. + page_token (str): + Optional. A pagination token returned from a previous call + to ``SearchFolders`` that indicates from where search should + continue. + query (str): + Optional. Search criteria used to select the folders to + return. If no search criteria is specified then all + accessible folders will be returned. + + Query expressions can be used to restrict results based upon + displayName, state and parent, where the operators ``=`` + (``:``) ``NOT``, ``AND`` and ``OR`` can be used along with + the suffix wildcard symbol ``*``. + + The ``displayName`` field in a query expression should use + escaped quotes for values that include whitespace to prevent + unexpected behavior. + + \| Field \| Description \| + \|-------------------------\|----------------------------------------\| + \| displayName \| Filters by displayName. \| \| parent \| + Filters by parent (for example: folders/123). \| \| state, + lifecycleState \| Filters by state. \| + + Some example queries are: + + - Query ``displayName=Test*`` returns Folder resources + whose display name starts with "Test". + - Query ``state=ACTIVE`` returns Folder resources with + ``state`` set to ``ACTIVE``. + - Query ``parent=folders/123`` returns Folder resources + that have ``folders/123`` as a parent resource. + - Query ``parent=folders/123 AND state=ACTIVE`` returns + active Folder resources that have ``folders/123`` as a + parent resource. + - Query ``displayName=\\"Test String\\"`` returns Folder + resources with display names that include both "Test" and + "String". + """ + + page_size = proto.Field(proto.INT32, number=1,) + page_token = proto.Field(proto.STRING, number=2,) + query = proto.Field(proto.STRING, number=3,) + + +class SearchFoldersResponse(proto.Message): + r"""The response message for searching folders. + + Attributes: + folders (Sequence[google.cloud.resourcemanager_v3.types.Folder]): + A possibly paginated folder search results. + the specified parent resource. + next_page_token (str): + A pagination token returned from a previous call to + ``SearchFolders`` that indicates from where searching should + continue. + """ + + @property + def raw_page(self): + return self + + folders = proto.RepeatedField(proto.MESSAGE, number=1, message="Folder",) + next_page_token = proto.Field(proto.STRING, number=2,) + + +class CreateFolderRequest(proto.Message): + r"""The CreateFolder request message. + + Attributes: + folder (google.cloud.resourcemanager_v3.types.Folder): + Required. The folder being created, only the + display name and parent will be consulted. All + other fields will be ignored. + """ + + folder = proto.Field(proto.MESSAGE, number=2, message="Folder",) + + +class CreateFolderMetadata(proto.Message): + r"""Metadata pertaining to the Folder creation process. + + Attributes: + display_name (str): + The display name of the folder. + parent (str): + The resource name of the folder or + organization we are creating the folder under. + """ + + display_name = proto.Field(proto.STRING, number=1,) + parent = proto.Field(proto.STRING, number=2,) + + +class UpdateFolderRequest(proto.Message): + r"""The request sent to the + [UpdateFolder][google.cloud.resourcemanager.v3.Folder.UpdateFolder] + method. + + Only the ``display_name`` field can be changed. All other fields + will be ignored. Use the + [MoveFolder][google.cloud.resourcemanager.v3.Folders.MoveFolder] + method to change the ``parent`` field. + + Attributes: + folder (google.cloud.resourcemanager_v3.types.Folder): + Required. The new definition of the Folder. It must include + the ``name`` field, which cannot be changed. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. Fields to be updated. Only the ``display_name`` + can be updated. + """ + + folder = proto.Field(proto.MESSAGE, number=1, message="Folder",) + update_mask = proto.Field( + proto.MESSAGE, number=2, message=field_mask_pb2.FieldMask, + ) + + +class UpdateFolderMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + Operation returned by UpdateFolder. + """ + + +class MoveFolderRequest(proto.Message): + r"""The MoveFolder request message. + + Attributes: + name (str): + Required. The resource name of the Folder to move. Must be + of the form folders/{folder_id} + destination_parent (str): + Required. The resource name of the folder or organization + which should be the folder's new parent. Must be of the form + ``folders/{folder_id}`` or ``organizations/{org_id}``. + """ + + name = proto.Field(proto.STRING, number=1,) + destination_parent = proto.Field(proto.STRING, number=2,) + + +class MoveFolderMetadata(proto.Message): + r"""Metadata pertaining to the folder move process. + + Attributes: + display_name (str): + The display name of the folder. + source_parent (str): + The resource name of the folder's parent. + destination_parent (str): + The resource name of the folder or + organization to move the folder to. + """ + + display_name = proto.Field(proto.STRING, number=1,) + source_parent = proto.Field(proto.STRING, number=2,) + destination_parent = proto.Field(proto.STRING, number=3,) + + +class DeleteFolderRequest(proto.Message): + r"""The DeleteFolder request message. + + Attributes: + name (str): + Required. The resource name of the folder to be deleted. + Must be of the form ``folders/{folder_id}``. + """ + + name = proto.Field(proto.STRING, number=1,) + + +class DeleteFolderMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + ``Operation`` returned by ``DeleteFolder``. + """ + + +class UndeleteFolderRequest(proto.Message): + r"""The UndeleteFolder request message. + + Attributes: + name (str): + Required. The resource name of the folder to undelete. Must + be of the form ``folders/{folder_id}``. + """ + + name = proto.Field(proto.STRING, number=1,) + + +class UndeleteFolderMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + ``Operation`` returned by ``UndeleteFolder``. + """ + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/resourcemanager_v3/types/organizations.py b/google/cloud/resourcemanager_v3/types/organizations.py new file mode 100644 index 0000000..008eb1d --- /dev/null +++ b/google/cloud/resourcemanager_v3/types/organizations.py @@ -0,0 +1,183 @@ +# -*- 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. +# +import proto # type: ignore + +from google.protobuf import timestamp_pb2 # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.resourcemanager.v3", + manifest={ + "Organization", + "GetOrganizationRequest", + "SearchOrganizationsRequest", + "SearchOrganizationsResponse", + "DeleteOrganizationMetadata", + "UndeleteOrganizationMetadata", + }, +) + + +class Organization(proto.Message): + r"""The root node in the resource hierarchy to which a particular + entity's (a company, for example) resources belong. + + Attributes: + name (str): + Output only. The resource name of the organization. This is + the organization's relative path in the API. Its format is + "organizations/[organization_id]". For example, + "organizations/1234". + display_name (str): + Output only. A human-readable string that + refers to the organization in the Google Cloud + Console. This string is set by the server and + cannot be changed. The string will be set to the + primary domain (for example, "google.com") of + the Google Workspace customer that owns the + organization. + directory_customer_id (str): + Immutable. The G Suite / Workspace customer + id used in the Directory API. + state (google.cloud.resourcemanager_v3.types.Organization.State): + Output only. The organization's current + lifecycle state. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Timestamp when the Organization + was created. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Timestamp when the Organization + was last modified. + delete_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Timestamp when the Organization + was requested for deletion. + etag (str): + Output only. A checksum computed by the + server based on the current value of the + Organization resource. This may be sent on + update and delete requests to ensure the client + has an up-to-date value before proceeding. + """ + + class State(proto.Enum): + r"""Organization lifecycle states.""" + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + DELETE_REQUESTED = 2 + + name = proto.Field(proto.STRING, number=1,) + display_name = proto.Field(proto.STRING, number=2,) + directory_customer_id = proto.Field(proto.STRING, number=3, oneof="owner",) + state = proto.Field(proto.ENUM, number=4, enum=State,) + create_time = proto.Field(proto.MESSAGE, number=5, message=timestamp_pb2.Timestamp,) + update_time = proto.Field(proto.MESSAGE, number=6, message=timestamp_pb2.Timestamp,) + delete_time = proto.Field(proto.MESSAGE, number=7, message=timestamp_pb2.Timestamp,) + etag = proto.Field(proto.STRING, number=8,) + + +class GetOrganizationRequest(proto.Message): + r"""The request sent to the ``GetOrganization`` method. The ``name`` + field is required. ``organization_id`` is no longer accepted. + + Attributes: + name (str): + Required. The resource name of the Organization to fetch. + This is the organization's relative path in the API, + formatted as "organizations/[organizationId]". For example, + "organizations/1234". + """ + + name = proto.Field(proto.STRING, number=1,) + + +class SearchOrganizationsRequest(proto.Message): + r"""The request sent to the ``SearchOrganizations`` method. + Attributes: + page_size (int): + Optional. The maximum number of organizations + to return in the response. If unspecified, + server picks an appropriate default. + page_token (str): + Optional. A pagination token returned from a previous call + to ``SearchOrganizations`` that indicates from where listing + should continue. + query (str): + Optional. An optional query string used to filter the + Organizations to return in the response. Query rules are + case-insensitive. + + \| Field \| Description \| + \|------------------\|--------------------------------------------\| + \| directoryCustomerId, owner.directoryCustomerId \| Filters + by directory customer id. \| \| domain \| Filters by domain. + \| + + Organizations may be queried by ``directoryCustomerId`` or + by ``domain``, where the domain is a G Suite domain, for + example: + + - Query ``directorycustomerid:123456789`` returns + Organization resources with + ``owner.directory_customer_id`` equal to ``123456789``. + - Query ``domain:google.com`` returns Organization + resources corresponding to the domain ``google.com``. + """ + + page_size = proto.Field(proto.INT32, number=1,) + page_token = proto.Field(proto.STRING, number=2,) + query = proto.Field(proto.STRING, number=3,) + + +class SearchOrganizationsResponse(proto.Message): + r"""The response returned from the ``SearchOrganizations`` method. + Attributes: + organizations (Sequence[google.cloud.resourcemanager_v3.types.Organization]): + The list of Organizations that matched the + search query, possibly paginated. + next_page_token (str): + A pagination token to be used to retrieve the + next page of results. If the result is too large + to fit within the page size specified in the + request, this field will be set with a token + that can be used to fetch the next page of + results. If this field is empty, it indicates + that this response contains the last page of + results. + """ + + @property + def raw_page(self): + return self + + organizations = proto.RepeatedField( + proto.MESSAGE, number=1, message="Organization", + ) + next_page_token = proto.Field(proto.STRING, number=2,) + + +class DeleteOrganizationMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + operation returned by DeleteOrganization. + """ + + +class UndeleteOrganizationMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + Operation returned by UndeleteOrganization. + """ + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/resourcemanager_v3/types/projects.py b/google/cloud/resourcemanager_v3/types/projects.py new file mode 100644 index 0000000..8a1c934 --- /dev/null +++ b/google/cloud/resourcemanager_v3/types/projects.py @@ -0,0 +1,454 @@ +# -*- 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. +# +import proto # type: ignore + +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.resourcemanager.v3", + manifest={ + "Project", + "GetProjectRequest", + "ListProjectsRequest", + "ListProjectsResponse", + "SearchProjectsRequest", + "SearchProjectsResponse", + "CreateProjectRequest", + "CreateProjectMetadata", + "UpdateProjectRequest", + "UpdateProjectMetadata", + "MoveProjectRequest", + "MoveProjectMetadata", + "DeleteProjectRequest", + "DeleteProjectMetadata", + "UndeleteProjectRequest", + "UndeleteProjectMetadata", + }, +) + + +class Project(proto.Message): + r"""A project is a high-level Google Cloud entity. It is a + container for ACLs, APIs, App Engine Apps, VMs, and other Google + Cloud Platform resources. + + Attributes: + name (str): + Output only. The unique resource name of the project. It is + an int64 generated number prefixed by "projects/". + + Example: ``projects/415104041262`` + parent (str): + Optional. A reference to a parent Resource. eg., + ``organizations/123`` or ``folders/876``. + project_id (str): + Immutable. The unique, user-assigned id of the project. It + must be 6 to 30 lowercase ASCII letters, digits, or hyphens. + It must start with a letter. Trailing hyphens are + prohibited. + + Example: ``tokyo-rain-123`` + state (google.cloud.resourcemanager_v3.types.Project.State): + Output only. The project lifecycle state. + display_name (str): + Optional. A user-assigned display name of the project. When + present it must be between 4 to 30 characters. Allowed + characters are: lowercase and uppercase letters, numbers, + hyphen, single-quote, double-quote, space, and exclamation + point. + + Example: ``My Project`` + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Creation time. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The most recent time this + resource was modified. + delete_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time at which this resource + was requested for deletion. + etag (str): + Output only. A checksum computed by the + server based on the current value of the Project + resource. This may be sent on update and delete + requests to ensure the client has an up-to-date + value before proceeding. + labels (Sequence[google.cloud.resourcemanager_v3.types.Project.LabelsEntry]): + Optional. The labels associated with this project. + + Label keys must be between 1 and 63 characters long and must + conform to the following regular expression: + [a-z]([-a-z0-9]*[a-z0-9])?. + + Label values must be between 0 and 63 characters long and + must conform to the regular expression + ([a-z]([-a-z0-9]*[a-z0-9])?)?. + + No more than 256 labels can be associated with a given + resource. + + Clients should store labels in a representation such as JSON + that does not depend on specific characters being + disallowed. + + Example: ``"myBusinessDimension" : "businessValue"`` + """ + + class State(proto.Enum): + r"""Project lifecycle states.""" + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + DELETE_REQUESTED = 2 + + name = proto.Field(proto.STRING, number=1,) + parent = proto.Field(proto.STRING, number=2,) + project_id = proto.Field(proto.STRING, number=3,) + state = proto.Field(proto.ENUM, number=4, enum=State,) + display_name = proto.Field(proto.STRING, number=5,) + create_time = proto.Field(proto.MESSAGE, number=6, message=timestamp_pb2.Timestamp,) + update_time = proto.Field(proto.MESSAGE, number=7, message=timestamp_pb2.Timestamp,) + delete_time = proto.Field(proto.MESSAGE, number=8, message=timestamp_pb2.Timestamp,) + etag = proto.Field(proto.STRING, number=9,) + labels = proto.MapField(proto.STRING, proto.STRING, number=10,) + + +class GetProjectRequest(proto.Message): + r"""The request sent to the + [GetProject][google.cloud.resourcemanager.v3.Projects.GetProject] + method. + + Attributes: + name (str): + Required. The name of the project (for example, + ``projects/415104041262``). + """ + + name = proto.Field(proto.STRING, number=1,) + + +class ListProjectsRequest(proto.Message): + r"""The request sent to the + [ListProjects][google.cloud.resourcemanager.v3.Projects.ListProjects] + method. + + Attributes: + parent (str): + Required. The name of the parent resource to + list projects under. + For example, setting this field to + 'folders/1234' would list all projects directly + under that folder. + page_token (str): + Optional. A pagination token returned from a previous call + to [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects] that + indicates from where listing should continue. + page_size (int): + Optional. The maximum number of projects to + return in the response. The server can return + fewer projects than requested. If unspecified, + server picks an appropriate default. + show_deleted (bool): + Optional. Indicate that projects in the ``DELETE_REQUESTED`` + state should also be returned. Normally only ``ACTIVE`` + projects are returned. + """ + + parent = proto.Field(proto.STRING, number=1,) + page_token = proto.Field(proto.STRING, number=2,) + page_size = proto.Field(proto.INT32, number=3,) + show_deleted = proto.Field(proto.BOOL, number=4,) + + +class ListProjectsResponse(proto.Message): + r"""A page of the response received from the + [ListProjects][google.cloud.resourcemanager.v3.Projects.ListProjects] + method. + + A paginated response where more pages are available has + ``next_page_token`` set. This token can be used in a subsequent + request to retrieve the next request page. + + NOTE: A response may contain fewer elements than the request + ``page_size`` and still have a ``next_page_token``. + + Attributes: + projects (Sequence[google.cloud.resourcemanager_v3.types.Project]): + The list of Projects under the parent. This + list can be paginated. + next_page_token (str): + Pagination token. + + If the result set is too large to fit in a single response, + this token is returned. It encodes the position of the + current result cursor. Feeding this value into a new list + request with the ``page_token`` parameter gives the next + page of the results. + + When ``next_page_token`` is not filled in, there is no next + page and the list returned is the last page in the result + set. + + Pagination tokens have a limited lifetime. + """ + + @property + def raw_page(self): + return self + + projects = proto.RepeatedField(proto.MESSAGE, number=1, message="Project",) + next_page_token = proto.Field(proto.STRING, number=2,) + + +class SearchProjectsRequest(proto.Message): + r"""The request sent to the + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + method. + + Attributes: + query (str): + Optional. A query string for searching for projects that the + caller has ``resourcemanager.projects.get`` permission to. + If multiple fields are included in the query, the it will + return results that match any of the fields. Some eligible + fields are: + + \| Field \| Description \| + \|-------------------------\|----------------------------------------------\| + \| displayName, name \| Filters by displayName. \| \| parent + \| Project's parent. (for example: folders/123, + organizations/*) Prefer parent field over parent.type and + parent.id. \| \| parent.type \| Parent's type: ``folder`` or + ``organization``. \| \| parent.id \| Parent's id number (for + example: 123) \| \| id, projectId \| Filters by projectId. + \| \| state, lifecycleState \| Filters by state. \| \| + labels \| Filters by label name or value. \| \| labels. + (where *key* is the name of a label) \| Filters by label + name. \| + + Search expressions are case insensitive. + + Some examples queries: + + \| Query \| Description \| + \|------------------\|-----------------------------------------------------\| + \| name:how\* \| The project's name starts with "how". \| \| + name:Howl \| The project's name is ``Howl`` or ``howl``. \| + \| name:HOWL \| Equivalent to above. \| \| NAME:howl \| + Equivalent to above. \| \| labels.color:\* \| The project + has the label ``color``. \| \| labels.color:red \| The + project's label ``color`` has the value ``red``. \| \| + labels.color:red labels.size:big \| The project's label + ``color`` has the value ``red`` and its label ``size`` has + the value ``big``. \| + + If no query is specified, the call will return projects for + which the user has the ``resourcemanager.projects.get`` + permission. + page_token (str): + Optional. A pagination token returned from a previous call + to [ListProjects] + [google.cloud.resourcemanager.v3.Projects.ListProjects] that + indicates from where listing should continue. + page_size (int): + Optional. The maximum number of projects to + return in the response. The server can return + fewer projects than requested. If unspecified, + server picks an appropriate default. + """ + + query = proto.Field(proto.STRING, number=1,) + page_token = proto.Field(proto.STRING, number=2,) + page_size = proto.Field(proto.INT32, number=3,) + + +class SearchProjectsResponse(proto.Message): + r"""A page of the response received from the + [SearchProjects][google.cloud.resourcemanager.v3.Projects.SearchProjects] + method. + + A paginated response where more pages are available has + ``next_page_token`` set. This token can be used in a subsequent + request to retrieve the next request page. + + Attributes: + projects (Sequence[google.cloud.resourcemanager_v3.types.Project]): + The list of Projects that matched the list + filter query. This list can be paginated. + next_page_token (str): + Pagination token. + + If the result set is too large to fit in a single response, + this token is returned. It encodes the position of the + current result cursor. Feeding this value into a new list + request with the ``page_token`` parameter gives the next + page of the results. + + When ``next_page_token`` is not filled in, there is no next + page and the list returned is the last page in the result + set. + + Pagination tokens have a limited lifetime. + """ + + @property + def raw_page(self): + return self + + projects = proto.RepeatedField(proto.MESSAGE, number=1, message="Project",) + next_page_token = proto.Field(proto.STRING, number=2,) + + +class CreateProjectRequest(proto.Message): + r"""The request sent to the + [CreateProject][google.cloud.resourcemanager.v3.Projects.CreateProject] + method. + + Attributes: + project (google.cloud.resourcemanager_v3.types.Project): + Required. The Project to create. + + Project ID is required. If the requested ID is unavailable, + the request fails. + + If the ``parent`` field is set, the + ``resourcemanager.projects.create`` permission is checked on + the parent resource. If no parent is set and the + authorization credentials belong to an Organziation, the + parent will be set to that Organization. + """ + + project = proto.Field(proto.MESSAGE, number=1, message="Project",) + + +class CreateProjectMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + Operation returned by CreateProject. It provides insight for when + significant phases of Project creation have completed. + + Attributes: + create_time (google.protobuf.timestamp_pb2.Timestamp): + Creation time of the project creation + workflow. + gettable (bool): + True if the project can be retrieved using ``GetProject``. + No other operations on the project are guaranteed to work + until the project creation is complete. + ready (bool): + True if the project creation process is + complete. + """ + + create_time = proto.Field(proto.MESSAGE, number=1, message=timestamp_pb2.Timestamp,) + gettable = proto.Field(proto.BOOL, number=2,) + ready = proto.Field(proto.BOOL, number=3,) + + +class UpdateProjectRequest(proto.Message): + r"""The request sent to the + [UpdateProject][google.cloud.resourcemanager.v3.Projects.UpdateProject] + method. + + Only the ``display_name`` and ``labels`` fields can be change. Use + the + [MoveProject][google.cloud.resourcemanager.v3.Projects.MoveProject] + method to change the ``parent`` field. + + Attributes: + project (google.cloud.resourcemanager_v3.types.Project): + Required. The new definition of the project. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. An update mask to selectively + update fields. + """ + + project = proto.Field(proto.MESSAGE, number=1, message="Project",) + update_mask = proto.Field( + proto.MESSAGE, number=2, message=field_mask_pb2.FieldMask, + ) + + +class UpdateProjectMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + Operation returned by UpdateProject. + """ + + +class MoveProjectRequest(proto.Message): + r"""The request sent to + [MoveProject][google.cloud.resourcemanager.v3.Projects.MoveProject] + method. + + Attributes: + name (str): + Required. The name of the project to move. + destination_parent (str): + Required. The new parent to move the Project + under. + """ + + name = proto.Field(proto.STRING, number=1,) + destination_parent = proto.Field(proto.STRING, number=2,) + + +class MoveProjectMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + Operation returned by MoveProject. + """ + + +class DeleteProjectRequest(proto.Message): + r"""[DeleteProject][google.cloud.resourcemanager.v3.Projects.DeleteProject] + method. + + Attributes: + name (str): + Required. The name of the Project (for example, + ``projects/415104041262``). + """ + + name = proto.Field(proto.STRING, number=1,) + + +class DeleteProjectMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + Operation returned by ``DeleteProject``. + """ + + +class UndeleteProjectRequest(proto.Message): + r"""The request sent to the [UndeleteProject] + [google.cloud.resourcemanager.v3.Projects.UndeleteProject] method. + + Attributes: + name (str): + Required. The name of the project (for example, + ``projects/415104041262``). + + Required. + """ + + name = proto.Field(proto.STRING, number=1,) + + +class UndeleteProjectMetadata(proto.Message): + r"""A status object which is used as the ``metadata`` field for the + Operation returned by ``UndeleteProject``. + """ + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/resourcemanager_v3/types/tag_bindings.py b/google/cloud/resourcemanager_v3/types/tag_bindings.py new file mode 100644 index 0000000..5ba5a20 --- /dev/null +++ b/google/cloud/resourcemanager_v3/types/tag_bindings.py @@ -0,0 +1,149 @@ +# -*- 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. +# +import proto # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.resourcemanager.v3", + manifest={ + "TagBinding", + "CreateTagBindingMetadata", + "CreateTagBindingRequest", + "DeleteTagBindingMetadata", + "DeleteTagBindingRequest", + "ListTagBindingsRequest", + "ListTagBindingsResponse", + }, +) + + +class TagBinding(proto.Message): + r"""A TagBinding represents a connection between a TagValue and a + cloud resource (currently project, folder, or organization). + Once a TagBinding is created, the TagValue is applied to all the + descendants of the cloud resource. + + Attributes: + name (str): + Output only. The name of the TagBinding. This is a String of + the form: + ``tagBindings/{full-resource-name}/{tag-value-name}`` (e.g. + ``tagBindings/%2F%2Fcloudresourcemanager.googleapis.com%2Fprojects%2F123/tagValues/456``). + parent (str): + The full resource name of the resource the TagValue is bound + to. E.g. + ``//cloudresourcemanager.googleapis.com/projects/123`` + tag_value (str): + The TagValue of the TagBinding. Must be of the form + ``tagValues/456``. + """ + + name = proto.Field(proto.STRING, number=1,) + parent = proto.Field(proto.STRING, number=2,) + tag_value = proto.Field(proto.STRING, number=3,) + + +class CreateTagBindingMetadata(proto.Message): + r"""Runtime operation information for creating a TagValue. """ + + +class CreateTagBindingRequest(proto.Message): + r"""The request message to create a TagBinding. + Attributes: + tag_binding (google.cloud.resourcemanager_v3.types.TagBinding): + Required. The TagBinding to be created. + validate_only (bool): + Optional. Set to true to perform the + validations necessary for creating the resource, + but not actually perform the action. + """ + + tag_binding = proto.Field(proto.MESSAGE, number=1, message="TagBinding",) + validate_only = proto.Field(proto.BOOL, number=2,) + + +class DeleteTagBindingMetadata(proto.Message): + r"""Runtime operation information for deleting a TagBinding. """ + + +class DeleteTagBindingRequest(proto.Message): + r"""The request message to delete a TagBinding. + Attributes: + name (str): + Required. The name of the TagBinding. This is a String of + the form: ``tagBindings/{id}`` (e.g. + ``tagBindings/%2F%2Fcloudresourcemanager.googleapis.com%2Fprojects%2F123/tagValues/456``). + """ + + name = proto.Field(proto.STRING, number=1,) + + +class ListTagBindingsRequest(proto.Message): + r"""The request message to list all TagBindings for a parent. + Attributes: + parent (str): + Required. The full resource name of a + resource for which you want to list existing + TagBindings. E.g. + "//cloudresourcemanager.googleapis.com/projects/123". + page_size (int): + Optional. The maximum number of TagBindings + to return in the response. The server allows a + maximum of 300 TagBindings to return. If + unspecified, the server will use 100 as the + default. + page_token (str): + Optional. A pagination token returned from a previous call + to ``ListTagBindings`` that indicates where this listing + should continue from. + """ + + parent = proto.Field(proto.STRING, number=1,) + page_size = proto.Field(proto.INT32, number=2,) + page_token = proto.Field(proto.STRING, number=3,) + + +class ListTagBindingsResponse(proto.Message): + r"""The ListTagBindings response. + Attributes: + tag_bindings (Sequence[google.cloud.resourcemanager_v3.types.TagBinding]): + A possibly paginated list of TagBindings for + the specified TagValue or resource. + next_page_token (str): + Pagination token. + + If the result set is too large to fit in a single response, + this token is returned. It encodes the position of the + current result cursor. Feeding this value into a new list + request with the ``page_token`` parameter gives the next + page of the results. + + When ``next_page_token`` is not filled in, there is no next + page and the list returned is the last page in the result + set. + + Pagination tokens have a limited lifetime. + """ + + @property + def raw_page(self): + return self + + tag_bindings = proto.RepeatedField(proto.MESSAGE, number=1, message="TagBinding",) + next_page_token = proto.Field(proto.STRING, number=2,) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/resourcemanager_v3/types/tag_keys.py b/google/cloud/resourcemanager_v3/types/tag_keys.py new file mode 100644 index 0000000..6f34e7b --- /dev/null +++ b/google/cloud/resourcemanager_v3/types/tag_keys.py @@ -0,0 +1,219 @@ +# -*- 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. +# +import proto # type: ignore + +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.resourcemanager.v3", + manifest={ + "TagKey", + "ListTagKeysRequest", + "ListTagKeysResponse", + "GetTagKeyRequest", + "CreateTagKeyRequest", + "CreateTagKeyMetadata", + "UpdateTagKeyRequest", + "UpdateTagKeyMetadata", + "DeleteTagKeyRequest", + "DeleteTagKeyMetadata", + }, +) + + +class TagKey(proto.Message): + r"""A TagKey, used to group a set of TagValues. + Attributes: + name (str): + Immutable. The resource name for a TagKey. Must be in the + format ``tagKeys/{tag_key_id}``, where ``tag_key_id`` is the + generated numeric id for the TagKey. + parent (str): + Immutable. The resource name of the new TagKey's parent. + Must be of the form ``organizations/{org_id}``. + short_name (str): + Required. Immutable. The user friendly name for a TagKey. + The short name should be unique for TagKeys within the same + tag namespace. + + The short name must be 1-63 characters, beginning and ending + with an alphanumeric character ([a-z0-9A-Z]) with dashes + (-), underscores (_), dots (.), and alphanumerics between. + namespaced_name (str): + Output only. Immutable. Namespaced name of + the TagKey. + description (str): + Optional. User-assigned description of the + TagKey. Must not exceed 256 characters. + Read-write. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Creation time. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Update time. + etag (str): + Optional. Entity tag which users can pass to + prevent race conditions. This field is always + set in server responses. See UpdateTagKeyRequest + for details. + """ + + name = proto.Field(proto.STRING, number=1,) + parent = proto.Field(proto.STRING, number=2,) + short_name = proto.Field(proto.STRING, number=3,) + namespaced_name = proto.Field(proto.STRING, number=4,) + description = proto.Field(proto.STRING, number=5,) + create_time = proto.Field(proto.MESSAGE, number=6, message=timestamp_pb2.Timestamp,) + update_time = proto.Field(proto.MESSAGE, number=7, message=timestamp_pb2.Timestamp,) + etag = proto.Field(proto.STRING, number=8,) + + +class ListTagKeysRequest(proto.Message): + r"""The request message for listing all TagKeys under a parent + resource. + + Attributes: + parent (str): + Required. The resource name of the new TagKey's parent. Must + be of the form ``folders/{folder_id}`` or + ``organizations/{org_id}``. + page_size (int): + Optional. The maximum number of TagKeys to + return in the response. The server allows a + maximum of 300 TagKeys to return. If + unspecified, the server will use 100 as the + default. + page_token (str): + Optional. A pagination token returned from a previous call + to ``ListTagKey`` that indicates where this listing should + continue from. + """ + + parent = proto.Field(proto.STRING, number=1,) + page_size = proto.Field(proto.INT32, number=2,) + page_token = proto.Field(proto.STRING, number=3,) + + +class ListTagKeysResponse(proto.Message): + r"""The ListTagKeys response message. + Attributes: + tag_keys (Sequence[google.cloud.resourcemanager_v3.types.TagKey]): + List of TagKeys that live under the specified + parent in the request. + next_page_token (str): + A pagination token returned from a previous call to + ``ListTagKeys`` that indicates from where listing should + continue. + """ + + @property + def raw_page(self): + return self + + tag_keys = proto.RepeatedField(proto.MESSAGE, number=1, message="TagKey",) + next_page_token = proto.Field(proto.STRING, number=2,) + + +class GetTagKeyRequest(proto.Message): + r"""The request message for getting a TagKey. + Attributes: + name (str): + Required. A resource name in the format ``tagKeys/{id}``, + such as ``tagKeys/123``. + """ + + name = proto.Field(proto.STRING, number=1,) + + +class CreateTagKeyRequest(proto.Message): + r"""The request message for creating a TagKey. + Attributes: + tag_key (google.cloud.resourcemanager_v3.types.TagKey): + Required. The TagKey to be created. Only fields + ``short_name``, ``description``, and ``parent`` are + considered during the creation request. + validate_only (bool): + Optional. Set to true to perform validations + necessary for creating the resource, but not + actually perform the action. + """ + + tag_key = proto.Field(proto.MESSAGE, number=1, message="TagKey",) + validate_only = proto.Field(proto.BOOL, number=2,) + + +class CreateTagKeyMetadata(proto.Message): + r"""Runtime operation information for creating a TagKey. """ + + +class UpdateTagKeyRequest(proto.Message): + r"""The request message for updating a TagKey. + Attributes: + tag_key (google.cloud.resourcemanager_v3.types.TagKey): + Required. The new definition of the TagKey. Only the + ``description`` and ``etag`` fields can be updated by this + request. If the ``etag`` field is not empty, it must match + the ``etag`` field of the existing tag key. Otherwise, + ``FAILED_PRECONDITION`` will be returned. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Fields to be updated. The mask may only contain + ``description`` or ``etag``. If omitted entirely, both + ``description`` and ``etag`` are assumed to be significant. + validate_only (bool): + Set as true to perform validations necessary + for updating the resource, but not actually + perform the action. + """ + + tag_key = proto.Field(proto.MESSAGE, number=1, message="TagKey",) + update_mask = proto.Field( + proto.MESSAGE, number=2, message=field_mask_pb2.FieldMask, + ) + validate_only = proto.Field(proto.BOOL, number=3,) + + +class UpdateTagKeyMetadata(proto.Message): + r"""Runtime operation information for updating a TagKey. """ + + +class DeleteTagKeyRequest(proto.Message): + r"""The request message for deleting a TagKey. + Attributes: + name (str): + Required. The resource name of a TagKey to be deleted in the + format ``tagKeys/123``. The TagKey cannot be a parent of any + existing TagValues or it will not be deleted successfully. + validate_only (bool): + Optional. Set as true to perform validations + necessary for deletion, but not actually perform + the action. + etag (str): + Optional. The etag known to the client for + the expected state of the TagKey. This is to be + used for optimistic concurrency. + """ + + name = proto.Field(proto.STRING, number=1,) + validate_only = proto.Field(proto.BOOL, number=2,) + etag = proto.Field(proto.STRING, number=3,) + + +class DeleteTagKeyMetadata(proto.Message): + r"""Runtime operation information for deleting a TagKey. """ + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/resourcemanager_v3/types/tag_values.py b/google/cloud/resourcemanager_v3/types/tag_values.py new file mode 100644 index 0000000..9618ea6 --- /dev/null +++ b/google/cloud/resourcemanager_v3/types/tag_values.py @@ -0,0 +1,222 @@ +# -*- 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. +# +import proto # type: ignore + +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.resourcemanager.v3", + manifest={ + "TagValue", + "ListTagValuesRequest", + "ListTagValuesResponse", + "GetTagValueRequest", + "CreateTagValueRequest", + "CreateTagValueMetadata", + "UpdateTagValueRequest", + "UpdateTagValueMetadata", + "DeleteTagValueRequest", + "DeleteTagValueMetadata", + }, +) + + +class TagValue(proto.Message): + r"""A TagValue is a child of a particular TagKey. This is used to + group cloud resources for the purpose of controlling them using + policies. + + Attributes: + name (str): + Immutable. Resource name for TagValue in the format + ``tagValues/456``. + parent (str): + Immutable. The resource name of the new TagValue's parent + TagKey. Must be of the form ``tagKeys/{tag_key_id}``. + short_name (str): + Required. Immutable. User-assigned short name for TagValue. + The short name should be unique for TagValues within the + same parent TagKey. + + The short name must be 63 characters or less, beginning and + ending with an alphanumeric character ([a-z0-9A-Z]) with + dashes (-), underscores (_), dots (.), and alphanumerics + between. + namespaced_name (str): + Output only. Namespaced name of the TagValue. Must be in the + format + ``{organization_id}/{tag_key_short_name}/{short_name}``. + description (str): + Optional. User-assigned description of the + TagValue. Must not exceed 256 characters. + + Read-write. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Creation time. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Update time. + etag (str): + Optional. Entity tag which users can pass to + prevent race conditions. This field is always + set in server responses. See + UpdateTagValueRequest for details. + """ + + name = proto.Field(proto.STRING, number=1,) + parent = proto.Field(proto.STRING, number=2,) + short_name = proto.Field(proto.STRING, number=3,) + namespaced_name = proto.Field(proto.STRING, number=4,) + description = proto.Field(proto.STRING, number=5,) + create_time = proto.Field(proto.MESSAGE, number=6, message=timestamp_pb2.Timestamp,) + update_time = proto.Field(proto.MESSAGE, number=7, message=timestamp_pb2.Timestamp,) + etag = proto.Field(proto.STRING, number=8,) + + +class ListTagValuesRequest(proto.Message): + r"""The request message for listing TagValues for the specified + TagKey. + + Attributes: + parent (str): + Required. Resource name for TagKey, parent of the TagValues + to be listed, in the format ``tagKeys/123``. + page_size (int): + Optional. The maximum number of TagValues to + return in the response. The server allows a + maximum of 300 TagValues to return. If + unspecified, the server will use 100 as the + default. + page_token (str): + Optional. A pagination token returned from a previous call + to ``ListTagValues`` that indicates where this listing + should continue from. + """ + + parent = proto.Field(proto.STRING, number=1,) + page_size = proto.Field(proto.INT32, number=2,) + page_token = proto.Field(proto.STRING, number=3,) + + +class ListTagValuesResponse(proto.Message): + r"""The ListTagValues response. + Attributes: + tag_values (Sequence[google.cloud.resourcemanager_v3.types.TagValue]): + A possibly paginated list of TagValues that + are direct descendants of the specified parent + TagKey. + next_page_token (str): + A pagination token returned from a previous call to + ``ListTagValues`` that indicates from where listing should + continue. This is currently not used, but the server may at + any point start supplying a valid token. + """ + + @property + def raw_page(self): + return self + + tag_values = proto.RepeatedField(proto.MESSAGE, number=1, message="TagValue",) + next_page_token = proto.Field(proto.STRING, number=2,) + + +class GetTagValueRequest(proto.Message): + r"""The request message for getting a TagValue. + Attributes: + name (str): + Required. Resource name for TagValue to be fetched in the + format ``tagValues/456``. + """ + + name = proto.Field(proto.STRING, number=1,) + + +class CreateTagValueRequest(proto.Message): + r"""The request message for creating a TagValue. + Attributes: + tag_value (google.cloud.resourcemanager_v3.types.TagValue): + Required. The TagValue to be created. Only fields + ``short_name``, ``description``, and ``parent`` are + considered during the creation request. + validate_only (bool): + Optional. Set as true to perform the + validations necessary for creating the resource, + but not actually perform the action. + """ + + tag_value = proto.Field(proto.MESSAGE, number=1, message="TagValue",) + validate_only = proto.Field(proto.BOOL, number=2,) + + +class CreateTagValueMetadata(proto.Message): + r"""Runtime operation information for creating a TagValue. """ + + +class UpdateTagValueRequest(proto.Message): + r"""The request message for updating a TagValue. + Attributes: + tag_value (google.cloud.resourcemanager_v3.types.TagValue): + Required. The new definition of the TagValue. Only fields + ``description`` and ``etag`` fields can be updated by this + request. If the ``etag`` field is nonempty, it must match + the ``etag`` field of the existing ControlGroup. Otherwise, + ``FAILED_PRECONDITION`` will be returned. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. Fields to be updated. + validate_only (bool): + Optional. True to perform validations + necessary for updating the resource, but not + actually perform the action. + """ + + tag_value = proto.Field(proto.MESSAGE, number=1, message="TagValue",) + update_mask = proto.Field( + proto.MESSAGE, number=2, message=field_mask_pb2.FieldMask, + ) + validate_only = proto.Field(proto.BOOL, number=3,) + + +class UpdateTagValueMetadata(proto.Message): + r"""Runtime operation information for updating a TagValue. """ + + +class DeleteTagValueRequest(proto.Message): + r"""The request message for deleting a TagValue. + Attributes: + name (str): + Required. Resource name for TagValue to be + deleted in the format tagValues/456. + validate_only (bool): + Optional. Set as true to perform the + validations necessary for deletion, but not + actually perform the action. + etag (str): + Optional. The etag known to the client for + the expected state of the TagValue. This is to + be used for optimistic concurrency. + """ + + name = proto.Field(proto.STRING, number=1,) + validate_only = proto.Field(proto.BOOL, number=2,) + etag = proto.Field(proto.STRING, number=3,) + + +class DeleteTagValueMetadata(proto.Message): + r"""Runtime operation information for deleting a TagValue. """ + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..4505b48 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +python_version = 3.6 +namespace_packages = True diff --git a/noxfile.py b/noxfile.py index cf5218b..03aa2f5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -18,6 +18,7 @@ from __future__ import absolute_import import os +import pathlib import shutil import nox @@ -27,8 +28,24 @@ BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] DEFAULT_PYTHON_VERSION = "3.8" -SYSTEM_TEST_PYTHON_VERSIONS = ["2.7", "3.8"] -UNIT_TEST_PYTHON_VERSIONS = ["2.7", "3.6", "3.7", "3.8", "3.9"] +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) @@ -45,16 +62,9 @@ def lint(session): session.run("flake8", "google", "tests") -@nox.session(python="3.6") +@nox.session(python=DEFAULT_PYTHON_VERSION) def blacken(session): - """Run black. - - Format code to uniform standard. - - This currently uses Python 3.6 due to the automated Kokoro run of synthtool. - That run uses an image that doesn't have 3.6 installed. Before updating this - check the state of the `gcp_ubuntu_config` we use for that Kokoro run. - """ + """Run black. Format code to uniform standard.""" session.install(BLACK_VERSION) session.run( "black", *BLACK_PATHS, @@ -70,15 +80,21 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. - 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", @@ -99,15 +115,18 @@ 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") # Check the value of `RUN_SYSTEM_TESTS` env var. It defaults to true. if os.environ.get("RUN_SYSTEM_TESTS", "true") == "false": session.skip("RUN_SYSTEM_TESTS is set to false, skipping") - # 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) @@ -120,16 +139,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) @@ -140,7 +169,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=99") + session.run("coverage", "report", "--show-missing", "--fail-under=98") session.run("coverage", "erase") @@ -150,7 +179,7 @@ def docs(session): """Build the docs for this library.""" session.install("-e", ".") - session.install("sphinx", "alabaster", "recommonmark") + session.install("sphinx==4.0.1", "alabaster", "recommonmark") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( @@ -172,9 +201,9 @@ def docfx(session): """Build the docfx yaml files for this library.""" session.install("-e", ".") - # sphinx-docfx-yaml supports up to sphinx version 1.5.5. - # https://github.com/docascode/sphinx-docfx-yaml/issues/97 - session.install("sphinx==1.5.5", "alabaster", "recommonmark", "sphinx-docfx-yaml") + session.install( + "sphinx==4.0.1", "alabaster", "recommonmark", "gcp-sphinx-docfx-yaml" + ) shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( diff --git a/owlbot.py b/owlbot.py new file mode 100644 index 0000000..2b509af --- /dev/null +++ b/owlbot.py @@ -0,0 +1,78 @@ +# 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. + +"""This script is used to synthesize generated parts of this library.""" + +import synthtool as s +from synthtool import gcp + +common = gcp.CommonTemplates() + +default_version = "v3" + +for library in s.get_staging_dirs(default_version): + # Resolve sphinx warning `Inline substitution_reference start-string without end-string` + # Remove tables from docstring. Requested change upstream in cl/377766167 due to https://google.aip.dev/192#formatting. + s.replace( + [ + library / f"google/cloud/resourcemanager_{library.name}/services/folders/*client.py", + library / f"google/cloud/resourcemanager_{library.name}/types/folders.py" + ], + "-------------------------[|]----------------------------------------", + "-------------------------\|----------------------------------------" + ) + s.replace( + [ + library / f"google/cloud/resourcemanager_{library.name}/services/organizations/*client.py", + library / f"google/cloud/resourcemanager_{library.name}/types/organizations.py" + ], + "------------------[|]--------------------------------------------", + "------------------\|--------------------------------------------" + ) + s.replace( + [ + library / f"google/cloud/resourcemanager_{library.name}/services/projects/*client.py", + library / f"google/cloud/resourcemanager_{library.name}/types/projects.py" + ], + "-------------------------[|]----------------------------------------------", + "-------------------------\|----------------------------------------------" + ) + s.replace( + [ + library / f"google/cloud/resourcemanager_{library.name}/services/projects/*client.py", + library / f"google/cloud/resourcemanager_{library.name}/types/projects.py" + ], + "------------------[|]-----------------------------------------------------", + "------------------\|-----------------------------------------------------" + ) + + # Work around gapic generator bug https://github.com/googleapis/gapic-generator-python/issues/902 + s.replace(library / f"google/cloud/resourcemanager_{library.name}/types/folders.py", + r""". + Attributes:""", + r""".\n + Attributes:""", + ) + + s.move(library, excludes=["setup.py", "README.rst", "docs/index.rst"]) + +s.remove_staging_dirs() + +# ---------------------------------------------------------------------------- +# Add templated files +# ---------------------------------------------------------------------------- +templated_files = common.py_library(cov_level=98, microgenerator=True) +s.move(templated_files) + +s.shell.run(["nox", "-s", "blacken"], hide_output=False) diff --git a/renovate.json b/renovate.json index 4fa9493..c048955 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,9 @@ { "extends": [ "config:base", ":preserveSemverRanges" - ] + ], + "ignorePaths": [".pre-commit-config.yaml"], + "pip_requirements": { + "fileMatch": ["requirements-test.txt", "samples/[\\S/]*constraints.txt", "samples/[\\S/]*constraints-test.txt"] + } } diff --git a/scripts/fixup_resourcemanager_v3_keywords.py b/scripts/fixup_resourcemanager_v3_keywords.py new file mode 100644 index 0000000..1a2b9a3 --- /dev/null +++ b/scripts/fixup_resourcemanager_v3_keywords.py @@ -0,0 +1,209 @@ +#! /usr/bin/env python3 +# -*- 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. +# +import argparse +import os +import libcst as cst +import pathlib +import sys +from typing import (Any, Callable, Dict, List, Sequence, Tuple) + + +def partition( + predicate: Callable[[Any], bool], + iterator: Sequence[Any] +) -> Tuple[List[Any], List[Any]]: + """A stable, out-of-place partition.""" + results = ([], []) + + for i in iterator: + results[int(predicate(i))].append(i) + + # Returns trueList, falseList + return results[1], results[0] + + +class resourcemanagerCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'create_folder': ('folder', ), + 'create_project': ('project', ), + 'create_tag_binding': ('tag_binding', 'validate_only', ), + 'create_tag_key': ('tag_key', 'validate_only', ), + 'create_tag_value': ('tag_value', 'validate_only', ), + 'delete_folder': ('name', ), + 'delete_project': ('name', ), + 'delete_tag_binding': ('name', ), + 'delete_tag_key': ('name', 'validate_only', 'etag', ), + 'delete_tag_value': ('name', 'validate_only', 'etag', ), + 'get_folder': ('name', ), + 'get_iam_policy': ('resource', 'options', ), + 'get_organization': ('name', ), + 'get_project': ('name', ), + 'get_tag_key': ('name', ), + 'get_tag_value': ('name', ), + 'list_folders': ('parent', 'page_size', 'page_token', 'show_deleted', ), + 'list_projects': ('parent', 'page_token', 'page_size', 'show_deleted', ), + 'list_tag_bindings': ('parent', 'page_size', 'page_token', ), + 'list_tag_keys': ('parent', 'page_size', 'page_token', ), + 'list_tag_values': ('parent', 'page_size', 'page_token', ), + 'move_folder': ('name', 'destination_parent', ), + 'move_project': ('name', 'destination_parent', ), + 'search_folders': ('page_size', 'page_token', 'query', ), + 'search_organizations': ('page_size', 'page_token', 'query', ), + 'search_projects': ('query', 'page_token', 'page_size', ), + 'set_iam_policy': ('resource', 'policy', ), + 'test_iam_permissions': ('resource', 'permissions', ), + 'undelete_folder': ('name', ), + 'undelete_project': ('name', ), + 'update_folder': ('folder', 'update_mask', ), + 'update_project': ('project', 'update_mask', ), + 'update_tag_key': ('tag_key', 'update_mask', 'validate_only', ), + 'update_tag_value': ('tag_value', 'update_mask', 'validate_only', ), + } + + def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: + try: + key = original.func.attr.value + kword_params = self.METHOD_TO_PARAMS[key] + except (AttributeError, KeyError): + # Either not a method from the API or too convoluted to be sure. + return updated + + # If the existing code is valid, keyword args come after positional args. + # Therefore, all positional args must map to the first parameters. + args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) + if any(k.keyword.value == "request" for k in kwargs): + # We've already fixed this file, don't fix it again. + return updated + + kwargs, ctrl_kwargs = partition( + lambda a: not a.keyword.value in self.CTRL_PARAMS, + kwargs + ) + + args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] + ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) + for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) + + request_arg = cst.Arg( + value=cst.Dict([ + cst.DictElement( + cst.SimpleString("'{}'".format(name)), +cst.Element(value=arg.value) + ) + # Note: the args + kwargs looks silly, but keep in mind that + # the control parameters had to be stripped out, and that + # those could have been passed positionally or by keyword. + for name, arg in zip(kword_params, args + kwargs)]), + keyword=cst.Name("request") + ) + + return updated.with_changes( + args=[request_arg] + ctrl_kwargs + ) + + +def fix_files( + in_dir: pathlib.Path, + out_dir: pathlib.Path, + *, + transformer=resourcemanagerCallTransformer(), +): + """Duplicate the input dir to the output dir, fixing file method calls. + + Preconditions: + * in_dir is a real directory + * out_dir is a real, empty directory + """ + pyfile_gen = ( + pathlib.Path(os.path.join(root, f)) + for root, _, files in os.walk(in_dir) + for f in files if os.path.splitext(f)[1] == ".py" + ) + + for fpath in pyfile_gen: + with open(fpath, 'r') as f: + src = f.read() + + # Parse the code and insert method call fixes. + tree = cst.parse_module(src) + updated = tree.visit(transformer) + + # Create the path and directory structure for the new file. + updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) + updated_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate the updated source file at the corresponding path. + with open(updated_path, 'w') as f: + f.write(updated.code) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="""Fix up source that uses the resourcemanager client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/setup.py b/setup.py index 5d8e045..f2d60c6 100644 --- a/setup.py +++ b/setup.py @@ -19,34 +19,33 @@ # Package metadata. - name = "google-cloud-resource-manager" description = "Google Cloud Resource Manager API client library" -version = "0.30.3" -# Should be one of: -# 'Development Status :: 3 - Alpha' -# 'Development Status :: 4 - Beta' -# 'Development Status :: 5 - Production/Stable' -release_status = "Development Status :: 3 - Alpha" -dependencies = ["google-cloud-core >= 1.1.0, < 2.0dev"] -extras = {} +version = "1.0.0" +release_status = "Development Status :: 4 - Beta" +url = "https://github.com/googleapis/python-resource-manager" +dependencies = [ + "google-api-core[grpc] >= 1.26.0, <2.0.0dev", + "proto-plus >= 1.15.0", + "packaging >= 14.3", + "grpc-google-iam-v1 >= 0.12.3, < 0.13dev", +] +extras = {"libcst": "libcst >= 0.2.5"} # Setup boilerplate below this line. - package_root = os.path.abspath(os.path.dirname(__file__)) readme_filename = os.path.join(package_root, "README.rst") with io.open(readme_filename, encoding="utf-8") as readme_file: readme = readme_file.read() -# Only include packages under the 'google' namespace. Do not include tests, -# benchmarks, etc. packages = [ - package for package in setuptools.find_packages() if package.startswith("google") + package + for package in setuptools.PEP420PackageFinder.find() + if package.startswith("google") ] -# Determine which namespaces are needed. namespaces = ["google"] if "google.cloud" in packages: namespaces.append("google.cloud") @@ -60,27 +59,25 @@ author="Google LLC", author_email="googleapis-packages@google.com", license="Apache 2.0", - url="https://github.com/googleapis/python-resource-manager", + url=url, + packages=setuptools.PEP420PackageFinder.find(), + namespace_packages=("google", "google.cloud"), + platforms="Posix; MacOS X; Windows", + include_package_data=True, + install_requires=dependencies, + python_requires=">=3.6", classifiers=[ release_status, "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", - "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Internet", + "Topic :: Software Development :: Libraries :: Python Modules", ], - platforms="Posix; MacOS X; Windows", - packages=packages, - namespace_packages=namespaces, - install_requires=dependencies, - extras_require=extras, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", - include_package_data=True, zip_safe=False, ) diff --git a/synth.metadata b/synth.metadata deleted file mode 100644 index 17198bd..0000000 --- a/synth.metadata +++ /dev/null @@ -1,84 +0,0 @@ -{ - "sources": [ - { - "git": { - "name": ".", - "remote": "https://github.com/googleapis/python-resource-manager.git", - "sha": "fa2c0d101c93475098abe284f68679d02566d23b" - } - }, - { - "git": { - "name": "synthtool", - "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f94318521f63085b9ccb43d42af89f153fb39f15" - } - } - ], - "generatedFiles": [ - ".coveragerc", - ".flake8", - ".github/CONTRIBUTING.md", - ".github/ISSUE_TEMPLATE/bug_report.md", - ".github/ISSUE_TEMPLATE/feature_request.md", - ".github/ISSUE_TEMPLATE/support_request.md", - ".github/PULL_REQUEST_TEMPLATE.md", - ".github/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", - "noxfile.py", - "renovate.json", - "scripts/decrypt-secrets.sh", - "scripts/readme-gen/readme_gen.py", - "scripts/readme-gen/templates/README.tmpl.rst", - "scripts/readme-gen/templates/auth.tmpl.rst", - "scripts/readme-gen/templates/auth_api_key.tmpl.rst", - "scripts/readme-gen/templates/install_deps.tmpl.rst", - "scripts/readme-gen/templates/install_portaudio.tmpl.rst", - "setup.cfg", - "testing/.gitignore" - ] -} \ No newline at end of file diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt new file mode 100644 index 0000000..e69de29 diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt new file mode 100644 index 0000000..e69de29 diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt new file mode 100644 index 0000000..1df556b --- /dev/null +++ b/testing/constraints-3.6.txt @@ -0,0 +1,11 @@ +# This constraints file is used to check that lower bounds +# are correct in setup.py +# List *all* library dependencies and extras in this file. +# Pin the version to the lower bound. +# +# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", +# Then this file should have foo==1.14.0 +google-api-core==1.26.0 +proto-plus==1.15.0 +packaging==14.3 +grpc-google-iam-v1==0.12.3 diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt new file mode 100644 index 0000000..e69de29 diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt new file mode 100644 index 0000000..e69de29 diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..4de6597 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,15 @@ +# -*- 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/__init__.py b/tests/unit/__init__.py index df379f1..4de6597 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,4 +1,5 @@ -# Copyright 2016 Google LLC +# -*- 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. @@ -11,3 +12,4 @@ # 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/__init__.py b/tests/unit/gapic/__init__.py new file mode 100644 index 0000000..4de6597 --- /dev/null +++ b/tests/unit/gapic/__init__.py @@ -0,0 +1,15 @@ +# -*- 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/resourcemanager_v3/__init__.py b/tests/unit/gapic/resourcemanager_v3/__init__.py new file mode 100644 index 0000000..4de6597 --- /dev/null +++ b/tests/unit/gapic/resourcemanager_v3/__init__.py @@ -0,0 +1,15 @@ +# -*- 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/resourcemanager_v3/test_folders.py b/tests/unit/gapic/resourcemanager_v3/test_folders.py new file mode 100644 index 0000000..09184fa --- /dev/null +++ b/tests/unit/gapic/resourcemanager_v3/test_folders.py @@ -0,0 +1,3417 @@ +# -*- 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. +# +import os +import mock +import packaging.version + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + + +from google.api_core import client_options +from google.api_core import exceptions as core_exceptions +from google.api_core import future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials as ga_credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.resourcemanager_v3.services.folders import FoldersAsyncClient +from google.cloud.resourcemanager_v3.services.folders import FoldersClient +from google.cloud.resourcemanager_v3.services.folders import pagers +from google.cloud.resourcemanager_v3.services.folders import transports +from google.cloud.resourcemanager_v3.services.folders.transports.base import ( + _GOOGLE_AUTH_VERSION, +) +from google.cloud.resourcemanager_v3.types import folders +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import options_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from google.type import expr_pb2 # type: ignore +import google.auth + + +# TODO(busunkim): Once google-auth >= 1.25.0 is required transitively +# through google-api-core: +# - Delete the auth "less than" test cases +# - Delete these pytest markers (Make the "greater than or equal to" tests the default). +requires_google_auth_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-auth < 1.25.0", +) +requires_google_auth_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-auth >= 1.25.0", +) + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert FoldersClient._get_default_mtls_endpoint(None) is None + assert FoldersClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + FoldersClient._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint + ) + assert ( + FoldersClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + FoldersClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert FoldersClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [FoldersClient, FoldersAsyncClient,]) +def test_folders_client_from_service_account_info(client_class): + creds = ga_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 == "cloudresourcemanager.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [FoldersClient, FoldersAsyncClient,]) +def test_folders_client_service_account_always_use_jwt(client_class): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + client = client_class(credentials=creds) + use_jwt.assert_not_called() + + +@pytest.mark.parametrize( + "transport_class,transport_name", + [ + (transports.FoldersGrpcTransport, "grpc"), + (transports.FoldersGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_folders_client_service_account_always_use_jwt_true( + transport_class, transport_name +): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + transport = transport_class(credentials=creds, always_use_jwt_access=True) + use_jwt.assert_called_once_with(True) + + +@pytest.mark.parametrize("client_class", [FoldersClient, FoldersAsyncClient,]) +def test_folders_client_from_service_account_file(client_class): + creds = ga_credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + 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 == "cloudresourcemanager.googleapis.com:443" + + +def test_folders_client_get_transport_class(): + transport = FoldersClient.get_transport_class() + available_transports = [ + transports.FoldersGrpcTransport, + ] + assert transport in available_transports + + transport = FoldersClient.get_transport_class("grpc") + assert transport == transports.FoldersGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (FoldersClient, transports.FoldersGrpcTransport, "grpc"), + (FoldersAsyncClient, transports.FoldersGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + FoldersClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FoldersClient) +) +@mock.patch.object( + FoldersAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FoldersAsyncClient) +) +def test_folders_client_client_options(client_class, transport_class, transport_name): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(FoldersClient, "get_transport_class") as gtc: + transport = transport_class(credentials=ga_credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(FoldersClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + 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, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + 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="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (FoldersClient, transports.FoldersGrpcTransport, "grpc", "true"), + ( + FoldersAsyncClient, + transports.FoldersGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (FoldersClient, transports.FoldersGrpcTransport, "grpc", "false"), + ( + FoldersAsyncClient, + transports.FoldersGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + FoldersClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FoldersClient) +) +@mock.patch.object( + FoldersAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FoldersAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_folders_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + 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, + 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. + 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=True, + ): + with mock.patch( + "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 + + patched.return_value = None + client = client_class() + 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 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", + [ + (FoldersClient, transports.FoldersGrpcTransport, "grpc"), + (FoldersAsyncClient, transports.FoldersGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_folders_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + 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", + [ + (FoldersClient, transports.FoldersGrpcTransport, "grpc"), + (FoldersAsyncClient, transports.FoldersGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_folders_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_folders_client_client_options_from_dict(): + with mock.patch( + "google.cloud.resourcemanager_v3.services.folders.transports.FoldersGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = FoldersClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_get_folder(transport: str = "grpc", request_type=folders.GetFolderRequest): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.Folder( + name="name_value", + parent="parent_value", + display_name="display_name_value", + state=folders.Folder.State.ACTIVE, + etag="etag_value", + ) + response = client.get_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.GetFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, folders.Folder) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.display_name == "display_name_value" + assert response.state == folders.Folder.State.ACTIVE + assert response.etag == "etag_value" + + +def test_get_folder_from_dict(): + test_get_folder(request_type=dict) + + +def test_get_folder_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_folder), "__call__") as call: + client.get_folder() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.GetFolderRequest() + + +@pytest.mark.asyncio +async def test_get_folder_async( + transport: str = "grpc_asyncio", request_type=folders.GetFolderRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + folders.Folder( + name="name_value", + parent="parent_value", + display_name="display_name_value", + state=folders.Folder.State.ACTIVE, + etag="etag_value", + ) + ) + response = await client.get_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.GetFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, folders.Folder) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.display_name == "display_name_value" + assert response.state == folders.Folder.State.ACTIVE + assert response.etag == "etag_value" + + +@pytest.mark.asyncio +async def test_get_folder_async_from_dict(): + await test_get_folder_async(request_type=dict) + + +def test_get_folder_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.GetFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_folder), "__call__") as call: + call.return_value = folders.Folder() + client.get_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_folder_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.GetFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_folder), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(folders.Folder()) + await client.get_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_get_folder_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.Folder() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_folder(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_get_folder_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_folder( + folders.GetFolderRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_folder_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.Folder() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(folders.Folder()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_folder(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_get_folder_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_folder( + folders.GetFolderRequest(), name="name_value", + ) + + +def test_list_folders(transport: str = "grpc", request_type=folders.ListFoldersRequest): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.ListFoldersResponse( + next_page_token="next_page_token_value", + ) + response = client.list_folders(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.ListFoldersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListFoldersPager) + assert response.next_page_token == "next_page_token_value" + + +def test_list_folders_from_dict(): + test_list_folders(request_type=dict) + + +def test_list_folders_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_folders), "__call__") as call: + client.list_folders() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.ListFoldersRequest() + + +@pytest.mark.asyncio +async def test_list_folders_async( + transport: str = "grpc_asyncio", request_type=folders.ListFoldersRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + folders.ListFoldersResponse(next_page_token="next_page_token_value",) + ) + response = await client.list_folders(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.ListFoldersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListFoldersAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_folders_async_from_dict(): + await test_list_folders_async(request_type=dict) + + +def test_list_folders_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.ListFoldersResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_folders(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +def test_list_folders_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_folders( + folders.ListFoldersRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_folders_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.ListFoldersResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + folders.ListFoldersResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_folders(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +@pytest.mark.asyncio +async def test_list_folders_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.list_folders( + folders.ListFoldersRequest(), parent="parent_value", + ) + + +def test_list_folders_pager(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_folders), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.ListFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.ListFoldersResponse(folders=[], next_page_token="def",), + folders.ListFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.ListFoldersResponse(folders=[folders.Folder(), folders.Folder(),],), + RuntimeError, + ) + + metadata = () + pager = client.list_folders(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, folders.Folder) for i in results) + + +def test_list_folders_pages(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_folders), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.ListFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.ListFoldersResponse(folders=[], next_page_token="def",), + folders.ListFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.ListFoldersResponse(folders=[folders.Folder(), folders.Folder(),],), + RuntimeError, + ) + pages = list(client.list_folders(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_list_folders_async_pager(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_folders), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.ListFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.ListFoldersResponse(folders=[], next_page_token="def",), + folders.ListFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.ListFoldersResponse(folders=[folders.Folder(), folders.Folder(),],), + RuntimeError, + ) + async_pager = await client.list_folders(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, folders.Folder) for i in responses) + + +@pytest.mark.asyncio +async def test_list_folders_async_pages(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_folders), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.ListFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.ListFoldersResponse(folders=[], next_page_token="def",), + folders.ListFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.ListFoldersResponse(folders=[folders.Folder(), folders.Folder(),],), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_folders(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_search_folders( + transport: str = "grpc", request_type=folders.SearchFoldersRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.SearchFoldersResponse( + next_page_token="next_page_token_value", + ) + response = client.search_folders(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.SearchFoldersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.SearchFoldersPager) + assert response.next_page_token == "next_page_token_value" + + +def test_search_folders_from_dict(): + test_search_folders(request_type=dict) + + +def test_search_folders_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_folders), "__call__") as call: + client.search_folders() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.SearchFoldersRequest() + + +@pytest.mark.asyncio +async def test_search_folders_async( + transport: str = "grpc_asyncio", request_type=folders.SearchFoldersRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + folders.SearchFoldersResponse(next_page_token="next_page_token_value",) + ) + response = await client.search_folders(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.SearchFoldersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.SearchFoldersAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_search_folders_async_from_dict(): + await test_search_folders_async(request_type=dict) + + +def test_search_folders_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.SearchFoldersResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.search_folders(query="query_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].query == "query_value" + + +def test_search_folders_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.search_folders( + folders.SearchFoldersRequest(), query="query_value", + ) + + +@pytest.mark.asyncio +async def test_search_folders_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_folders), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = folders.SearchFoldersResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + folders.SearchFoldersResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.search_folders(query="query_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].query == "query_value" + + +@pytest.mark.asyncio +async def test_search_folders_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.search_folders( + folders.SearchFoldersRequest(), query="query_value", + ) + + +def test_search_folders_pager(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_folders), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.SearchFoldersResponse(folders=[], next_page_token="def",), + folders.SearchFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(),], + ), + RuntimeError, + ) + + metadata = () + pager = client.search_folders(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, folders.Folder) for i in results) + + +def test_search_folders_pages(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_folders), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.SearchFoldersResponse(folders=[], next_page_token="def",), + folders.SearchFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(),], + ), + RuntimeError, + ) + pages = list(client.search_folders(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_search_folders_async_pager(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_folders), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.SearchFoldersResponse(folders=[], next_page_token="def",), + folders.SearchFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(),], + ), + RuntimeError, + ) + async_pager = await client.search_folders(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, folders.Folder) for i in responses) + + +@pytest.mark.asyncio +async def test_search_folders_async_pages(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_folders), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(), folders.Folder(),], + next_page_token="abc", + ), + folders.SearchFoldersResponse(folders=[], next_page_token="def",), + folders.SearchFoldersResponse( + folders=[folders.Folder(),], next_page_token="ghi", + ), + folders.SearchFoldersResponse( + folders=[folders.Folder(), folders.Folder(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.search_folders(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_create_folder( + transport: str = "grpc", request_type=folders.CreateFolderRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.create_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.CreateFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_folder_from_dict(): + test_create_folder(request_type=dict) + + +def test_create_folder_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_folder), "__call__") as call: + client.create_folder() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.CreateFolderRequest() + + +@pytest.mark.asyncio +async def test_create_folder_async( + transport: str = "grpc_asyncio", request_type=folders.CreateFolderRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.create_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.CreateFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_folder_async_from_dict(): + await test_create_folder_async(request_type=dict) + + +def test_create_folder_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_folder(folder=folders.Folder(name="name_value"),) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].folder == folders.Folder(name="name_value") + + +def test_create_folder_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.create_folder( + folders.CreateFolderRequest(), folder=folders.Folder(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_folder_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_folder(folder=folders.Folder(name="name_value"),) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].folder == folders.Folder(name="name_value") + + +@pytest.mark.asyncio +async def test_create_folder_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.create_folder( + folders.CreateFolderRequest(), folder=folders.Folder(name="name_value"), + ) + + +def test_update_folder( + transport: str = "grpc", request_type=folders.UpdateFolderRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.update_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.UpdateFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_update_folder_from_dict(): + test_update_folder(request_type=dict) + + +def test_update_folder_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_folder), "__call__") as call: + client.update_folder() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.UpdateFolderRequest() + + +@pytest.mark.asyncio +async def test_update_folder_async( + transport: str = "grpc_asyncio", request_type=folders.UpdateFolderRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.update_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.UpdateFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_update_folder_async_from_dict(): + await test_update_folder_async(request_type=dict) + + +def test_update_folder_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.UpdateFolderRequest() + + request.folder.name = "folder.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_folder), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.update_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "folder.name=folder.name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_folder_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.UpdateFolderRequest() + + request.folder.name = "folder.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_folder), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.update_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "folder.name=folder.name/value",) in kw["metadata"] + + +def test_update_folder_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_folder( + folder=folders.Folder(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].folder == folders.Folder(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +def test_update_folder_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.update_folder( + folders.UpdateFolderRequest(), + folder=folders.Folder(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_folder_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_folder( + folder=folders.Folder(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].folder == folders.Folder(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_folder_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.update_folder( + folders.UpdateFolderRequest(), + folder=folders.Folder(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +def test_move_folder(transport: str = "grpc", request_type=folders.MoveFolderRequest): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.move_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.MoveFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_move_folder_from_dict(): + test_move_folder(request_type=dict) + + +def test_move_folder_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_folder), "__call__") as call: + client.move_folder() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.MoveFolderRequest() + + +@pytest.mark.asyncio +async def test_move_folder_async( + transport: str = "grpc_asyncio", request_type=folders.MoveFolderRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.move_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.MoveFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_move_folder_async_from_dict(): + await test_move_folder_async(request_type=dict) + + +def test_move_folder_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.MoveFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_folder), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.move_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_move_folder_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.MoveFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_folder), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.move_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_move_folder_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.move_folder( + name="name_value", destination_parent="destination_parent_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + assert args[0].destination_parent == "destination_parent_value" + + +def test_move_folder_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.move_folder( + folders.MoveFolderRequest(), + name="name_value", + destination_parent="destination_parent_value", + ) + + +@pytest.mark.asyncio +async def test_move_folder_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.move_folder( + name="name_value", destination_parent="destination_parent_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + assert args[0].destination_parent == "destination_parent_value" + + +@pytest.mark.asyncio +async def test_move_folder_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.move_folder( + folders.MoveFolderRequest(), + name="name_value", + destination_parent="destination_parent_value", + ) + + +def test_delete_folder( + transport: str = "grpc", request_type=folders.DeleteFolderRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.delete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.DeleteFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_delete_folder_from_dict(): + test_delete_folder(request_type=dict) + + +def test_delete_folder_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_folder), "__call__") as call: + client.delete_folder() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.DeleteFolderRequest() + + +@pytest.mark.asyncio +async def test_delete_folder_async( + transport: str = "grpc_asyncio", request_type=folders.DeleteFolderRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.delete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.DeleteFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_delete_folder_async_from_dict(): + await test_delete_folder_async(request_type=dict) + + +def test_delete_folder_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.DeleteFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_folder), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.delete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_delete_folder_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.DeleteFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_folder), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.delete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_delete_folder_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.delete_folder(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_delete_folder_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.delete_folder( + folders.DeleteFolderRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_folder_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.delete_folder(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_delete_folder_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.delete_folder( + folders.DeleteFolderRequest(), name="name_value", + ) + + +def test_undelete_folder( + transport: str = "grpc", request_type=folders.UndeleteFolderRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.undelete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == folders.UndeleteFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_undelete_folder_from_dict(): + test_undelete_folder(request_type=dict) + + +def test_undelete_folder_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_folder), "__call__") as call: + client.undelete_folder() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == folders.UndeleteFolderRequest() + + +@pytest.mark.asyncio +async def test_undelete_folder_async( + transport: str = "grpc_asyncio", request_type=folders.UndeleteFolderRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.undelete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == folders.UndeleteFolderRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_undelete_folder_async_from_dict(): + await test_undelete_folder_async(request_type=dict) + + +def test_undelete_folder_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.UndeleteFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_folder), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.undelete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_undelete_folder_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = folders.UndeleteFolderRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_folder), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.undelete_folder(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_undelete_folder_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.undelete_folder(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_undelete_folder_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.undelete_folder( + folders.UndeleteFolderRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_undelete_folder_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_folder), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.undelete_folder(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_undelete_folder_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.undelete_folder( + folders.UndeleteFolderRequest(), name="name_value", + ) + + +def test_get_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_get_iam_policy_from_dict(): + test_get_iam_policy(request_type=dict) + + +def test_get_iam_policy_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + client.get_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_get_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_get_iam_policy_async_from_dict(): + await test_get_iam_policy_async(request_type=dict) + + +def test_get_iam_policy_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_iam_policy_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_get_iam_policy_from_dict_foreign(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.get_iam_policy( + request={ + "resource": "resource_value", + "options": options_pb2.GetPolicyOptions(requested_policy_version=2598), + } + ) + call.assert_called() + + +def test_get_iam_policy_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_get_iam_policy_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +def test_set_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_set_iam_policy_from_dict(): + test_set_iam_policy(request_type=dict) + + +def test_set_iam_policy_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + client.set_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_set_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_set_iam_policy_async_from_dict(): + await test_set_iam_policy_async(request_type=dict) + + +def test_set_iam_policy_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_set_iam_policy_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_set_iam_policy_from_dict_foreign(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.set_iam_policy( + request={ + "resource": "resource_value", + "policy": policy_pb2.Policy(version=774), + } + ) + call.assert_called() + + +def test_set_iam_policy_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_set_iam_policy_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +def test_test_iam_permissions( + transport: str = "grpc", request_type=iam_policy_pb2.TestIamPermissionsRequest +): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + response = client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +def test_test_iam_permissions_from_dict(): + test_test_iam_permissions(request_type=dict) + + +def test_test_iam_permissions_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 = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + client.test_iam_permissions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async( + transport: str = "grpc_asyncio", + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + ) + response = await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async_from_dict(): + await test_test_iam_permissions_async(request_type=dict) + + +def test_test_iam_permissions_field_headers(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_field_headers_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_test_iam_permissions_from_dict_foreign(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + response = client.test_iam_permissions( + request={ + "resource": "resource_value", + "permissions": ["permissions_value"], + } + ) + call.assert_called() + + +def test_test_iam_permissions_flattened(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +def test_test_iam_permissions_flattened_error(): + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_error_async(): + client = FoldersAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.FoldersGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.FoldersGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = FoldersClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.FoldersGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = FoldersClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.FoldersGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + client = FoldersClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.FoldersGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.FoldersGrpcAsyncIOTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.FoldersGrpcTransport, transports.FoldersGrpcAsyncIOTransport,], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = FoldersClient(credentials=ga_credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.FoldersGrpcTransport,) + + +def test_folders_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(core_exceptions.DuplicateCredentialArgs): + transport = transports.FoldersTransport( + credentials=ga_credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_folders_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.resourcemanager_v3.services.folders.transports.FoldersTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.FoldersTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "get_folder", + "list_folders", + "search_folders", + "create_folder", + "update_folder", + "move_folder", + "delete_folder", + "undelete_folder", + "get_iam_policy", + "set_iam_policy", + "test_iam_permissions", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +@requires_google_auth_gte_1_25_0 +def test_folders_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.folders.transports.FoldersTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.FoldersTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@requires_google_auth_lt_1_25_0 +def test_folders_base_transport_with_credentials_file_old_google_auth(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.folders.transports.FoldersTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.FoldersTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +def test_folders_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch( + "google.cloud.resourcemanager_v3.services.folders.transports.FoldersTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.FoldersTransport() + adc.assert_called_once() + + +@requires_google_auth_gte_1_25_0 +def test_folders_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + FoldersClient() + adc.assert_called_once_with( + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@requires_google_auth_lt_1_25_0 +def test_folders_auth_adc_old_google_auth(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + FoldersClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.FoldersGrpcTransport, transports.FoldersGrpcAsyncIOTransport,], +) +@requires_google_auth_gte_1_25_0 +def test_folders_transport_auth_adc(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + adc.assert_called_once_with( + scopes=["1", "2"], + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.FoldersGrpcTransport, transports.FoldersGrpcAsyncIOTransport,], +) +@requires_google_auth_lt_1_25_0 +def test_folders_transport_auth_adc_old_google_auth(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.FoldersGrpcTransport, grpc_helpers), + (transports.FoldersGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +def test_folders_transport_create_channel(transport_class, grpc_helpers): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object( + google.auth, "default", autospec=True + ) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = ga_credentials.AnonymousCredentials() + adc.return_value = (creds, None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + + create_channel.assert_called_with( + "cloudresourcemanager.googleapis.com:443", + credentials=creds, + credentials_file=None, + quota_project_id="octopus", + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + scopes=["1", "2"], + default_host="cloudresourcemanager.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.FoldersGrpcTransport, transports.FoldersGrpcAsyncIOTransport], +) +def test_folders_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = ga_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=None, + 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_folders_host_no_port(): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:443" + + +def test_folders_host_with_port(): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:8000" + + +def test_folders_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.FoldersGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_folders_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.FoldersGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + 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.FoldersGrpcTransport, transports.FoldersGrpcAsyncIOTransport], +) +def test_folders_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = ga_credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.FoldersGrpcTransport, transports.FoldersGrpcAsyncIOTransport], +) +def test_folders_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_folders_grpc_lro_client(): + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_folders_grpc_lro_async_client(): + client = FoldersAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_folder_path(): + folder = "squid" + expected = "folders/{folder}".format(folder=folder,) + actual = FoldersClient.folder_path(folder) + assert expected == actual + + +def test_parse_folder_path(): + expected = { + "folder": "clam", + } + path = FoldersClient.folder_path(**expected) + + # Check that the path construction is reversible. + actual = FoldersClient.parse_folder_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = FoldersClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = FoldersClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = FoldersClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + expected = "folders/{folder}".format(folder=folder,) + actual = FoldersClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = FoldersClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = FoldersClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + expected = "organizations/{organization}".format(organization=organization,) + actual = FoldersClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = FoldersClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = FoldersClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + expected = "projects/{project}".format(project=project,) + actual = FoldersClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = FoldersClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = FoldersClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = FoldersClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = FoldersClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = FoldersClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.FoldersTransport, "_prep_wrapped_messages" + ) as prep: + client = FoldersClient( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.FoldersTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = FoldersClient.get_transport_class() + transport = transport_class( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/resourcemanager_v3/test_organizations.py b/tests/unit/gapic/resourcemanager_v3/test_organizations.py new file mode 100644 index 0000000..8db7a18 --- /dev/null +++ b/tests/unit/gapic/resourcemanager_v3/test_organizations.py @@ -0,0 +1,2324 @@ +# -*- 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. +# +import os +import mock +import packaging.version + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + + +from google.api_core import client_options +from google.api_core import exceptions as core_exceptions +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.auth import credentials as ga_credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.resourcemanager_v3.services.organizations import ( + OrganizationsAsyncClient, +) +from google.cloud.resourcemanager_v3.services.organizations import OrganizationsClient +from google.cloud.resourcemanager_v3.services.organizations import pagers +from google.cloud.resourcemanager_v3.services.organizations import transports +from google.cloud.resourcemanager_v3.services.organizations.transports.base import ( + _GOOGLE_AUTH_VERSION, +) +from google.cloud.resourcemanager_v3.types import organizations +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import options_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.oauth2 import service_account +from google.protobuf import timestamp_pb2 # type: ignore +from google.type import expr_pb2 # type: ignore +import google.auth + + +# TODO(busunkim): Once google-auth >= 1.25.0 is required transitively +# through google-api-core: +# - Delete the auth "less than" test cases +# - Delete these pytest markers (Make the "greater than or equal to" tests the default). +requires_google_auth_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-auth < 1.25.0", +) +requires_google_auth_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-auth >= 1.25.0", +) + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert OrganizationsClient._get_default_mtls_endpoint(None) is None + assert ( + OrganizationsClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + OrganizationsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + OrganizationsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + OrganizationsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + OrganizationsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [OrganizationsClient, OrganizationsAsyncClient,] +) +def test_organizations_client_from_service_account_info(client_class): + creds = ga_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 == "cloudresourcemanager.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [OrganizationsClient, OrganizationsAsyncClient,] +) +def test_organizations_client_service_account_always_use_jwt(client_class): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + client = client_class(credentials=creds) + use_jwt.assert_not_called() + + +@pytest.mark.parametrize( + "transport_class,transport_name", + [ + (transports.OrganizationsGrpcTransport, "grpc"), + (transports.OrganizationsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_organizations_client_service_account_always_use_jwt_true( + transport_class, transport_name +): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + transport = transport_class(credentials=creds, always_use_jwt_access=True) + use_jwt.assert_called_once_with(True) + + +@pytest.mark.parametrize( + "client_class", [OrganizationsClient, OrganizationsAsyncClient,] +) +def test_organizations_client_from_service_account_file(client_class): + creds = ga_credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + 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 == "cloudresourcemanager.googleapis.com:443" + + +def test_organizations_client_get_transport_class(): + transport = OrganizationsClient.get_transport_class() + available_transports = [ + transports.OrganizationsGrpcTransport, + ] + assert transport in available_transports + + transport = OrganizationsClient.get_transport_class("grpc") + assert transport == transports.OrganizationsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (OrganizationsClient, transports.OrganizationsGrpcTransport, "grpc"), + ( + OrganizationsAsyncClient, + transports.OrganizationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + OrganizationsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(OrganizationsClient), +) +@mock.patch.object( + OrganizationsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(OrganizationsAsyncClient), +) +def test_organizations_client_client_options( + client_class, transport_class, transport_name +): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(OrganizationsClient, "get_transport_class") as gtc: + transport = transport_class(credentials=ga_credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(OrganizationsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + 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, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + 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="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (OrganizationsClient, transports.OrganizationsGrpcTransport, "grpc", "true"), + ( + OrganizationsAsyncClient, + transports.OrganizationsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (OrganizationsClient, transports.OrganizationsGrpcTransport, "grpc", "false"), + ( + OrganizationsAsyncClient, + transports.OrganizationsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + OrganizationsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(OrganizationsClient), +) +@mock.patch.object( + OrganizationsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(OrganizationsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_organizations_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + 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, + 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. + 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=True, + ): + with mock.patch( + "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 + + patched.return_value = None + client = client_class() + 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 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", + [ + (OrganizationsClient, transports.OrganizationsGrpcTransport, "grpc"), + ( + OrganizationsAsyncClient, + transports.OrganizationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_organizations_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + 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", + [ + (OrganizationsClient, transports.OrganizationsGrpcTransport, "grpc"), + ( + OrganizationsAsyncClient, + transports.OrganizationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_organizations_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_organizations_client_client_options_from_dict(): + with mock.patch( + "google.cloud.resourcemanager_v3.services.organizations.transports.OrganizationsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = OrganizationsClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_get_organization( + transport: str = "grpc", request_type=organizations.GetOrganizationRequest +): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_organization), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = organizations.Organization( + name="name_value", + display_name="display_name_value", + state=organizations.Organization.State.ACTIVE, + etag="etag_value", + directory_customer_id="directory_customer_id_value", + ) + response = client.get_organization(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == organizations.GetOrganizationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, organizations.Organization) + assert response.name == "name_value" + assert response.display_name == "display_name_value" + assert response.state == organizations.Organization.State.ACTIVE + assert response.etag == "etag_value" + + +def test_get_organization_from_dict(): + test_get_organization(request_type=dict) + + +def test_get_organization_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 = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_organization), "__call__") as call: + client.get_organization() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == organizations.GetOrganizationRequest() + + +@pytest.mark.asyncio +async def test_get_organization_async( + transport: str = "grpc_asyncio", request_type=organizations.GetOrganizationRequest +): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_organization), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + organizations.Organization( + name="name_value", + display_name="display_name_value", + state=organizations.Organization.State.ACTIVE, + etag="etag_value", + ) + ) + response = await client.get_organization(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == organizations.GetOrganizationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, organizations.Organization) + assert response.name == "name_value" + assert response.display_name == "display_name_value" + assert response.state == organizations.Organization.State.ACTIVE + assert response.etag == "etag_value" + + +@pytest.mark.asyncio +async def test_get_organization_async_from_dict(): + await test_get_organization_async(request_type=dict) + + +def test_get_organization_field_headers(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = organizations.GetOrganizationRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_organization), "__call__") as call: + call.return_value = organizations.Organization() + client.get_organization(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_organization_field_headers_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = organizations.GetOrganizationRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_organization), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + organizations.Organization() + ) + await client.get_organization(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_get_organization_flattened(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_organization), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = organizations.Organization() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_organization(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_get_organization_flattened_error(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_organization( + organizations.GetOrganizationRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_organization_flattened_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_organization), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = organizations.Organization() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + organizations.Organization() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_organization(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_get_organization_flattened_error_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_organization( + organizations.GetOrganizationRequest(), name="name_value", + ) + + +def test_search_organizations( + transport: str = "grpc", request_type=organizations.SearchOrganizationsRequest +): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = organizations.SearchOrganizationsResponse( + next_page_token="next_page_token_value", + ) + response = client.search_organizations(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == organizations.SearchOrganizationsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.SearchOrganizationsPager) + assert response.next_page_token == "next_page_token_value" + + +def test_search_organizations_from_dict(): + test_search_organizations(request_type=dict) + + +def test_search_organizations_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 = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), "__call__" + ) as call: + client.search_organizations() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == organizations.SearchOrganizationsRequest() + + +@pytest.mark.asyncio +async def test_search_organizations_async( + transport: str = "grpc_asyncio", + request_type=organizations.SearchOrganizationsRequest, +): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + organizations.SearchOrganizationsResponse( + next_page_token="next_page_token_value", + ) + ) + response = await client.search_organizations(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == organizations.SearchOrganizationsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.SearchOrganizationsAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_search_organizations_async_from_dict(): + await test_search_organizations_async(request_type=dict) + + +def test_search_organizations_flattened(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = organizations.SearchOrganizationsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.search_organizations(query="query_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].query == "query_value" + + +def test_search_organizations_flattened_error(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.search_organizations( + organizations.SearchOrganizationsRequest(), query="query_value", + ) + + +@pytest.mark.asyncio +async def test_search_organizations_flattened_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = organizations.SearchOrganizationsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + organizations.SearchOrganizationsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.search_organizations(query="query_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].query == "query_value" + + +@pytest.mark.asyncio +async def test_search_organizations_flattened_error_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.search_organizations( + organizations.SearchOrganizationsRequest(), query="query_value", + ) + + +def test_search_organizations_pager(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + organizations.Organization(), + ], + next_page_token="abc", + ), + organizations.SearchOrganizationsResponse( + organizations=[], next_page_token="def", + ), + organizations.SearchOrganizationsResponse( + organizations=[organizations.Organization(),], next_page_token="ghi", + ), + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + ], + ), + RuntimeError, + ) + + metadata = () + pager = client.search_organizations(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, organizations.Organization) for i in results) + + +def test_search_organizations_pages(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + organizations.Organization(), + ], + next_page_token="abc", + ), + organizations.SearchOrganizationsResponse( + organizations=[], next_page_token="def", + ), + organizations.SearchOrganizationsResponse( + organizations=[organizations.Organization(),], next_page_token="ghi", + ), + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + ], + ), + RuntimeError, + ) + pages = list(client.search_organizations(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_search_organizations_async_pager(): + client = OrganizationsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + organizations.Organization(), + ], + next_page_token="abc", + ), + organizations.SearchOrganizationsResponse( + organizations=[], next_page_token="def", + ), + organizations.SearchOrganizationsResponse( + organizations=[organizations.Organization(),], next_page_token="ghi", + ), + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + ], + ), + RuntimeError, + ) + async_pager = await client.search_organizations(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, organizations.Organization) for i in responses) + + +@pytest.mark.asyncio +async def test_search_organizations_async_pages(): + client = OrganizationsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_organizations), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + organizations.Organization(), + ], + next_page_token="abc", + ), + organizations.SearchOrganizationsResponse( + organizations=[], next_page_token="def", + ), + organizations.SearchOrganizationsResponse( + organizations=[organizations.Organization(),], next_page_token="ghi", + ), + organizations.SearchOrganizationsResponse( + organizations=[ + organizations.Organization(), + organizations.Organization(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.search_organizations(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_get_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_get_iam_policy_from_dict(): + test_get_iam_policy(request_type=dict) + + +def test_get_iam_policy_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 = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + client.get_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_get_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_get_iam_policy_async_from_dict(): + await test_get_iam_policy_async(request_type=dict) + + +def test_get_iam_policy_field_headers(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_iam_policy_field_headers_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_get_iam_policy_from_dict_foreign(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.get_iam_policy( + request={ + "resource": "resource_value", + "options": options_pb2.GetPolicyOptions(requested_policy_version=2598), + } + ) + call.assert_called() + + +def test_get_iam_policy_flattened(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_get_iam_policy_flattened_error(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_error_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +def test_set_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_set_iam_policy_from_dict(): + test_set_iam_policy(request_type=dict) + + +def test_set_iam_policy_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 = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + client.set_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_set_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_set_iam_policy_async_from_dict(): + await test_set_iam_policy_async(request_type=dict) + + +def test_set_iam_policy_field_headers(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_set_iam_policy_field_headers_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_set_iam_policy_from_dict_foreign(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.set_iam_policy( + request={ + "resource": "resource_value", + "policy": policy_pb2.Policy(version=774), + } + ) + call.assert_called() + + +def test_set_iam_policy_flattened(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_set_iam_policy_flattened_error(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_error_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +def test_test_iam_permissions( + transport: str = "grpc", request_type=iam_policy_pb2.TestIamPermissionsRequest +): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + response = client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +def test_test_iam_permissions_from_dict(): + test_test_iam_permissions(request_type=dict) + + +def test_test_iam_permissions_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 = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + client.test_iam_permissions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async( + transport: str = "grpc_asyncio", + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + ) + response = await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async_from_dict(): + await test_test_iam_permissions_async(request_type=dict) + + +def test_test_iam_permissions_field_headers(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_field_headers_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_test_iam_permissions_from_dict_foreign(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + response = client.test_iam_permissions( + request={ + "resource": "resource_value", + "permissions": ["permissions_value"], + } + ) + call.assert_called() + + +def test_test_iam_permissions_flattened(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +def test_test_iam_permissions_flattened_error(): + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_error_async(): + client = OrganizationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.OrganizationsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.OrganizationsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = OrganizationsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.OrganizationsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = OrganizationsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.OrganizationsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + client = OrganizationsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.OrganizationsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.OrganizationsGrpcAsyncIOTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.OrganizationsGrpcTransport, + transports.OrganizationsGrpcAsyncIOTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = OrganizationsClient(credentials=ga_credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.OrganizationsGrpcTransport,) + + +def test_organizations_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(core_exceptions.DuplicateCredentialArgs): + transport = transports.OrganizationsTransport( + credentials=ga_credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_organizations_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.resourcemanager_v3.services.organizations.transports.OrganizationsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.OrganizationsTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "get_organization", + "search_organizations", + "get_iam_policy", + "set_iam_policy", + "test_iam_permissions", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +@requires_google_auth_gte_1_25_0 +def test_organizations_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.organizations.transports.OrganizationsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.OrganizationsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@requires_google_auth_lt_1_25_0 +def test_organizations_base_transport_with_credentials_file_old_google_auth(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.organizations.transports.OrganizationsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.OrganizationsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +def test_organizations_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch( + "google.cloud.resourcemanager_v3.services.organizations.transports.OrganizationsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.OrganizationsTransport() + adc.assert_called_once() + + +@requires_google_auth_gte_1_25_0 +def test_organizations_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + OrganizationsClient() + adc.assert_called_once_with( + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@requires_google_auth_lt_1_25_0 +def test_organizations_auth_adc_old_google_auth(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + OrganizationsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.OrganizationsGrpcTransport, + transports.OrganizationsGrpcAsyncIOTransport, + ], +) +@requires_google_auth_gte_1_25_0 +def test_organizations_transport_auth_adc(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + adc.assert_called_once_with( + scopes=["1", "2"], + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.OrganizationsGrpcTransport, + transports.OrganizationsGrpcAsyncIOTransport, + ], +) +@requires_google_auth_lt_1_25_0 +def test_organizations_transport_auth_adc_old_google_auth(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.OrganizationsGrpcTransport, grpc_helpers), + (transports.OrganizationsGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +def test_organizations_transport_create_channel(transport_class, grpc_helpers): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object( + google.auth, "default", autospec=True + ) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = ga_credentials.AnonymousCredentials() + adc.return_value = (creds, None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + + create_channel.assert_called_with( + "cloudresourcemanager.googleapis.com:443", + credentials=creds, + credentials_file=None, + quota_project_id="octopus", + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + scopes=["1", "2"], + default_host="cloudresourcemanager.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.OrganizationsGrpcTransport, + transports.OrganizationsGrpcAsyncIOTransport, + ], +) +def test_organizations_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = ga_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=None, + 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_organizations_host_no_port(): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:443" + + +def test_organizations_host_with_port(): + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:8000" + + +def test_organizations_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.OrganizationsGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_organizations_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.OrganizationsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + 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.OrganizationsGrpcTransport, + transports.OrganizationsGrpcAsyncIOTransport, + ], +) +def test_organizations_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = ga_credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.OrganizationsGrpcTransport, + transports.OrganizationsGrpcAsyncIOTransport, + ], +) +def test_organizations_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_organization_path(): + organization = "squid" + expected = "organizations/{organization}".format(organization=organization,) + actual = OrganizationsClient.organization_path(organization) + assert expected == actual + + +def test_parse_organization_path(): + expected = { + "organization": "clam", + } + path = OrganizationsClient.organization_path(**expected) + + # Check that the path construction is reversible. + actual = OrganizationsClient.parse_organization_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = OrganizationsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = OrganizationsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = OrganizationsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + expected = "folders/{folder}".format(folder=folder,) + actual = OrganizationsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = OrganizationsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = OrganizationsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + expected = "organizations/{organization}".format(organization=organization,) + actual = OrganizationsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = OrganizationsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = OrganizationsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + expected = "projects/{project}".format(project=project,) + actual = OrganizationsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = OrganizationsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = OrganizationsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = OrganizationsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = OrganizationsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = OrganizationsClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.OrganizationsTransport, "_prep_wrapped_messages" + ) as prep: + client = OrganizationsClient( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.OrganizationsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = OrganizationsClient.get_transport_class() + transport = transport_class( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/resourcemanager_v3/test_projects.py b/tests/unit/gapic/resourcemanager_v3/test_projects.py new file mode 100644 index 0000000..5c2bdfc --- /dev/null +++ b/tests/unit/gapic/resourcemanager_v3/test_projects.py @@ -0,0 +1,3446 @@ +# -*- 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. +# +import os +import mock +import packaging.version + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + + +from google.api_core import client_options +from google.api_core import exceptions as core_exceptions +from google.api_core import future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials as ga_credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.resourcemanager_v3.services.projects import ProjectsAsyncClient +from google.cloud.resourcemanager_v3.services.projects import ProjectsClient +from google.cloud.resourcemanager_v3.services.projects import pagers +from google.cloud.resourcemanager_v3.services.projects import transports +from google.cloud.resourcemanager_v3.services.projects.transports.base import ( + _GOOGLE_AUTH_VERSION, +) +from google.cloud.resourcemanager_v3.types import projects +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import options_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from google.type import expr_pb2 # type: ignore +import google.auth + + +# TODO(busunkim): Once google-auth >= 1.25.0 is required transitively +# through google-api-core: +# - Delete the auth "less than" test cases +# - Delete these pytest markers (Make the "greater than or equal to" tests the default). +requires_google_auth_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-auth < 1.25.0", +) +requires_google_auth_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-auth >= 1.25.0", +) + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert ProjectsClient._get_default_mtls_endpoint(None) is None + assert ProjectsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + ProjectsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ProjectsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ProjectsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ProjectsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [ProjectsClient, ProjectsAsyncClient,]) +def test_projects_client_from_service_account_info(client_class): + creds = ga_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 == "cloudresourcemanager.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [ProjectsClient, ProjectsAsyncClient,]) +def test_projects_client_service_account_always_use_jwt(client_class): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + client = client_class(credentials=creds) + use_jwt.assert_not_called() + + +@pytest.mark.parametrize( + "transport_class,transport_name", + [ + (transports.ProjectsGrpcTransport, "grpc"), + (transports.ProjectsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_projects_client_service_account_always_use_jwt_true( + transport_class, transport_name +): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + transport = transport_class(credentials=creds, always_use_jwt_access=True) + use_jwt.assert_called_once_with(True) + + +@pytest.mark.parametrize("client_class", [ProjectsClient, ProjectsAsyncClient,]) +def test_projects_client_from_service_account_file(client_class): + creds = ga_credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + 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 == "cloudresourcemanager.googleapis.com:443" + + +def test_projects_client_get_transport_class(): + transport = ProjectsClient.get_transport_class() + available_transports = [ + transports.ProjectsGrpcTransport, + ] + assert transport in available_transports + + transport = ProjectsClient.get_transport_class("grpc") + assert transport == transports.ProjectsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ProjectsClient, transports.ProjectsGrpcTransport, "grpc"), + (ProjectsAsyncClient, transports.ProjectsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + ProjectsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(ProjectsClient) +) +@mock.patch.object( + ProjectsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ProjectsAsyncClient), +) +def test_projects_client_client_options(client_class, transport_class, transport_name): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(ProjectsClient, "get_transport_class") as gtc: + transport = transport_class(credentials=ga_credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(ProjectsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + 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, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + 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="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (ProjectsClient, transports.ProjectsGrpcTransport, "grpc", "true"), + ( + ProjectsAsyncClient, + transports.ProjectsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (ProjectsClient, transports.ProjectsGrpcTransport, "grpc", "false"), + ( + ProjectsAsyncClient, + transports.ProjectsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + ProjectsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(ProjectsClient) +) +@mock.patch.object( + ProjectsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ProjectsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_projects_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + 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, + 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. + 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=True, + ): + with mock.patch( + "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 + + patched.return_value = None + client = client_class() + 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 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", + [ + (ProjectsClient, transports.ProjectsGrpcTransport, "grpc"), + (ProjectsAsyncClient, transports.ProjectsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_projects_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + 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", + [ + (ProjectsClient, transports.ProjectsGrpcTransport, "grpc"), + (ProjectsAsyncClient, transports.ProjectsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_projects_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_projects_client_client_options_from_dict(): + with mock.patch( + "google.cloud.resourcemanager_v3.services.projects.transports.ProjectsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ProjectsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_get_project(transport: str = "grpc", request_type=projects.GetProjectRequest): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.Project( + name="name_value", + parent="parent_value", + project_id="project_id_value", + state=projects.Project.State.ACTIVE, + display_name="display_name_value", + etag="etag_value", + ) + response = client.get_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.GetProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, projects.Project) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.project_id == "project_id_value" + assert response.state == projects.Project.State.ACTIVE + assert response.display_name == "display_name_value" + assert response.etag == "etag_value" + + +def test_get_project_from_dict(): + test_get_project(request_type=dict) + + +def test_get_project_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_project), "__call__") as call: + client.get_project() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.GetProjectRequest() + + +@pytest.mark.asyncio +async def test_get_project_async( + transport: str = "grpc_asyncio", request_type=projects.GetProjectRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + projects.Project( + name="name_value", + parent="parent_value", + project_id="project_id_value", + state=projects.Project.State.ACTIVE, + display_name="display_name_value", + etag="etag_value", + ) + ) + response = await client.get_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.GetProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, projects.Project) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.project_id == "project_id_value" + assert response.state == projects.Project.State.ACTIVE + assert response.display_name == "display_name_value" + assert response.etag == "etag_value" + + +@pytest.mark.asyncio +async def test_get_project_async_from_dict(): + await test_get_project_async(request_type=dict) + + +def test_get_project_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.GetProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_project), "__call__") as call: + call.return_value = projects.Project() + client.get_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_project_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.GetProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_project), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(projects.Project()) + await client.get_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_get_project_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.Project() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_project(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_get_project_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_project( + projects.GetProjectRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_project_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.Project() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(projects.Project()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_project(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_get_project_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_project( + projects.GetProjectRequest(), name="name_value", + ) + + +def test_list_projects( + transport: str = "grpc", request_type=projects.ListProjectsRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.ListProjectsResponse( + next_page_token="next_page_token_value", + ) + response = client.list_projects(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.ListProjectsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListProjectsPager) + assert response.next_page_token == "next_page_token_value" + + +def test_list_projects_from_dict(): + test_list_projects(request_type=dict) + + +def test_list_projects_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_projects), "__call__") as call: + client.list_projects() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.ListProjectsRequest() + + +@pytest.mark.asyncio +async def test_list_projects_async( + transport: str = "grpc_asyncio", request_type=projects.ListProjectsRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + projects.ListProjectsResponse(next_page_token="next_page_token_value",) + ) + response = await client.list_projects(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.ListProjectsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListProjectsAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_projects_async_from_dict(): + await test_list_projects_async(request_type=dict) + + +def test_list_projects_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.ListProjectsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_projects(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +def test_list_projects_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_projects( + projects.ListProjectsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_projects_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.ListProjectsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + projects.ListProjectsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_projects(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +@pytest.mark.asyncio +async def test_list_projects_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.list_projects( + projects.ListProjectsRequest(), parent="parent_value", + ) + + +def test_list_projects_pager(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_projects), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.ListProjectsResponse(projects=[], next_page_token="def",), + projects.ListProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + + metadata = () + pager = client.list_projects(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, projects.Project) for i in results) + + +def test_list_projects_pages(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_projects), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.ListProjectsResponse(projects=[], next_page_token="def",), + projects.ListProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + pages = list(client.list_projects(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_list_projects_async_pager(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_projects), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.ListProjectsResponse(projects=[], next_page_token="def",), + projects.ListProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + async_pager = await client.list_projects(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, projects.Project) for i in responses) + + +@pytest.mark.asyncio +async def test_list_projects_async_pages(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_projects), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.ListProjectsResponse(projects=[], next_page_token="def",), + projects.ListProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.ListProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_projects(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_search_projects( + transport: str = "grpc", request_type=projects.SearchProjectsRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.SearchProjectsResponse( + next_page_token="next_page_token_value", + ) + response = client.search_projects(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.SearchProjectsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.SearchProjectsPager) + assert response.next_page_token == "next_page_token_value" + + +def test_search_projects_from_dict(): + test_search_projects(request_type=dict) + + +def test_search_projects_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_projects), "__call__") as call: + client.search_projects() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.SearchProjectsRequest() + + +@pytest.mark.asyncio +async def test_search_projects_async( + transport: str = "grpc_asyncio", request_type=projects.SearchProjectsRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + projects.SearchProjectsResponse(next_page_token="next_page_token_value",) + ) + response = await client.search_projects(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.SearchProjectsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.SearchProjectsAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_search_projects_async_from_dict(): + await test_search_projects_async(request_type=dict) + + +def test_search_projects_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.SearchProjectsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.search_projects(query="query_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].query == "query_value" + + +def test_search_projects_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.search_projects( + projects.SearchProjectsRequest(), query="query_value", + ) + + +@pytest.mark.asyncio +async def test_search_projects_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_projects), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = projects.SearchProjectsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + projects.SearchProjectsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.search_projects(query="query_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].query == "query_value" + + +@pytest.mark.asyncio +async def test_search_projects_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.search_projects( + projects.SearchProjectsRequest(), query="query_value", + ) + + +def test_search_projects_pager(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_projects), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.SearchProjectsResponse(projects=[], next_page_token="def",), + projects.SearchProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + + metadata = () + pager = client.search_projects(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, projects.Project) for i in results) + + +def test_search_projects_pages(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_projects), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.SearchProjectsResponse(projects=[], next_page_token="def",), + projects.SearchProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + pages = list(client.search_projects(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_search_projects_async_pager(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_projects), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.SearchProjectsResponse(projects=[], next_page_token="def",), + projects.SearchProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + async_pager = await client.search_projects(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, projects.Project) for i in responses) + + +@pytest.mark.asyncio +async def test_search_projects_async_pages(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.search_projects), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(), projects.Project(),], + next_page_token="abc", + ), + projects.SearchProjectsResponse(projects=[], next_page_token="def",), + projects.SearchProjectsResponse( + projects=[projects.Project(),], next_page_token="ghi", + ), + projects.SearchProjectsResponse( + projects=[projects.Project(), projects.Project(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.search_projects(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_create_project( + transport: str = "grpc", request_type=projects.CreateProjectRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.create_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.CreateProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_project_from_dict(): + test_create_project(request_type=dict) + + +def test_create_project_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_project), "__call__") as call: + client.create_project() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.CreateProjectRequest() + + +@pytest.mark.asyncio +async def test_create_project_async( + transport: str = "grpc_asyncio", request_type=projects.CreateProjectRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.create_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.CreateProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_project_async_from_dict(): + await test_create_project_async(request_type=dict) + + +def test_create_project_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_project(project=projects.Project(name="name_value"),) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].project == projects.Project(name="name_value") + + +def test_create_project_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.create_project( + projects.CreateProjectRequest(), + project=projects.Project(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_project_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_project( + project=projects.Project(name="name_value"), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].project == projects.Project(name="name_value") + + +@pytest.mark.asyncio +async def test_create_project_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.create_project( + projects.CreateProjectRequest(), + project=projects.Project(name="name_value"), + ) + + +def test_update_project( + transport: str = "grpc", request_type=projects.UpdateProjectRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.update_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.UpdateProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_update_project_from_dict(): + test_update_project(request_type=dict) + + +def test_update_project_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_project), "__call__") as call: + client.update_project() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.UpdateProjectRequest() + + +@pytest.mark.asyncio +async def test_update_project_async( + transport: str = "grpc_asyncio", request_type=projects.UpdateProjectRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.update_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.UpdateProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_update_project_async_from_dict(): + await test_update_project_async(request_type=dict) + + +def test_update_project_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.UpdateProjectRequest() + + request.project.name = "project.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_project), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.update_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "project.name=project.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_project_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.UpdateProjectRequest() + + request.project.name = "project.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_project), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.update_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "project.name=project.name/value",) in kw[ + "metadata" + ] + + +def test_update_project_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_project( + project=projects.Project(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].project == projects.Project(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +def test_update_project_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.update_project( + projects.UpdateProjectRequest(), + project=projects.Project(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_project_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_project( + project=projects.Project(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].project == projects.Project(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_project_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.update_project( + projects.UpdateProjectRequest(), + project=projects.Project(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +def test_move_project( + transport: str = "grpc", request_type=projects.MoveProjectRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.move_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.MoveProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_move_project_from_dict(): + test_move_project(request_type=dict) + + +def test_move_project_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_project), "__call__") as call: + client.move_project() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.MoveProjectRequest() + + +@pytest.mark.asyncio +async def test_move_project_async( + transport: str = "grpc_asyncio", request_type=projects.MoveProjectRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.move_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.MoveProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_move_project_async_from_dict(): + await test_move_project_async(request_type=dict) + + +def test_move_project_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.MoveProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_project), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.move_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_move_project_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.MoveProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_project), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.move_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_move_project_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.move_project( + name="name_value", destination_parent="destination_parent_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + assert args[0].destination_parent == "destination_parent_value" + + +def test_move_project_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.move_project( + projects.MoveProjectRequest(), + name="name_value", + destination_parent="destination_parent_value", + ) + + +@pytest.mark.asyncio +async def test_move_project_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.move_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.move_project( + name="name_value", destination_parent="destination_parent_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + assert args[0].destination_parent == "destination_parent_value" + + +@pytest.mark.asyncio +async def test_move_project_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.move_project( + projects.MoveProjectRequest(), + name="name_value", + destination_parent="destination_parent_value", + ) + + +def test_delete_project( + transport: str = "grpc", request_type=projects.DeleteProjectRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.delete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.DeleteProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_delete_project_from_dict(): + test_delete_project(request_type=dict) + + +def test_delete_project_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_project), "__call__") as call: + client.delete_project() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.DeleteProjectRequest() + + +@pytest.mark.asyncio +async def test_delete_project_async( + transport: str = "grpc_asyncio", request_type=projects.DeleteProjectRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.delete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.DeleteProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_delete_project_async_from_dict(): + await test_delete_project_async(request_type=dict) + + +def test_delete_project_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.DeleteProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_project), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.delete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_delete_project_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.DeleteProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_project), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.delete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_delete_project_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.delete_project(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_delete_project_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.delete_project( + projects.DeleteProjectRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_project_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.delete_project(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_delete_project_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.delete_project( + projects.DeleteProjectRequest(), name="name_value", + ) + + +def test_undelete_project( + transport: str = "grpc", request_type=projects.UndeleteProjectRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.undelete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == projects.UndeleteProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_undelete_project_from_dict(): + test_undelete_project(request_type=dict) + + +def test_undelete_project_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_project), "__call__") as call: + client.undelete_project() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == projects.UndeleteProjectRequest() + + +@pytest.mark.asyncio +async def test_undelete_project_async( + transport: str = "grpc_asyncio", request_type=projects.UndeleteProjectRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.undelete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == projects.UndeleteProjectRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_undelete_project_async_from_dict(): + await test_undelete_project_async(request_type=dict) + + +def test_undelete_project_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.UndeleteProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_project), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.undelete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_undelete_project_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = projects.UndeleteProjectRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_project), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.undelete_project(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_undelete_project_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.undelete_project(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_undelete_project_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.undelete_project( + projects.UndeleteProjectRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_undelete_project_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.undelete_project), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.undelete_project(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_undelete_project_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.undelete_project( + projects.UndeleteProjectRequest(), name="name_value", + ) + + +def test_get_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_get_iam_policy_from_dict(): + test_get_iam_policy(request_type=dict) + + +def test_get_iam_policy_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + client.get_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_get_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_get_iam_policy_async_from_dict(): + await test_get_iam_policy_async(request_type=dict) + + +def test_get_iam_policy_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_iam_policy_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_get_iam_policy_from_dict_foreign(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.get_iam_policy( + request={ + "resource": "resource_value", + "options": options_pb2.GetPolicyOptions(requested_policy_version=2598), + } + ) + call.assert_called() + + +def test_get_iam_policy_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_get_iam_policy_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +def test_set_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_set_iam_policy_from_dict(): + test_set_iam_policy(request_type=dict) + + +def test_set_iam_policy_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + client.set_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_set_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_set_iam_policy_async_from_dict(): + await test_set_iam_policy_async(request_type=dict) + + +def test_set_iam_policy_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_set_iam_policy_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_set_iam_policy_from_dict_foreign(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.set_iam_policy( + request={ + "resource": "resource_value", + "policy": policy_pb2.Policy(version=774), + } + ) + call.assert_called() + + +def test_set_iam_policy_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_set_iam_policy_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +def test_test_iam_permissions( + transport: str = "grpc", request_type=iam_policy_pb2.TestIamPermissionsRequest +): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + response = client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +def test_test_iam_permissions_from_dict(): + test_test_iam_permissions(request_type=dict) + + +def test_test_iam_permissions_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 = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + client.test_iam_permissions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async( + transport: str = "grpc_asyncio", + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + ) + response = await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async_from_dict(): + await test_test_iam_permissions_async(request_type=dict) + + +def test_test_iam_permissions_field_headers(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_field_headers_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_test_iam_permissions_from_dict_foreign(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + response = client.test_iam_permissions( + request={ + "resource": "resource_value", + "permissions": ["permissions_value"], + } + ) + call.assert_called() + + +def test_test_iam_permissions_flattened(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +def test_test_iam_permissions_flattened_error(): + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_error_async(): + client = ProjectsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.ProjectsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ProjectsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ProjectsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ProjectsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ProjectsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ProjectsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + client = ProjectsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ProjectsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ProjectsGrpcAsyncIOTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.ProjectsGrpcTransport, transports.ProjectsGrpcAsyncIOTransport,], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = ProjectsClient(credentials=ga_credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.ProjectsGrpcTransport,) + + +def test_projects_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(core_exceptions.DuplicateCredentialArgs): + transport = transports.ProjectsTransport( + credentials=ga_credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_projects_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.resourcemanager_v3.services.projects.transports.ProjectsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ProjectsTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "get_project", + "list_projects", + "search_projects", + "create_project", + "update_project", + "move_project", + "delete_project", + "undelete_project", + "get_iam_policy", + "set_iam_policy", + "test_iam_permissions", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +@requires_google_auth_gte_1_25_0 +def test_projects_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.projects.transports.ProjectsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.ProjectsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@requires_google_auth_lt_1_25_0 +def test_projects_base_transport_with_credentials_file_old_google_auth(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.projects.transports.ProjectsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.ProjectsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +def test_projects_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch( + "google.cloud.resourcemanager_v3.services.projects.transports.ProjectsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.ProjectsTransport() + adc.assert_called_once() + + +@requires_google_auth_gte_1_25_0 +def test_projects_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + ProjectsClient() + adc.assert_called_once_with( + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@requires_google_auth_lt_1_25_0 +def test_projects_auth_adc_old_google_auth(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + ProjectsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.ProjectsGrpcTransport, transports.ProjectsGrpcAsyncIOTransport,], +) +@requires_google_auth_gte_1_25_0 +def test_projects_transport_auth_adc(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + adc.assert_called_once_with( + scopes=["1", "2"], + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.ProjectsGrpcTransport, transports.ProjectsGrpcAsyncIOTransport,], +) +@requires_google_auth_lt_1_25_0 +def test_projects_transport_auth_adc_old_google_auth(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.ProjectsGrpcTransport, grpc_helpers), + (transports.ProjectsGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +def test_projects_transport_create_channel(transport_class, grpc_helpers): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object( + google.auth, "default", autospec=True + ) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = ga_credentials.AnonymousCredentials() + adc.return_value = (creds, None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + + create_channel.assert_called_with( + "cloudresourcemanager.googleapis.com:443", + credentials=creds, + credentials_file=None, + quota_project_id="octopus", + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + scopes=["1", "2"], + default_host="cloudresourcemanager.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.ProjectsGrpcTransport, transports.ProjectsGrpcAsyncIOTransport], +) +def test_projects_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = ga_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=None, + 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_projects_host_no_port(): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:443" + + +def test_projects_host_with_port(): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:8000" + + +def test_projects_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ProjectsGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_projects_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ProjectsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + 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.ProjectsGrpcTransport, transports.ProjectsGrpcAsyncIOTransport], +) +def test_projects_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = ga_credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.ProjectsGrpcTransport, transports.ProjectsGrpcAsyncIOTransport], +) +def test_projects_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_projects_grpc_lro_client(): + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_projects_grpc_lro_async_client(): + client = ProjectsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_project_path(): + project = "squid" + expected = "projects/{project}".format(project=project,) + actual = ProjectsClient.project_path(project) + assert expected == actual + + +def test_parse_project_path(): + expected = { + "project": "clam", + } + path = ProjectsClient.project_path(**expected) + + # Check that the path construction is reversible. + actual = ProjectsClient.parse_project_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = ProjectsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = ProjectsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = ProjectsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + expected = "folders/{folder}".format(folder=folder,) + actual = ProjectsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = ProjectsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = ProjectsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + expected = "organizations/{organization}".format(organization=organization,) + actual = ProjectsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = ProjectsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = ProjectsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + expected = "projects/{project}".format(project=project,) + actual = ProjectsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = ProjectsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = ProjectsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = ProjectsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = ProjectsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = ProjectsClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.ProjectsTransport, "_prep_wrapped_messages" + ) as prep: + client = ProjectsClient( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.ProjectsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = ProjectsClient.get_transport_class() + transport = transport_class( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/resourcemanager_v3/test_tag_bindings.py b/tests/unit/gapic/resourcemanager_v3/test_tag_bindings.py new file mode 100644 index 0000000..d31101a --- /dev/null +++ b/tests/unit/gapic/resourcemanager_v3/test_tag_bindings.py @@ -0,0 +1,1770 @@ +# -*- 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. +# +import os +import mock +import packaging.version + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + + +from google.api_core import client_options +from google.api_core import exceptions as core_exceptions +from google.api_core import future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials as ga_credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.resourcemanager_v3.services.tag_bindings import TagBindingsAsyncClient +from google.cloud.resourcemanager_v3.services.tag_bindings import TagBindingsClient +from google.cloud.resourcemanager_v3.services.tag_bindings import pagers +from google.cloud.resourcemanager_v3.services.tag_bindings import transports +from google.cloud.resourcemanager_v3.services.tag_bindings.transports.base import ( + _GOOGLE_AUTH_VERSION, +) +from google.cloud.resourcemanager_v3.types import tag_bindings +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +import google.auth + + +# TODO(busunkim): Once google-auth >= 1.25.0 is required transitively +# through google-api-core: +# - Delete the auth "less than" test cases +# - Delete these pytest markers (Make the "greater than or equal to" tests the default). +requires_google_auth_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-auth < 1.25.0", +) +requires_google_auth_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-auth >= 1.25.0", +) + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert TagBindingsClient._get_default_mtls_endpoint(None) is None + assert ( + TagBindingsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + ) + assert ( + TagBindingsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + TagBindingsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + TagBindingsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert TagBindingsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [TagBindingsClient, TagBindingsAsyncClient,]) +def test_tag_bindings_client_from_service_account_info(client_class): + creds = ga_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 == "cloudresourcemanager.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [TagBindingsClient, TagBindingsAsyncClient,]) +def test_tag_bindings_client_service_account_always_use_jwt(client_class): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + client = client_class(credentials=creds) + use_jwt.assert_not_called() + + +@pytest.mark.parametrize( + "transport_class,transport_name", + [ + (transports.TagBindingsGrpcTransport, "grpc"), + (transports.TagBindingsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_tag_bindings_client_service_account_always_use_jwt_true( + transport_class, transport_name +): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + transport = transport_class(credentials=creds, always_use_jwt_access=True) + use_jwt.assert_called_once_with(True) + + +@pytest.mark.parametrize("client_class", [TagBindingsClient, TagBindingsAsyncClient,]) +def test_tag_bindings_client_from_service_account_file(client_class): + creds = ga_credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + 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 == "cloudresourcemanager.googleapis.com:443" + + +def test_tag_bindings_client_get_transport_class(): + transport = TagBindingsClient.get_transport_class() + available_transports = [ + transports.TagBindingsGrpcTransport, + ] + assert transport in available_transports + + transport = TagBindingsClient.get_transport_class("grpc") + assert transport == transports.TagBindingsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TagBindingsClient, transports.TagBindingsGrpcTransport, "grpc"), + ( + TagBindingsAsyncClient, + transports.TagBindingsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + TagBindingsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagBindingsClient) +) +@mock.patch.object( + TagBindingsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TagBindingsAsyncClient), +) +def test_tag_bindings_client_client_options( + client_class, transport_class, transport_name +): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(TagBindingsClient, "get_transport_class") as gtc: + transport = transport_class(credentials=ga_credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(TagBindingsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + 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, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + 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="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (TagBindingsClient, transports.TagBindingsGrpcTransport, "grpc", "true"), + ( + TagBindingsAsyncClient, + transports.TagBindingsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (TagBindingsClient, transports.TagBindingsGrpcTransport, "grpc", "false"), + ( + TagBindingsAsyncClient, + transports.TagBindingsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TagBindingsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagBindingsClient) +) +@mock.patch.object( + TagBindingsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TagBindingsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_tag_bindings_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + 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, + 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. + 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=True, + ): + with mock.patch( + "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 + + patched.return_value = None + client = client_class() + 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 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", + [ + (TagBindingsClient, transports.TagBindingsGrpcTransport, "grpc"), + ( + TagBindingsAsyncClient, + transports.TagBindingsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_tag_bindings_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + 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", + [ + (TagBindingsClient, transports.TagBindingsGrpcTransport, "grpc"), + ( + TagBindingsAsyncClient, + transports.TagBindingsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_tag_bindings_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_tag_bindings_client_client_options_from_dict(): + with mock.patch( + "google.cloud.resourcemanager_v3.services.tag_bindings.transports.TagBindingsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = TagBindingsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_tag_bindings( + transport: str = "grpc", request_type=tag_bindings.ListTagBindingsRequest +): + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = tag_bindings.ListTagBindingsResponse( + next_page_token="next_page_token_value", + ) + response = client.list_tag_bindings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.ListTagBindingsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTagBindingsPager) + assert response.next_page_token == "next_page_token_value" + + +def test_list_tag_bindings_from_dict(): + test_list_tag_bindings(request_type=dict) + + +def test_list_tag_bindings_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 = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), "__call__" + ) as call: + client.list_tag_bindings() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.ListTagBindingsRequest() + + +@pytest.mark.asyncio +async def test_list_tag_bindings_async( + transport: str = "grpc_asyncio", request_type=tag_bindings.ListTagBindingsRequest +): + client = TagBindingsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_bindings.ListTagBindingsResponse( + next_page_token="next_page_token_value", + ) + ) + response = await client.list_tag_bindings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.ListTagBindingsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTagBindingsAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_tag_bindings_async_from_dict(): + await test_list_tag_bindings_async(request_type=dict) + + +def test_list_tag_bindings_flattened(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = tag_bindings.ListTagBindingsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_tag_bindings(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +def test_list_tag_bindings_flattened_error(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_tag_bindings( + tag_bindings.ListTagBindingsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_tag_bindings_flattened_async(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = tag_bindings.ListTagBindingsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_bindings.ListTagBindingsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_tag_bindings(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +@pytest.mark.asyncio +async def test_list_tag_bindings_flattened_error_async(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.list_tag_bindings( + tag_bindings.ListTagBindingsRequest(), parent="parent_value", + ) + + +def test_list_tag_bindings_pager(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_bindings.ListTagBindingsResponse( + tag_bindings=[ + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + ], + next_page_token="abc", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[], next_page_token="def", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(),], next_page_token="ghi", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(), tag_bindings.TagBinding(),], + ), + RuntimeError, + ) + + metadata = () + pager = client.list_tag_bindings(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, tag_bindings.TagBinding) for i in results) + + +def test_list_tag_bindings_pages(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_bindings.ListTagBindingsResponse( + tag_bindings=[ + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + ], + next_page_token="abc", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[], next_page_token="def", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(),], next_page_token="ghi", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(), tag_bindings.TagBinding(),], + ), + RuntimeError, + ) + pages = list(client.list_tag_bindings(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_list_tag_bindings_async_pager(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_bindings.ListTagBindingsResponse( + tag_bindings=[ + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + ], + next_page_token="abc", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[], next_page_token="def", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(),], next_page_token="ghi", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(), tag_bindings.TagBinding(),], + ), + RuntimeError, + ) + async_pager = await client.list_tag_bindings(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, tag_bindings.TagBinding) for i in responses) + + +@pytest.mark.asyncio +async def test_list_tag_bindings_async_pages(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_bindings), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_bindings.ListTagBindingsResponse( + tag_bindings=[ + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + tag_bindings.TagBinding(), + ], + next_page_token="abc", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[], next_page_token="def", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(),], next_page_token="ghi", + ), + tag_bindings.ListTagBindingsResponse( + tag_bindings=[tag_bindings.TagBinding(), tag_bindings.TagBinding(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_tag_bindings(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_create_tag_binding( + transport: str = "grpc", request_type=tag_bindings.CreateTagBindingRequest +): + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.create_tag_binding(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.CreateTagBindingRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_tag_binding_from_dict(): + test_create_tag_binding(request_type=dict) + + +def test_create_tag_binding_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 = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_tag_binding), "__call__" + ) as call: + client.create_tag_binding() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.CreateTagBindingRequest() + + +@pytest.mark.asyncio +async def test_create_tag_binding_async( + transport: str = "grpc_asyncio", request_type=tag_bindings.CreateTagBindingRequest +): + client = TagBindingsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.create_tag_binding(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.CreateTagBindingRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_tag_binding_async_from_dict(): + await test_create_tag_binding_async(request_type=dict) + + +def test_create_tag_binding_flattened(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_tag_binding( + tag_binding=tag_bindings.TagBinding(name="name_value"), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].tag_binding == tag_bindings.TagBinding(name="name_value") + + +def test_create_tag_binding_flattened_error(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.create_tag_binding( + tag_bindings.CreateTagBindingRequest(), + tag_binding=tag_bindings.TagBinding(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_tag_binding_flattened_async(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_tag_binding( + tag_binding=tag_bindings.TagBinding(name="name_value"), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].tag_binding == tag_bindings.TagBinding(name="name_value") + + +@pytest.mark.asyncio +async def test_create_tag_binding_flattened_error_async(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.create_tag_binding( + tag_bindings.CreateTagBindingRequest(), + tag_binding=tag_bindings.TagBinding(name="name_value"), + ) + + +def test_delete_tag_binding( + transport: str = "grpc", request_type=tag_bindings.DeleteTagBindingRequest +): + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.delete_tag_binding(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.DeleteTagBindingRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_delete_tag_binding_from_dict(): + test_delete_tag_binding(request_type=dict) + + +def test_delete_tag_binding_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 = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_tag_binding), "__call__" + ) as call: + client.delete_tag_binding() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.DeleteTagBindingRequest() + + +@pytest.mark.asyncio +async def test_delete_tag_binding_async( + transport: str = "grpc_asyncio", request_type=tag_bindings.DeleteTagBindingRequest +): + client = TagBindingsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.delete_tag_binding(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_bindings.DeleteTagBindingRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_delete_tag_binding_async_from_dict(): + await test_delete_tag_binding_async(request_type=dict) + + +def test_delete_tag_binding_field_headers(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_bindings.DeleteTagBindingRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_tag_binding), "__call__" + ) as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.delete_tag_binding(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_delete_tag_binding_field_headers_async(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_bindings.DeleteTagBindingRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_tag_binding), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.delete_tag_binding(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_delete_tag_binding_flattened(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.delete_tag_binding(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_delete_tag_binding_flattened_error(): + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.delete_tag_binding( + tag_bindings.DeleteTagBindingRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_tag_binding_flattened_async(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_tag_binding), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.delete_tag_binding(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_delete_tag_binding_flattened_error_async(): + client = TagBindingsAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.delete_tag_binding( + tag_bindings.DeleteTagBindingRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.TagBindingsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.TagBindingsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagBindingsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.TagBindingsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagBindingsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.TagBindingsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + client = TagBindingsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.TagBindingsGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.TagBindingsGrpcAsyncIOTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagBindingsGrpcTransport, transports.TagBindingsGrpcAsyncIOTransport,], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = TagBindingsClient(credentials=ga_credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.TagBindingsGrpcTransport,) + + +def test_tag_bindings_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(core_exceptions.DuplicateCredentialArgs): + transport = transports.TagBindingsTransport( + credentials=ga_credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_tag_bindings_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.resourcemanager_v3.services.tag_bindings.transports.TagBindingsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.TagBindingsTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_tag_bindings", + "create_tag_binding", + "delete_tag_binding", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +@requires_google_auth_gte_1_25_0 +def test_tag_bindings_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_bindings.transports.TagBindingsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagBindingsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@requires_google_auth_lt_1_25_0 +def test_tag_bindings_base_transport_with_credentials_file_old_google_auth(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_bindings.transports.TagBindingsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagBindingsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +def test_tag_bindings_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_bindings.transports.TagBindingsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagBindingsTransport() + adc.assert_called_once() + + +@requires_google_auth_gte_1_25_0 +def test_tag_bindings_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + TagBindingsClient() + adc.assert_called_once_with( + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@requires_google_auth_lt_1_25_0 +def test_tag_bindings_auth_adc_old_google_auth(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + TagBindingsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagBindingsGrpcTransport, transports.TagBindingsGrpcAsyncIOTransport,], +) +@requires_google_auth_gte_1_25_0 +def test_tag_bindings_transport_auth_adc(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + adc.assert_called_once_with( + scopes=["1", "2"], + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagBindingsGrpcTransport, transports.TagBindingsGrpcAsyncIOTransport,], +) +@requires_google_auth_lt_1_25_0 +def test_tag_bindings_transport_auth_adc_old_google_auth(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.TagBindingsGrpcTransport, grpc_helpers), + (transports.TagBindingsGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +def test_tag_bindings_transport_create_channel(transport_class, grpc_helpers): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object( + google.auth, "default", autospec=True + ) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = ga_credentials.AnonymousCredentials() + adc.return_value = (creds, None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + + create_channel.assert_called_with( + "cloudresourcemanager.googleapis.com:443", + credentials=creds, + credentials_file=None, + quota_project_id="octopus", + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + scopes=["1", "2"], + default_host="cloudresourcemanager.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagBindingsGrpcTransport, transports.TagBindingsGrpcAsyncIOTransport], +) +def test_tag_bindings_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = ga_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=None, + 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_tag_bindings_host_no_port(): + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:443" + + +def test_tag_bindings_host_with_port(): + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:8000" + + +def test_tag_bindings_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TagBindingsGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_tag_bindings_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TagBindingsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + 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.TagBindingsGrpcTransport, transports.TagBindingsGrpcAsyncIOTransport], +) +def test_tag_bindings_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = ga_credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TagBindingsGrpcTransport, transports.TagBindingsGrpcAsyncIOTransport], +) +def test_tag_bindings_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_tag_bindings_grpc_lro_client(): + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_tag_bindings_grpc_lro_async_client(): + client = TagBindingsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_tag_binding_path(): + tag_binding = "squid" + expected = "tagBindings/{tag_binding}".format(tag_binding=tag_binding,) + actual = TagBindingsClient.tag_binding_path(tag_binding) + assert expected == actual + + +def test_parse_tag_binding_path(): + expected = { + "tag_binding": "clam", + } + path = TagBindingsClient.tag_binding_path(**expected) + + # Check that the path construction is reversible. + actual = TagBindingsClient.parse_tag_binding_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TagBindingsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = TagBindingsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TagBindingsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + expected = "folders/{folder}".format(folder=folder,) + actual = TagBindingsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = TagBindingsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TagBindingsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + expected = "organizations/{organization}".format(organization=organization,) + actual = TagBindingsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = TagBindingsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TagBindingsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + expected = "projects/{project}".format(project=project,) + actual = TagBindingsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = TagBindingsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TagBindingsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = TagBindingsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = TagBindingsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TagBindingsClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.TagBindingsTransport, "_prep_wrapped_messages" + ) as prep: + client = TagBindingsClient( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.TagBindingsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = TagBindingsClient.get_transport_class() + transport = transport_class( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/resourcemanager_v3/test_tag_keys.py b/tests/unit/gapic/resourcemanager_v3/test_tag_keys.py new file mode 100644 index 0000000..b5598a7 --- /dev/null +++ b/tests/unit/gapic/resourcemanager_v3/test_tag_keys.py @@ -0,0 +1,2772 @@ +# -*- 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. +# +import os +import mock +import packaging.version + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + + +from google.api_core import client_options +from google.api_core import exceptions as core_exceptions +from google.api_core import future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials as ga_credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.resourcemanager_v3.services.tag_keys import TagKeysAsyncClient +from google.cloud.resourcemanager_v3.services.tag_keys import TagKeysClient +from google.cloud.resourcemanager_v3.services.tag_keys import pagers +from google.cloud.resourcemanager_v3.services.tag_keys import transports +from google.cloud.resourcemanager_v3.services.tag_keys.transports.base import ( + _GOOGLE_AUTH_VERSION, +) +from google.cloud.resourcemanager_v3.types import tag_keys +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import options_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from google.type import expr_pb2 # type: ignore +import google.auth + + +# TODO(busunkim): Once google-auth >= 1.25.0 is required transitively +# through google-api-core: +# - Delete the auth "less than" test cases +# - Delete these pytest markers (Make the "greater than or equal to" tests the default). +requires_google_auth_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-auth < 1.25.0", +) +requires_google_auth_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-auth >= 1.25.0", +) + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert TagKeysClient._get_default_mtls_endpoint(None) is None + assert TagKeysClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + TagKeysClient._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint + ) + assert ( + TagKeysClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + TagKeysClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert TagKeysClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [TagKeysClient, TagKeysAsyncClient,]) +def test_tag_keys_client_from_service_account_info(client_class): + creds = ga_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 == "cloudresourcemanager.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [TagKeysClient, TagKeysAsyncClient,]) +def test_tag_keys_client_service_account_always_use_jwt(client_class): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + client = client_class(credentials=creds) + use_jwt.assert_not_called() + + +@pytest.mark.parametrize( + "transport_class,transport_name", + [ + (transports.TagKeysGrpcTransport, "grpc"), + (transports.TagKeysGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_tag_keys_client_service_account_always_use_jwt_true( + transport_class, transport_name +): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + transport = transport_class(credentials=creds, always_use_jwt_access=True) + use_jwt.assert_called_once_with(True) + + +@pytest.mark.parametrize("client_class", [TagKeysClient, TagKeysAsyncClient,]) +def test_tag_keys_client_from_service_account_file(client_class): + creds = ga_credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + 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 == "cloudresourcemanager.googleapis.com:443" + + +def test_tag_keys_client_get_transport_class(): + transport = TagKeysClient.get_transport_class() + available_transports = [ + transports.TagKeysGrpcTransport, + ] + assert transport in available_transports + + transport = TagKeysClient.get_transport_class("grpc") + assert transport == transports.TagKeysGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TagKeysClient, transports.TagKeysGrpcTransport, "grpc"), + (TagKeysAsyncClient, transports.TagKeysGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + TagKeysClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagKeysClient) +) +@mock.patch.object( + TagKeysAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagKeysAsyncClient) +) +def test_tag_keys_client_client_options(client_class, transport_class, transport_name): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(TagKeysClient, "get_transport_class") as gtc: + transport = transport_class(credentials=ga_credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(TagKeysClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + 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, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + 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="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (TagKeysClient, transports.TagKeysGrpcTransport, "grpc", "true"), + ( + TagKeysAsyncClient, + transports.TagKeysGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (TagKeysClient, transports.TagKeysGrpcTransport, "grpc", "false"), + ( + TagKeysAsyncClient, + transports.TagKeysGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TagKeysClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagKeysClient) +) +@mock.patch.object( + TagKeysAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagKeysAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_tag_keys_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + 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, + 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. + 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=True, + ): + with mock.patch( + "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 + + patched.return_value = None + client = client_class() + 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 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", + [ + (TagKeysClient, transports.TagKeysGrpcTransport, "grpc"), + (TagKeysAsyncClient, transports.TagKeysGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_tag_keys_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + 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", + [ + (TagKeysClient, transports.TagKeysGrpcTransport, "grpc"), + (TagKeysAsyncClient, transports.TagKeysGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_tag_keys_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_tag_keys_client_client_options_from_dict(): + with mock.patch( + "google.cloud.resourcemanager_v3.services.tag_keys.transports.TagKeysGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = TagKeysClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_tag_keys( + transport: str = "grpc", request_type=tag_keys.ListTagKeysRequest +): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_keys), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_keys.ListTagKeysResponse( + next_page_token="next_page_token_value", + ) + response = client.list_tag_keys(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.ListTagKeysRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTagKeysPager) + assert response.next_page_token == "next_page_token_value" + + +def test_list_tag_keys_from_dict(): + test_list_tag_keys(request_type=dict) + + +def test_list_tag_keys_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_keys), "__call__") as call: + client.list_tag_keys() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.ListTagKeysRequest() + + +@pytest.mark.asyncio +async def test_list_tag_keys_async( + transport: str = "grpc_asyncio", request_type=tag_keys.ListTagKeysRequest +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_keys), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_keys.ListTagKeysResponse(next_page_token="next_page_token_value",) + ) + response = await client.list_tag_keys(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.ListTagKeysRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTagKeysAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_tag_keys_async_from_dict(): + await test_list_tag_keys_async(request_type=dict) + + +def test_list_tag_keys_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_keys), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_keys.ListTagKeysResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_tag_keys(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +def test_list_tag_keys_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_tag_keys( + tag_keys.ListTagKeysRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_tag_keys_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_keys), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_keys.ListTagKeysResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_keys.ListTagKeysResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_tag_keys(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +@pytest.mark.asyncio +async def test_list_tag_keys_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.list_tag_keys( + tag_keys.ListTagKeysRequest(), parent="parent_value", + ) + + +def test_list_tag_keys_pager(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_keys), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(), tag_keys.TagKey(),], + next_page_token="abc", + ), + tag_keys.ListTagKeysResponse(tag_keys=[], next_page_token="def",), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(),], next_page_token="ghi", + ), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(),], + ), + RuntimeError, + ) + + metadata = () + pager = client.list_tag_keys(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, tag_keys.TagKey) for i in results) + + +def test_list_tag_keys_pages(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_keys), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(), tag_keys.TagKey(),], + next_page_token="abc", + ), + tag_keys.ListTagKeysResponse(tag_keys=[], next_page_token="def",), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(),], next_page_token="ghi", + ), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(),], + ), + RuntimeError, + ) + pages = list(client.list_tag_keys(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_list_tag_keys_async_pager(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_keys), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(), tag_keys.TagKey(),], + next_page_token="abc", + ), + tag_keys.ListTagKeysResponse(tag_keys=[], next_page_token="def",), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(),], next_page_token="ghi", + ), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(),], + ), + RuntimeError, + ) + async_pager = await client.list_tag_keys(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, tag_keys.TagKey) for i in responses) + + +@pytest.mark.asyncio +async def test_list_tag_keys_async_pages(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_keys), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(), tag_keys.TagKey(),], + next_page_token="abc", + ), + tag_keys.ListTagKeysResponse(tag_keys=[], next_page_token="def",), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(),], next_page_token="ghi", + ), + tag_keys.ListTagKeysResponse( + tag_keys=[tag_keys.TagKey(), tag_keys.TagKey(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_tag_keys(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_get_tag_key(transport: str = "grpc", request_type=tag_keys.GetTagKeyRequest): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_keys.TagKey( + name="name_value", + parent="parent_value", + short_name="short_name_value", + namespaced_name="namespaced_name_value", + description="description_value", + etag="etag_value", + ) + response = client.get_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.GetTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, tag_keys.TagKey) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.short_name == "short_name_value" + assert response.namespaced_name == "namespaced_name_value" + assert response.description == "description_value" + assert response.etag == "etag_value" + + +def test_get_tag_key_from_dict(): + test_get_tag_key(request_type=dict) + + +def test_get_tag_key_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_key), "__call__") as call: + client.get_tag_key() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.GetTagKeyRequest() + + +@pytest.mark.asyncio +async def test_get_tag_key_async( + transport: str = "grpc_asyncio", request_type=tag_keys.GetTagKeyRequest +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_keys.TagKey( + name="name_value", + parent="parent_value", + short_name="short_name_value", + namespaced_name="namespaced_name_value", + description="description_value", + etag="etag_value", + ) + ) + response = await client.get_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.GetTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, tag_keys.TagKey) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.short_name == "short_name_value" + assert response.namespaced_name == "namespaced_name_value" + assert response.description == "description_value" + assert response.etag == "etag_value" + + +@pytest.mark.asyncio +async def test_get_tag_key_async_from_dict(): + await test_get_tag_key_async(request_type=dict) + + +def test_get_tag_key_field_headers(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_keys.GetTagKeyRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_key), "__call__") as call: + call.return_value = tag_keys.TagKey() + client.get_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_tag_key_field_headers_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_keys.GetTagKeyRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_key), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(tag_keys.TagKey()) + await client.get_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_get_tag_key_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_keys.TagKey() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_tag_key(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_get_tag_key_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_tag_key( + tag_keys.GetTagKeyRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_tag_key_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_keys.TagKey() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(tag_keys.TagKey()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_tag_key(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_get_tag_key_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_tag_key( + tag_keys.GetTagKeyRequest(), name="name_value", + ) + + +def test_create_tag_key( + transport: str = "grpc", request_type=tag_keys.CreateTagKeyRequest +): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.create_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.CreateTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_tag_key_from_dict(): + test_create_tag_key(request_type=dict) + + +def test_create_tag_key_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_key), "__call__") as call: + client.create_tag_key() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.CreateTagKeyRequest() + + +@pytest.mark.asyncio +async def test_create_tag_key_async( + transport: str = "grpc_asyncio", request_type=tag_keys.CreateTagKeyRequest +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.create_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.CreateTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_tag_key_async_from_dict(): + await test_create_tag_key_async(request_type=dict) + + +def test_create_tag_key_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_tag_key(tag_key=tag_keys.TagKey(name="name_value"),) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].tag_key == tag_keys.TagKey(name="name_value") + + +def test_create_tag_key_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.create_tag_key( + tag_keys.CreateTagKeyRequest(), tag_key=tag_keys.TagKey(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_tag_key_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_tag_key( + tag_key=tag_keys.TagKey(name="name_value"), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].tag_key == tag_keys.TagKey(name="name_value") + + +@pytest.mark.asyncio +async def test_create_tag_key_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.create_tag_key( + tag_keys.CreateTagKeyRequest(), tag_key=tag_keys.TagKey(name="name_value"), + ) + + +def test_update_tag_key( + transport: str = "grpc", request_type=tag_keys.UpdateTagKeyRequest +): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.update_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.UpdateTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_update_tag_key_from_dict(): + test_update_tag_key(request_type=dict) + + +def test_update_tag_key_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_key), "__call__") as call: + client.update_tag_key() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.UpdateTagKeyRequest() + + +@pytest.mark.asyncio +async def test_update_tag_key_async( + transport: str = "grpc_asyncio", request_type=tag_keys.UpdateTagKeyRequest +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.update_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.UpdateTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_update_tag_key_async_from_dict(): + await test_update_tag_key_async(request_type=dict) + + +def test_update_tag_key_field_headers(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_keys.UpdateTagKeyRequest() + + request.tag_key.name = "tag_key.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_key), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.update_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "tag_key.name=tag_key.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_tag_key_field_headers_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_keys.UpdateTagKeyRequest() + + request.tag_key.name = "tag_key.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_key), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.update_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "tag_key.name=tag_key.name/value",) in kw[ + "metadata" + ] + + +def test_update_tag_key_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_tag_key( + tag_key=tag_keys.TagKey(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].tag_key == tag_keys.TagKey(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +def test_update_tag_key_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.update_tag_key( + tag_keys.UpdateTagKeyRequest(), + tag_key=tag_keys.TagKey(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_tag_key_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_tag_key( + tag_key=tag_keys.TagKey(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].tag_key == tag_keys.TagKey(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_tag_key_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.update_tag_key( + tag_keys.UpdateTagKeyRequest(), + tag_key=tag_keys.TagKey(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_tag_key( + transport: str = "grpc", request_type=tag_keys.DeleteTagKeyRequest +): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.delete_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.DeleteTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_delete_tag_key_from_dict(): + test_delete_tag_key(request_type=dict) + + +def test_delete_tag_key_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_key), "__call__") as call: + client.delete_tag_key() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.DeleteTagKeyRequest() + + +@pytest.mark.asyncio +async def test_delete_tag_key_async( + transport: str = "grpc_asyncio", request_type=tag_keys.DeleteTagKeyRequest +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.delete_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_keys.DeleteTagKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_delete_tag_key_async_from_dict(): + await test_delete_tag_key_async(request_type=dict) + + +def test_delete_tag_key_field_headers(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_keys.DeleteTagKeyRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_key), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.delete_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_delete_tag_key_field_headers_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_keys.DeleteTagKeyRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_key), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.delete_tag_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_delete_tag_key_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.delete_tag_key(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_delete_tag_key_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.delete_tag_key( + tag_keys.DeleteTagKeyRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_tag_key_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.delete_tag_key(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_delete_tag_key_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.delete_tag_key( + tag_keys.DeleteTagKeyRequest(), name="name_value", + ) + + +def test_get_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_get_iam_policy_from_dict(): + test_get_iam_policy(request_type=dict) + + +def test_get_iam_policy_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + client.get_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_get_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_get_iam_policy_async_from_dict(): + await test_get_iam_policy_async(request_type=dict) + + +def test_get_iam_policy_field_headers(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_iam_policy_field_headers_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_get_iam_policy_from_dict_foreign(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.get_iam_policy( + request={ + "resource": "resource_value", + "options": options_pb2.GetPolicyOptions(requested_policy_version=2598), + } + ) + call.assert_called() + + +def test_get_iam_policy_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_get_iam_policy_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +def test_set_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_set_iam_policy_from_dict(): + test_set_iam_policy(request_type=dict) + + +def test_set_iam_policy_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + client.set_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_set_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_set_iam_policy_async_from_dict(): + await test_set_iam_policy_async(request_type=dict) + + +def test_set_iam_policy_field_headers(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_set_iam_policy_field_headers_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_set_iam_policy_from_dict_foreign(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.set_iam_policy( + request={ + "resource": "resource_value", + "policy": policy_pb2.Policy(version=774), + } + ) + call.assert_called() + + +def test_set_iam_policy_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_set_iam_policy_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +def test_test_iam_permissions( + transport: str = "grpc", request_type=iam_policy_pb2.TestIamPermissionsRequest +): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + response = client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +def test_test_iam_permissions_from_dict(): + test_test_iam_permissions(request_type=dict) + + +def test_test_iam_permissions_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 = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + client.test_iam_permissions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async( + transport: str = "grpc_asyncio", + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + ) + response = await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async_from_dict(): + await test_test_iam_permissions_async(request_type=dict) + + +def test_test_iam_permissions_field_headers(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_field_headers_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_test_iam_permissions_from_dict_foreign(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + response = client.test_iam_permissions( + request={ + "resource": "resource_value", + "permissions": ["permissions_value"], + } + ) + call.assert_called() + + +def test_test_iam_permissions_flattened(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +def test_test_iam_permissions_flattened_error(): + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_error_async(): + client = TagKeysAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.TagKeysGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.TagKeysGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagKeysClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.TagKeysGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagKeysClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.TagKeysGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + client = TagKeysClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.TagKeysGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.TagKeysGrpcAsyncIOTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagKeysGrpcTransport, transports.TagKeysGrpcAsyncIOTransport,], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = TagKeysClient(credentials=ga_credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.TagKeysGrpcTransport,) + + +def test_tag_keys_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(core_exceptions.DuplicateCredentialArgs): + transport = transports.TagKeysTransport( + credentials=ga_credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_tag_keys_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.resourcemanager_v3.services.tag_keys.transports.TagKeysTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.TagKeysTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_tag_keys", + "get_tag_key", + "create_tag_key", + "update_tag_key", + "delete_tag_key", + "get_iam_policy", + "set_iam_policy", + "test_iam_permissions", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +@requires_google_auth_gte_1_25_0 +def test_tag_keys_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_keys.transports.TagKeysTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagKeysTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@requires_google_auth_lt_1_25_0 +def test_tag_keys_base_transport_with_credentials_file_old_google_auth(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_keys.transports.TagKeysTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagKeysTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +def test_tag_keys_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_keys.transports.TagKeysTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagKeysTransport() + adc.assert_called_once() + + +@requires_google_auth_gte_1_25_0 +def test_tag_keys_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + TagKeysClient() + adc.assert_called_once_with( + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@requires_google_auth_lt_1_25_0 +def test_tag_keys_auth_adc_old_google_auth(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + TagKeysClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagKeysGrpcTransport, transports.TagKeysGrpcAsyncIOTransport,], +) +@requires_google_auth_gte_1_25_0 +def test_tag_keys_transport_auth_adc(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + adc.assert_called_once_with( + scopes=["1", "2"], + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagKeysGrpcTransport, transports.TagKeysGrpcAsyncIOTransport,], +) +@requires_google_auth_lt_1_25_0 +def test_tag_keys_transport_auth_adc_old_google_auth(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.TagKeysGrpcTransport, grpc_helpers), + (transports.TagKeysGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +def test_tag_keys_transport_create_channel(transport_class, grpc_helpers): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object( + google.auth, "default", autospec=True + ) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = ga_credentials.AnonymousCredentials() + adc.return_value = (creds, None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + + create_channel.assert_called_with( + "cloudresourcemanager.googleapis.com:443", + credentials=creds, + credentials_file=None, + quota_project_id="octopus", + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + scopes=["1", "2"], + default_host="cloudresourcemanager.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagKeysGrpcTransport, transports.TagKeysGrpcAsyncIOTransport], +) +def test_tag_keys_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = ga_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=None, + 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_tag_keys_host_no_port(): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:443" + + +def test_tag_keys_host_with_port(): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:8000" + + +def test_tag_keys_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TagKeysGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_tag_keys_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TagKeysGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + 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.TagKeysGrpcTransport, transports.TagKeysGrpcAsyncIOTransport], +) +def test_tag_keys_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = ga_credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TagKeysGrpcTransport, transports.TagKeysGrpcAsyncIOTransport], +) +def test_tag_keys_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_tag_keys_grpc_lro_client(): + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_tag_keys_grpc_lro_async_client(): + client = TagKeysAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_tag_key_path(): + tag_key = "squid" + expected = "tagKeys/{tag_key}".format(tag_key=tag_key,) + actual = TagKeysClient.tag_key_path(tag_key) + assert expected == actual + + +def test_parse_tag_key_path(): + expected = { + "tag_key": "clam", + } + path = TagKeysClient.tag_key_path(**expected) + + # Check that the path construction is reversible. + actual = TagKeysClient.parse_tag_key_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TagKeysClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = TagKeysClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TagKeysClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + expected = "folders/{folder}".format(folder=folder,) + actual = TagKeysClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = TagKeysClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TagKeysClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + expected = "organizations/{organization}".format(organization=organization,) + actual = TagKeysClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = TagKeysClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TagKeysClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + expected = "projects/{project}".format(project=project,) + actual = TagKeysClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = TagKeysClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TagKeysClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = TagKeysClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = TagKeysClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TagKeysClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.TagKeysTransport, "_prep_wrapped_messages" + ) as prep: + client = TagKeysClient( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.TagKeysTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = TagKeysClient.get_transport_class() + transport = transport_class( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/resourcemanager_v3/test_tag_values.py b/tests/unit/gapic/resourcemanager_v3/test_tag_values.py new file mode 100644 index 0000000..bb0262e --- /dev/null +++ b/tests/unit/gapic/resourcemanager_v3/test_tag_values.py @@ -0,0 +1,2811 @@ +# -*- 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. +# +import os +import mock +import packaging.version + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + + +from google.api_core import client_options +from google.api_core import exceptions as core_exceptions +from google.api_core import future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials as ga_credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.resourcemanager_v3.services.tag_values import TagValuesAsyncClient +from google.cloud.resourcemanager_v3.services.tag_values import TagValuesClient +from google.cloud.resourcemanager_v3.services.tag_values import pagers +from google.cloud.resourcemanager_v3.services.tag_values import transports +from google.cloud.resourcemanager_v3.services.tag_values.transports.base import ( + _GOOGLE_AUTH_VERSION, +) +from google.cloud.resourcemanager_v3.types import tag_values +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import options_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore +from google.type import expr_pb2 # type: ignore +import google.auth + + +# TODO(busunkim): Once google-auth >= 1.25.0 is required transitively +# through google-api-core: +# - Delete the auth "less than" test cases +# - Delete these pytest markers (Make the "greater than or equal to" tests the default). +requires_google_auth_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-auth < 1.25.0", +) +requires_google_auth_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-auth >= 1.25.0", +) + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +# If default endpoint is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint(client): + return ( + "foo.googleapis.com" + if ("localhost" in client.DEFAULT_ENDPOINT) + else client.DEFAULT_ENDPOINT + ) + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert TagValuesClient._get_default_mtls_endpoint(None) is None + assert TagValuesClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + TagValuesClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + TagValuesClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + TagValuesClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert TagValuesClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [TagValuesClient, TagValuesAsyncClient,]) +def test_tag_values_client_from_service_account_info(client_class): + creds = ga_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 == "cloudresourcemanager.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [TagValuesClient, TagValuesAsyncClient,]) +def test_tag_values_client_service_account_always_use_jwt(client_class): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + client = client_class(credentials=creds) + use_jwt.assert_not_called() + + +@pytest.mark.parametrize( + "transport_class,transport_name", + [ + (transports.TagValuesGrpcTransport, "grpc"), + (transports.TagValuesGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_tag_values_client_service_account_always_use_jwt_true( + transport_class, transport_name +): + with mock.patch.object( + service_account.Credentials, "with_always_use_jwt_access", create=True + ) as use_jwt: + creds = service_account.Credentials(None, None, None) + transport = transport_class(credentials=creds, always_use_jwt_access=True) + use_jwt.assert_called_once_with(True) + + +@pytest.mark.parametrize("client_class", [TagValuesClient, TagValuesAsyncClient,]) +def test_tag_values_client_from_service_account_file(client_class): + creds = ga_credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + 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 == "cloudresourcemanager.googleapis.com:443" + + +def test_tag_values_client_get_transport_class(): + transport = TagValuesClient.get_transport_class() + available_transports = [ + transports.TagValuesGrpcTransport, + ] + assert transport in available_transports + + transport = TagValuesClient.get_transport_class("grpc") + assert transport == transports.TagValuesGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (TagValuesClient, transports.TagValuesGrpcTransport, "grpc"), + ( + TagValuesAsyncClient, + transports.TagValuesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + TagValuesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagValuesClient) +) +@mock.patch.object( + TagValuesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TagValuesAsyncClient), +) +def test_tag_values_client_client_options( + client_class, transport_class, transport_name +): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(TagValuesClient, "get_transport_class") as gtc: + transport = transport_class(credentials=ga_credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(TagValuesClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + 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, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + 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="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (TagValuesClient, transports.TagValuesGrpcTransport, "grpc", "true"), + ( + TagValuesAsyncClient, + transports.TagValuesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (TagValuesClient, transports.TagValuesGrpcTransport, "grpc", "false"), + ( + TagValuesAsyncClient, + transports.TagValuesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TagValuesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(TagValuesClient) +) +@mock.patch.object( + TagValuesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TagValuesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_tag_values_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + 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, + 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. + 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=True, + ): + with mock.patch( + "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 + + patched.return_value = None + client = client_class() + 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 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", + [ + (TagValuesClient, transports.TagValuesGrpcTransport, "grpc"), + ( + TagValuesAsyncClient, + transports.TagValuesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_tag_values_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + 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", + [ + (TagValuesClient, transports.TagValuesGrpcTransport, "grpc"), + ( + TagValuesAsyncClient, + transports.TagValuesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_tag_values_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_tag_values_client_client_options_from_dict(): + with mock.patch( + "google.cloud.resourcemanager_v3.services.tag_values.transports.TagValuesGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = TagValuesClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_tag_values( + transport: str = "grpc", request_type=tag_values.ListTagValuesRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_values), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_values.ListTagValuesResponse( + next_page_token="next_page_token_value", + ) + response = client.list_tag_values(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.ListTagValuesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTagValuesPager) + assert response.next_page_token == "next_page_token_value" + + +def test_list_tag_values_from_dict(): + test_list_tag_values(request_type=dict) + + +def test_list_tag_values_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_values), "__call__") as call: + client.list_tag_values() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.ListTagValuesRequest() + + +@pytest.mark.asyncio +async def test_list_tag_values_async( + transport: str = "grpc_asyncio", request_type=tag_values.ListTagValuesRequest +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_values), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_values.ListTagValuesResponse(next_page_token="next_page_token_value",) + ) + response = await client.list_tag_values(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.ListTagValuesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTagValuesAsyncPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_tag_values_async_from_dict(): + await test_list_tag_values_async(request_type=dict) + + +def test_list_tag_values_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_values), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_values.ListTagValuesResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_tag_values(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +def test_list_tag_values_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_tag_values( + tag_values.ListTagValuesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_tag_values_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_values), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_values.ListTagValuesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_values.ListTagValuesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_tag_values(parent="parent_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].parent == "parent_value" + + +@pytest.mark.asyncio +async def test_list_tag_values_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.list_tag_values( + tag_values.ListTagValuesRequest(), parent="parent_value", + ) + + +def test_list_tag_values_pager(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_values), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_values.ListTagValuesResponse( + tag_values=[ + tag_values.TagValue(), + tag_values.TagValue(), + tag_values.TagValue(), + ], + next_page_token="abc", + ), + tag_values.ListTagValuesResponse(tag_values=[], next_page_token="def",), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(),], next_page_token="ghi", + ), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(), tag_values.TagValue(),], + ), + RuntimeError, + ) + + metadata = () + pager = client.list_tag_values(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, tag_values.TagValue) for i in results) + + +def test_list_tag_values_pages(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_tag_values), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_values.ListTagValuesResponse( + tag_values=[ + tag_values.TagValue(), + tag_values.TagValue(), + tag_values.TagValue(), + ], + next_page_token="abc", + ), + tag_values.ListTagValuesResponse(tag_values=[], next_page_token="def",), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(),], next_page_token="ghi", + ), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(), tag_values.TagValue(),], + ), + RuntimeError, + ) + pages = list(client.list_tag_values(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_list_tag_values_async_pager(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_values), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_values.ListTagValuesResponse( + tag_values=[ + tag_values.TagValue(), + tag_values.TagValue(), + tag_values.TagValue(), + ], + next_page_token="abc", + ), + tag_values.ListTagValuesResponse(tag_values=[], next_page_token="def",), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(),], next_page_token="ghi", + ), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(), tag_values.TagValue(),], + ), + RuntimeError, + ) + async_pager = await client.list_tag_values(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, tag_values.TagValue) for i in responses) + + +@pytest.mark.asyncio +async def test_list_tag_values_async_pages(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_tag_values), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + tag_values.ListTagValuesResponse( + tag_values=[ + tag_values.TagValue(), + tag_values.TagValue(), + tag_values.TagValue(), + ], + next_page_token="abc", + ), + tag_values.ListTagValuesResponse(tag_values=[], next_page_token="def",), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(),], next_page_token="ghi", + ), + tag_values.ListTagValuesResponse( + tag_values=[tag_values.TagValue(), tag_values.TagValue(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_tag_values(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_get_tag_value( + transport: str = "grpc", request_type=tag_values.GetTagValueRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_values.TagValue( + name="name_value", + parent="parent_value", + short_name="short_name_value", + namespaced_name="namespaced_name_value", + description="description_value", + etag="etag_value", + ) + response = client.get_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.GetTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, tag_values.TagValue) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.short_name == "short_name_value" + assert response.namespaced_name == "namespaced_name_value" + assert response.description == "description_value" + assert response.etag == "etag_value" + + +def test_get_tag_value_from_dict(): + test_get_tag_value(request_type=dict) + + +def test_get_tag_value_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_value), "__call__") as call: + client.get_tag_value() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.GetTagValueRequest() + + +@pytest.mark.asyncio +async def test_get_tag_value_async( + transport: str = "grpc_asyncio", request_type=tag_values.GetTagValueRequest +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + tag_values.TagValue( + name="name_value", + parent="parent_value", + short_name="short_name_value", + namespaced_name="namespaced_name_value", + description="description_value", + etag="etag_value", + ) + ) + response = await client.get_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.GetTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, tag_values.TagValue) + assert response.name == "name_value" + assert response.parent == "parent_value" + assert response.short_name == "short_name_value" + assert response.namespaced_name == "namespaced_name_value" + assert response.description == "description_value" + assert response.etag == "etag_value" + + +@pytest.mark.asyncio +async def test_get_tag_value_async_from_dict(): + await test_get_tag_value_async(request_type=dict) + + +def test_get_tag_value_field_headers(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_values.GetTagValueRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_value), "__call__") as call: + call.return_value = tag_values.TagValue() + client.get_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_tag_value_field_headers_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_values.GetTagValueRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_value), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(tag_values.TagValue()) + await client.get_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_get_tag_value_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_values.TagValue() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_tag_value(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_get_tag_value_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_tag_value( + tag_values.GetTagValueRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_tag_value_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = tag_values.TagValue() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(tag_values.TagValue()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_tag_value(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_get_tag_value_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_tag_value( + tag_values.GetTagValueRequest(), name="name_value", + ) + + +def test_create_tag_value( + transport: str = "grpc", request_type=tag_values.CreateTagValueRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.create_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.CreateTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_tag_value_from_dict(): + test_create_tag_value(request_type=dict) + + +def test_create_tag_value_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_value), "__call__") as call: + client.create_tag_value() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.CreateTagValueRequest() + + +@pytest.mark.asyncio +async def test_create_tag_value_async( + transport: str = "grpc_asyncio", request_type=tag_values.CreateTagValueRequest +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.create_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.CreateTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_tag_value_async_from_dict(): + await test_create_tag_value_async(request_type=dict) + + +def test_create_tag_value_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_tag_value(tag_value=tag_values.TagValue(name="name_value"),) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].tag_value == tag_values.TagValue(name="name_value") + + +def test_create_tag_value_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.create_tag_value( + tag_values.CreateTagValueRequest(), + tag_value=tag_values.TagValue(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_tag_value_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_tag_value( + tag_value=tag_values.TagValue(name="name_value"), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].tag_value == tag_values.TagValue(name="name_value") + + +@pytest.mark.asyncio +async def test_create_tag_value_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.create_tag_value( + tag_values.CreateTagValueRequest(), + tag_value=tag_values.TagValue(name="name_value"), + ) + + +def test_update_tag_value( + transport: str = "grpc", request_type=tag_values.UpdateTagValueRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.update_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.UpdateTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_update_tag_value_from_dict(): + test_update_tag_value(request_type=dict) + + +def test_update_tag_value_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_value), "__call__") as call: + client.update_tag_value() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.UpdateTagValueRequest() + + +@pytest.mark.asyncio +async def test_update_tag_value_async( + transport: str = "grpc_asyncio", request_type=tag_values.UpdateTagValueRequest +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.update_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.UpdateTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_update_tag_value_async_from_dict(): + await test_update_tag_value_async(request_type=dict) + + +def test_update_tag_value_field_headers(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_values.UpdateTagValueRequest() + + request.tag_value.name = "tag_value.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_value), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.update_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "tag_value.name=tag_value.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_tag_value_field_headers_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_values.UpdateTagValueRequest() + + request.tag_value.name = "tag_value.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_value), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.update_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "tag_value.name=tag_value.name/value",) in kw[ + "metadata" + ] + + +def test_update_tag_value_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_tag_value( + tag_value=tag_values.TagValue(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].tag_value == tag_values.TagValue(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +def test_update_tag_value_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.update_tag_value( + tag_values.UpdateTagValueRequest(), + tag_value=tag_values.TagValue(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_tag_value_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_tag_value( + tag_value=tag_values.TagValue(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].tag_value == tag_values.TagValue(name="name_value") + assert args[0].update_mask == field_mask_pb2.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_tag_value_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.update_tag_value( + tag_values.UpdateTagValueRequest(), + tag_value=tag_values.TagValue(name="name_value"), + update_mask=field_mask_pb2.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_tag_value( + transport: str = "grpc", request_type=tag_values.DeleteTagValueRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.delete_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.DeleteTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_delete_tag_value_from_dict(): + test_delete_tag_value(request_type=dict) + + +def test_delete_tag_value_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_value), "__call__") as call: + client.delete_tag_value() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.DeleteTagValueRequest() + + +@pytest.mark.asyncio +async def test_delete_tag_value_async( + transport: str = "grpc_asyncio", request_type=tag_values.DeleteTagValueRequest +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + response = await client.delete_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == tag_values.DeleteTagValueRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_delete_tag_value_async_from_dict(): + await test_delete_tag_value_async(request_type=dict) + + +def test_delete_tag_value_field_headers(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_values.DeleteTagValueRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_value), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.delete_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_delete_tag_value_field_headers_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = tag_values.DeleteTagValueRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_value), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.delete_tag_value(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_delete_tag_value_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.delete_tag_value(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +def test_delete_tag_value_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.delete_tag_value( + tag_values.DeleteTagValueRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_tag_value_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_tag_value), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.delete_tag_value(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_delete_tag_value_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.delete_tag_value( + tag_values.DeleteTagValueRequest(), name="name_value", + ) + + +def test_get_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_get_iam_policy_from_dict(): + test_get_iam_policy(request_type=dict) + + +def test_get_iam_policy_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + client.get_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_get_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.GetIamPolicyRequest +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.GetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_get_iam_policy_async_from_dict(): + await test_get_iam_policy_async(request_type=dict) + + +def test_get_iam_policy_field_headers(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_iam_policy_field_headers_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.GetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.get_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_get_iam_policy_from_dict_foreign(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.get_iam_policy( + request={ + "resource": "resource_value", + "options": options_pb2.GetPolicyOptions(requested_policy_version=2598), + } + ) + call.assert_called() + + +def test_get_iam_policy_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_get_iam_policy_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_get_iam_policy_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_iam_policy( + iam_policy_pb2.GetIamPolicyRequest(), resource="resource_value", + ) + + +def test_set_iam_policy( + transport: str = "grpc", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy(version=774, etag=b"etag_blob",) + response = client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +def test_set_iam_policy_from_dict(): + test_set_iam_policy(request_type=dict) + + +def test_set_iam_policy_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + client.set_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + +@pytest.mark.asyncio +async def test_set_iam_policy_async( + transport: str = "grpc_asyncio", request_type=iam_policy_pb2.SetIamPolicyRequest +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + policy_pb2.Policy(version=774, etag=b"etag_blob",) + ) + response = await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.SetIamPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + assert response.version == 774 + assert response.etag == b"etag_blob" + + +@pytest.mark.asyncio +async def test_set_iam_policy_async_from_dict(): + await test_set_iam_policy_async(request_type=dict) + + +def test_set_iam_policy_field_headers(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = policy_pb2.Policy() + client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_set_iam_policy_field_headers_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.SetIamPolicyRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + await client.set_iam_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_set_iam_policy_from_dict_foreign(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + response = client.set_iam_policy( + request={ + "resource": "resource_value", + "policy": policy_pb2.Policy(version=774), + } + ) + call.assert_called() + + +def test_set_iam_policy_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +def test_set_iam_policy_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = policy_pb2.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(policy_pb2.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.set_iam_policy(resource="resource_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + + +@pytest.mark.asyncio +async def test_set_iam_policy_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.set_iam_policy( + iam_policy_pb2.SetIamPolicyRequest(), resource="resource_value", + ) + + +def test_test_iam_permissions( + transport: str = "grpc", request_type=iam_policy_pb2.TestIamPermissionsRequest +): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + response = client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +def test_test_iam_permissions_from_dict(): + test_test_iam_permissions(request_type=dict) + + +def test_test_iam_permissions_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 = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + client.test_iam_permissions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async( + transport: str = "grpc_asyncio", + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse( + permissions=["permissions_value"], + ) + ) + response = await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == iam_policy_pb2.TestIamPermissionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + assert response.permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_async_from_dict(): + await test_test_iam_permissions_async(request_type=dict) + + +def test_test_iam_permissions_field_headers(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_field_headers_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = iam_policy_pb2.TestIamPermissionsRequest() + + request.resource = "resource/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + await client.test_iam_permissions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "resource=resource/value",) in kw["metadata"] + + +def test_test_iam_permissions_from_dict_foreign(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + response = client.test_iam_permissions( + request={ + "resource": "resource_value", + "permissions": ["permissions_value"], + } + ) + call.assert_called() + + +def test_test_iam_permissions_flattened(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +def test_test_iam_permissions_flattened_error(): + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iam_policy_pb2.TestIamPermissionsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + iam_policy_pb2.TestIamPermissionsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.test_iam_permissions( + resource="resource_value", permissions=["permissions_value"], + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].resource == "resource_value" + assert args[0].permissions == ["permissions_value"] + + +@pytest.mark.asyncio +async def test_test_iam_permissions_flattened_error_async(): + client = TagValuesAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.test_iam_permissions( + iam_policy_pb2.TestIamPermissionsRequest(), + resource="resource_value", + permissions=["permissions_value"], + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.TagValuesGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.TagValuesGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagValuesClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.TagValuesGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TagValuesClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.TagValuesGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + client = TagValuesClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.TagValuesGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.TagValuesGrpcAsyncIOTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagValuesGrpcTransport, transports.TagValuesGrpcAsyncIOTransport,], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = TagValuesClient(credentials=ga_credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.TagValuesGrpcTransport,) + + +def test_tag_values_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(core_exceptions.DuplicateCredentialArgs): + transport = transports.TagValuesTransport( + credentials=ga_credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_tag_values_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.resourcemanager_v3.services.tag_values.transports.TagValuesTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.TagValuesTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_tag_values", + "get_tag_value", + "create_tag_value", + "update_tag_value", + "delete_tag_value", + "get_iam_policy", + "set_iam_policy", + "test_iam_permissions", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +@requires_google_auth_gte_1_25_0 +def test_tag_values_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_values.transports.TagValuesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagValuesTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@requires_google_auth_lt_1_25_0 +def test_tag_values_base_transport_with_credentials_file_old_google_auth(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + google.auth, "load_credentials_from_file", autospec=True + ) as load_creds, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_values.transports.TagValuesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagValuesTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +def test_tag_values_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch( + "google.cloud.resourcemanager_v3.services.tag_values.transports.TagValuesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport = transports.TagValuesTransport() + adc.assert_called_once() + + +@requires_google_auth_gte_1_25_0 +def test_tag_values_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + TagValuesClient() + adc.assert_called_once_with( + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@requires_google_auth_lt_1_25_0 +def test_tag_values_auth_adc_old_google_auth(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + TagValuesClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagValuesGrpcTransport, transports.TagValuesGrpcAsyncIOTransport,], +) +@requires_google_auth_gte_1_25_0 +def test_tag_values_transport_auth_adc(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + adc.assert_called_once_with( + scopes=["1", "2"], + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagValuesGrpcTransport, transports.TagValuesGrpcAsyncIOTransport,], +) +@requires_google_auth_lt_1_25_0 +def test_tag_values_transport_auth_adc_old_google_auth(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(google.auth, "default", autospec=True) as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.TagValuesGrpcTransport, grpc_helpers), + (transports.TagValuesGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +def test_tag_values_transport_create_channel(transport_class, grpc_helpers): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object( + google.auth, "default", autospec=True + ) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = ga_credentials.AnonymousCredentials() + adc.return_value = (creds, None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + + create_channel.assert_called_with( + "cloudresourcemanager.googleapis.com:443", + credentials=creds, + credentials_file=None, + quota_project_id="octopus", + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + ), + scopes=["1", "2"], + default_host="cloudresourcemanager.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.TagValuesGrpcTransport, transports.TagValuesGrpcAsyncIOTransport], +) +def test_tag_values_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = ga_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=None, + 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_tag_values_host_no_port(): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:443" + + +def test_tag_values_host_with_port(): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="cloudresourcemanager.googleapis.com:8000" + ), + ) + assert client.transport._host == "cloudresourcemanager.googleapis.com:8000" + + +def test_tag_values_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TagValuesGrpcTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +def test_tag_values_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.TagValuesGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + 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.TagValuesGrpcTransport, transports.TagValuesGrpcAsyncIOTransport], +) +def test_tag_values_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = ga_credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.TagValuesGrpcTransport, transports.TagValuesGrpcAsyncIOTransport], +) +def test_tag_values_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=None, + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_tag_values_grpc_lro_client(): + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_tag_values_grpc_lro_async_client(): + client = TagValuesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_tag_value_path(): + tag_value = "squid" + expected = "tagValues/{tag_value}".format(tag_value=tag_value,) + actual = TagValuesClient.tag_value_path(tag_value) + assert expected == actual + + +def test_parse_tag_value_path(): + expected = { + "tag_value": "clam", + } + path = TagValuesClient.tag_value_path(**expected) + + # Check that the path construction is reversible. + actual = TagValuesClient.parse_tag_value_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TagValuesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = TagValuesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TagValuesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + expected = "folders/{folder}".format(folder=folder,) + actual = TagValuesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = TagValuesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TagValuesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + expected = "organizations/{organization}".format(organization=organization,) + actual = TagValuesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = TagValuesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TagValuesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + expected = "projects/{project}".format(project=project,) + actual = TagValuesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = TagValuesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TagValuesClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = TagValuesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = TagValuesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TagValuesClient.parse_common_location_path(path) + assert expected == actual + + +def test_client_withDEFAULT_CLIENT_INFO(): + client_info = gapic_v1.client_info.ClientInfo() + + with mock.patch.object( + transports.TagValuesTransport, "_prep_wrapped_messages" + ) as prep: + client = TagValuesClient( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.TagValuesTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = TagValuesClient.get_transport_class() + transport = transport_class( + credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/test__http.py b/tests/unit/test__http.py deleted file mode 100644 index 63c32a8..0000000 --- a/tests/unit/test__http.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2015 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -import mock - - -class TestConnection(unittest.TestCase): - @staticmethod - def _get_target_class(): - from google.cloud.resource_manager._http import Connection - - return Connection - - def _make_one(self, *args, **kw): - return self._get_target_class()(*args, **kw) - - def test_build_api_url_no_extra_query_params(self): - from six.moves.urllib.parse import parse_qsl - from six.moves.urllib.parse import urlsplit - - conn = self._make_one(object()) - uri = conn.build_api_url("/foo") - scheme, netloc, path, qs, _ = urlsplit(uri) - self.assertEqual("%s://%s" % (scheme, netloc), conn.API_BASE_URL) - self.assertEqual(path, "/".join(["", conn.API_VERSION, "foo"])) - parms = dict(parse_qsl(qs)) - pretty_print = parms.pop("prettyPrint", "false") - self.assertEqual(pretty_print, "false") - self.assertEqual(parms, {}) - - def test_build_api_url_w_custom_endpoint(self): - from six.moves.urllib.parse import parse_qsl - from six.moves.urllib.parse import urlsplit - - custom_endpoint = "https://foo-cloudresourcemanager.googleapis.com" - conn = self._make_one(object(), api_endpoint=custom_endpoint) - uri = conn.build_api_url("/foo") - scheme, netloc, path, qs, _ = urlsplit(uri) - self.assertEqual("%s://%s" % (scheme, netloc), custom_endpoint) - self.assertEqual(path, "/".join(["", conn.API_VERSION, "foo"])) - parms = dict(parse_qsl(qs)) - pretty_print = parms.pop("prettyPrint", "false") - self.assertEqual(pretty_print, "false") - self.assertEqual(parms, {}) - - def test_build_api_url_w_extra_query_params(self): - from six.moves.urllib.parse import parse_qsl - from six.moves.urllib.parse import urlsplit - - conn = self._make_one(object()) - uri = conn.build_api_url("/foo", {"bar": "baz"}) - scheme, netloc, path, qs, _ = urlsplit(uri) - self.assertEqual("%s://%s" % (scheme, netloc), conn.API_BASE_URL) - self.assertEqual(path, "/".join(["", conn.API_VERSION, "foo"])) - parms = dict(parse_qsl(qs)) - self.assertEqual(parms["bar"], "baz") - - def test_extra_headers(self): - import requests - from google.cloud import _http as base_http - - http = mock.create_autospec(requests.Session, instance=True) - response = requests.Response() - response.status_code = 200 - data = b"brent-spiner" - response._content = data - http.request.return_value = response - client = mock.Mock(_http=http, spec=["_http"]) - - conn = self._make_one(client) - req_data = "req-data-boring" - result = conn.api_request("GET", "/rainbow", data=req_data, expect_json=False) - self.assertEqual(result, data) - - expected_headers = { - "Accept-Encoding": "gzip", - base_http.CLIENT_INFO_HEADER: conn.user_agent, - "User-Agent": conn.user_agent, - } - expected_uri = conn.build_api_url("/rainbow") - http.request.assert_called_once_with( - data=req_data, - headers=expected_headers, - method="GET", - url=expected_uri, - timeout=60, - ) diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py deleted file mode 100644 index 2742008..0000000 --- a/tests/unit/test_client.py +++ /dev/null @@ -1,380 +0,0 @@ -# Copyright 2015 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -import mock - - -def _make_credentials(): - import google.auth.credentials - - return mock.Mock(spec=google.auth.credentials.Credentials) - - -class TestClient(unittest.TestCase): - @staticmethod - def _get_target_class(): - from google.cloud.resource_manager.client import Client - - return Client - - def _make_one(self, *args, **kw): - return self._get_target_class()(*args, **kw) - - def test_ctor_wo_client_info(self): - from google.cloud._http import ClientInfo - from google.cloud.resource_manager._http import Connection - - http = object() - credentials = _make_credentials() - client = self._make_one(credentials=credentials, _http=http) - self.assertIsInstance(client._connection, Connection) - self.assertIs(client._credentials, credentials) - self.assertIs(client._http_internal, http) - self.assertIsInstance(client._connection._client_info, ClientInfo) - - def test_ctor_w_client_info(self): - from google.cloud._http import ClientInfo - from google.cloud.resource_manager._http import Connection - - http = object() - client_info = ClientInfo() - credentials = _make_credentials() - client = self._make_one( - credentials=credentials, _http=http, client_info=client_info - ) - self.assertIsInstance(client._connection, Connection) - self.assertIs(client._credentials, credentials) - self.assertIs(client._http_internal, http) - self.assertIs(client._connection._client_info, client_info) - - def test_ctor_w_empty_client_options(self): - from google.api_core.client_options import ClientOptions - - http = object() - client_options = ClientOptions() - client = self._make_one(_http=http, client_options=client_options) - self.assertEqual( - client._connection.API_BASE_URL, client._connection.DEFAULT_API_ENDPOINT - ) - - def test_ctor_w_client_options_object(self): - from google.api_core.client_options import ClientOptions - - http = object() - client_options = ClientOptions( - api_endpoint="https://foo-cloudresourcemanager.googleapis.com" - ) - client = self._make_one(_http=http, client_options=client_options) - self.assertEqual( - client._connection.API_BASE_URL, - "https://foo-cloudresourcemanager.googleapis.com", - ) - - def test_ctor_w_client_options_dict(self): - http = object() - client_options = { - "api_endpoint": "https://foo-cloudresourcemanager.googleapis.com" - } - - client = self._make_one(_http=http, client_options=client_options) - self.assertEqual( - client._connection.API_BASE_URL, - "https://foo-cloudresourcemanager.googleapis.com", - ) - - def test_new_project_factory(self): - from google.cloud.resource_manager.project import Project - - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - project_id = "project_id" - name = object() - labels = object() - project = client.new_project(project_id, name=name, labels=labels) - - self.assertIsInstance(project, Project) - self.assertEqual(project._client, client) - self.assertEqual(project.project_id, project_id) - self.assertEqual(project.name, name) - self.assertEqual(project.labels, labels) - - def test_fetch_project(self): - from google.cloud.resource_manager.project import Project - - project_id = "project-id" - project_number = 123 - project_name = "Project Name" - labels = {"env": "prod"} - project_resource = { - "projectId": project_id, - "projectNumber": project_number, - "name": project_name, - "labels": labels, - "lifecycleState": "ACTIVE", - } - - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - # Patch the connection with one we can easily control. - client._connection = _Connection(project_resource) - - project = client.fetch_project(project_id) - self.assertIsInstance(project, Project) - self.assertEqual(project._client, client) - self.assertEqual(project.project_id, project_id) - self.assertEqual(project.name, project_name) - self.assertEqual(project.labels, labels) - - def test_list_projects_return_type(self): - from google.api_core import page_iterator - - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - # Patch the connection with one we can easily control. - client._connection = _Connection({}) - - results = client.list_projects() - self.assertIsInstance(results, page_iterator.HTTPIterator) - - def test_list_projects_no_paging(self): - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - - PROJECT_ID = "project-id" - PROJECT_NUMBER = 1 - STATUS = "ACTIVE" - PROJECTS_RESOURCE = { - "projects": [ - { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "lifecycleState": STATUS, - } - ] - } - # Patch the connection with one we can easily control. - client._connection = _Connection(PROJECTS_RESOURCE) - # Make sure there will be no paging. - self.assertFalse("nextPageToken" in PROJECTS_RESOURCE) - - results = list(client.list_projects()) - - (project,) = results - self.assertEqual(project.project_id, PROJECT_ID) - self.assertEqual(project.number, PROJECT_NUMBER) - self.assertEqual(project.status, STATUS) - - def test_list_projects_with_paging(self): - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - - PROJECT_ID1 = "project-id" - PROJECT_NUMBER1 = 1 - STATUS = "ACTIVE" - TOKEN = "next-page-token" - FIRST_PROJECTS_RESOURCE = { - "projects": [ - { - "projectId": PROJECT_ID1, - "projectNumber": PROJECT_NUMBER1, - "lifecycleState": STATUS, - } - ], - "nextPageToken": TOKEN, - } - PROJECT_ID2 = "project-id-2" - PROJECT_NUMBER2 = 42 - SECOND_PROJECTS_RESOURCE = { - "projects": [ - { - "projectId": PROJECT_ID2, - "projectNumber": PROJECT_NUMBER2, - "lifecycleState": STATUS, - } - ] - } - # Patch the connection with one we can easily control. - client._connection = _Connection( - FIRST_PROJECTS_RESOURCE, SECOND_PROJECTS_RESOURCE - ) - - # Page size = 1 with two response means we'll have two requests. - results = list(client.list_projects(page_size=1)) - - # Check that the results are as expected. - project1, project2 = results - self.assertEqual(project1.project_id, PROJECT_ID1) - self.assertEqual(project1.number, PROJECT_NUMBER1) - self.assertEqual(project1.status, STATUS) - self.assertEqual(project2.project_id, PROJECT_ID2) - self.assertEqual(project2.number, PROJECT_NUMBER2) - self.assertEqual(project2.status, STATUS) - - # Check that two requests were required since page_size=1. - request1, request2 = client._connection._requested - self.assertEqual( - request1, - {"path": "/projects", "method": "GET", "query_params": {"pageSize": 1}}, - ) - self.assertEqual( - request2, - { - "path": "/projects", - "method": "GET", - "query_params": {"pageSize": 1, "pageToken": TOKEN}, - }, - ) - - def test_list_projects_with_filter(self): - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - - PROJECT_ID = "project-id" - PROJECT_NUMBER = 1 - STATUS = "ACTIVE" - PROJECTS_RESOURCE = { - "projects": [ - { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "lifecycleState": STATUS, - } - ] - } - # Patch the connection with one we can easily control. - client._connection = _Connection(PROJECTS_RESOURCE) - - FILTER_PARAMS = {"id": "project-id"} - results = list(client.list_projects(filter_params=FILTER_PARAMS)) - - (project,) = results - self.assertEqual(project.project_id, PROJECT_ID) - self.assertEqual(project.number, PROJECT_NUMBER) - self.assertEqual(project.status, STATUS) - - # Check that the filter made it in the request. - FLATTENED_FILTER_PARAMS = "id:project-id" - (request,) = client._connection._requested - self.assertEqual( - request, - { - "path": "/projects", - "method": "GET", - "query_params": {"filter": FLATTENED_FILTER_PARAMS}, - }, - ) - - def test_list_projects_with_multiple_filters(self): - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - - PROJECT_ID = "project-id" - PROJECT_NAME = "MyProjectName" - PROJECT_NUMBER = 1 - STATUS = "ACTIVE" - PROJECTS_RESOURCE = { - "projects": [ - { - "projectId": PROJECT_ID, - "name": PROJECT_NAME, - "projectNumber": PROJECT_NUMBER, - "lifecycleState": STATUS, - } - ] - } - # Patch the connection with one we can easily control. - client._connection = _Connection(PROJECTS_RESOURCE) - - FILTER_PARAMS = {"id": "project-id", "name": "MyProjectName"} - results = list(client.list_projects(filter_params=FILTER_PARAMS)) - - (project,) = results - self.assertEqual(project.project_id, PROJECT_ID) - self.assertEqual(project.number, PROJECT_NUMBER) - self.assertEqual(project.status, STATUS) - - # Check that the filter made it in the request. - (request,) = client._connection._requested - self.assertEquals(len(request.keys()), 3) - self.assertEqual(request["path"], "/projects") - self.assertEqual(request["method"], "GET") - - # Filter param order can change (since it is in a dict) - request_filter_params = request["query_params"]["filter"].split() - self.assertEquals(len(request_filter_params), 2) - self.assertTrue("id:project-id" in request_filter_params) - self.assertTrue("name:MyProjectName" in request_filter_params) - - def test_page_empty_response(self): - from google.api_core import page_iterator - - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - iterator = client.list_projects() - page = page_iterator.Page(iterator, (), None) - iterator._page = page - self.assertEqual(page.num_items, 0) - self.assertEqual(page.remaining, 0) - self.assertEqual(list(page), []) - - def test_page_non_empty_response(self): - import six - from google.cloud.resource_manager.project import Project - - project_id = "project-id" - project_name = "My Project Name" - project_number = 12345678 - project_labels = {"env": "prod"} - project_lifecycle_state = "ACTIVE" - api_resource = { - "projectId": project_id, - "name": project_name, - "projectNumber": project_number, - "labels": project_labels, - "lifecycleState": project_lifecycle_state, - } - response = {"projects": [api_resource]} - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - - def dummy_response(): - return response - - iterator = client.list_projects() - iterator._get_next_page_response = dummy_response - - page = six.next(iterator.pages) - self.assertEqual(page.num_items, 1) - project = six.next(page) - self.assertEqual(page.remaining, 0) - self.assertIsInstance(project, Project) - self.assertEqual(project.project_id, project_id) - self.assertEqual(project._client, client) - self.assertEqual(project.name, project_name) - self.assertEqual(project.number, project_number) - self.assertEqual(project.labels, project_labels) - self.assertEqual(project.status, project_lifecycle_state) - - -class _Connection(object): - def __init__(self, *responses): - self._responses = responses - self._requested = [] - - def api_request(self, **kw): - self._requested.append(kw) - response, self._responses = self._responses[0], self._responses[1:] - return response diff --git a/tests/unit/test_project.py b/tests/unit/test_project.py deleted file mode 100644 index 0ecc44c..0000000 --- a/tests/unit/test_project.py +++ /dev/null @@ -1,328 +0,0 @@ -# Copyright 2015 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - - -class TestProject(unittest.TestCase): - @staticmethod - def _get_target_class(): - from google.cloud.resource_manager.project import Project - - return Project - - def _make_one(self, *args, **kw): - return self._get_target_class()(*args, **kw) - - def test_constructor_defaults(self): - client = object() - PROJECT_ID = "project-id" - project = self._make_one(PROJECT_ID, client) - self.assertEqual(project.project_id, PROJECT_ID) - self.assertEqual(project._client, client) - self.assertIsNone(project.name) - self.assertIsNone(project.number) - self.assertEqual(project.labels, {}) - self.assertIsNone(project.status) - self.assertIsNone(project.parent) - - def test_constructor_explicit(self): - client = object() - PROJECT_ID = "project-id" - DISPLAY_NAME = "name" - LABELS = {"foo": "bar"} - project = self._make_one(PROJECT_ID, client, name=DISPLAY_NAME, labels=LABELS) - self.assertEqual(project.project_id, PROJECT_ID) - self.assertEqual(project._client, client) - self.assertEqual(project.name, DISPLAY_NAME) - self.assertIsNone(project.number) - self.assertEqual(project.labels, LABELS) - self.assertIsNone(project.status) - self.assertIsNone(project.parent) - - def test_from_api_repr(self): - client = object() - PROJECT_ID = "project-id" - PROJECT_NAME = "My Project Name" - PROJECT_NUMBER = 12345678 - PROJECT_LABELS = {"env": "prod"} - PROJECT_LIFECYCLE_STATE = "ACTIVE" - PARENT = {"type": "organization", "id": "433637338579"} - - resource = { - "projectId": PROJECT_ID, - "name": PROJECT_NAME, - "projectNumber": PROJECT_NUMBER, - "labels": PROJECT_LABELS, - "lifecycleState": PROJECT_LIFECYCLE_STATE, - "parent": PARENT, - } - project = self._get_target_class().from_api_repr(resource, client) - self.assertEqual(project.project_id, PROJECT_ID) - self.assertEqual(project._client, client) - self.assertEqual(project.name, PROJECT_NAME) - self.assertEqual(project.number, PROJECT_NUMBER) - self.assertEqual(project.labels, PROJECT_LABELS) - self.assertEqual(project.status, PROJECT_LIFECYCLE_STATE) - self.assertEqual(project.parent, PARENT) - - def test_full_name(self): - PROJECT_ID = "project-id" - project = self._make_one(PROJECT_ID, None) - self.assertEqual("projects/%s" % PROJECT_ID, project.full_name) - - def test_full_name_missing_id(self): - project = self._make_one(None, None) - with self.assertRaises(ValueError): - self.assertIsNone(project.full_name) - - def test_path(self): - PROJECT_ID = "project-id" - project = self._make_one(PROJECT_ID, None) - self.assertEqual("/projects/%s" % PROJECT_ID, project.path) - - def test_create(self): - PROJECT_ID = "project-id" - PROJECT_NUMBER = 123 - PROJECT_RESOURCE = { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "name": "Project Name", - "labels": {}, - "lifecycleState": "ACTIVE", - "parent": {"type": "organization", "id": "433637338589"}, - } - connection = _Connection(PROJECT_RESOURCE) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - self.assertIsNone(project.number) - project.create() - self.assertEqual(project.number, PROJECT_NUMBER) - (request,) = connection._requested - - expected_request = { - "method": "POST", - "data": {"projectId": PROJECT_ID, "labels": {}, "name": None}, - "path": "/projects", - } - self.assertEqual(request, expected_request) - - def test_reload(self): - PROJECT_ID = "project-id" - PROJECT_NUMBER = 123 - PROJECT_RESOURCE = { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "name": "Project Name", - "labels": {"env": "prod"}, - "lifecycleState": "ACTIVE", - "parent": {"type": "organization", "id": "433637338579"}, - } - connection = _Connection(PROJECT_RESOURCE) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - self.assertIsNone(project.number) - self.assertIsNone(project.name) - self.assertEqual(project.labels, {}) - self.assertIsNone(project.status) - project.reload() - self.assertEqual(project.name, PROJECT_RESOURCE["name"]) - self.assertEqual(project.number, PROJECT_NUMBER) - self.assertEqual(project.labels, PROJECT_RESOURCE["labels"]) - self.assertEqual(project.status, PROJECT_RESOURCE["lifecycleState"]) - - (request,) = connection._requested - # NOTE: data is not in the request since a GET request. - expected_request = {"method": "GET", "path": project.path} - self.assertEqual(request, expected_request) - - def test_exists(self): - PROJECT_ID = "project-id" - connection = _Connection({"projectId": PROJECT_ID}) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - self.assertTrue(project.exists()) - - def test_exists_with_explicitly_passed_client(self): - PROJECT_ID = "project-id" - connection = _Connection({"projectId": PROJECT_ID}) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, None) - self.assertTrue(project.exists(client=client)) - - def test_exists_with_missing_client(self): - PROJECT_ID = "project-id" - project = self._make_one(PROJECT_ID, None) - with self.assertRaises(AttributeError): - project.exists() - - def test_exists_not_found(self): - PROJECT_ID = "project-id" - connection = _Connection() - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - self.assertFalse(project.exists()) - - def test_update(self): - PROJECT_ID = "project-id" - PROJECT_NUMBER = 123 - PROJECT_NAME = "Project Name" - LABELS = {"env": "prod"} - PROJECT_RESOURCE = { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "name": PROJECT_NAME, - "labels": LABELS, - "lifecycleState": "ACTIVE", - } - connection = _Connection(PROJECT_RESOURCE) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - project.name = PROJECT_NAME - project.labels = LABELS - project.update() - - (request,) = connection._requested - expected_request = { - "method": "PUT", - "data": {"name": PROJECT_NAME, "labels": LABELS, "parent": None}, - "path": project.path, - } - self.assertEqual(request, expected_request) - - def test_delete_without_reload_data(self): - PROJECT_ID = "project-id" - PROJECT_NUMBER = 123 - PROJECT_RESOURCE = { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "name": "Project Name", - "labels": {"env": "prod"}, - "lifecycleState": "ACTIVE", - "parent": {"type": "organization", "id": "433637338579"}, - } - connection = _Connection(PROJECT_RESOURCE) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - project.delete(reload_data=False) - - (request,) = connection._requested - # NOTE: data is not in the request since a DELETE request. - expected_request = {"method": "DELETE", "path": project.path} - self.assertEqual(request, expected_request) - - def test_delete_with_reload_data(self): - PROJECT_ID = "project-id" - PROJECT_NUMBER = 123 - PROJECT_RESOURCE = { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "name": "Project Name", - "labels": {"env": "prod"}, - "lifecycleState": "ACTIVE", - "parent": {"type": "organization", "id": "433637338579"}, - } - DELETING_PROJECT = PROJECT_RESOURCE.copy() - DELETING_PROJECT["lifecycleState"] = NEW_STATE = "DELETE_REQUESTED" - - connection = _Connection(PROJECT_RESOURCE, DELETING_PROJECT) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - project.delete(reload_data=True) - self.assertEqual(project.status, NEW_STATE) - - delete_request, get_request = connection._requested - # NOTE: data is not in the request since a DELETE request. - expected_delete_request = {"method": "DELETE", "path": project.path} - self.assertEqual(delete_request, expected_delete_request) - - # NOTE: data is not in the request since a GET request. - expected_get_request = {"method": "GET", "path": project.path} - self.assertEqual(get_request, expected_get_request) - - def test_undelete_without_reload_data(self): - PROJECT_ID = "project-id" - PROJECT_NUMBER = 123 - PROJECT_RESOURCE = { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "name": "Project Name", - "labels": {"env": "prod"}, - "lifecycleState": "DELETE_REQUESTED", - "parent": {"type": "organization", "id": "433637338579"}, - } - connection = _Connection(PROJECT_RESOURCE) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - project.undelete(reload_data=False) - - (request,) = connection._requested - # NOTE: data is not in the request, undelete doesn't need it. - expected_request = {"method": "POST", "path": project.path + ":undelete"} - self.assertEqual(request, expected_request) - - def test_undelete_with_reload_data(self): - PROJECT_ID = "project-id" - PROJECT_NUMBER = 123 - PROJECT_RESOURCE = { - "projectId": PROJECT_ID, - "projectNumber": PROJECT_NUMBER, - "name": "Project Name", - "labels": {"env": "prod"}, - "lifecycleState": "DELETE_REQUESTED", - "parent": {"type": "organization", "id": "433637338579"}, - } - UNDELETED_PROJECT = PROJECT_RESOURCE.copy() - UNDELETED_PROJECT["lifecycleState"] = NEW_STATE = "ACTIVE" - - connection = _Connection(PROJECT_RESOURCE, UNDELETED_PROJECT) - client = _Client(connection=connection) - project = self._make_one(PROJECT_ID, client) - project.undelete(reload_data=True) - self.assertEqual(project.status, NEW_STATE) - - undelete_request, get_request = connection._requested - # NOTE: data is not in the request, undelete doesn't need it. - expected_undelete_request = { - "method": "POST", - "path": project.path + ":undelete", - } - self.assertEqual(undelete_request, expected_undelete_request) - - # NOTE: data is not in the request since a GET request. - expected_get_request = {"method": "GET", "path": project.path} - self.assertEqual(get_request, expected_get_request) - - -class _Connection(object): - def __init__(self, *responses): - self._responses = responses - self._requested = [] - - def api_request(self, **kw): - from google.cloud.exceptions import NotFound - - self._requested.append(kw) - - try: - response, self._responses = self._responses[0], self._responses[1:] - except IndexError: - raise NotFound("miss") - else: - return response - - -class _Client(object): - def __init__(self, connection=None): - self._connection = connection